From 7bcfa88cc1988d8496b988020f10366dd145eef1 Mon Sep 17 00:00:00 2001 From: Git User Date: Fri, 16 Apr 2021 08:36:46 -0700 Subject: [PATCH 001/170] Initial empty repository From cb9d543e8acb94ea31a41cfa00f660d2d5ef1e45 Mon Sep 17 00:00:00 2001 From: Fei Mao Date: Tue, 19 Oct 2021 19:03:59 +0800 Subject: [PATCH 002/170] touch: add drivers Add all drivers for new platforms. Change-Id: Ie9947b0c6f8ddfee7dab6dfa80d6aca62323f4da Signed-off-by: Fei Mao --- focaltech_touch/focaltech_common.h | 167 + focaltech_touch/focaltech_config.h | 277 + focaltech_touch/focaltech_core.c | 3364 +++++++++ focaltech_touch/focaltech_core.h | 389 ++ focaltech_touch/focaltech_esdcheck.c | 465 ++ focaltech_touch/focaltech_ex_fun.c | 1229 ++++ focaltech_touch/focaltech_ex_mode.c | 359 + focaltech_touch/focaltech_flash.c | 2079 ++++++ focaltech_touch/focaltech_flash.h | 216 + .../focaltech_upgrade_ft3518.c | 292 + focaltech_touch/focaltech_gesture.c | 477 ++ focaltech_touch/focaltech_i2c.c | 548 ++ .../focaltech_point_report_check.c | 135 + nt36xxx/nt36xxx.c | 4601 +++++++++++++ nt36xxx/nt36xxx.h | 386 ++ nt36xxx/nt36xxx_ext_proc.c | 1537 +++++ nt36xxx/nt36xxx_fw_update.c | 2002 ++++++ nt36xxx/nt36xxx_mem_map.h | 607 ++ nt36xxx/nt36xxx_mp_ctrlram.c | 1480 ++++ nt36xxx/nt36xxx_mp_ctrlram.h | 460 ++ nt36xxx/nt36xxx_spi.c | 8 + nt36xxx/nt36xxx_spi_ext_proc.c | 8 + nt36xxx/nt36xxx_spi_fw_update.c | 8 + st/fts.c | 6066 +++++++++++++++++ st/fts.h | 396 ++ st/fts_aoi_event.c | 153 + st/fts_driver_test.c | 1107 +++ st/fts_gui.c | 412 ++ st/fts_lib/ftsCompensation.c | 744 ++ st/fts_lib/ftsCompensation.h | 207 + st/fts_lib/ftsCrossCompile.c | 77 + st/fts_lib/ftsCrossCompile.h | 75 + st/fts_lib/ftsError.c | 262 + st/fts_lib/ftsError.h | 181 + st/fts_lib/ftsFlash.c | 1178 ++++ st/fts_lib/ftsFlash.h | 109 + st/fts_lib/ftsFrame.c | 519 ++ st/fts_lib/ftsFrame.h | 76 + st/fts_lib/ftsGesture.c | 651 ++ st/fts_lib/ftsGesture.h | 123 + st/fts_lib/ftsHardware.h | 213 + st/fts_lib/ftsIO.c | 526 ++ st/fts_lib/ftsIO.h | 64 + st/fts_lib/ftsSoftware.h | 191 + st/fts_lib/ftsTest.c | 3844 +++++++++++ st/fts_lib/ftsTest.h | 193 + st/fts_lib/ftsTime.c | 109 + st/fts_lib/ftsTime.h | 54 + st/fts_lib/ftsTool.c | 1348 ++++ st/fts_lib/ftsTool.h | 107 + synaptics_dsx/synaptics_dsx_active_pen.c | 606 ++ synaptics_dsx/synaptics_dsx_core.c | 5079 ++++++++++++++ synaptics_dsx/synaptics_dsx_core.h | 536 ++ synaptics_dsx/synaptics_dsx_fw_update.c | 5797 ++++++++++++++++ synaptics_dsx/synaptics_dsx_gesture.c | 2291 +++++++ synaptics_dsx/synaptics_dsx_i2c.c | 672 ++ synaptics_dsx/synaptics_dsx_proximity.c | 673 ++ synaptics_dsx/synaptics_dsx_rmi_dev.c | 1075 +++ synaptics_dsx/synaptics_dsx_rmi_hid_i2c.c | 989 +++ synaptics_dsx/synaptics_dsx_spi.c | 698 ++ synaptics_dsx/synaptics_dsx_test_reporting.c | 5324 +++++++++++++++ synaptics_dsx/synaptics_dsx_video.c | 403 ++ 62 files changed, 64222 insertions(+) create mode 100644 focaltech_touch/focaltech_common.h create mode 100644 focaltech_touch/focaltech_config.h create mode 100644 focaltech_touch/focaltech_core.c create mode 100644 focaltech_touch/focaltech_core.h create mode 100644 focaltech_touch/focaltech_esdcheck.c create mode 100644 focaltech_touch/focaltech_ex_fun.c create mode 100644 focaltech_touch/focaltech_ex_mode.c create mode 100644 focaltech_touch/focaltech_flash.c create mode 100644 focaltech_touch/focaltech_flash.h create mode 100644 focaltech_touch/focaltech_flash/focaltech_upgrade_ft3518.c create mode 100644 focaltech_touch/focaltech_gesture.c create mode 100644 focaltech_touch/focaltech_i2c.c create mode 100644 focaltech_touch/focaltech_point_report_check.c create mode 100644 nt36xxx/nt36xxx.c create mode 100644 nt36xxx/nt36xxx.h create mode 100644 nt36xxx/nt36xxx_ext_proc.c create mode 100644 nt36xxx/nt36xxx_fw_update.c create mode 100644 nt36xxx/nt36xxx_mem_map.h create mode 100644 nt36xxx/nt36xxx_mp_ctrlram.c create mode 100644 nt36xxx/nt36xxx_mp_ctrlram.h create mode 100644 nt36xxx/nt36xxx_spi.c create mode 100644 nt36xxx/nt36xxx_spi_ext_proc.c create mode 100644 nt36xxx/nt36xxx_spi_fw_update.c create mode 100644 st/fts.c create mode 100644 st/fts.h create mode 100644 st/fts_aoi_event.c create mode 100644 st/fts_driver_test.c create mode 100644 st/fts_gui.c create mode 100644 st/fts_lib/ftsCompensation.c create mode 100644 st/fts_lib/ftsCompensation.h create mode 100644 st/fts_lib/ftsCrossCompile.c create mode 100644 st/fts_lib/ftsCrossCompile.h create mode 100644 st/fts_lib/ftsError.c create mode 100644 st/fts_lib/ftsError.h create mode 100644 st/fts_lib/ftsFlash.c create mode 100644 st/fts_lib/ftsFlash.h create mode 100644 st/fts_lib/ftsFrame.c create mode 100644 st/fts_lib/ftsFrame.h create mode 100644 st/fts_lib/ftsGesture.c create mode 100644 st/fts_lib/ftsGesture.h create mode 100644 st/fts_lib/ftsHardware.h create mode 100644 st/fts_lib/ftsIO.c create mode 100644 st/fts_lib/ftsIO.h create mode 100644 st/fts_lib/ftsSoftware.h create mode 100644 st/fts_lib/ftsTest.c create mode 100644 st/fts_lib/ftsTest.h create mode 100644 st/fts_lib/ftsTime.c create mode 100644 st/fts_lib/ftsTime.h create mode 100644 st/fts_lib/ftsTool.c create mode 100644 st/fts_lib/ftsTool.h create mode 100644 synaptics_dsx/synaptics_dsx_active_pen.c create mode 100644 synaptics_dsx/synaptics_dsx_core.c create mode 100644 synaptics_dsx/synaptics_dsx_core.h create mode 100644 synaptics_dsx/synaptics_dsx_fw_update.c create mode 100644 synaptics_dsx/synaptics_dsx_gesture.c create mode 100644 synaptics_dsx/synaptics_dsx_i2c.c create mode 100644 synaptics_dsx/synaptics_dsx_proximity.c create mode 100644 synaptics_dsx/synaptics_dsx_rmi_dev.c create mode 100644 synaptics_dsx/synaptics_dsx_rmi_hid_i2c.c create mode 100644 synaptics_dsx/synaptics_dsx_spi.c create mode 100644 synaptics_dsx/synaptics_dsx_test_reporting.c create mode 100644 synaptics_dsx/synaptics_dsx_video.c diff --git a/focaltech_touch/focaltech_common.h b/focaltech_touch/focaltech_common.h new file mode 100644 index 0000000000..c9e93e1875 --- /dev/null +++ b/focaltech_touch/focaltech_common.h @@ -0,0 +1,167 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +/***************************************************************************** +* +* File Name: focaltech_common.h +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-16 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +#ifndef __LINUX_FOCALTECH_COMMON_H__ +#define __LINUX_FOCALTECH_COMMON_H__ + +#include "focaltech_config.h" + +/***************************************************************************** +* Macro definitions using #define +*****************************************************************************/ +#define FTS_DRIVER_VERSION "Focaltech V3.2 20200422" + +#define BYTE_OFF_0(x) (u8)((x) & 0xFF) +#define BYTE_OFF_8(x) (u8)(((x) >> 8) & 0xFF) +#define BYTE_OFF_16(x) (u8)(((x) >> 16) & 0xFF) +#define BYTE_OFF_24(x) (u8)(((x) >> 24) & 0xFF) +#define FLAGBIT(x) (0x00000001 << (x)) +#define FLAGBITS(x, y) ((0xFFFFFFFF >> (32 - (y) - 1)) & (0xFFFFFFFF << (x))) + +#define FLAG_ICSERIALS_LEN 8 +#define FLAG_HID_BIT 10 +#define FLAG_IDC_BIT 11 + +#define IC_SERIALS(type) ((type) & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define IC_TO_SERIALS(x) ((x) & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define FTS_CHIP_IDC(type) (((type) & FLAGBIT(FLAG_IDC_BIT)) == FLAGBIT(FLAG_IDC_BIT)) +#define FTS_HID_SUPPORTTED(type) (((type) & FLAGBIT(FLAG_HID_BIT)) == FLAGBIT(FLAG_HID_BIT)) + +#define FILE_NAME_LENGTH 128 +#define ENABLE 1 +#define DISABLE 0 +#define VALID 1 +#define INVALID 0 +#define FTS_CMD_START1 0x55 +#define FTS_CMD_START2 0xAA +#define FTS_CMD_START_DELAY 12 +#define FTS_CMD_READ_ID 0x90 +#define FTS_CMD_READ_ID_LEN 4 +#define FTS_CMD_READ_ID_LEN_INCELL 1 +#define FTS_CMD_READ_FW_CONF 0xA8 +/*register address*/ +#define FTS_REG_INT_CNT 0x8F +#define FTS_REG_FLOW_WORK_CNT 0x91 +#define FTS_REG_WORKMODE 0x00 +#define FTS_REG_WORKMODE_FACTORY_VALUE 0x40 +#define FTS_REG_WORKMODE_WORK_VALUE 0x00 +#define FTS_REG_ESDCHECK_DISABLE 0x8D +#define FTS_REG_CHIP_ID 0xA3 +#define FTS_REG_CHIP_ID2 0x9F +#define FTS_REG_POWER_MODE 0xA5 +#define FTS_REG_POWER_MODE_SLEEP 0x03 +#define FTS_REG_FW_VER 0xA6 +#define FTS_REG_VENDOR_ID 0xA8 +#define FTS_REG_LCD_BUSY_NUM 0xAB +#define FTS_REG_FACE_DEC_MODE_EN 0xB0 +#define FTS_REG_FACTORY_MODE_DETACH_FLAG 0xB4 +#define FTS_REG_FACE_DEC_MODE_STATUS 0x01 +#define FTS_REG_IDE_PARA_VER_ID 0xB5 +#define FTS_REG_IDE_PARA_STATUS 0xB6 +#define FTS_REG_GLOVE_MODE_EN 0xC0 +#define FTS_REG_COVER_MODE_EN 0xC1 +#define FTS_REG_REPORT_RATE 0x88 +#define FTS_REG_CHARGER_MODE_EN 0x8B +#define FTS_REG_GESTURE_EN 0xD0 +#define FTS_REG_GESTURE_OUTPUT_ADDRESS 0xD3 +#define FTS_REG_MODULE_ID 0xE3 +#define FTS_REG_LIC_VER 0xE4 +#define FTS_REG_ESD_SATURATE 0xED + +#define FTS_SYSFS_ECHO_ON(buf) (buf[0] == '1') +#define FTS_SYSFS_ECHO_OFF(buf) (buf[0] == '0') + +#define kfree_safe(pbuf) do {\ + if (pbuf) {\ + kfree(pbuf);\ + pbuf = NULL;\ + }\ +} while(0) + +/***************************************************************************** +* Alternative mode (When something goes wrong, +* the modules may be able to solve the problem.) +*****************************************************************************/ +/* + * point report check + * default: disable + */ +#define FTS_POINT_REPORT_CHECK_EN 0 + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +struct ft_chip_t { + u64 type; + u8 chip_idh; + u8 chip_idl; + u8 rom_idh; + u8 rom_idl; + u8 pb_idh; + u8 pb_idl; + u8 bl_idh; + u8 bl_idl; +}; + +struct ts_ic_info { + bool is_incell; + bool hid_supported; + struct ft_chip_t ids; +}; + +/***************************************************************************** +* DEBUG function define here +*****************************************************************************/ +#if FTS_DEBUG_EN +#define FTS_DEBUG(fmt, args...) do { \ + printk("[FTS_TS]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_FUNC_ENTER() do { \ + printk("[FTS_TS]%s: Enter\n", __func__); \ +} while (0) + +#define FTS_FUNC_EXIT() do { \ + printk("[FTS_TS]%s: Exit(%d)\n", __func__, __LINE__); \ +} while (0) +#else /* #if FTS_DEBUG_EN*/ +#define FTS_DEBUG(fmt, args...) +#define FTS_FUNC_ENTER() +#define FTS_FUNC_EXIT() +#endif + +#define FTS_INFO(fmt, args...) do { \ + printk(KERN_INFO "[FTS_TS/I]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_ERROR(fmt, args...) do { \ + printk(KERN_ERR "[FTS_TS/E]%s:"fmt"\n", __func__, ##args); \ +} while (0) +#endif /* __LINUX_FOCALTECH_COMMON_H__ */ diff --git a/focaltech_touch/focaltech_config.h b/focaltech_touch/focaltech_config.h new file mode 100644 index 0000000000..c2b68bcbab --- /dev/null +++ b/focaltech_touch/focaltech_config.h @@ -0,0 +1,277 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/************************************************************************ +* +* File Name: focaltech_config.h +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: global configurations +* +* Version: v1.0 +* +************************************************************************/ +#ifndef _LINUX_FOCLATECH_CONFIG_H_ +#define _LINUX_FOCLATECH_CONFIG_H_ + +/**************************************************/ +/****** G: A, I: B, S: C, U: D ******************/ +/****** chip type defines, do not modify *********/ +#define _FT8716 0x87160805 +#define _FT8736 0x87360806 +#define _FT8006M 0x80060807 +#define _FT8607 0x86070809 +#define _FT8006U 0x8006D80B +#define _FT8006S 0x8006A80B +#define _FT8613 0x8613080C +#define _FT8719 0x8719080D +#define _FT8739 0x8739080E +#define _FT8615 0x8615080F +#define _FT8201 0x82010810 +#define _FT8006P 0x86220811 +#define _FT7251 0x72510812 +#define _FT7252 0x72520813 +#define _FT8613S 0x8613C814 +#define _FT8756 0x87560815 +#define _FT8302 0x83020816 +#define _FT8009 0x80090817 +#define _FT8656 0x86560818 +#define _FT8006S_AA 0x86320819 +#define _FT7250 0x7250081A +#define _FT7120 0x7120081B +#define _FT8720 0x8720081C +#define _FT8016 0x8016081D + + +#define _FT5416 0x54160402 +#define _FT5426 0x54260402 +#define _FT5435 0x54350402 +#define _FT5436 0x54360402 +#define _FT5526 0x55260402 +#define _FT5526I 0x5526B402 +#define _FT5446 0x54460402 +#define _FT5346 0x53460402 +#define _FT5446I 0x5446B402 +#define _FT5346I 0x5346B402 +#define _FT7661 0x76610402 +#define _FT7511 0x75110402 +#define _FT7421 0x74210402 +#define _FT7681 0x76810402 +#define _FT3C47U 0x3C47D402 +#define _FT3417 0x34170402 +#define _FT3517 0x35170402 +#define _FT3327 0x33270402 +#define _FT3427 0x34270402 +#define _FT7311 0x73110402 +#define _FT5526_V00 0x5526C402 + +#define _FT5626 0x56260401 +#define _FT5726 0x57260401 +#define _FT5826B 0x5826B401 +#define _FT5826S 0x5826C401 +#define _FT7811 0x78110401 +#define _FT3D47 0x3D470401 +#define _FT3617 0x36170401 +#define _FT3717 0x37170401 +#define _FT3817B 0x3817B401 +#define _FT3517U 0x3517D401 + +#define _FT6236U 0x6236D003 +#define _FT6336G 0x6336A003 +#define _FT6336U 0x6336D003 +#define _FT6436U 0x6436D003 +#define _FT6436T 0x6436E003 + +#define _FT3267 0x32670004 +#define _FT3367 0x33670004 + +#define _FT3327DQQ_XXX 0x3327D482 +#define _FT5446DQS_XXX 0x5446D482 + +#define _FT3427_003 0x3427D482 +#define _FT3427G_003 0x3427A482 +#define _FT5446_003 0x5446D482 +#define _FT5446_Q03 0x5446C482 +#define _FT5446_P03 0x5446A481 +#define _FT5426_003 0x5426D482 +#define _FT5526_003 0x5526D482 + +#define _FT3518 0x35180481 +#define _FT3518U 0x3518D481 +#define _FT3558 0x35580481 +#define _FT3528 0x35280481 +#define _FT5536 0x55360481 +#define _FT5536L 0x5536E481 +#define _FT3418 0x34180481 + +#define _FT5446U 0x5446D083 +#define _FT5456U 0x5456D083 +#define _FT3417U 0x3417D083 +#define _FT5426U 0x5426D083 +#define _FT3428 0x34280083 +#define _FT3437U 0x3437D083 + +#define _FT7302 0x73020084 +#define _FT7202 0x72020084 +#define _FT3308 0x33080084 +#define _FT6446 0x64460084 + +#define _FT6346U 0x6346D085 +#define _FT6346G 0x6346A085 +#define _FT3067 0x30670085 +#define _FT3068 0x30680085 +#define _FT3168 0x31680085 +#define _FT3268 0x32680085 +#define _FT6146 0x61460085 + +#define _FT5726_003 0x5726D486 +#define _FT5726_V03 0x5726C486 + +#define _FT3618 0x36180487 +#define _FT5646 0x56460487 +#define _FT3A58 0x3A580487 +#define _FT3B58 0x3B580487 +#define _FT3D58 0x3D580487 +#define _FT5936 0x59360487 +#define _FT5A36 0x5A360487 +#define _FT5B36 0x5B360487 +#define _FT5D36 0x5D360487 +#define _FT5946 0x59460487 +#define _FT5A46 0x5A460487 +#define _FT5B46 0x5B460487 +#define _FT5D46 0x5D460487 + +#define _FT3658U 0x3658D488 + +/******************* Enables *********************/ +/*********** 1 to enable, 0 to disable ***********/ + +/* + * show debug log info + * enable it for debug, disable it for release + */ +#define FTS_DEBUG_EN 0 + +/* + * Linux MultiTouch Protocol + * 1: Protocol B(default), 0: Protocol A + */ +#define FTS_MT_PROTOCOL_B_EN 1 + +/* + * Report Pressure in multitouch + * 1:enable(default),0:disable +*/ +#define FTS_REPORT_PRESSURE_EN 1 + +/* + * Gesture function enable + * default: disable + */ +#define FTS_GESTURE_EN 0 + +/* + * ESD check & protection + * default: disable + */ +#define FTS_ESDCHECK_EN 0 + +/* + * Production test enable + * 1: enable, 0:disable(default) + */ +#define FTS_TEST_EN 0 + +/* + * Pinctrl enable + * default: disable + */ +#define FTS_PINCTRL_EN 1 + +/* + * Customer power enable + * enable it when customer need control TP power + * default: disable + */ +#define FTS_POWER_SOURCE_CUST_EN 1 + +/****************************************************/ + +/********************** Upgrade ****************************/ +/* + * auto upgrade + */ +#define FTS_AUTO_UPGRADE_EN 1 + +/* + * auto upgrade for lcd cfg + */ +#define FTS_AUTO_LIC_UPGRADE_EN 0 + +/* + * Numbers of modules support + */ +#define FTS_GET_MODULE_NUM 0 + +/* + * module_id: mean vendor_id generally, also maybe gpio or lcm_id... + * If means vendor_id, the FTS_MODULE_ID = PANEL_ID << 8 + VENDOR_ID + * FTS_GET_MODULE_NUM == 0/1, no check module id, you may ignore them + * FTS_GET_MODULE_NUM >= 2, compatible with FTS_MODULE2_ID + * FTS_GET_MODULE_NUM >= 3, compatible with FTS_MODULE3_ID + */ +#define FTS_MODULE_ID 0x0000 +#define FTS_MODULE2_ID 0x0000 +#define FTS_MODULE3_ID 0x0000 + +/* + * Need set the following when get firmware via firmware_request() + * For example: if module'vendor is tianma, + * #define FTS_MODULE_NAME "tianma" + * then file_name will be "focaltech_ts_fw_tianma" + * You should rename fw to "focaltech_ts_fw_tianma", and push it into + * etc/firmware or by customers + */ +#define FTS_MODULE_NAME "gvo" +#define FTS_MODULE2_NAME "" +#define FTS_MODULE3_NAME "" + +/* + * FW.i file for auto upgrade, you must replace it with your own + * define your own fw_file, the sample one to be replaced is invalid + * NOTE: if FTS_GET_MODULE_NUM > 1, it's the fw corresponding with FTS_VENDOR_ID + */ +#define FTS_UPGRADE_FW_FILE "include/firmware/fw_sample.i" + +/* + * if FTS_GET_MODULE_NUM >= 2, fw corrsponding with FTS_VENDOR_ID2 + * define your own fw_file, the sample one is invalid + */ +#define FTS_UPGRADE_FW2_FILE "include/firmware/fw_sample.i" + +/* + * if FTS_GET_MODULE_NUM >= 3, fw corrsponding with FTS_VENDOR_ID3 + * define your own fw_file, the sample one is invalid + */ +#define FTS_UPGRADE_FW3_FILE "include/firmware/fw_sample.i" + +/*********************************************************/ + +#endif /* _LINUX_FOCLATECH_CONFIG_H_ */ diff --git a/focaltech_touch/focaltech_core.c b/focaltech_touch/focaltech_core.c new file mode 100644 index 0000000000..9abd6cecaf --- /dev/null +++ b/focaltech_touch/focaltech_core.c @@ -0,0 +1,3364 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +/***************************************************************************** +* +* File Name: focaltech_core.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: entrance for focaltech ts driver +* +* Version: V1.0 +* +*****************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_DRM) +#include +#elif defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#define FTS_SUSPEND_LEVEL 1 /* Early-suspend level */ +#endif +#include "focaltech_core.h" + +#if defined(CONFIG_FTS_TRUSTED_TOUCH) +#include +#include +#include +#include +#include +#include +#include +#include +#include "linux/gunyah/gh_msgq.h" +#include "linux/gunyah/gh_rm_drv.h" +#include +#include +#endif + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_DRIVER_NAME "fts_ts" +#define INTERVAL_READ_REG 200 /* unit:ms */ +#define TIMEOUT_READ_REG 1000 /* unit:ms */ +#if FTS_POWER_SOURCE_CUST_EN +#define FTS_VTG_MIN_UV 3000000 +#define FTS_VTG_MAX_UV 3300000 +#define FTS_LOAD_MAX_UA 30000 +#define FTS_LOAD_AVDD_UA 10000 +#define FTS_LOAD_DISABLE_UA 0 +#define FTS_I2C_VTG_MIN_UV 1800000 +#define FTS_I2C_VTG_MAX_UV 1800000 +#endif + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +struct fts_ts_data *fts_data; + +#if defined(CONFIG_DRM) +static struct drm_panel *active_panel; +static void fts_ts_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *event, void *client_data); +#endif + +static struct ft_chip_t ctype[] = { + {0x88, 0x56, 0x52, 0x00, 0x00, 0x00, 0x00, 0x56, 0xB2}, + {0x81, 0x54, 0x52, 0x54, 0x52, 0x00, 0x00, 0x54, 0x5C}, +}; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static int fts_ts_suspend(struct device *dev); +static int fts_ts_resume(struct device *dev); +static irqreturn_t fts_irq_handler(int irq, void *data); +static int fts_ts_probe_delayed(struct fts_ts_data *fts_data); +static int fts_ts_enable_reg(struct fts_ts_data *ts_data, bool enable); + +static void fts_ts_register_for_panel_events(struct device_node *dp, + struct fts_ts_data *ts_data) +{ + const char *touch_type; + int rc = 0; + void *cookie = NULL; + + rc = of_property_read_string(dp, "focaltech,touch-type", + &touch_type); + if (rc) { + dev_warn(&fts_data->client->dev, + "%s: No touch type\n", __func__); + return; + } + if (strcmp(touch_type, "primary")) { + pr_err("Invalid touch type\n"); + return; + } + + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, active_panel, + &fts_ts_panel_notifier_callback, ts_data); + if (!cookie) { + pr_err("Failed to register for panel events\n"); + return; + } + + FTS_DEBUG("registered for panel notifications panel: 0x%x\n", + active_panel); + + ts_data->notifier_cookie = cookie; +} + +#ifdef CONFIG_FTS_TRUSTED_TOUCH + +static void fts_ts_trusted_touch_abort_handler(struct fts_ts_data *fts_data, + int error); +static struct gh_acl_desc *fts_ts_vm_get_acl(enum gh_vm_names vm_name) +{ + struct gh_acl_desc *acl_desc; + gh_vmid_t vmid; + + gh_rm_get_vmid(vm_name, &vmid); + + acl_desc = kzalloc(offsetof(struct gh_acl_desc, acl_entries[1]), + GFP_KERNEL); + if (!acl_desc) + return ERR_PTR(ENOMEM); + + acl_desc->n_acl_entries = 1; + acl_desc->acl_entries[0].vmid = vmid; + acl_desc->acl_entries[0].perms = GH_RM_ACL_R | GH_RM_ACL_W; + + return acl_desc; +} + +static struct gh_sgl_desc *fts_ts_vm_get_sgl( + struct trusted_touch_vm_info *vm_info) +{ + struct gh_sgl_desc *sgl_desc; + int i; + + sgl_desc = kzalloc(offsetof(struct gh_sgl_desc, + sgl_entries[vm_info->iomem_list_size]), GFP_KERNEL); + if (!sgl_desc) + return ERR_PTR(ENOMEM); + + sgl_desc->n_sgl_entries = vm_info->iomem_list_size; + + for (i = 0; i < vm_info->iomem_list_size; i++) { + sgl_desc->sgl_entries[i].ipa_base = vm_info->iomem_bases[i]; + sgl_desc->sgl_entries[i].size = vm_info->iomem_sizes[i]; + } + + return sgl_desc; +} + +static int fts_ts_populate_vm_info_iomem(struct fts_ts_data *fts_data) +{ + int i, gpio, rc = 0; + int num_regs, num_sizes, num_gpios, list_size; + struct resource res; + struct device_node *np = fts_data->dev->of_node; + struct trusted_touch_vm_info *vm_info = fts_data->vm_info; + + num_regs = of_property_count_u32_elems(np, "focaltech,trusted-touch-io-bases"); + if (num_regs < 0) { + FTS_ERROR("Invalid number of IO regions specified\n"); + return -EINVAL; + } + + num_sizes = of_property_count_u32_elems(np, "focaltech,trusted-touch-io-sizes"); + if (num_sizes < 0) { + FTS_ERROR("Invalid number of IO regions specified\n"); + return -EINVAL; + } + + if (num_regs != num_sizes) { + FTS_ERROR("IO bases and sizes array lengths mismatch\n"); + return -EINVAL; + } + + num_gpios = of_gpio_named_count(np, "focaltech,trusted-touch-vm-gpio-list"); + if (num_gpios < 0) { + dev_warn(fts_data->dev, "Ignoring invalid trusted gpio list: %d\n", num_gpios); + num_gpios = 0; + } + + list_size = num_regs + num_gpios; + vm_info->iomem_list_size = list_size; + vm_info->iomem_bases = devm_kcalloc(fts_data->dev, list_size, sizeof(*vm_info->iomem_bases), + GFP_KERNEL); + if (!vm_info->iomem_bases) + return -ENOMEM; + + vm_info->iomem_sizes = devm_kcalloc(fts_data->dev, list_size, sizeof(*vm_info->iomem_sizes), + GFP_KERNEL); + if (!vm_info->iomem_sizes) + return -ENOMEM; + + for (i = 0; i < num_gpios; ++i) { + gpio = of_get_named_gpio(np, "focaltech,trusted-touch-vm-gpio-list", i); + if (gpio < 0 || !gpio_is_valid(gpio)) { + FTS_ERROR("Invalid gpio %d at position %d\n", gpio, i); + return gpio; + } + + if (!msm_gpio_get_pin_address(gpio, &res)) { + FTS_ERROR("Failed to retrieve gpio-%d resource\n", gpio); + return -ENODATA; + } + + vm_info->iomem_bases[i] = res.start; + vm_info->iomem_sizes[i] = resource_size(&res); + } + + rc = of_property_read_u32_array(np, "focaltech,trusted-touch-io-bases", + &vm_info->iomem_bases[i], list_size - i); + if (rc) { + FTS_ERROR("Failed to read trusted touch io bases:%d\n", rc); + return rc; + } + + rc = of_property_read_u32_array(np, "focaltech,trusted-touch-io-sizes", + &vm_info->iomem_sizes[i], list_size - i); + if (rc) { + FTS_ERROR("Failed to read trusted touch io sizes:%d\n", rc); + return rc; + } + + return 0; +} + +static int fts_ts_populate_vm_info(struct fts_ts_data *fts_data) +{ + int rc; + struct trusted_touch_vm_info *vm_info; + struct device_node *np = fts_data->dev->of_node; + + vm_info = devm_kzalloc(fts_data->dev, sizeof(struct trusted_touch_vm_info), GFP_KERNEL); + if (!vm_info) + return -ENOMEM; + + fts_data->vm_info = vm_info; + vm_info->vm_name = GH_TRUSTED_VM; + rc = of_property_read_u32(np, "focaltech,trusted-touch-spi-irq", &vm_info->hw_irq); + if (rc) { + pr_err("Failed to read trusted touch SPI irq:%d\n", rc); + return rc; + } + + rc = fts_ts_populate_vm_info_iomem(fts_data); + if (rc) { + pr_err("Failed to read trusted touch mmio ranges:%d\n", rc); + return rc; + } + + rc = of_property_read_string(np, "focaltech,trusted-touch-type", + &vm_info->trusted_touch_type); + if (rc) { + pr_warn("%s: No trusted touch type selection made\n", __func__); + vm_info->mem_tag = GH_MEM_NOTIFIER_TAG_TOUCH_PRIMARY; + vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH_PRIMARY; + rc = 0; + } else if (!strcmp(vm_info->trusted_touch_type, "primary")) { + vm_info->mem_tag = GH_MEM_NOTIFIER_TAG_TOUCH_PRIMARY; + vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH_PRIMARY; + } else if (!strcmp(vm_info->trusted_touch_type, "secondary")) { + vm_info->mem_tag = GH_MEM_NOTIFIER_TAG_TOUCH_SECONDARY; + vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH_SECONDARY; + } + + return 0; +} + +static void fts_ts_destroy_vm_info(struct fts_ts_data *fts_data) +{ + kfree(fts_data->vm_info->iomem_sizes); + kfree(fts_data->vm_info->iomem_bases); + kfree(fts_data->vm_info); +} + +static void fts_ts_vm_deinit(struct fts_ts_data *fts_data) +{ + if (fts_data->vm_info->mem_cookie) + gh_mem_notifier_unregister(fts_data->vm_info->mem_cookie); + fts_ts_destroy_vm_info(fts_data); +} + +static int fts_ts_trusted_touch_get_vm_state(struct fts_ts_data *fts_data) +{ + return atomic_read(&fts_data->vm_info->vm_state); +} + +static void fts_ts_trusted_touch_set_vm_state(struct fts_ts_data *fts_data, + int state) +{ + atomic_set(&fts_data->vm_info->vm_state, state); +} + +#ifdef CONFIG_ARCH_QTI_VM +static int fts_ts_vm_mem_release(struct fts_ts_data *fts_data); +static void fts_ts_trusted_touch_tvm_vm_mode_disable(struct fts_ts_data *fts_data); +static void fts_ts_trusted_touch_abort_tvm(struct fts_ts_data *fts_data); +static void fts_ts_trusted_touch_event_notify(struct fts_ts_data *fts_data, int event); + +void fts_ts_trusted_touch_tvm_i2c_failure_report(struct fts_ts_data *fts_data) +{ + pr_err("initiating trusted touch abort due to i2c failure\n"); + fts_ts_trusted_touch_abort_handler(fts_data, + TRUSTED_TOUCH_EVENT_I2C_FAILURE); +} + +static void fts_ts_trusted_touch_reset_gpio_toggle(struct fts_ts_data *fts_data) +{ + void __iomem *base; + + if (fts_data->bus_type != BUS_TYPE_I2C) + return; + + base = ioremap(TOUCH_RESET_GPIO_BASE, TOUCH_RESET_GPIO_SIZE); + writel_relaxed(0x1, base + TOUCH_RESET_GPIO_OFFSET); + /* wait until toggle to finish*/ + wmb(); + writel_relaxed(0x0, base + TOUCH_RESET_GPIO_OFFSET); + /* wait until toggle to finish*/ + wmb(); + iounmap(base); +} + +static void fts_trusted_touch_intr_gpio_toggle(struct fts_ts_data *fts_data, + bool enable) +{ + void __iomem *base; + u32 val; + + if (fts_data->bus_type != BUS_TYPE_I2C) + return; + + base = ioremap(TOUCH_INTR_GPIO_BASE, TOUCH_INTR_GPIO_SIZE); + val = readl_relaxed(base + TOUCH_RESET_GPIO_OFFSET); + if (enable) { + val |= BIT(0); + writel_relaxed(val, base + TOUCH_INTR_GPIO_OFFSET); + /* wait until toggle to finish*/ + wmb(); + } else { + val &= ~BIT(0); + writel_relaxed(val, base + TOUCH_INTR_GPIO_OFFSET); + /* wait until toggle to finish*/ + wmb(); + } + iounmap(base); +} + +static int fts_ts_sgl_cmp(const void *a, const void *b) +{ + struct gh_sgl_entry *left = (struct gh_sgl_entry *)a; + struct gh_sgl_entry *right = (struct gh_sgl_entry *)b; + + return (left->ipa_base - right->ipa_base); +} + +static int fts_ts_vm_compare_sgl_desc(struct gh_sgl_desc *expected, + struct gh_sgl_desc *received) +{ + int idx; + + if (expected->n_sgl_entries != received->n_sgl_entries) + return -E2BIG; + sort(received->sgl_entries, received->n_sgl_entries, + sizeof(received->sgl_entries[0]), fts_ts_sgl_cmp, NULL); + sort(expected->sgl_entries, expected->n_sgl_entries, + sizeof(expected->sgl_entries[0]), fts_ts_sgl_cmp, NULL); + + for (idx = 0; idx < expected->n_sgl_entries; idx++) { + struct gh_sgl_entry *left = &expected->sgl_entries[idx]; + struct gh_sgl_entry *right = &received->sgl_entries[idx]; + + if ((left->ipa_base != right->ipa_base) || + (left->size != right->size)) { + pr_err("sgl mismatch: left_base:%d right base:%d left size:%d right size:%d\n", + left->ipa_base, right->ipa_base, + left->size, right->size); + return -EINVAL; + } + } + return 0; +} + +static int fts_ts_vm_handle_vm_hardware(struct fts_ts_data *fts_data) +{ + int rc = 0; + + if (atomic_read(&fts_data->delayed_vm_probe_pending)) { + rc = fts_ts_probe_delayed(fts_data); + if (rc) { + pr_err(" Delayed probe failure on VM!\n"); + return rc; + } + atomic_set(&fts_data->delayed_vm_probe_pending, 0); + return rc; + } + + fts_irq_enable(); + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_INTERRUPT_ENABLED); + return rc; +} + +static void fts_ts_trusted_touch_tvm_vm_mode_enable(struct fts_ts_data *fts_data) +{ + + struct gh_sgl_desc *sgl_desc, *expected_sgl_desc; + struct gh_acl_desc *acl_desc; + struct irq_data *irq_data; + int rc = 0; + int irq = 0; + + if (fts_ts_trusted_touch_get_vm_state(fts_data) != TVM_ALL_RESOURCES_LENT_NOTIFIED) { + pr_err("All lend notifications not received\n"); + fts_ts_trusted_touch_event_notify(fts_data, + TRUSTED_TOUCH_EVENT_NOTIFICATIONS_PENDING); + return; + } + + acl_desc = fts_ts_vm_get_acl(GH_TRUSTED_VM); + if (IS_ERR(acl_desc)) { + pr_err("failed to populated acl data:rc=%d\n", + PTR_ERR(acl_desc)); + goto accept_fail; + } + + sgl_desc = gh_rm_mem_accept(fts_data->vm_info->vm_mem_handle, + GH_RM_MEM_TYPE_IO, + GH_RM_TRANS_TYPE_LEND, + GH_RM_MEM_ACCEPT_VALIDATE_ACL_ATTRS | + GH_RM_MEM_ACCEPT_VALIDATE_LABEL | + GH_RM_MEM_ACCEPT_DONE, TRUSTED_TOUCH_MEM_LABEL, + acl_desc, NULL, NULL, 0); + if (IS_ERR_OR_NULL(sgl_desc)) { + pr_err("failed to do mem accept :rc=%d\n", + PTR_ERR(sgl_desc)); + goto acl_fail; + } + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IOMEM_ACCEPTED); + + /* Initiate session on tvm */ + if (fts_data->bus_type == BUS_TYPE_I2C) + rc = pm_runtime_get_sync(fts_data->client->adapter->dev.parent); + else + rc = pm_runtime_get_sync(fts_data->spi->master->dev.parent); + + if (rc < 0) { + pr_err("failed to get sync rc:%d\n", rc); + goto acl_fail; + } + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_I2C_SESSION_ACQUIRED); + + expected_sgl_desc = fts_ts_vm_get_sgl(fts_data->vm_info); + if (fts_ts_vm_compare_sgl_desc(expected_sgl_desc, sgl_desc)) { + pr_err("IO sg list does not match\n"); + goto sgl_cmp_fail; + } + + kfree(expected_sgl_desc); + kfree(acl_desc); + + irq = gh_irq_accept(fts_data->vm_info->irq_label, -1, IRQ_TYPE_EDGE_RISING); + fts_trusted_touch_intr_gpio_toggle(fts_data, false); + if (irq < 0) { + pr_err("failed to accept irq\n"); + goto accept_fail; + } + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IRQ_ACCEPTED); + + + irq_data = irq_get_irq_data(irq); + if (!irq_data) { + pr_err("Invalid irq data for trusted touch\n"); + goto accept_fail; + } + if (!irq_data->hwirq) { + pr_err("Invalid irq in irq data\n"); + goto accept_fail; + } + if (irq_data->hwirq != fts_data->vm_info->hw_irq) { + pr_err("Invalid irq lent\n"); + goto accept_fail; + } + + pr_debug("irq:returned from accept:%d\n", irq); + fts_data->irq = irq; + + rc = fts_ts_vm_handle_vm_hardware(fts_data); + if (rc) { + pr_err(" Delayed probe failure on VM!\n"); + goto accept_fail; + } + atomic_set(&fts_data->trusted_touch_enabled, 1); + pr_info("trusted touch enabled\n"); + + return; +sgl_cmp_fail: + kfree(expected_sgl_desc); +acl_fail: + kfree(acl_desc); +accept_fail: + fts_ts_trusted_touch_abort_handler(fts_data, + TRUSTED_TOUCH_EVENT_ACCEPT_FAILURE); +} +static void fts_ts_vm_irq_on_lend_callback(void *data, + unsigned long notif_type, + enum gh_irq_label label) +{ + struct fts_ts_data *fts_data = data; + + pr_debug("received irq lend request for label:%d\n", label); + if (fts_ts_trusted_touch_get_vm_state(fts_data) == TVM_IOMEM_LENT_NOTIFIED) + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_ALL_RESOURCES_LENT_NOTIFIED); + else + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IRQ_LENT_NOTIFIED); +} + +static void fts_ts_vm_mem_on_lend_handler(enum gh_mem_notifier_tag tag, + unsigned long notif_type, void *entry_data, void *notif_msg) +{ + struct gh_rm_notif_mem_shared_payload *payload; + struct trusted_touch_vm_info *vm_info; + struct fts_ts_data *fts_data; + + fts_data = (struct fts_ts_data *)entry_data; + vm_info = fts_data->vm_info; + if (!vm_info) { + pr_err("Invalid vm_info\n"); + return; + } + + if (notif_type != GH_RM_NOTIF_MEM_SHARED || + tag != vm_info->mem_tag) { + pr_err("Invalid command passed from rm\n"); + return; + } + + if (!entry_data || !notif_msg) { + pr_err("Invalid entry data passed from rm\n"); + return; + } + + + payload = (struct gh_rm_notif_mem_shared_payload *)notif_msg; + if (payload->trans_type != GH_RM_TRANS_TYPE_LEND || + payload->label != TRUSTED_TOUCH_MEM_LABEL) { + pr_err("Invalid label or transaction type\n"); + return; + } + + vm_info->vm_mem_handle = payload->mem_handle; + pr_debug("received mem lend request with handle:%d\n", + vm_info->vm_mem_handle); + if (fts_ts_trusted_touch_get_vm_state(fts_data) == TVM_IRQ_LENT_NOTIFIED) + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_ALL_RESOURCES_LENT_NOTIFIED); + else + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IOMEM_LENT_NOTIFIED); +} + +static int fts_ts_vm_mem_release(struct fts_ts_data *fts_data) +{ + int rc = 0; + + if (!fts_data->vm_info->vm_mem_handle) { + pr_err("Invalid memory handle\n"); + return -EINVAL; + } + + rc = gh_rm_mem_release(fts_data->vm_info->vm_mem_handle, 0); + if (rc) + pr_err("VM mem release failed: rc=%d\n", rc); + + rc = gh_rm_mem_notify(fts_data->vm_info->vm_mem_handle, + GH_RM_MEM_NOTIFY_OWNER_RELEASED, + fts_data->vm_info->mem_tag, 0); + if (rc) + pr_err("Failed to notify mem release to PVM: rc=%d\n"); + pr_debug("vm mem release succeded\n"); + + fts_data->vm_info->vm_mem_handle = 0; + return rc; +} + +static void fts_ts_trusted_touch_tvm_vm_mode_disable(struct fts_ts_data *fts_data) +{ + int rc = 0; + + if (atomic_read(&fts_data->trusted_touch_abort_status)) { + fts_ts_trusted_touch_abort_tvm(fts_data); + return; + } + + fts_irq_disable(); + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_INTERRUPT_DISABLED); + + rc = gh_irq_release(fts_data->vm_info->irq_label); + if (rc) { + pr_err("Failed to release irq rc:%d\n", rc); + goto error; + } else { + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IRQ_RELEASED); + } + rc = gh_irq_release_notify(fts_data->vm_info->irq_label); + if (rc) + pr_err("Failed to notify release irq rc:%d\n", rc); + + pr_debug("vm irq release succeded\n"); + + fts_release_all_finger(); + + if (fts_data->bus_type == BUS_TYPE_I2C) + pm_runtime_put_sync(fts_data->client->adapter->dev.parent); + else + pm_runtime_put_sync(fts_data->spi->master->dev.parent); + + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_I2C_SESSION_RELEASED); + rc = fts_ts_vm_mem_release(fts_data); + if (rc) { + pr_err("Failed to release mem rc:%d\n", rc); + goto error; + } else { + fts_ts_trusted_touch_set_vm_state(fts_data, TVM_IOMEM_RELEASED); + } + fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_TVM_INIT); + atomic_set(&fts_data->trusted_touch_enabled, 0); + pr_info("trusted touch disabled\n"); + return; +error: + fts_ts_trusted_touch_abort_handler(fts_data, + TRUSTED_TOUCH_EVENT_RELEASE_FAILURE); +} + +int fts_ts_handle_trusted_touch_tvm(struct fts_ts_data *fts_data, int value) +{ + int err = 0; + + switch (value) { + case 0: + if ((atomic_read(&fts_data->trusted_touch_enabled) == 0) && + (atomic_read(&fts_data->trusted_touch_abort_status) == 0)) { + pr_err("Trusted touch is already disabled\n"); + break; + } + if (atomic_read(&fts_data->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + fts_ts_trusted_touch_tvm_vm_mode_disable(fts_data); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + case 1: + if (atomic_read(&fts_data->trusted_touch_enabled)) { + pr_err("Trusted touch usecase underway\n"); + err = -EBUSY; + break; + } + if (atomic_read(&fts_data->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + fts_ts_trusted_touch_tvm_vm_mode_enable(fts_data); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + default: + FTS_ERROR("unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + + return err; +} + +static void fts_ts_trusted_touch_abort_tvm(struct fts_ts_data *fts_data) +{ + int rc = 0; + int vm_state = fts_ts_trusted_touch_get_vm_state(fts_data); + + if (vm_state >= TRUSTED_TOUCH_TVM_STATE_MAX) { + pr_err("invalid tvm driver state: %d\n", vm_state); + return; + } + + switch (vm_state) { + case TVM_INTERRUPT_ENABLED: + fts_irq_disable(); + case TVM_IRQ_ACCEPTED: + case TVM_INTERRUPT_DISABLED: + rc = gh_irq_release(fts_data->vm_info->irq_label); + if (rc) + pr_err("Failed to release irq rc:%d\n", rc); + rc = gh_irq_release_notify(fts_data->vm_info->irq_label); + if (rc) + pr_err("Failed to notify irq release rc:%d\n", rc); + case TVM_I2C_SESSION_ACQUIRED: + case TVM_IOMEM_ACCEPTED: + case TVM_IRQ_RELEASED: + fts_release_all_finger(); + if (fts_data->bus_type == BUS_TYPE_I2C) + pm_runtime_put_sync(fts_data->client->adapter->dev.parent); + else + pm_runtime_put_sync(fts_data->spi->master->dev.parent); + case TVM_I2C_SESSION_RELEASED: + rc = fts_ts_vm_mem_release(fts_data); + if (rc) + pr_err("Failed to release mem rc:%d\n", rc); + case TVM_IOMEM_RELEASED: + case TVM_ALL_RESOURCES_LENT_NOTIFIED: + case TRUSTED_TOUCH_TVM_INIT: + case TVM_IRQ_LENT_NOTIFIED: + case TVM_IOMEM_LENT_NOTIFIED: + atomic_set(&fts_data->trusted_touch_enabled, 0); + } + + atomic_set(&fts_data->trusted_touch_abort_status, 0); + fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_TVM_INIT); +} + +#else + +static void fts_ts_bus_put(struct fts_ts_data *fts_data); + +static void fts_ts_trusted_touch_abort_pvm(struct fts_ts_data *fts_data) +{ + int rc = 0; + int vm_state = fts_ts_trusted_touch_get_vm_state(fts_data); + + if (vm_state >= TRUSTED_TOUCH_PVM_STATE_MAX) { + pr_err("Invalid driver state: %d\n", vm_state); + return; + } + + switch (vm_state) { + case PVM_IRQ_RELEASE_NOTIFIED: + case PVM_ALL_RESOURCES_RELEASE_NOTIFIED: + case PVM_IRQ_LENT: + case PVM_IRQ_LENT_NOTIFIED: + rc = gh_irq_reclaim(fts_data->vm_info->irq_label); + if (rc) + pr_err("failed to reclaim irq on pvm rc:%d\n", rc); + case PVM_IRQ_RECLAIMED: + case PVM_IOMEM_LENT: + case PVM_IOMEM_LENT_NOTIFIED: + case PVM_IOMEM_RELEASE_NOTIFIED: + rc = gh_rm_mem_reclaim(fts_data->vm_info->vm_mem_handle, 0); + if (rc) + pr_err("failed to reclaim iomem on pvm rc:%d\n", rc); + fts_data->vm_info->vm_mem_handle = 0; + case PVM_IOMEM_RECLAIMED: + case PVM_INTERRUPT_DISABLED: + fts_irq_enable(); + case PVM_I2C_RESOURCE_ACQUIRED: + case PVM_INTERRUPT_ENABLED: + fts_ts_bus_put(fts_data); + case TRUSTED_TOUCH_PVM_INIT: + case PVM_I2C_RESOURCE_RELEASED: + atomic_set(&fts_data->trusted_touch_enabled, 0); + atomic_set(&fts_data->trusted_touch_transition, 0); + } + + atomic_set(&fts_data->trusted_touch_abort_status, 0); + + fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_PVM_INIT); +} + +static int fts_ts_clk_prepare_enable(struct fts_ts_data *fts_data) +{ + int ret; + + ret = clk_prepare_enable(fts_data->iface_clk); + if (ret) { + FTS_ERROR("error on clk_prepare_enable(iface_clk):%d\n", ret); + return ret; + } + + ret = clk_prepare_enable(fts_data->core_clk); + if (ret) { + clk_disable_unprepare(fts_data->iface_clk); + FTS_ERROR("error clk_prepare_enable(core_clk):%d\n", ret); + } + return ret; +} + +static void fts_ts_clk_disable_unprepare(struct fts_ts_data *fts_data) +{ + clk_disable_unprepare(fts_data->core_clk); + clk_disable_unprepare(fts_data->iface_clk); +} + +static int fts_ts_bus_get(struct fts_ts_data *fts_data) +{ + int rc = 0; + struct device *dev = NULL; + + cancel_work_sync(&fts_data->resume_work); + reinit_completion(&fts_data->trusted_touch_powerdown); + fts_ts_enable_reg(fts_data, true); + + if (fts_data->bus_type == BUS_TYPE_I2C) + dev = fts_data->client->adapter->dev.parent; + else + dev = fts_data->spi->master->dev.parent; + + mutex_lock(&fts_data->fts_clk_io_ctrl_mutex); + rc = pm_runtime_get_sync(dev); + if (rc >= 0 && fts_data->core_clk != NULL && + fts_data->iface_clk != NULL) { + rc = fts_ts_clk_prepare_enable(fts_data); + if (rc) + pm_runtime_put_sync(dev); + } + + mutex_unlock(&fts_data->fts_clk_io_ctrl_mutex); + return rc; +} + +static void fts_ts_bus_put(struct fts_ts_data *fts_data) +{ + struct device *dev = NULL; + + if (fts_data->bus_type == BUS_TYPE_I2C) + dev = fts_data->client->adapter->dev.parent; + else + dev = fts_data->spi->master->dev.parent; + + mutex_lock(&fts_data->fts_clk_io_ctrl_mutex); + if (fts_data->core_clk != NULL && fts_data->iface_clk != NULL) + fts_ts_clk_disable_unprepare(fts_data); + pm_runtime_put_sync(dev); + mutex_unlock(&fts_data->fts_clk_io_ctrl_mutex); + complete(&fts_data->trusted_touch_powerdown); + fts_ts_enable_reg(fts_data, false); +} + +static struct gh_notify_vmid_desc *fts_ts_vm_get_vmid(gh_vmid_t vmid) +{ + struct gh_notify_vmid_desc *vmid_desc; + + vmid_desc = kzalloc(offsetof(struct gh_notify_vmid_desc, + vmid_entries[1]), GFP_KERNEL); + if (!vmid_desc) + return ERR_PTR(ENOMEM); + + vmid_desc->n_vmid_entries = 1; + vmid_desc->vmid_entries[0].vmid = vmid; + return vmid_desc; +} + +static void fts_trusted_touch_pvm_vm_mode_disable(struct fts_ts_data *fts_data) +{ + int rc = 0; + + atomic_set(&fts_data->trusted_touch_transition, 1); + + if (atomic_read(&fts_data->trusted_touch_abort_status)) { + fts_ts_trusted_touch_abort_pvm(fts_data); + return; + } + + if (fts_ts_trusted_touch_get_vm_state(fts_data) != PVM_ALL_RESOURCES_RELEASE_NOTIFIED) + pr_err("all release notifications are not received yet\n"); + + rc = gh_rm_mem_reclaim(fts_data->vm_info->vm_mem_handle, 0); + if (rc) { + pr_err("Trusted touch VM mem reclaim failed rc:%d\n", rc); + goto error; + } + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IOMEM_RECLAIMED); + fts_data->vm_info->vm_mem_handle = 0; + pr_debug("vm mem reclaim succeded!\n"); + + rc = gh_irq_reclaim(fts_data->vm_info->irq_label); + if (rc) { + pr_err("failed to reclaim irq on pvm rc:%d\n", rc); + goto error; + } + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IRQ_RECLAIMED); + pr_debug("vm irq reclaim succeded!\n"); + + fts_irq_enable(); + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_INTERRUPT_ENABLED); + fts_ts_bus_put(fts_data); + atomic_set(&fts_data->trusted_touch_transition, 0); + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_I2C_RESOURCE_RELEASED); + fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_PVM_INIT); + atomic_set(&fts_data->trusted_touch_enabled, 0); + pr_info("trusted touch disabled\n"); + return; +error: + fts_ts_trusted_touch_abort_handler(fts_data, + TRUSTED_TOUCH_EVENT_RECLAIM_FAILURE); +} + +static void fts_ts_vm_irq_on_release_callback(void *data, + unsigned long notif_type, + enum gh_irq_label label) +{ + struct fts_ts_data *fts_data = data; + + if (notif_type != GH_RM_NOTIF_VM_IRQ_RELEASED) { + pr_err("invalid notification type\n"); + return; + } + + if (fts_ts_trusted_touch_get_vm_state(fts_data) == PVM_IOMEM_RELEASE_NOTIFIED) + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_ALL_RESOURCES_RELEASE_NOTIFIED); + else + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IRQ_RELEASE_NOTIFIED); +} + +static void fts_ts_vm_mem_on_release_handler(enum gh_mem_notifier_tag tag, + unsigned long notif_type, void *entry_data, void *notif_msg) +{ + struct gh_rm_notif_mem_released_payload *release_payload; + struct trusted_touch_vm_info *vm_info; + struct fts_ts_data *fts_data; + + fts_data = (struct fts_ts_data *)entry_data; + vm_info = fts_data->vm_info; + if (!vm_info) { + pr_err(" Invalid vm_info\n"); + return; + } + + if (notif_type != GH_RM_NOTIF_MEM_RELEASED) { + pr_err(" Invalid notification type\n"); + return; + } + + if (tag != vm_info->mem_tag) { + pr_err(" Invalid tag\n"); + return; + } + + if (!entry_data || !notif_msg) { + pr_err(" Invalid data or notification message\n"); + return; + } + + release_payload = (struct gh_rm_notif_mem_released_payload *)notif_msg; + if (release_payload->mem_handle != vm_info->vm_mem_handle) { + pr_err("Invalid mem handle detected\n"); + return; + } + + if (fts_ts_trusted_touch_get_vm_state(fts_data) == PVM_IRQ_RELEASE_NOTIFIED) + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_ALL_RESOURCES_RELEASE_NOTIFIED); + else + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IOMEM_RELEASE_NOTIFIED); +} + +static int fts_ts_vm_mem_lend(struct fts_ts_data *fts_data) +{ + struct gh_acl_desc *acl_desc; + struct gh_sgl_desc *sgl_desc; + struct gh_notify_vmid_desc *vmid_desc; + gh_memparcel_handle_t mem_handle; + gh_vmid_t trusted_vmid; + int rc = 0; + + acl_desc = fts_ts_vm_get_acl(GH_TRUSTED_VM); + if (IS_ERR(acl_desc)) { + pr_err("Failed to get acl of IO memories for Trusted touch\n"); + PTR_ERR(acl_desc); + return -EINVAL; + } + + sgl_desc = fts_ts_vm_get_sgl(fts_data->vm_info); + if (IS_ERR(sgl_desc)) { + pr_err("Failed to get sgl of IO memories for Trusted touch\n"); + PTR_ERR(sgl_desc); + rc = -EINVAL; + goto sgl_error; + } + + rc = gh_rm_mem_lend(GH_RM_MEM_TYPE_IO, 0, TRUSTED_TOUCH_MEM_LABEL, + acl_desc, sgl_desc, NULL, &mem_handle); + if (rc) { + pr_err("Failed to lend IO memories for Trusted touch rc:%d\n", + rc); + goto error; + } + + pr_info("vm mem lend succeded\n"); + + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IOMEM_LENT); + + gh_rm_get_vmid(GH_TRUSTED_VM, &trusted_vmid); + + vmid_desc = fts_ts_vm_get_vmid(trusted_vmid); + + rc = gh_rm_mem_notify(mem_handle, GH_RM_MEM_NOTIFY_RECIPIENT_SHARED, + fts_data->vm_info->mem_tag, vmid_desc); + if (rc) { + pr_err("Failed to notify mem lend to hypervisor rc:%d\n", rc); + goto vmid_error; + } + + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IOMEM_LENT_NOTIFIED); + + fts_data->vm_info->vm_mem_handle = mem_handle; +vmid_error: + kfree(vmid_desc); +error: + kfree(sgl_desc); +sgl_error: + kfree(acl_desc); + + return rc; +} + +static int fts_ts_trusted_touch_pvm_vm_mode_enable(struct fts_ts_data *fts_data) +{ + int rc = 0; + struct trusted_touch_vm_info *vm_info = fts_data->vm_info; + + atomic_set(&fts_data->trusted_touch_transition, 1); + mutex_lock(&fts_data->transition_lock); + + if (fts_data->suspended) { + FTS_ERROR("Invalid power state for operation\n"); + atomic_set(&fts_data->trusted_touch_transition, 0); + rc = -EPERM; + goto error; + } + + /* i2c session start and resource acquire */ + if (fts_ts_bus_get(fts_data) < 0) { + FTS_ERROR("fts_ts_bus_get failed\n"); + rc = -EIO; + goto error; + } + + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_I2C_RESOURCE_ACQUIRED); + /* flush pending interurpts from FIFO */ + fts_irq_disable(); + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_INTERRUPT_DISABLED); + fts_release_all_finger(); + + rc = fts_ts_vm_mem_lend(fts_data); + if (rc) { + pr_err("Failed to lend memory\n"); + goto abort_handler; + } + pr_debug("vm mem lend succeded\n"); + rc = gh_irq_lend_v2(vm_info->irq_label, vm_info->vm_name, + fts_data->irq, &fts_ts_vm_irq_on_release_callback, fts_data); + if (rc) { + pr_err("Failed to lend irq\n"); + goto abort_handler; + } + + pr_debug("vm irq lend succeded for irq:%d\n", fts_data->irq); + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IRQ_LENT); + + rc = gh_irq_lend_notify(vm_info->irq_label); + if (rc) { + pr_err("Failed to notify irq\n"); + goto abort_handler; + } + fts_ts_trusted_touch_set_vm_state(fts_data, PVM_IRQ_LENT_NOTIFIED); + + mutex_unlock(&fts_data->transition_lock); + atomic_set(&fts_data->trusted_touch_transition, 0); + atomic_set(&fts_data->trusted_touch_enabled, 1); + pr_info("trusted touch enabled\n"); + return rc; + +abort_handler: + fts_ts_trusted_touch_abort_handler(fts_data, TRUSTED_TOUCH_EVENT_LEND_FAILURE); + +error: + mutex_unlock(&fts_data->transition_lock); + return rc; +} + +int fts_ts_handle_trusted_touch_pvm(struct fts_ts_data *fts_data, int value) +{ + int err = 0; + + switch (value) { + case 0: + if (atomic_read(&fts_data->trusted_touch_enabled) == 0 && + (atomic_read(&fts_data->trusted_touch_abort_status) == 0)) { + pr_err("Trusted touch is already disabled\n"); + break; + } + if (atomic_read(&fts_data->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + fts_trusted_touch_pvm_vm_mode_disable(fts_data); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + case 1: + if (atomic_read(&fts_data->trusted_touch_enabled)) { + pr_err("Trusted touch usecase underway\n"); + err = -EBUSY; + break; + } + if (atomic_read(&fts_data->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + err = fts_ts_trusted_touch_pvm_vm_mode_enable(fts_data); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + default: + FTS_ERROR("unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + return err; +} + +#endif + +static void fts_ts_trusted_touch_event_notify(struct fts_ts_data *fts_data, int event) +{ + atomic_set(&fts_data->trusted_touch_event, event); + sysfs_notify(&fts_data->dev->kobj, NULL, "trusted_touch_event"); +} + +static void fts_ts_trusted_touch_abort_handler(struct fts_ts_data *fts_data, int error) +{ + atomic_set(&fts_data->trusted_touch_abort_status, error); + pr_err("TUI session aborted with failure:%d\n", error); + fts_ts_trusted_touch_event_notify(fts_data, error); +#ifdef CONFIG_ARCH_QTI_VM + pr_err("Resetting touch controller\n"); + if (fts_ts_trusted_touch_get_vm_state(fts_data) >= TVM_IOMEM_ACCEPTED && + error == TRUSTED_TOUCH_EVENT_I2C_FAILURE) { + pr_err("Resetting touch controller\n"); + fts_ts_trusted_touch_reset_gpio_toggle(fts_data); + } +#endif +} + +static int fts_ts_vm_init(struct fts_ts_data *fts_data) +{ + int rc = 0; + struct trusted_touch_vm_info *vm_info; + void *mem_cookie; + + rc = fts_ts_populate_vm_info(fts_data); + if (rc) { + pr_err("Cannot setup vm pipeline\n"); + rc = -EINVAL; + goto fail; + } + + vm_info = fts_data->vm_info; +#ifdef CONFIG_ARCH_QTI_VM + mem_cookie = gh_mem_notifier_register(vm_info->mem_tag, + fts_ts_vm_mem_on_lend_handler, fts_data); + if (!mem_cookie) { + pr_err("Failed to register on lend mem notifier\n"); + rc = -EINVAL; + goto init_fail; + } + vm_info->mem_cookie = mem_cookie; + rc = gh_irq_wait_for_lend_v2(vm_info->irq_label, GH_PRIMARY_VM, + &fts_ts_vm_irq_on_lend_callback, fts_data); + fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_TVM_INIT); +#else + mem_cookie = gh_mem_notifier_register(vm_info->mem_tag, + fts_ts_vm_mem_on_release_handler, fts_data); + if (!mem_cookie) { + pr_err("Failed to register on release mem notifier\n"); + rc = -EINVAL; + goto init_fail; + } + vm_info->mem_cookie = mem_cookie; + fts_ts_trusted_touch_set_vm_state(fts_data, TRUSTED_TOUCH_PVM_INIT); +#endif + return rc; +init_fail: + fts_ts_vm_deinit(fts_data); +fail: + return rc; +} + +static void fts_ts_dt_parse_trusted_touch_info(struct fts_ts_data *fts_data) +{ + struct device_node *np = fts_data->dev->of_node; + int rc = 0; + const char *selection; + const char *environment; + + rc = of_property_read_string(np, "focaltech,trusted-touch-mode", + &selection); + if (rc) { + dev_warn(fts_data->dev, + "%s: No trusted touch mode selection made\n", __func__); + atomic_set(&fts_data->trusted_touch_mode, + TRUSTED_TOUCH_MODE_NONE); + return; + } + + if (!strcmp(selection, "vm_mode")) { + atomic_set(&fts_data->trusted_touch_mode, + TRUSTED_TOUCH_VM_MODE); + pr_err("Selected trusted touch mode to VM mode\n"); + } else { + atomic_set(&fts_data->trusted_touch_mode, + TRUSTED_TOUCH_MODE_NONE); + pr_err("Invalid trusted_touch mode\n"); + } + + rc = of_property_read_string(np, "focaltech,touch-environment", + &environment); + if (rc) { + dev_warn(fts_data->dev, + "%s: No trusted touch mode environment\n", __func__); + } + fts_data->touch_environment = environment; + pr_err("Trusted touch environment:%s\n", + fts_data->touch_environment); +} + +static void fts_ts_trusted_touch_init(struct fts_ts_data *fts_data) +{ + int rc = 0; + + atomic_set(&fts_data->trusted_touch_initialized, 0); + fts_ts_dt_parse_trusted_touch_info(fts_data); + + if (atomic_read(&fts_data->trusted_touch_mode) == + TRUSTED_TOUCH_MODE_NONE) + return; + + init_completion(&fts_data->trusted_touch_powerdown); + + /* Get clocks */ + fts_data->core_clk = devm_clk_get(fts_data->dev->parent, + "m-ahb"); + if (IS_ERR(fts_data->core_clk)) { + fts_data->core_clk = NULL; + dev_warn(fts_data->dev, + "%s: core_clk is not defined\n", __func__); + } + + fts_data->iface_clk = devm_clk_get(fts_data->dev->parent, + "se-clk"); + if (IS_ERR(fts_data->iface_clk)) { + fts_data->iface_clk = NULL; + dev_warn(fts_data->dev, + "%s: iface_clk is not defined\n", __func__); + } + + if (atomic_read(&fts_data->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + rc = fts_ts_vm_init(fts_data); + if (rc) + pr_err("Failed to init VM\n"); + } + atomic_set(&fts_data->trusted_touch_initialized, 1); +} + +#endif + +/***************************************************************************** +* Name: fts_wait_tp_to_valid +* Brief: Read chip id until TP FW become valid(Timeout: TIMEOUT_READ_REG), +* need call when reset/power on/resume... +* Input: +* Output: +* Return: return 0 if tp valid, otherwise return error code +*****************************************************************************/ +int fts_wait_tp_to_valid(void) +{ + int ret = 0; + int cnt = 0; + u8 idh = 0; + u8 idl = 0; + u8 chip_idh = fts_data->ic_info.ids.chip_idh; + u8 chip_idl = fts_data->ic_info.ids.chip_idl; + + do { + ret = fts_read_reg(FTS_REG_CHIP_ID, &idh); + ret = fts_read_reg(FTS_REG_CHIP_ID2, &idl); + if ((ret < 0) || (idh != chip_idh) || (idl != chip_idl)) { + FTS_DEBUG("TP Not Ready,ReadData:0x%02x%02x", idh, idl); + } else if ((idh == chip_idh) && (idl == chip_idl)) { + FTS_INFO("TP Ready,Device ID:0x%02x%02x", idh, idl); + return 0; + } + cnt++; + msleep(INTERVAL_READ_REG); + } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG); + + return -EIO; +} + +/***************************************************************************** +* Name: fts_tp_state_recovery +* Brief: Need execute this function when reset +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_tp_state_recovery(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + /* wait tp stable */ + fts_wait_tp_to_valid(); + /* recover TP charger state 0x8B */ + /* recover TP glove state 0xC0 */ + /* recover TP cover state 0xC1 */ + fts_ex_mode_recovery(ts_data); + /* recover TP gesture state 0xD0 */ + fts_gesture_recovery(ts_data); + FTS_FUNC_EXIT(); +} + +int fts_reset_proc(int hdelayms) +{ + FTS_DEBUG("tp reset"); + gpio_direction_output(fts_data->pdata->reset_gpio, 0); + msleep(1); + gpio_direction_output(fts_data->pdata->reset_gpio, 1); + if (hdelayms) { + msleep(hdelayms); + } + + return 0; +} + +void fts_irq_disable(void) +{ + unsigned long irqflags; + + FTS_FUNC_ENTER(); + spin_lock_irqsave(&fts_data->irq_lock, irqflags); + + if (!fts_data->irq_disabled) { +#ifdef CONFIG_FTS_TRUSTED_TOUCH + if (atomic_read(&fts_data->trusted_touch_transition)) + disable_irq_wake(fts_data->irq); + else + disable_irq_nosync(fts_data->irq); +#else + disable_irq_nosync(fts_data->irq); +#endif + fts_data->irq_disabled = true; + } + + spin_unlock_irqrestore(&fts_data->irq_lock, irqflags); + FTS_FUNC_EXIT(); +} + +void fts_irq_enable(void) +{ + unsigned long irqflags = 0; + + FTS_FUNC_ENTER(); + spin_lock_irqsave(&fts_data->irq_lock, irqflags); + + if (fts_data->irq_disabled) { +#ifdef CONFIG_FTS_TRUSTED_TOUCH + if (atomic_read(&fts_data->trusted_touch_transition)) + enable_irq_wake(fts_data->irq); + else + enable_irq(fts_data->irq); +#else + enable_irq(fts_data->irq); +#endif + fts_data->irq_disabled = false; + } + + spin_unlock_irqrestore(&fts_data->irq_lock, irqflags); + FTS_FUNC_EXIT(); +} + +void fts_hid2std(void) +{ + int ret = 0; + u8 buf[3] = {0xEB, 0xAA, 0x09}; + + if (fts_data->bus_type != BUS_TYPE_I2C) + return; + + ret = fts_write(buf, 3); + if (ret < 0) { + FTS_ERROR("hid2std cmd write fail"); + return; + } + + msleep(20); + buf[0] = buf[1] = buf[2] = 0; + ret = fts_read(NULL, 0, buf, 3); + if (ret < 0) + FTS_ERROR("hid2std cmd read fail"); + else if ((buf[0] == 0xEB) && (buf[1] == 0xAA) && (buf[2] == 0x08)) + FTS_DEBUG("hidi2c change to stdi2c successful"); + else + FTS_DEBUG("hidi2c change to stdi2c not support or fail"); + +} + +static int fts_get_chip_types( + struct fts_ts_data *ts_data, + u8 id_h, u8 id_l, bool fw_valid) +{ + int i = 0; + u32 ctype_entries = sizeof(ctype) / sizeof(struct ft_chip_t); + + if ((0x0 == id_h) || (0x0 == id_l)) { + FTS_ERROR("id_h/id_l is 0"); + return -EINVAL; + } + + FTS_DEBUG("verify id:0x%02x%02x", id_h, id_l); + for (i = 0; i < ctype_entries; i++) { + if (VALID == fw_valid) { + if ((id_h == ctype[i].chip_idh) && (id_l == ctype[i].chip_idl)) + break; + } else { + if (((id_h == ctype[i].rom_idh) && (id_l == ctype[i].rom_idl)) + || ((id_h == ctype[i].pb_idh) && (id_l == ctype[i].pb_idl)) + || ((id_h == ctype[i].bl_idh) && (id_l == ctype[i].bl_idl))) + break; + } + } + + if (i >= ctype_entries) + return -ENODATA; + + ts_data->ic_info.ids = ctype[i]; + return 0; +} + +static int fts_read_bootid(struct fts_ts_data *ts_data, u8 *id) +{ + int ret = 0; + u8 chip_id[2] = { 0 }; + u8 id_cmd[4] = { 0 }; + u32 id_cmd_len = 0; + + id_cmd[0] = FTS_CMD_START1; + id_cmd[1] = FTS_CMD_START2; + ret = fts_write(id_cmd, 2); + if (ret < 0) { + FTS_ERROR("start cmd write fail"); + return ret; + } + + msleep(FTS_CMD_START_DELAY); + id_cmd[0] = FTS_CMD_READ_ID; + id_cmd[1] = id_cmd[2] = id_cmd[3] = 0x00; + if (ts_data->ic_info.is_incell) + id_cmd_len = FTS_CMD_READ_ID_LEN_INCELL; + else + id_cmd_len = FTS_CMD_READ_ID_LEN; + ret = fts_read(id_cmd, id_cmd_len, chip_id, 2); + if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) { + FTS_ERROR("read boot id fail,read:0x%02x%02x", chip_id[0], chip_id[1]); + return -EIO; + } + + id[0] = chip_id[0]; + id[1] = chip_id[1]; + return 0; +} + +/***************************************************************************** +* Name: fts_get_ic_information +* Brief: read chip id to get ic information, after run the function, driver w- +* ill know which IC is it. +* If cant get the ic information, maybe not focaltech's touch IC, need +* unregister the driver +* Input: +* Output: +* Return: return 0 if get correct ic information, otherwise return error code +*****************************************************************************/ +static int fts_get_ic_information(struct fts_ts_data *ts_data) +{ + int ret = 0; + int cnt = 0; + u8 chip_id[2] = { 0 }; + u32 type = ts_data->pdata->type; + + ts_data->ic_info.is_incell = FTS_CHIP_IDC(type); + ts_data->ic_info.hid_supported = FTS_HID_SUPPORTTED(type); + + do { + ret = fts_read_reg(FTS_REG_CHIP_ID, &chip_id[0]); + ret = fts_read_reg(FTS_REG_CHIP_ID2, &chip_id[1]); + if ((ret < 0) || (0x0 == chip_id[0]) || (0x0 == chip_id[1])) { + FTS_DEBUG("i2c read invalid, read:0x%02x%02x", + chip_id[0], chip_id[1]); + } else { + ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], VALID); + if (!ret) + break; + else + FTS_DEBUG("TP not ready, read:0x%02x%02x", + chip_id[0], chip_id[1]); + } + + cnt++; + msleep(INTERVAL_READ_REG); + } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG); + + if ((cnt * INTERVAL_READ_REG) >= TIMEOUT_READ_REG) { + FTS_INFO("fw is invalid, need read boot id"); + if (ts_data->ic_info.hid_supported) { + fts_hid2std(); + } + + ret = fts_read_bootid(ts_data, &chip_id[0]); + if (ret < 0) { + FTS_ERROR("read boot id fail"); + return ret; + } + + ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], INVALID); + if (ret < 0) { + FTS_ERROR("can't get ic informaton"); + return ret; + } + } + + FTS_INFO("get ic information, chip id = 0x%02x%02x", + ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl); + + return 0; +} + +/***************************************************************************** +* Reprot related +*****************************************************************************/ +static void fts_show_touch_buffer(u8 *data, int datalen) +{ + int i = 0; + int count = 0; + char *tmpbuf = NULL; + + tmpbuf = kzalloc(1024, GFP_KERNEL); + if (!tmpbuf) { + FTS_ERROR("tmpbuf zalloc fail"); + return; + } + + for (i = 0; i < datalen; i++) { + count += snprintf(tmpbuf + count, 1024 - count, "%02X,", data[i]); + if (count >= 1024) + break; + } + FTS_DEBUG("point buffer:%s", tmpbuf); + + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } +} + +void fts_release_all_finger(void) +{ + struct input_dev *input_dev = fts_data->input_dev; +#if FTS_MT_PROTOCOL_B_EN + u32 finger_count = 0; + u32 max_touches = fts_data->pdata->max_touch_number; +#endif + + FTS_FUNC_ENTER(); + mutex_lock(&fts_data->report_mutex); +#if FTS_MT_PROTOCOL_B_EN + for (finger_count = 0; finger_count < max_touches; finger_count++) { + input_mt_slot(input_dev, finger_count); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } +#else + input_mt_sync(input_dev); +#endif + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); + + fts_data->touchs = 0; + fts_data->key_state = 0; + mutex_unlock(&fts_data->report_mutex); + FTS_FUNC_EXIT(); +} + +/***************************************************************************** +* Name: fts_input_report_key +* Brief: process key events,need report key-event if key enable. +* if point's coordinate is in (x_dim-50,y_dim-50) ~ (x_dim+50,y_dim+50), +* need report it to key event. +* x_dim: parse from dts, means key x_coordinate, dimension:+-50 +* y_dim: parse from dts, means key y_coordinate, dimension:+-50 +* Input: +* Output: +* Return: return 0 if it's key event, otherwise return error code +*****************************************************************************/ +static int fts_input_report_key(struct fts_ts_data *data, int index) +{ + int i = 0; + int x = data->events[index].x; + int y = data->events[index].y; + int *x_dim = &data->pdata->key_x_coords[0]; + int *y_dim = &data->pdata->key_y_coords[0]; + + if (!data->pdata->have_key) { + return -EINVAL; + } + + for (i = 0; i < data->pdata->key_number; i++) { + if ((x >= x_dim[i] - FTS_KEY_DIM) && (x <= x_dim[i] + FTS_KEY_DIM) && + (y >= y_dim[i] - FTS_KEY_DIM) && (y <= y_dim[i] + FTS_KEY_DIM)) { + if (EVENT_DOWN(data->events[index].flag) + && !(data->key_state & (1 << i))) { + input_report_key(data->input_dev, data->pdata->keys[i], 1); + data->key_state |= (1 << i); + FTS_DEBUG("Key%d(%d,%d) DOWN!", i, x, y); + } else if (EVENT_UP(data->events[index].flag) + && (data->key_state & (1 << i))) { + input_report_key(data->input_dev, data->pdata->keys[i], 0); + data->key_state &= ~(1 << i); + FTS_DEBUG("Key%d(%d,%d) Up!", i, x, y); + } + return 0; + } + } + return -EINVAL; +} + +#if FTS_MT_PROTOCOL_B_EN +static int fts_input_report_b(struct fts_ts_data *data) +{ + int i = 0; + int uppoint = 0; + int touchs = 0; + bool va_reported = false; + u32 max_touch_num = data->pdata->max_touch_number; + struct ts_event *events = data->events; + + for (i = 0; i < data->touch_point; i++) { + if (fts_input_report_key(data, i) == 0) + continue; + + va_reported = true; + input_mt_slot(data->input_dev, events[i].id); + + if (EVENT_DOWN(events[i].flag)) { + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true); + +#if FTS_REPORT_PRESSURE_EN + if (events[i].p <= 0) { + events[i].p = 0x3f; + } + input_report_abs(data->input_dev, ABS_MT_PRESSURE, events[i].p); +#endif + if (events[i].area <= 0) { + events[i].area = 0x09; + } + input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].area); + input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x); + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y); + + touchs |= BIT(events[i].id); + data->touchs |= BIT(events[i].id); + + if ((data->log_level >= 2) || + ((1 == data->log_level) && (FTS_TOUCH_DOWN == events[i].flag))) { + FTS_DEBUG("[B]P%d(%d, %d)[p:%d,tm:%d] DOWN!", + events[i].id, + events[i].x, events[i].y, + events[i].p, events[i].area); + } + } else { + uppoint++; + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); + data->touchs &= ~BIT(events[i].id); + if (data->log_level >= 1) { + FTS_DEBUG("[B]P%d UP!", events[i].id); + } + } + } + + if (unlikely(data->touchs ^ touchs)) { + for (i = 0; i < max_touch_num; i++) { + if (BIT(i) & (data->touchs ^ touchs)) { + if (data->log_level >= 1) { + FTS_DEBUG("[B]P%d UP!", i); + } + va_reported = true; + input_mt_slot(data->input_dev, i); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); + } + } + } + data->touchs = touchs; + + if (va_reported) { + /* touchs==0, there's no point but key */ + if (EVENT_NO_DOWN(data) || (!touchs)) { + if (data->log_level >= 1) { + FTS_DEBUG("[B]Points All Up!"); + } + input_report_key(data->input_dev, BTN_TOUCH, 0); + } else { + input_report_key(data->input_dev, BTN_TOUCH, 1); + } + } + + input_sync(data->input_dev); + return 0; +} + +#else +static int fts_input_report_a(struct fts_ts_data *data) +{ + int i = 0; + int touchs = 0; + bool va_reported = false; + struct ts_event *events = data->events; + + for (i = 0; i < data->touch_point; i++) { + if (fts_input_report_key(data, i) == 0) { + continue; + } + + va_reported = true; + if (EVENT_DOWN(events[i].flag)) { + input_report_abs(data->input_dev, ABS_MT_TRACKING_ID, events[i].id); +#if FTS_REPORT_PRESSURE_EN + if (events[i].p <= 0) { + events[i].p = 0x3f; + } + input_report_abs(data->input_dev, ABS_MT_PRESSURE, events[i].p); +#endif + if (events[i].area <= 0) { + events[i].area = 0x09; + } + input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, events[i].area); + + input_report_abs(data->input_dev, ABS_MT_POSITION_X, events[i].x); + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, events[i].y); + + input_mt_sync(data->input_dev); + + if ((data->log_level >= 2) || + ((1 == data->log_level) && (FTS_TOUCH_DOWN == events[i].flag))) { + FTS_DEBUG("[A]P%d(%d, %d)[p:%d,tm:%d] DOWN!", + events[i].id, + events[i].x, events[i].y, + events[i].p, events[i].area); + } + touchs++; + } + } + + /* last point down, current no point but key */ + if (data->touchs && !touchs) { + va_reported = true; + } + data->touchs = touchs; + + if (va_reported) { + if (EVENT_NO_DOWN(data)) { + if (data->log_level >= 1) { + FTS_DEBUG("[A]Points All Up!"); + } + input_report_key(data->input_dev, BTN_TOUCH, 0); + input_mt_sync(data->input_dev); + } else { + input_report_key(data->input_dev, BTN_TOUCH, 1); + } + } + + input_sync(data->input_dev); + return 0; +} +#endif + +static int fts_read_touchdata(struct fts_ts_data *data) +{ + int ret = 0; + u8 *buf = data->point_buf; + + memset(buf, 0xFF, data->pnt_buf_size); + buf[0] = 0x01; + + if (data->gesture_mode) { + if (0 == fts_gesture_readdata(data, NULL)) { + FTS_INFO("succuss to get gesture data in irq handler"); + return 1; + } + } + + ret = fts_read(buf, 1, buf + 1, data->pnt_buf_size - 1); + if (ret < 0) { + FTS_ERROR("read touchdata failed, ret:%d", ret); + return ret; + } + + if (data->log_level >= 3) { + fts_show_touch_buffer(buf, data->pnt_buf_size); + } + + return 0; +} + +static int fts_read_parse_touchdata(struct fts_ts_data *data) +{ + int ret = 0; + int i = 0; + u8 pointid = 0; + int base = 0; + struct ts_event *events = data->events; + int max_touch_num = data->pdata->max_touch_number; + u8 *buf = data->point_buf; + + ret = fts_read_touchdata(data); + if (ret) { + return ret; + } + + data->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F; + data->touch_point = 0; + + if (data->ic_info.is_incell) { + if ((data->point_num == 0x0F) && (buf[2] == 0xFF) && (buf[3] == 0xFF) + && (buf[4] == 0xFF) && (buf[5] == 0xFF) && (buf[6] == 0xFF)) { + FTS_DEBUG("touch buff is 0xff, need recovery state"); + fts_release_all_finger(); + fts_tp_state_recovery(data); + return -EIO; + } + } + + if (data->point_num > max_touch_num) { + FTS_INFO("invalid point_num(%d)", data->point_num); + return -EIO; + } + + for (i = 0; i < max_touch_num; i++) { + base = FTS_ONE_TCH_LEN * i; + pointid = (buf[FTS_TOUCH_ID_POS + base]) >> 4; + if (pointid >= FTS_MAX_ID) + break; + else if (pointid >= max_touch_num) { + FTS_ERROR("ID(%d) beyond max_touch_number", pointid); + return -EINVAL; + } + + data->touch_point++; + events[i].x = ((buf[FTS_TOUCH_X_H_POS + base] & 0x0F) << 8) + + (buf[FTS_TOUCH_X_L_POS + base] & 0xFF); + events[i].y = ((buf[FTS_TOUCH_Y_H_POS + base] & 0x0F) << 8) + + (buf[FTS_TOUCH_Y_L_POS + base] & 0xFF); + events[i].flag = buf[FTS_TOUCH_EVENT_POS + base] >> 6; + events[i].id = buf[FTS_TOUCH_ID_POS + base] >> 4; + events[i].area = buf[FTS_TOUCH_AREA_POS + base] >> 4; + events[i].p = buf[FTS_TOUCH_PRE_POS + base]; + + if (EVENT_DOWN(events[i].flag) && (data->point_num == 0)) { + FTS_INFO("abnormal touch data from fw"); + return -EIO; + } + } + + if (data->touch_point == 0) { + FTS_INFO("no touch point information"); + return -EIO; + } + + return 0; +} + +static void fts_irq_read_report(void) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + +#if FTS_ESDCHECK_EN + fts_esdcheck_set_intr(1); +#endif + +#if FTS_POINT_REPORT_CHECK_EN + fts_prc_queue_work(ts_data); +#endif + + ret = fts_read_parse_touchdata(ts_data); + if (ret == 0) { + mutex_lock(&ts_data->report_mutex); +#if FTS_MT_PROTOCOL_B_EN + fts_input_report_b(ts_data); +#else + fts_input_report_a(ts_data); +#endif + mutex_unlock(&ts_data->report_mutex); + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_set_intr(0); +#endif +} + +static irqreturn_t fts_irq_handler(int irq, void *data) +{ + struct fts_ts_data *fts_data = data; + + if (!fts_data) { + pr_err("%s: Invalid fts_data\n", __func__); + return IRQ_HANDLED; + } + + if (!mutex_trylock(&fts_data->transition_lock)) + return IRQ_HANDLED; + + fts_irq_read_report(); + mutex_unlock(&fts_data->transition_lock); + + return IRQ_HANDLED; +} + +static int fts_irq_registration(struct fts_ts_data *ts_data) +{ + int ret = 0; + struct fts_ts_platform_data *pdata = ts_data->pdata; + +#ifdef CONFIG_ARCH_QTI_VM + pdata->irq_gpio_flags = IRQF_TRIGGER_RISING | IRQF_ONESHOT; + FTS_INFO("irq:%d, flag:%x", ts_data->irq, pdata->irq_gpio_flags); + ret = request_threaded_irq(ts_data->irq, NULL, fts_irq_handler, + pdata->irq_gpio_flags, + FTS_DRIVER_NAME, ts_data); +#else + ts_data->irq = gpio_to_irq(pdata->irq_gpio); + pdata->irq_gpio_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + FTS_INFO("irq:%d, flag:%x", ts_data->irq, pdata->irq_gpio_flags); + ret = request_threaded_irq(ts_data->irq, NULL, fts_irq_handler, + pdata->irq_gpio_flags, + FTS_DRIVER_NAME, ts_data); +#endif + return ret; +} + +static int fts_input_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + int key_num = 0; + struct fts_ts_platform_data *pdata = ts_data->pdata; + struct input_dev *input_dev; + + FTS_FUNC_ENTER(); + input_dev = input_allocate_device(); + if (!input_dev) { + FTS_ERROR("Failed to allocate memory for input device"); + return -ENOMEM; + } + + /* Init and register Input device */ + input_dev->name = FTS_DRIVER_NAME; + if (ts_data->bus_type == BUS_TYPE_I2C) + input_dev->id.bustype = BUS_I2C; + else + input_dev->id.bustype = BUS_SPI; + input_dev->dev.parent = ts_data->dev; + + input_set_drvdata(input_dev, ts_data); + + __set_bit(EV_SYN, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + if (pdata->have_key) { + FTS_INFO("set key capabilities"); + for (key_num = 0; key_num < pdata->key_number; key_num++) + input_set_capability(input_dev, EV_KEY, pdata->keys[key_num]); + } + +#if FTS_MT_PROTOCOL_B_EN + input_mt_init_slots(input_dev, pdata->max_touch_number, INPUT_MT_DIRECT); +#else + input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 0x0F, 0, 0); +#endif + input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min, pdata->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min, pdata->y_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0xFF, 0, 0); +#if FTS_REPORT_PRESSURE_EN + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0xFF, 0, 0); +#endif + + ret = input_register_device(input_dev); + if (ret) { + FTS_ERROR("Input device registration failed"); + input_set_drvdata(input_dev, NULL); + input_free_device(input_dev); + input_dev = NULL; + return ret; + } + + ts_data->input_dev = input_dev; + + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_report_buffer_init(struct fts_ts_data *ts_data) +{ + int point_num = 0; + int events_num = 0; + + point_num = FTS_MAX_POINTS_SUPPORT; + ts_data->pnt_buf_size = FTS_TOUCH_DATA_LEN + FTS_GESTURE_DATA_LEN; + ts_data->point_buf = (u8 *)kzalloc(ts_data->pnt_buf_size + 1, GFP_KERNEL); + if (!ts_data->point_buf) { + FTS_ERROR("failed to alloc memory for point buf"); + return -ENOMEM; + } + + events_num = point_num * sizeof(struct ts_event); + ts_data->events = (struct ts_event *)kzalloc(events_num, GFP_KERNEL); + if (!ts_data->events) { + FTS_ERROR("failed to alloc memory for point events"); + kfree_safe(ts_data->point_buf); + return -ENOMEM; + } + + return 0; +} + +#if FTS_POWER_SOURCE_CUST_EN +/***************************************************************************** +* Power Control +*****************************************************************************/ +#if FTS_PINCTRL_EN +static int fts_pinctrl_init(struct fts_ts_data *ts) +{ + int ret = 0; + + ts->pinctrl = devm_pinctrl_get(ts->dev); + if (IS_ERR_OR_NULL(ts->pinctrl)) { + FTS_ERROR("Failed to get pinctrl, please check dts"); + ret = PTR_ERR(ts->pinctrl); + goto err_pinctrl_get; + } + + ts->pins_active = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_active"); + if (IS_ERR_OR_NULL(ts->pins_active)) { + FTS_ERROR("Pin state[active] not found"); + ret = PTR_ERR(ts->pins_active); + goto err_pinctrl_lookup; + } + + ts->pins_suspend = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_suspend"); + if (IS_ERR_OR_NULL(ts->pins_suspend)) { + FTS_ERROR("Pin state[suspend] not found"); + ret = PTR_ERR(ts->pins_suspend); + goto err_pinctrl_lookup; + } + + ts->pins_release = pinctrl_lookup_state(ts->pinctrl, "pmx_ts_release"); + if (IS_ERR_OR_NULL(ts->pins_release)) { + FTS_ERROR("Pin state[release] not found"); + ret = PTR_ERR(ts->pins_release); + } + + return 0; +err_pinctrl_lookup: + if (ts->pinctrl) { + devm_pinctrl_put(ts->pinctrl); + } +err_pinctrl_get: + ts->pinctrl = NULL; + ts->pins_release = NULL; + ts->pins_suspend = NULL; + ts->pins_active = NULL; + return ret; +} + +static int fts_pinctrl_select_normal(struct fts_ts_data *ts) +{ + int ret = 0; + + if (ts->pinctrl && ts->pins_active) { + ret = pinctrl_select_state(ts->pinctrl, ts->pins_active); + if (ret < 0) { + FTS_ERROR("Set normal pin state error:%d", ret); + } + } + + return ret; +} + +static int fts_pinctrl_select_suspend(struct fts_ts_data *ts) +{ + int ret = 0; + + if (ts->pinctrl && ts->pins_suspend) { + ret = pinctrl_select_state(ts->pinctrl, ts->pins_suspend); + if (ret < 0) { + FTS_ERROR("Set suspend pin state error:%d", ret); + } + } + + return ret; +} + +static int fts_pinctrl_select_release(struct fts_ts_data *ts) +{ + int ret = 0; + + if (ts->pinctrl) { + if (IS_ERR_OR_NULL(ts->pins_release)) { + devm_pinctrl_put(ts->pinctrl); + ts->pinctrl = NULL; + } else { + ret = pinctrl_select_state(ts->pinctrl, ts->pins_release); + if (ret < 0) + FTS_ERROR("Set gesture pin state error:%d", ret); + } + } + + return ret; +} +#endif /* FTS_PINCTRL_EN */ + +static int fts_power_configure(struct fts_ts_data *ts_data, bool enable) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + + if (enable) { + if (regulator_count_voltages(ts_data->vdd) > 0) { + ret = regulator_set_load(ts_data->vdd, FTS_LOAD_MAX_UA); + if (ret) { + FTS_ERROR("vdd regulator set_load failed ret=%d", ret); + return ret; + } + + ret = regulator_set_voltage(ts_data->vdd, FTS_VTG_MIN_UV, + FTS_VTG_MAX_UV); + if (ret) { + FTS_ERROR("vdd regulator set_vtg failed ret=%d", ret); + goto err_vdd_load; + } + } + + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + if (regulator_count_voltages(ts_data->vcc_i2c) > 0) { + ret = regulator_set_load(ts_data->vcc_i2c, FTS_LOAD_AVDD_UA); + if (ret) { + FTS_ERROR("vcc_i2c regulator set_load failed ret=%d", ret); + goto err_vdd_load; + } + + ret = regulator_set_voltage(ts_data->vcc_i2c, + FTS_I2C_VTG_MIN_UV, + FTS_I2C_VTG_MAX_UV); + if (ret) { + FTS_ERROR("vcc_i2c regulator set_vtg failed,ret=%d", ret); + goto err_vcc_load; + } + } + } + } else { + if (regulator_count_voltages(ts_data->vdd) > 0) { + ret = regulator_set_load(ts_data->vdd, FTS_LOAD_DISABLE_UA); + if (ret) { + FTS_ERROR("vdd regulator set_load failed ret=%d", ret); + return ret; + } + } + + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + if (regulator_count_voltages(ts_data->vcc_i2c) > 0) { + ret = regulator_set_load(ts_data->vcc_i2c, FTS_LOAD_DISABLE_UA); + if (ret) { + FTS_ERROR("vcc_i2c regulator set_load failed ret=%d", ret); + return ret; + } + } + } + } + + FTS_FUNC_EXIT(); + return ret; + +err_vcc_load: + regulator_set_load(ts_data->vcc_i2c, FTS_LOAD_DISABLE_UA); +err_vdd_load: + regulator_set_load(ts_data->vdd, FTS_LOAD_DISABLE_UA); + return ret; +} + +static int fts_ts_enable_reg(struct fts_ts_data *ts_data, bool enable) +{ + int ret = 0; + + if (IS_ERR_OR_NULL(ts_data->vdd)) { + FTS_ERROR("vdd is invalid"); + return -EINVAL; + } + + if (enable) { + fts_power_configure(ts_data, true); + ret = regulator_enable(ts_data->vdd); + if (ret) + FTS_ERROR("enable vdd regulator failed,ret=%d", ret); + + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + ret = regulator_enable(ts_data->vcc_i2c); + if (ret) + FTS_ERROR("enable vcc_i2c regulator failed,ret=%d", ret); + } + } else { + ret = regulator_disable(ts_data->vdd); + if (ret) + FTS_ERROR("disable vdd regulator failed,ret=%d", ret); + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + ret = regulator_disable(ts_data->vcc_i2c); + if (ret) + FTS_ERROR("disable vcc_i2c regulator failed,ret=%d", ret); + } + fts_power_configure(ts_data, false); + } + + return ret; +} + +static int fts_power_source_ctrl(struct fts_ts_data *ts_data, int enable) +{ + int ret = 0; + + if (IS_ERR_OR_NULL(ts_data->vdd)) { + FTS_ERROR("vdd is invalid"); + return -EINVAL; + } + + FTS_FUNC_ENTER(); + if (enable) { + if (ts_data->power_disabled) { + FTS_DEBUG("regulator enable !"); + gpio_direction_output(ts_data->pdata->reset_gpio, 0); + msleep(1); + ret = fts_ts_enable_reg(ts_data, true); + if (ret) + FTS_ERROR("Touch reg enable failed\n"); + ts_data->power_disabled = false; + } + } else { + if (!ts_data->power_disabled) { + FTS_DEBUG("regulator disable !"); + gpio_direction_output(ts_data->pdata->reset_gpio, 0); + msleep(1); + ret = fts_ts_enable_reg(ts_data, false); + if (ret) + FTS_ERROR("Touch reg disable failed"); + ts_data->power_disabled = true; + } + } + + FTS_FUNC_EXIT(); + return ret; +} + +/***************************************************************************** +* Name: fts_power_source_init +* Brief: Init regulator power:vdd/vcc_io(if have), generally, no vcc_io +* vdd---->vdd-supply in dts, kernel will auto add "-supply" to parse +* Must be call after fts_gpio_configure() execute,because this function +* will operate reset-gpio which request gpio in fts_gpio_configure() +* Input: +* Output: +* Return: return 0 if init power successfully, otherwise return error code +*****************************************************************************/ +static int fts_power_source_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + ts_data->vdd = regulator_get(ts_data->dev, "vdd"); + if (IS_ERR_OR_NULL(ts_data->vdd)) { + ret = PTR_ERR(ts_data->vdd); + FTS_ERROR("get vdd regulator failed,ret=%d", ret); + return ret; + } + + ts_data->vcc_i2c = regulator_get(ts_data->dev, "vcc_i2c"); + if (IS_ERR_OR_NULL(ts_data->vcc_i2c)) + FTS_INFO("get vcc_i2c regulator failed"); + +#if FTS_PINCTRL_EN + fts_pinctrl_init(ts_data); + fts_pinctrl_select_normal(ts_data); +#endif + + ts_data->power_disabled = true; + ret = fts_power_source_ctrl(ts_data, ENABLE); + if (ret) { + FTS_ERROR("fail to enable power(regulator)"); + } + + FTS_FUNC_EXIT(); + return ret; +} + +static int fts_power_source_exit(struct fts_ts_data *ts_data) +{ +#if FTS_PINCTRL_EN + fts_pinctrl_select_release(ts_data); +#endif + + fts_power_source_ctrl(ts_data, DISABLE); + + if (!IS_ERR_OR_NULL(ts_data->vdd)) { + if (regulator_count_voltages(ts_data->vdd) > 0) + regulator_set_voltage(ts_data->vdd, 0, FTS_VTG_MAX_UV); + regulator_put(ts_data->vdd); + } + + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + if (regulator_count_voltages(ts_data->vcc_i2c) > 0) + regulator_set_voltage(ts_data->vcc_i2c, 0, FTS_I2C_VTG_MAX_UV); + regulator_put(ts_data->vcc_i2c); + } + + return 0; +} + +static int fts_power_source_suspend(struct fts_ts_data *ts_data) +{ + int ret = 0; + +#if FTS_PINCTRL_EN + fts_pinctrl_select_suspend(ts_data); +#endif + + ret = fts_power_source_ctrl(ts_data, DISABLE); + if (ret < 0) { + FTS_ERROR("power off fail, ret=%d", ret); + } + + return ret; +} + +static int fts_power_source_resume(struct fts_ts_data *ts_data) +{ + int ret = 0; + +#if FTS_PINCTRL_EN + fts_pinctrl_select_normal(ts_data); +#endif + + ret = fts_power_source_ctrl(ts_data, ENABLE); + if (ret < 0) { + FTS_ERROR("power on fail, ret=%d", ret); + } + + return ret; +} +#endif /* FTS_POWER_SOURCE_CUST_EN */ + +static int fts_gpio_configure(struct fts_ts_data *data) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + /* request irq gpio */ + if (gpio_is_valid(data->pdata->irq_gpio)) { + ret = gpio_request(data->pdata->irq_gpio, "fts_irq_gpio"); + if (ret) { + FTS_ERROR("[GPIO]irq gpio request failed"); + goto err_irq_gpio_req; + } + + ret = gpio_direction_input(data->pdata->irq_gpio); + if (ret) { + FTS_ERROR("[GPIO]set_direction for irq gpio failed"); + goto err_irq_gpio_dir; + } + } + + /* request reset gpio */ + if (gpio_is_valid(data->pdata->reset_gpio)) { + ret = gpio_request(data->pdata->reset_gpio, "fts_reset_gpio"); + if (ret) { + FTS_ERROR("[GPIO]reset gpio request failed"); + goto err_irq_gpio_dir; + } + + ret = gpio_direction_output(data->pdata->reset_gpio, 1); + if (ret) { + FTS_ERROR("[GPIO]set_direction for reset gpio failed"); + goto err_reset_gpio_dir; + } + } + + FTS_FUNC_EXIT(); + return 0; + +err_reset_gpio_dir: + if (gpio_is_valid(data->pdata->reset_gpio)) + gpio_free(data->pdata->reset_gpio); +err_irq_gpio_dir: + if (gpio_is_valid(data->pdata->irq_gpio)) + gpio_free(data->pdata->irq_gpio); +err_irq_gpio_req: + FTS_FUNC_EXIT(); + return ret; +} + +static int fts_get_dt_coords(struct device *dev, char *name, + struct fts_ts_platform_data *pdata) +{ + int ret = 0; + u32 coords[FTS_COORDS_ARR_SIZE] = { 0 }; + struct property *prop; + struct device_node *np = dev->of_node; + int coords_size; + + prop = of_find_property(np, name, NULL); + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + + coords_size = prop->length / sizeof(u32); + if (coords_size != FTS_COORDS_ARR_SIZE) { + FTS_ERROR("invalid:%s, size:%d", name, coords_size); + return -EINVAL; + } + + ret = of_property_read_u32_array(np, name, coords, coords_size); + if (ret < 0) { + FTS_ERROR("Unable to read %s, please check dts", name); + pdata->x_min = FTS_X_MIN_DISPLAY_DEFAULT; + pdata->y_min = FTS_Y_MIN_DISPLAY_DEFAULT; + pdata->x_max = FTS_X_MAX_DISPLAY_DEFAULT; + pdata->y_max = FTS_Y_MAX_DISPLAY_DEFAULT; + return -ENODATA; + } else { + pdata->x_min = coords[0]; + pdata->y_min = coords[1]; + pdata->x_max = coords[2]; + pdata->y_max = coords[3]; + } + + FTS_INFO("display x(%d %d) y(%d %d)", pdata->x_min, pdata->x_max, + pdata->y_min, pdata->y_max); + return 0; +} + +static int fts_parse_dt(struct device *dev, struct fts_ts_platform_data *pdata) +{ + int ret = 0; + struct device_node *np = dev->of_node; + u32 temp_val = 0; + + FTS_FUNC_ENTER(); + + ret = fts_get_dt_coords(dev, "focaltech,display-coords", pdata); + if (ret < 0) + FTS_ERROR("Unable to get display-coords"); + + /* key */ + pdata->have_key = of_property_read_bool(np, "focaltech,have-key"); + if (pdata->have_key) { + ret = of_property_read_u32(np, "focaltech,key-number", &pdata->key_number); + if (ret < 0) + FTS_ERROR("Key number undefined!"); + + ret = of_property_read_u32_array(np, "focaltech,keys", + pdata->keys, pdata->key_number); + if (ret < 0) + FTS_ERROR("Keys undefined!"); + else if (pdata->key_number > FTS_MAX_KEYS) + pdata->key_number = FTS_MAX_KEYS; + + ret = of_property_read_u32_array(np, "focaltech,key-x-coords", + pdata->key_x_coords, + pdata->key_number); + if (ret < 0) + FTS_ERROR("Key Y Coords undefined!"); + + ret = of_property_read_u32_array(np, "focaltech,key-y-coords", + pdata->key_y_coords, + pdata->key_number); + if (ret < 0) + FTS_ERROR("Key X Coords undefined!"); + + FTS_INFO("VK Number:%d, key:(%d,%d,%d), " + "coords:(%d,%d),(%d,%d),(%d,%d)", + pdata->key_number, + pdata->keys[0], pdata->keys[1], pdata->keys[2], + pdata->key_x_coords[0], pdata->key_y_coords[0], + pdata->key_x_coords[1], pdata->key_y_coords[1], + pdata->key_x_coords[2], pdata->key_y_coords[2]); + } + + /* reset, irq gpio info */ + pdata->reset_gpio = of_get_named_gpio_flags(np, "focaltech,reset-gpio", + 0, &pdata->reset_gpio_flags); + if (pdata->reset_gpio < 0) + FTS_ERROR("Unable to get reset_gpio"); + + pdata->irq_gpio = of_get_named_gpio_flags(np, "focaltech,irq-gpio", + 0, &pdata->irq_gpio_flags); + if (pdata->irq_gpio < 0) + FTS_ERROR("Unable to get irq_gpio"); + + ret = of_property_read_u32(np, "focaltech,max-touch-number", &temp_val); + if (ret < 0) { + FTS_ERROR("Unable to get max-touch-number, please check dts"); + pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT; + } else { + if (temp_val < 2) + pdata->max_touch_number = 2; /* max_touch_number must >= 2 */ + else if (temp_val > FTS_MAX_POINTS_SUPPORT) + pdata->max_touch_number = FTS_MAX_POINTS_SUPPORT; + else + pdata->max_touch_number = temp_val; + } + + FTS_INFO("max touch number:%d, irq gpio:%d, reset gpio:%d", + pdata->max_touch_number, pdata->irq_gpio, pdata->reset_gpio); + + ret = of_property_read_u32(np, "focaltech,ic-type", &temp_val); + if (ret < 0) + pdata->type = _FT3518; + else + pdata->type = temp_val; + + FTS_FUNC_EXIT(); + return 0; +} + +#if defined(CONFIG_DRM) +static void fts_resume_work(struct work_struct *work) +{ + struct fts_ts_data *ts_data = container_of(work, struct fts_ts_data, + resume_work); + + fts_ts_resume(ts_data->dev); +} + +static void fts_ts_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data) +{ + struct fts_ts_data *ts_data = client_data; + + if (!notification) { + pr_err("Invalid notification\n"); + return; + } + + FTS_DEBUG("Notification type:%d, early_trigger:%d", + notification->notif_type, + notification->notif_data.early_trigger); + switch (notification->notif_type) { + case DRM_PANEL_EVENT_UNBLANK: + if (notification->notif_data.early_trigger) + FTS_DEBUG("resume notification pre commit\n"); + else + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + break; + case DRM_PANEL_EVENT_BLANK: + if (notification->notif_data.early_trigger) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); + } else { + FTS_DEBUG("suspend notification post commit\n"); + } + break; + case DRM_PANEL_EVENT_BLANK_LP: + FTS_DEBUG("received lp event\n"); + break; + case DRM_PANEL_EVENT_FPS_CHANGE: + FTS_DEBUG("shashank:Received fps change old fps:%d new fps:%d\n", + notification->notif_data.old_fps, + notification->notif_data.new_fps); + break; + default: + FTS_DEBUG("notification serviced :%d\n", + notification->notif_type); + break; + } +} + +#elif defined(CONFIG_FB) +static void fts_resume_work(struct work_struct *work) +{ + struct fts_ts_data *ts_data = container_of(work, struct fts_ts_data, + resume_work); + + fts_ts_resume(ts_data->dev); +} + +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank = NULL; + struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, + fb_notif); + + if (!(event == FB_EARLY_EVENT_BLANK || event == FB_EVENT_BLANK)) { + FTS_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + FTS_INFO("FB event:%lu,blank:%d", event, *blank); + switch (*blank) { + case FB_BLANK_UNBLANK: + if (FB_EARLY_EVENT_BLANK == event) { + FTS_INFO("resume: event = %lu, not care\n", event); + } else if (FB_EVENT_BLANK == event) { + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + } + break; + + case FB_BLANK_POWERDOWN: + if (FB_EARLY_EVENT_BLANK == event) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); + } else if (FB_EVENT_BLANK == event) { + FTS_INFO("suspend: event = %lu, not care\n", event); + } + break; + + default: + FTS_INFO("FB BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void fts_ts_early_suspend(struct early_suspend *handler) +{ + struct fts_ts_data *ts_data = container_of(handler, struct fts_ts_data, + early_suspend); + + fts_ts_suspend(ts_data->dev); +} + +static void fts_ts_late_resume(struct early_suspend *handler) +{ + struct fts_ts_data *ts_data = container_of(handler, struct fts_ts_data, + early_suspend); + + fts_ts_resume(ts_data->dev); +} +#endif + +static int fts_ts_probe_delayed(struct fts_ts_data *fts_data) +{ + int ret = 0; + +/* Avoid setting up hardware for TVM during probe */ +#ifdef CONFIG_FTS_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + if (!atomic_read(&fts_data->delayed_vm_probe_pending)) { + atomic_set(&fts_data->delayed_vm_probe_pending, 1); + return 0; + } + goto tvm_setup; +#endif +#endif + ret = fts_gpio_configure(fts_data); + if (ret) { + FTS_ERROR("configure the gpios fail"); + goto err_gpio_config; + } + +#if FTS_POWER_SOURCE_CUST_EN + ret = fts_power_source_init(fts_data); + if (ret) { + FTS_ERROR("fail to get power(regulator)"); + goto err_power_init; + } +#endif + + if (!FTS_CHIP_IDC(fts_data->pdata->type)) + fts_reset_proc(200); + + ret = fts_get_ic_information(fts_data); + if (ret) { + FTS_ERROR("not focal IC, unregister driver"); + goto err_irq_req; + } + +#ifdef CONFIG_ARCH_QTI_VM +tvm_setup: +#endif + ret = fts_irq_registration(fts_data); + if (ret) { + FTS_ERROR("request irq failed"); +#ifdef CONFIG_ARCH_QTI_VM + return ret; +#endif + goto err_irq_req; + } + +#ifdef CONFIG_ARCH_QTI_VM + return ret; +#endif + + ret = fts_fwupg_init(fts_data); + if (ret) + FTS_ERROR("init fw upgrade fail"); + + return 0; + +err_irq_req: + if (gpio_is_valid(fts_data->pdata->reset_gpio)) + gpio_free(fts_data->pdata->reset_gpio); + if (gpio_is_valid(fts_data->pdata->irq_gpio)) + gpio_free(fts_data->pdata->irq_gpio); +#if FTS_POWER_SOURCE_CUST_EN +err_power_init: + fts_power_source_exit(fts_data); +#endif +err_gpio_config: + return ret; +} + +static int fts_ts_probe_entry(struct fts_ts_data *ts_data) +{ + int ret = 0; + int pdata_size = sizeof(struct fts_ts_platform_data); + + FTS_FUNC_ENTER(); + FTS_INFO("%s", FTS_DRIVER_VERSION); + ts_data->pdata = kzalloc(pdata_size, GFP_KERNEL); + if (!ts_data->pdata) { + FTS_ERROR("allocate memory for platform_data fail"); + return -ENOMEM; + } + + if (ts_data->dev->of_node) { + ret = fts_parse_dt(ts_data->dev, ts_data->pdata); + if (ret) + FTS_ERROR("device-tree parse fail"); + } else { + if (ts_data->dev->platform_data) { + memcpy(ts_data->pdata, ts_data->dev->platform_data, pdata_size); + } else { + FTS_ERROR("platform_data is null"); + return -ENODEV; + } + } + + ts_data->ts_workqueue = create_singlethread_workqueue("fts_wq"); + if (!ts_data->ts_workqueue) { + FTS_ERROR("create fts workqueue fail"); + } + + spin_lock_init(&ts_data->irq_lock); + mutex_init(&ts_data->report_mutex); + mutex_init(&ts_data->bus_lock); + mutex_init(&ts_data->transition_lock); + + /* Init communication interface */ + ret = fts_bus_init(ts_data); + if (ret) { + FTS_ERROR("bus initialize fail"); + goto err_bus_init; + } + + ret = fts_input_init(ts_data); + if (ret) { + FTS_ERROR("input initialize fail"); + goto err_input_init; + } + + ret = fts_report_buffer_init(ts_data); + if (ret) { + FTS_ERROR("report buffer init fail"); + goto err_report_buffer; + } + + ret = fts_create_apk_debug_channel(ts_data); + if (ret) { + FTS_ERROR("create apk debug node fail"); + } + + ret = fts_create_sysfs(ts_data); + if (ret) { + FTS_ERROR("create sysfs node fail"); + } + +#if FTS_POINT_REPORT_CHECK_EN + ret = fts_point_report_check_init(ts_data); + if (ret) { + FTS_ERROR("init point report check fail"); + } +#endif + + ret = fts_ex_mode_init(ts_data); + if (ret) { + FTS_ERROR("init glove/cover/charger fail"); + } + + ret = fts_gesture_init(ts_data); + if (ret) { + FTS_ERROR("init gesture fail"); + } + + +#if FTS_ESDCHECK_EN + ret = fts_esdcheck_init(ts_data); + if (ret) { + FTS_ERROR("init esd check fail"); + } +#endif + +#ifdef CONFIG_FTS_TRUSTED_TOUCH + fts_ts_trusted_touch_init(ts_data); + mutex_init(&(ts_data->fts_clk_io_ctrl_mutex)); +#endif + ret = fts_ts_probe_delayed(ts_data); + if (ret) { + FTS_ERROR("Failed to enable resources\n"); + goto err_probe_delayed; + } + +#if defined(CONFIG_DRM) + if (ts_data->ts_workqueue) + INIT_WORK(&ts_data->resume_work, fts_resume_work); + + if (!strcmp(fts_data->touch_environment, "pvm")) + fts_ts_register_for_panel_events(ts_data->dev->of_node, ts_data); +#elif defined(CONFIG_FB) + if (ts_data->ts_workqueue) { + INIT_WORK(&ts_data->resume_work, fts_resume_work); + } + ts_data->fb_notif.notifier_call = fb_notifier_callback; + ret = fb_register_client(&ts_data->fb_notif); + if (ret) { + FTS_ERROR("[FB]Unable to register fb_notifier: %d", ret); + } +#elif defined(CONFIG_HAS_EARLYSUSPEND) + ts_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + FTS_SUSPEND_LEVEL; + ts_data->early_suspend.suspend = fts_ts_early_suspend; + ts_data->early_suspend.resume = fts_ts_late_resume; + register_early_suspend(&ts_data->early_suspend); +#endif + + FTS_FUNC_EXIT(); + return 0; + +err_probe_delayed: + kfree_safe(ts_data->point_buf); + kfree_safe(ts_data->events); +err_report_buffer: + input_unregister_device(ts_data->input_dev); +err_input_init: + if (ts_data->ts_workqueue) + destroy_workqueue(ts_data->ts_workqueue); +err_bus_init: + kfree_safe(ts_data->bus_tx_buf); + kfree_safe(ts_data->bus_rx_buf); + kfree_safe(ts_data->pdata); + + FTS_FUNC_EXIT(); + return ret; +} + +static int fts_ts_remove_entry(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + +#if FTS_POINT_REPORT_CHECK_EN + fts_point_report_check_exit(ts_data); +#endif + + fts_release_apk_debug_channel(ts_data); + fts_remove_sysfs(ts_data); + fts_ex_mode_exit(ts_data); + + fts_fwupg_exit(ts_data); + + +#if FTS_ESDCHECK_EN + fts_esdcheck_exit(ts_data); +#endif + + fts_gesture_exit(ts_data); + fts_bus_exit(ts_data); + + free_irq(ts_data->irq, ts_data); + input_unregister_device(ts_data->input_dev); + + if (ts_data->ts_workqueue) + destroy_workqueue(ts_data->ts_workqueue); + +#if defined(CONFIG_DRM) + if (active_panel && ts_data->notifier_cookie) + panel_event_notifier_unregister(ts_data->notifier_cookie); + +#elif defined(CONFIG_FB) + if (fb_unregister_client(&ts_data->fb_notif)) + FTS_ERROR("Error occurred while unregistering fb_notifier."); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts_data->early_suspend); +#endif + + if (gpio_is_valid(ts_data->pdata->reset_gpio)) + gpio_free(ts_data->pdata->reset_gpio); + + if (gpio_is_valid(ts_data->pdata->irq_gpio)) + gpio_free(ts_data->pdata->irq_gpio); + +#if FTS_POWER_SOURCE_CUST_EN + fts_power_source_exit(ts_data); +#endif + + kfree_safe(ts_data->point_buf); + kfree_safe(ts_data->events); + + kfree_safe(ts_data->pdata); + kfree_safe(ts_data); + + FTS_FUNC_EXIT(); + + return 0; +} + +static int fts_ts_suspend(struct device *dev) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + + FTS_FUNC_ENTER(); + if (ts_data->suspended) { + FTS_INFO("Already in suspend state"); + return 0; + } + + if (ts_data->fw_loading) { + FTS_INFO("fw upgrade in process, can't suspend"); + return 0; + } + +#ifdef CONFIG_FTS_TRUSTED_TOUCH + if (atomic_read(&fts_data->trusted_touch_transition) + || atomic_read(&fts_data->trusted_touch_enabled)) + wait_for_completion_interruptible( + &fts_data->trusted_touch_powerdown); +#endif + + mutex_lock(&ts_data->transition_lock); + +#if FTS_ESDCHECK_EN + fts_esdcheck_suspend(); +#endif + + if (ts_data->gesture_mode) { + fts_gesture_suspend(ts_data); + } else { + fts_irq_disable(); + + FTS_INFO("make TP enter into sleep mode"); + ret = fts_write_reg(FTS_REG_POWER_MODE, FTS_REG_POWER_MODE_SLEEP); + if (ret < 0) + FTS_ERROR("set TP to sleep mode fail, ret=%d", ret); + + if (!ts_data->ic_info.is_incell) { +#if FTS_POWER_SOURCE_CUST_EN + ret = fts_power_source_suspend(ts_data); + if (ret < 0) { + FTS_ERROR("power enter suspend fail"); + } +#endif + } + } + + fts_release_all_finger(); + ts_data->suspended = true; + mutex_unlock(&ts_data->transition_lock); + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_ts_resume(struct device *dev) +{ + struct fts_ts_data *ts_data = fts_data; + + FTS_FUNC_ENTER(); + if (!ts_data->suspended) { + FTS_DEBUG("Already in awake state"); + return 0; + } + +#ifdef CONFIG_FTS_TRUSTED_TOUCH + + if (atomic_read(&ts_data->trusted_touch_transition)) + wait_for_completion_interruptible( + &ts_data->trusted_touch_powerdown); +#endif + + mutex_lock(&ts_data->transition_lock); + + fts_release_all_finger(); + + if (!ts_data->ic_info.is_incell) { +#if FTS_POWER_SOURCE_CUST_EN + fts_power_source_resume(ts_data); +#endif + fts_reset_proc(200); + } + + fts_wait_tp_to_valid(); + fts_ex_mode_recovery(ts_data); + +#if FTS_ESDCHECK_EN + fts_esdcheck_resume(); +#endif + + if (ts_data->gesture_mode) { + fts_gesture_resume(ts_data); + } else { + fts_irq_enable(); + } + + ts_data->suspended = false; + mutex_unlock(&ts_data->transition_lock); + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* TP Driver +*****************************************************************************/ +static int fts_ts_check_dt(struct device_node *np) +{ + int i; + int count; + struct device_node *node; + struct drm_panel *panel; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) + return 0; + + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + panel = of_drm_find_panel(node); + of_node_put(node); + if (!IS_ERR(panel)) { + active_panel = panel; + return 0; + } + } + + return PTR_ERR(panel); +} + +static int fts_ts_check_default_tp(struct device_node *dt, const char *prop) +{ + const char **active_tp = NULL; + int count, tmp, score = 0; + const char *active; + int ret, i; + + count = of_property_count_strings(dt->parent, prop); + if (count <= 0 || count > 3) + return -ENODEV; + + active_tp = kcalloc(count, sizeof(char *), GFP_KERNEL); + if (!active_tp) { + FTS_ERROR("FTS alloc failed\n"); + return -ENOMEM; + } + + ret = of_property_read_string_array(dt->parent, prop, + active_tp, count); + if (ret < 0) { + FTS_ERROR("fail to read %s %d\n", prop, ret); + ret = -ENODEV; + goto out; + } + + for (i = 0; i < count; i++) { + active = active_tp[i]; + if (active != NULL) { + tmp = of_device_is_compatible(dt, active); + if (tmp > 0) + score++; + } + } + + if (score <= 0) { + FTS_INFO("not match this driver\n"); + ret = -ENODEV; + goto out; + } + ret = 0; +out: + kfree(active_tp); + return ret; +} + +static int fts_ts_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + struct fts_ts_data *ts_data = NULL; + struct device_node *dp = client->dev.of_node; + + FTS_INFO("Touch Screen(I2C BUS) driver prboe..."); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + FTS_ERROR("I2C not supported"); + return -ENODEV; + } + + ret = fts_ts_check_dt(dp); + if (ret == -EPROBE_DEFER) + return ret; + + if (ret) { + if (!fts_ts_check_default_tp(dp, "qcom,i2c-touch-active")) + ret = -EPROBE_DEFER; + else + ret = -ENODEV; + + return ret; + } + + /* malloc memory for global struct variable */ + ts_data = (struct fts_ts_data *)kzalloc(sizeof(*ts_data), GFP_KERNEL); + if (!ts_data) { + FTS_ERROR("allocate memory for fts_data fail"); + return -ENOMEM; + } + + fts_data = ts_data; + ts_data->client = client; + ts_data->dev = &client->dev; + ts_data->log_level = 1; + ts_data->fw_is_running = 0; + ts_data->bus_type = BUS_TYPE_I2C; + i2c_set_clientdata(client, ts_data); + + ret = fts_ts_probe_entry(ts_data); + if (ret) { + FTS_ERROR("Touch Screen(I2C BUS) driver probe fail"); + kfree_safe(ts_data); + return ret; + } + + FTS_INFO("Touch Screen(I2C BUS) driver prboe successfully"); + return 0; +} + +static int fts_ts_i2c_remove(struct i2c_client *client) +{ + return fts_ts_remove_entry(i2c_get_clientdata(client)); +} + +static const struct i2c_device_id fts_ts_i2c_id[] = { + {FTS_DRIVER_NAME, 0}, + {}, +}; +static const struct of_device_id fts_dt_match[] = { + {.compatible = "focaltech,fts_ts", }, + {}, +}; +MODULE_DEVICE_TABLE(of, fts_dt_match); + +static struct i2c_driver fts_ts_i2c_driver = { + .probe = fts_ts_i2c_probe, + .remove = fts_ts_i2c_remove, + .driver = { + .name = FTS_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(fts_dt_match), + }, + .id_table = fts_ts_i2c_id, +}; + +static int __init fts_ts_i2c_init(void) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + ret = i2c_add_driver(&fts_ts_i2c_driver); + if (ret != 0) + FTS_ERROR("Focaltech touch screen driver init failed!"); + + FTS_FUNC_EXIT(); + return ret; +} + +static void __exit fts_ts_i2c_exit(void) +{ + i2c_del_driver(&fts_ts_i2c_driver); +} + +static int fts_ts_spi_probe(struct spi_device *spi) +{ + int ret = 0; + struct fts_ts_data *ts_data = NULL; + struct device_node *dp = spi->dev.of_node; + + FTS_INFO("Touch Screen(SPI BUS) driver prboe..."); + + ret = fts_ts_check_dt(dp); + if (ret == -EPROBE_DEFER) + return ret; + + if (ret) { + if (!fts_ts_check_default_tp(dp, "qcom,spi-touch-active")) + ret = -EPROBE_DEFER; + else + ret = -ENODEV; + + return ret; + } + + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret) { + FTS_ERROR("spi setup fail"); + return ret; + } + + /* malloc memory for global struct variable */ + ts_data = kzalloc(sizeof(*ts_data), GFP_KERNEL); + if (!ts_data) { + FTS_ERROR("allocate memory for fts_data fail"); + return -ENOMEM; + } + + fts_data = ts_data; + ts_data->spi = spi; + ts_data->dev = &spi->dev; + ts_data->log_level = 1; + + ts_data->bus_type = BUS_TYPE_SPI_V2; + spi_set_drvdata(spi, ts_data); + + ret = fts_ts_probe_entry(ts_data); + if (ret) { + FTS_ERROR("Touch Screen(SPI BUS) driver probe fail"); + kfree_safe(ts_data); + return ret; + } + + FTS_INFO("Touch Screen(SPI BUS) driver prboe successfully"); + return 0; +} + +static int fts_ts_spi_remove(struct spi_device *spi) +{ + return fts_ts_remove_entry(spi_get_drvdata(spi)); +} + +static const struct spi_device_id fts_ts_spi_id[] = { + {FTS_DRIVER_NAME, 0}, + {}, +}; + +static struct spi_driver fts_ts_spi_driver = { + .probe = fts_ts_spi_probe, + .remove = fts_ts_spi_remove, + .driver = { + .name = FTS_DRIVER_NAME, + .owner = THIS_MODULE, +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + .pm = &fts_dev_pm_ops, +#endif + .of_match_table = of_match_ptr(fts_dt_match), + }, + .id_table = fts_ts_spi_id, +}; + +static int __init fts_ts_spi_init(void) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + ret = spi_register_driver(&fts_ts_spi_driver); + if (ret != 0) + FTS_ERROR("Focaltech touch screen driver init failed!"); + + FTS_FUNC_EXIT(); + return ret; +} + +static void __exit fts_ts_spi_exit(void) +{ + spi_unregister_driver(&fts_ts_spi_driver); +} + +static int __init fts_ts_init(void) +{ + int ret = 0; + + ret = fts_ts_i2c_init(); + if (ret) + FTS_ERROR("Focaltech I2C driver init failed!"); + + ret = fts_ts_spi_init(); + if (ret) + FTS_ERROR("Focaltech SPI driver init failed!"); + + return ret; +} + +static void __exit fts_ts_exit(void) +{ + fts_ts_i2c_exit(); + fts_ts_spi_exit(); +} + +#ifdef CONFIG_ARCH_QTI_VM +module_init(fts_ts_init); +#else +late_initcall(fts_ts_init); +#endif +module_exit(fts_ts_exit); + +MODULE_AUTHOR("FocalTech Driver Team"); +MODULE_DESCRIPTION("FocalTech Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/focaltech_touch/focaltech_core.h b/focaltech_touch/focaltech_core.h new file mode 100644 index 0000000000..9be61d55fd --- /dev/null +++ b/focaltech_touch/focaltech_core.h @@ -0,0 +1,389 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +/***************************************************************************** +* +* File Name: focaltech_core.h + +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +#ifndef __LINUX_FOCALTECH_CORE_H__ +#define __LINUX_FOCALTECH_CORE_H__ +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "focaltech_common.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_MAX_POINTS_SUPPORT 10 /* constant value, can't be changed */ +#define FTS_MAX_KEYS 4 +#define FTS_KEY_DIM 10 +#define FTS_ONE_TCH_LEN 6 +#define FTS_TOUCH_DATA_LEN (FTS_MAX_POINTS_SUPPORT * FTS_ONE_TCH_LEN + 3) + +#define FTS_GESTURE_POINTS_MAX 6 +#define FTS_GESTURE_DATA_LEN (FTS_GESTURE_POINTS_MAX * 4 + 4) + +#define FTS_MAX_ID 0x0A +#define FTS_TOUCH_X_H_POS 3 +#define FTS_TOUCH_X_L_POS 4 +#define FTS_TOUCH_Y_H_POS 5 +#define FTS_TOUCH_Y_L_POS 6 +#define FTS_TOUCH_PRE_POS 7 +#define FTS_TOUCH_AREA_POS 8 +#define FTS_TOUCH_POINT_NUM 2 +#define FTS_TOUCH_EVENT_POS 3 +#define FTS_TOUCH_ID_POS 5 +#define FTS_COORDS_ARR_SIZE 4 +#define FTS_X_MIN_DISPLAY_DEFAULT 0 +#define FTS_Y_MIN_DISPLAY_DEFAULT 0 +#define FTS_X_MAX_DISPLAY_DEFAULT 720 +#define FTS_Y_MAX_DISPLAY_DEFAULT 1280 + +#define FTS_TOUCH_DOWN 0 +#define FTS_TOUCH_UP 1 +#define FTS_TOUCH_CONTACT 2 +#define EVENT_DOWN(flag) ((FTS_TOUCH_DOWN == flag) || (FTS_TOUCH_CONTACT == flag)) +#define EVENT_UP(flag) (FTS_TOUCH_UP == flag) +#define EVENT_NO_DOWN(data) (!data->point_num) + +#define FTX_MAX_COMPATIBLE_TYPE 4 +#define FTX_MAX_COMMMAND_LENGTH 16 + + +/***************************************************************************** +* Alternative mode (When something goes wrong, the modules may be able to solve the problem.) +*****************************************************************************/ +/* + * For commnication error in PM(deep sleep) state + */ +#define FTS_PATCH_COMERR_PM 0 +#define FTS_TIMEOUT_COMERR_PM 700 + + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +struct ftxxxx_proc { + struct proc_dir_entry *proc_entry; + u8 opmode; + u8 cmd_len; + u8 cmd[FTX_MAX_COMMMAND_LENGTH]; +}; + +struct fts_ts_platform_data { + u32 type; + u32 irq_gpio; + u32 irq_gpio_flags; + u32 reset_gpio; + u32 reset_gpio_flags; + bool have_key; + u32 key_number; + u32 keys[FTS_MAX_KEYS]; + u32 key_y_coords[FTS_MAX_KEYS]; + u32 key_x_coords[FTS_MAX_KEYS]; + u32 x_max; + u32 y_max; + u32 x_min; + u32 y_min; + u32 max_touch_number; +}; + +struct ts_event { + int x; /*x coordinate */ + int y; /*y coordinate */ + int p; /* pressure */ + int flag; /* touch event flag: 0 -- down; 1-- up; 2 -- contact */ + int id; /*touch ID */ + int area; +}; + +enum trusted_touch_mode_config { + TRUSTED_TOUCH_VM_MODE, + TRUSTED_TOUCH_MODE_NONE +}; + +enum trusted_touch_pvm_states { + TRUSTED_TOUCH_PVM_INIT, + PVM_I2C_RESOURCE_ACQUIRED, + PVM_INTERRUPT_DISABLED, + PVM_IOMEM_LENT, + PVM_IOMEM_LENT_NOTIFIED, + PVM_IRQ_LENT, + PVM_IRQ_LENT_NOTIFIED, + PVM_IOMEM_RELEASE_NOTIFIED, + PVM_IRQ_RELEASE_NOTIFIED, + PVM_ALL_RESOURCES_RELEASE_NOTIFIED, + PVM_IRQ_RECLAIMED, + PVM_IOMEM_RECLAIMED, + PVM_INTERRUPT_ENABLED, + PVM_I2C_RESOURCE_RELEASED, + TRUSTED_TOUCH_PVM_STATE_MAX +}; + +enum trusted_touch_tvm_states { + TRUSTED_TOUCH_TVM_INIT, + TVM_IOMEM_LENT_NOTIFIED, + TVM_IRQ_LENT_NOTIFIED, + TVM_ALL_RESOURCES_LENT_NOTIFIED, + TVM_IOMEM_ACCEPTED, + TVM_I2C_SESSION_ACQUIRED, + TVM_IRQ_ACCEPTED, + TVM_INTERRUPT_ENABLED, + TVM_INTERRUPT_DISABLED, + TVM_IRQ_RELEASED, + TVM_I2C_SESSION_RELEASED, + TVM_IOMEM_RELEASED, + TRUSTED_TOUCH_TVM_STATE_MAX +}; + +#ifdef CONFIG_FTS_TRUSTED_TOUCH +#define TRUSTED_TOUCH_MEM_LABEL 0x7 + +#define TOUCH_RESET_GPIO_BASE 0xF114000 +#define TOUCH_RESET_GPIO_SIZE 0x1000 +#define TOUCH_RESET_GPIO_OFFSET 0x4 +#define TOUCH_INTR_GPIO_BASE 0xF115000 +#define TOUCH_INTR_GPIO_SIZE 0x1000 +#define TOUCH_INTR_GPIO_OFFSET 0x8 + +#define TRUSTED_TOUCH_EVENT_LEND_FAILURE -1 +#define TRUSTED_TOUCH_EVENT_LEND_NOTIFICATION_FAILURE -2 +#define TRUSTED_TOUCH_EVENT_ACCEPT_FAILURE -3 +#define TRUSTED_TOUCH_EVENT_FUNCTIONAL_FAILURE -4 +#define TRUSTED_TOUCH_EVENT_RELEASE_FAILURE -5 +#define TRUSTED_TOUCH_EVENT_RECLAIM_FAILURE -6 +#define TRUSTED_TOUCH_EVENT_I2C_FAILURE -7 +#define TRUSTED_TOUCH_EVENT_NOTIFICATIONS_PENDING 5 + +struct trusted_touch_vm_info { + enum gh_irq_label irq_label; + enum gh_mem_notifier_tag mem_tag; + enum gh_vm_names vm_name; + const char *trusted_touch_type; + u32 hw_irq; + gh_memparcel_handle_t vm_mem_handle; + u32 *iomem_bases; + u32 *iomem_sizes; + u32 iomem_list_size; + void *mem_cookie; + atomic_t vm_state; +}; +#endif + +struct fts_ts_data { + struct i2c_client *client; + struct spi_device *spi; + struct device *dev; + struct input_dev *input_dev; + struct fts_ts_platform_data *pdata; + struct ts_ic_info ic_info; + struct workqueue_struct *ts_workqueue; + struct work_struct fwupg_work; + struct delayed_work esdcheck_work; + struct delayed_work prc_work; + struct work_struct resume_work; + struct work_struct suspend_work; + struct ftxxxx_proc proc; + spinlock_t irq_lock; + struct mutex report_mutex; + struct mutex bus_lock; + struct mutex transition_lock; + int irq; + int log_level; + int fw_is_running; /* confirm fw is running when using spi:default 0 */ + int dummy_byte; +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + struct completion pm_completion; + bool pm_suspend; +#endif + bool suspended; + bool fw_loading; + bool irq_disabled; + bool power_disabled; + bool glove_mode; + bool cover_mode; + bool charger_mode; + bool gesture_mode; /* gesture enable or disable, default: disable */ + int report_rate; + /* multi-touch */ + struct ts_event *events; + u8 *bus_tx_buf; + u8 *bus_rx_buf; + int bus_type; + u8 *point_buf; + void *notifier_cookie; + int pnt_buf_size; + int touchs; + int key_state; + int touch_point; + int point_num; + struct regulator *vdd; + struct regulator *vcc_i2c; +#if FTS_PINCTRL_EN + struct pinctrl *pinctrl; + struct pinctrl_state *pins_active; + struct pinctrl_state *pins_suspend; + struct pinctrl_state *pins_release; +#endif +#if defined(CONFIG_FB) || defined(CONFIG_DRM) + struct notifier_block fb_notif; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif + +#ifdef CONFIG_FTS_TRUSTED_TOUCH + struct trusted_touch_vm_info *vm_info; + struct mutex fts_clk_io_ctrl_mutex; + const char *touch_environment; + struct completion trusted_touch_powerdown; + struct clk *core_clk; + struct clk *iface_clk; + atomic_t trusted_touch_initialized; + atomic_t trusted_touch_enabled; + atomic_t trusted_touch_transition; + atomic_t trusted_touch_event; + atomic_t trusted_touch_abort_status; + atomic_t delayed_vm_probe_pending; + atomic_t trusted_touch_mode; +#endif +}; + +enum _FTS_BUS_TYPE { + BUS_TYPE_NONE, + BUS_TYPE_I2C, + BUS_TYPE_SPI, + BUS_TYPE_SPI_V2, +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +extern struct fts_ts_data *fts_data; + +/* communication interface */ +int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen); +int fts_read_reg(u8 addr, u8 *value); +int fts_write(u8 *writebuf, u32 writelen); +int fts_write_reg(u8 addr, u8 value); +void fts_hid2std(void); +int fts_bus_init(struct fts_ts_data *ts_data); +int fts_bus_exit(struct fts_ts_data *ts_data); + +/* Gesture functions */ +int fts_gesture_init(struct fts_ts_data *ts_data); +int fts_gesture_exit(struct fts_ts_data *ts_data); +void fts_gesture_recovery(struct fts_ts_data *ts_data); +int fts_gesture_readdata(struct fts_ts_data *ts_data, u8 *data); +int fts_gesture_suspend(struct fts_ts_data *ts_data); +int fts_gesture_resume(struct fts_ts_data *ts_data); + +/* Apk and functions */ +int fts_create_apk_debug_channel(struct fts_ts_data *); +void fts_release_apk_debug_channel(struct fts_ts_data *); + +/* ADB functions */ +int fts_create_sysfs(struct fts_ts_data *ts_data); +int fts_remove_sysfs(struct fts_ts_data *ts_data); + +/* ESD */ +#if FTS_ESDCHECK_EN +int fts_esdcheck_init(struct fts_ts_data *ts_data); +int fts_esdcheck_exit(struct fts_ts_data *ts_data); +int fts_esdcheck_switch(bool enable); +int fts_esdcheck_proc_busy(bool proc_debug); +int fts_esdcheck_set_intr(bool intr); +int fts_esdcheck_suspend(void); +int fts_esdcheck_resume(void); +#endif + +/* Production test */ +#if FTS_TEST_EN +int fts_test_init(struct fts_ts_data *ts_data); +int fts_test_exit(struct fts_ts_data *ts_data); +#endif + +/* Point Report Check*/ +#if FTS_POINT_REPORT_CHECK_EN +int fts_point_report_check_init(struct fts_ts_data *ts_data); +int fts_point_report_check_exit(struct fts_ts_data *ts_data); +void fts_prc_queue_work(struct fts_ts_data *ts_data); +#endif + +/* FW upgrade */ +int fts_fwupg_init(struct fts_ts_data *ts_data); +int fts_fwupg_exit(struct fts_ts_data *ts_data); +int fts_enter_test_environment(bool test_state); + +/* Other */ +int fts_reset_proc(int hdelayms); +int fts_wait_tp_to_valid(void); +void fts_release_all_finger(void); +void fts_tp_state_recovery(struct fts_ts_data *ts_data); +int fts_ex_mode_init(struct fts_ts_data *ts_data); +int fts_ex_mode_exit(struct fts_ts_data *ts_data); +int fts_ex_mode_recovery(struct fts_ts_data *ts_data); + +void fts_irq_disable(void); +void fts_irq_enable(void); +int fts_ts_handle_trusted_touch_pvm(struct fts_ts_data *ts_data, int value); +int fts_ts_handle_trusted_touch_tvm(struct fts_ts_data *ts_data, int value); +#ifdef CONFIG_FTS_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM +void fts_ts_trusted_touch_tvm_i2c_failure_report(struct fts_ts_data *fts_data); +#endif +#endif +#endif /* __LINUX_FOCALTECH_CORE_H__ */ diff --git a/focaltech_touch/focaltech_esdcheck.c b/focaltech_touch/focaltech_esdcheck.c new file mode 100644 index 0000000000..295f91d2f8 --- /dev/null +++ b/focaltech_touch/focaltech_esdcheck.c @@ -0,0 +1,465 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_esdcheck.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-03 +* +* Abstract: ESD check function +* +* Version: v1.0 +* +* Revision History: +* v1.0: +* First release. By luougojin 2016-08-03 +* v1.1: By luougojin 2017-02-15 +* 1. Add LCD_ESD_PATCH to control idc_esdcheck_lcderror +*****************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +#if FTS_ESDCHECK_EN +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define ESDCHECK_WAIT_TIME 1000 /* ms */ +#define LCD_ESD_PATCH 0 +#define ESDCHECK_INTRCNT_MAX 2 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +struct fts_esdcheck_st { + u8 mode : 1; /* 1- need check esd 0- no esd check */ + u8 suspend : 1; + u8 proc_debug : 1; /* apk or adb use */ + u8 intr : 1; /* 1- Interrupt trigger */ + u8 unused : 4; + u8 intr_cnt; + u8 flow_work_hold_cnt; /* Flow Work Cnt(reg0x91) keep a same value for x times. >=5 times is ESD, need reset */ + u8 flow_work_cnt_last; /* Save Flow Work Cnt(reg0x91) value */ + u32 hardware_reset_cnt; + u32 nack_cnt; + u32 dataerror_cnt; +}; + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static struct fts_esdcheck_st fts_esdcheck_data; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* functions body +*****************************************************************************/ +#if LCD_ESD_PATCH +int lcd_need_reset; +static int tp_need_recovery; /* LCD reset cause Tp reset */ +int idc_esdcheck_lcderror(struct fts_ts_data *ts_data) +{ + int ret = 0; + u8 val = 0; + + FTS_DEBUG("check LCD ESD"); + if ( (tp_need_recovery == 1) && (lcd_need_reset == 0) ) { + tp_need_recovery = 0; + /* LCD reset, need recover TP state */ + fts_release_all_finger(); + fts_tp_state_recovery(ts_data); + } + + ret = fts_read_reg(FTS_REG_ESD_SATURATE, &val); + if ( ret < 0) { + FTS_ERROR("read reg0xED fail,ret:%d", ret); + return -EIO; + } + + if (val == 0xAA) { + /* + * 1. Set flag lcd_need_reset = 1; + * 2. LCD driver need reset(recovery) LCD and set lcd_need_reset to 0 + * 3. recover TP state + */ + FTS_INFO("LCD ESD, need execute LCD reset"); + lcd_need_reset = 1; + tp_need_recovery = 1; + } + + return 0; +} +#endif + +static int fts_esdcheck_tp_reset(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + + fts_esdcheck_data.flow_work_hold_cnt = 0; + fts_esdcheck_data.hardware_reset_cnt++; + + fts_reset_proc(200); + fts_release_all_finger(); + fts_tp_state_recovery(ts_data); + + FTS_FUNC_EXIT(); + return 0; +} + +static bool get_chip_id(struct fts_ts_data *ts_data) +{ + int ret = 0; + int i = 0; + u8 reg_value = 0; + u8 reg_addr = 0; + u8 chip_id = ts_data->ic_info.ids.chip_idh; + + for (i = 0; i < 3; i++) { + reg_addr = FTS_REG_CHIP_ID; + ret = fts_read(®_addr, 1, ®_value, 1); + if (ret < 0) { + FTS_ERROR("read chip id fail,ret:%d", ret); + fts_esdcheck_data.nack_cnt++; + } else { + if (reg_value == chip_id) { + break; + } else { + FTS_DEBUG("read chip_id:%x,retry:%d", reg_value, i); + fts_esdcheck_data.dataerror_cnt++; + } + } + msleep(10); + } + + /* if can't get correct data in 3 times, then need hardware reset */ + if (i >= 3) { + FTS_ERROR("read chip id 3 times fail, need execute TP reset"); + return true; + } + + return false; +} + +/***************************************************************************** +* Name: get_flow_cnt +* Brief: Read flow cnt(0x91) +* Input: +* Output: +* Return: 1(true) - Reg 0x91(flow cnt) abnormal: hold a value for 5 times +* 0(false) - Reg 0x91(flow cnt) normal +*****************************************************************************/ +static bool get_flow_cnt(struct fts_ts_data *ts_data) +{ + int ret = 0; + u8 reg_value = 0; + u8 reg_addr = 0; + + reg_addr = FTS_REG_FLOW_WORK_CNT; + ret = fts_read(®_addr, 1, ®_value, 1); + if (ret < 0) { + FTS_ERROR("read reg0x91 fail,ret:%d", ret); + fts_esdcheck_data.nack_cnt++; + } else { + if ( reg_value == fts_esdcheck_data.flow_work_cnt_last ) { + FTS_DEBUG("reg0x91,val:%x,last:%x", reg_value, + fts_esdcheck_data.flow_work_cnt_last); + fts_esdcheck_data.flow_work_hold_cnt++; + } else { + fts_esdcheck_data.flow_work_hold_cnt = 0; + } + + fts_esdcheck_data.flow_work_cnt_last = reg_value; + } + + /* Flow Work Cnt keep a value for 5 times, need execute TP reset */ + if (fts_esdcheck_data.flow_work_hold_cnt >= 5) { + FTS_DEBUG("reg0x91 keep a value for 5 times, need execute TP reset"); + return true; + } + + return false; +} + +static int esdcheck_algorithm(struct fts_ts_data *ts_data) +{ + int ret = 0; + u8 reg_value = 0; + u8 reg_addr = 0; + bool hardware_reset = 0; + + /* 1. esdcheck is interrupt, then return */ + if (fts_esdcheck_data.intr == 1) { + fts_esdcheck_data.intr_cnt++; + if (fts_esdcheck_data.intr_cnt > ESDCHECK_INTRCNT_MAX) + fts_esdcheck_data.intr = 0; + else + return 0; + } + + /* 2. check power state, if suspend, no need check esd */ + if (fts_esdcheck_data.suspend == 1) { + FTS_DEBUG("In suspend, not check esd"); + /* because in suspend state, adb can be used, when upgrade FW, will + * active ESD check(active = 1); But in suspend, then will don't + * queue_delayed_work, when resume, don't check ESD again + */ + return 0; + } + + /* 3. check fts_esdcheck_data.proc_debug state, if 1-proc busy, no need check esd*/ + if (fts_esdcheck_data.proc_debug == 1) { + FTS_INFO("In apk/adb command mode, not check esd"); + return 0; + } + + /* 4. In factory mode, can't check esd */ + reg_addr = FTS_REG_WORKMODE; + ret = fts_read_reg(reg_addr, ®_value); + if ( ret < 0 ) { + fts_esdcheck_data.nack_cnt++; + } else if ( (reg_value & 0x70) != FTS_REG_WORKMODE_WORK_VALUE) { + FTS_DEBUG("not in work mode(%x), no check esd", reg_value); + return 0; + } + + /* 5. IDC esd check lcd default:close */ +#if LCD_ESD_PATCH + idc_esdcheck_lcderror(ts_data); +#endif + + /* 6. Get Chip ID */ + hardware_reset = get_chip_id(ts_data); + + /* 7. get Flow work cnt: 0x91 If no change for 5 times, then ESD and reset */ + if (!hardware_reset) { + hardware_reset = get_flow_cnt(ts_data); + } + + /* 8. If need hardware reset, then handle it here */ + if (hardware_reset == 1) { + FTS_DEBUG("NoACK=%d, Error Data=%d, Hardware Reset=%d", + fts_esdcheck_data.nack_cnt, + fts_esdcheck_data.dataerror_cnt, + fts_esdcheck_data.hardware_reset_cnt); + fts_esdcheck_tp_reset(ts_data); + } + + return 0; +} + +static void esdcheck_func(struct work_struct *work) +{ + struct fts_ts_data *ts_data = container_of(work, + struct fts_ts_data, esdcheck_work.work); + + if (ENABLE == fts_esdcheck_data.mode) { + esdcheck_algorithm(ts_data); + queue_delayed_work(ts_data->ts_workqueue, + &ts_data->esdcheck_work, + msecs_to_jiffies(ESDCHECK_WAIT_TIME)); + } +} + +int fts_esdcheck_set_intr(bool intr) +{ + /* interrupt don't add debug message */ + fts_esdcheck_data.intr = intr; + fts_esdcheck_data.intr_cnt = (u8)intr; + return 0; +} + +static int fts_esdcheck_get_status(void) +{ + /* interrupt don't add debug message */ + return fts_esdcheck_data.mode; +} + +/***************************************************************************** +* Name: fts_esdcheck_proc_busy +* Brief: When APK or ADB command access TP via driver, then need set proc_debug, +* then will not check ESD. +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_proc_busy(bool proc_debug) +{ + fts_esdcheck_data.proc_debug = proc_debug; + return 0; +} + +/***************************************************************************** +* Name: fts_esdcheck_switch +* Brief: FTS esd check function switch. +* Input: enable: 1 - Enable esd check +* 0 - Disable esd check +* Output: +* Return: +*****************************************************************************/ +int fts_esdcheck_switch(bool enable) +{ + struct fts_ts_data *ts_data = fts_data; + FTS_FUNC_ENTER(); + if (fts_esdcheck_data.mode == ENABLE) { + if (enable) { + FTS_DEBUG("ESD check start"); + fts_esdcheck_data.flow_work_hold_cnt = 0; + fts_esdcheck_data.flow_work_cnt_last = 0; + fts_esdcheck_data.intr = 0; + fts_esdcheck_data.intr_cnt = 0; + queue_delayed_work(ts_data->ts_workqueue, + &ts_data->esdcheck_work, + msecs_to_jiffies(ESDCHECK_WAIT_TIME)); + } else { + FTS_DEBUG("ESD check stop"); + cancel_delayed_work_sync(&ts_data->esdcheck_work); + } + } + + FTS_FUNC_EXIT(); + return 0; +} + +int fts_esdcheck_suspend(void) +{ + FTS_FUNC_ENTER(); + fts_esdcheck_switch(DISABLE); + fts_esdcheck_data.suspend = 1; + fts_esdcheck_data.intr = 0; + fts_esdcheck_data.intr_cnt = 0; + FTS_FUNC_EXIT(); + return 0; +} + +int fts_esdcheck_resume( void ) +{ + FTS_FUNC_ENTER(); + fts_esdcheck_switch(ENABLE); + fts_esdcheck_data.suspend = 0; + fts_esdcheck_data.intr = 0; + fts_esdcheck_data.intr_cnt = 0; + FTS_FUNC_EXIT(); + return 0; +} + +static ssize_t fts_esdcheck_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_DEBUG("enable esdcheck"); + fts_esdcheck_data.mode = ENABLE; + fts_esdcheck_switch(ENABLE); + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_DEBUG("disable esdcheck"); + fts_esdcheck_switch(DISABLE); + fts_esdcheck_data.mode = DISABLE; + } + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_esdcheck_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "Esd check: %s\n", \ + fts_esdcheck_get_status() ? "On" : "Off"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* sysfs esd node + * read example: cat fts_esd_mode ---read esd mode + * write example:echo 01 > fts_esd_mode ---make esdcheck enable + * + */ +static DEVICE_ATTR (fts_esd_mode, S_IRUGO | S_IWUSR, fts_esdcheck_show, fts_esdcheck_store); + +static struct attribute *fts_esd_mode_attrs[] = { + + &dev_attr_fts_esd_mode.attr, + NULL, +}; + +static struct attribute_group fts_esd_group = { + .attrs = fts_esd_mode_attrs, +}; + +int fts_create_esd_sysfs(struct device *dev) +{ + int ret = 0; + + ret = sysfs_create_group(&dev->kobj, &fts_esd_group); + if ( ret != 0) { + FTS_ERROR("fts_create_esd_sysfs(sysfs) create fail"); + sysfs_remove_group(&dev->kobj, &fts_esd_group); + return ret; + } + return 0; +} + +int fts_esdcheck_init(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + + if (ts_data->ts_workqueue) { + INIT_DELAYED_WORK(&ts_data->esdcheck_work, esdcheck_func); + } else { + FTS_ERROR("fts workqueue is NULL, can't run esd check func!"); + return -EINVAL; + } + + memset((u8 *)&fts_esdcheck_data, 0, sizeof(struct fts_esdcheck_st)); + + fts_esdcheck_data.mode = ENABLE; + fts_esdcheck_data.intr = 0; + fts_esdcheck_data.intr_cnt = 0; + fts_esdcheck_switch(ENABLE); + fts_create_esd_sysfs(ts_data->dev); + FTS_FUNC_EXIT(); + return 0; +} + +int fts_esdcheck_exit(struct fts_ts_data *ts_data) +{ + sysfs_remove_group(&ts_data->dev->kobj, &fts_esd_group); + return 0; +} + +#endif /* FTS_ESDCHECK_EN */ + diff --git a/focaltech_touch/focaltech_ex_fun.c b/focaltech_touch/focaltech_ex_fun.c new file mode 100644 index 0000000000..518a7682cd --- /dev/null +++ b/focaltech_touch/focaltech_ex_fun.c @@ -0,0 +1,1229 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/***************************************************************************** +* +* File Name: Focaltech_ex_fun.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define PROC_UPGRADE 0 +#define PROC_READ_REGISTER 1 +#define PROC_WRITE_REGISTER 2 +#define PROC_AUTOCLB 4 +#define PROC_UPGRADE_INFO 5 +#define PROC_WRITE_DATA 6 +#define PROC_READ_DATA 7 +#define PROC_SET_TEST_FLAG 8 +#define PROC_SET_SLAVE_ADDR 10 +#define PROC_HW_RESET 11 +#define PROC_READ_STATUS 12 +#define PROC_SET_BOOT_MODE 13 +#define PROC_ENTER_TEST_ENVIRONMENT 14 +#define PROC_NAME "ftxxxx-debug" +#define PROC_BUF_SIZE 256 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +enum { + RWREG_OP_READ = 0, + RWREG_OP_WRITE = 1, +}; + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static struct rwreg_operation_t { + int type; /* 0: read, 1: write */ + int reg; /* register */ + int len; /* read/write length */ + int val; /* length = 1; read: return value, write: op return */ + int res; /* 0: success, otherwise: fail */ + char *opbuf; /* length >= 1, read return value, write: op return */ +} rw_op; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) +static ssize_t fts_debug_write( + struct file *filp, const char __user *buff, size_t count, loff_t *ppos) +{ + u8 *writebuf = NULL; + u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; + int buflen = count; + int writelen = 0; + int ret = 0; + char tmp[PROC_BUF_SIZE]; + struct fts_ts_data *ts_data = fts_data; + struct ftxxxx_proc *proc = &ts_data->proc; + + if ((buflen <= 1) || (buflen > PAGE_SIZE)) { + FTS_ERROR("apk proc wirte count(%d>%d) fail", buflen, (int)PAGE_SIZE); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + writebuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == writebuf) { + FTS_ERROR("apk proc wirte buf zalloc fail"); + return -ENOMEM; + } + } else { + writebuf = tmpbuf; + } + + if (copy_from_user(writebuf, buff, buflen)) { + FTS_ERROR("[APK]: copy from user error!!"); + ret = -EFAULT; + goto proc_write_err; + } + + proc->opmode = writebuf[0]; + switch (proc->opmode) { + case PROC_SET_TEST_FLAG: + FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x", writebuf[1]); + if (writebuf[1] == 0) { +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ENABLE); +#endif + } else { +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(DISABLE); +#endif + } + break; + + case PROC_READ_REGISTER: + proc->cmd[0] = writebuf[1]; + break; + + case PROC_WRITE_REGISTER: + ret = fts_write_reg(writebuf[1], writebuf[2]); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_REGISTER write error"); + goto proc_write_err; + } + break; + + case PROC_READ_DATA: + writelen = buflen - 1; + if (writelen >= FTX_MAX_COMMMAND_LENGTH) { + FTS_ERROR("cmd(PROC_READ_DATA) len(%d) fail", writelen); + goto proc_write_err; + } + memcpy(proc->cmd, writebuf + 1, writelen); + proc->cmd_len = writelen; + ret = fts_write(writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA write error"); + goto proc_write_err; + } + break; + + case PROC_WRITE_DATA: + writelen = buflen - 1; + ret = fts_write(writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_DATA write error"); + goto proc_write_err; + } + break; + + case PROC_SET_SLAVE_ADDR: + break; + + case PROC_HW_RESET: + snprintf(tmp, PROC_BUF_SIZE, "%s", writebuf + 1); + tmp[buflen - 1] = '\0'; + if (strncmp(tmp, "focal_driver", 12) == 0) { + FTS_INFO("APK execute HW Reset"); + fts_reset_proc(0); + } + break; + + case PROC_SET_BOOT_MODE: + FTS_DEBUG("[APK]: PROC_SET_BOOT_MODE = %x", writebuf[1]); + if (0 == writebuf[1]) { + ts_data->fw_is_running = true; + } else { + ts_data->fw_is_running = false; + } + break; + + case PROC_ENTER_TEST_ENVIRONMENT: + FTS_DEBUG("[APK]: PROC_ENTER_TEST_ENVIRONMENT = %x", writebuf[1]); + if (0 == writebuf[1]) { + fts_enter_test_environment(0); + } else { + fts_enter_test_environment(1); + } + break; + + default: + break; + } + + ret = buflen; +proc_write_err: + if ((buflen > PROC_BUF_SIZE) && writebuf) { + kfree(writebuf); + writebuf = NULL; + } + return ret; +} + +static ssize_t fts_debug_read( + struct file *filp, char __user *buff, size_t count, loff_t *ppos) +{ + int ret = 0; + int num_read_chars = 0; + int buflen = count; + u8 *readbuf = NULL; + u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; + struct fts_ts_data *ts_data = fts_data; + struct ftxxxx_proc *proc = &ts_data->proc; + + if ((buflen <= 0) || (buflen > PAGE_SIZE)) { + FTS_ERROR("apk proc read count(%d>%d) fail", buflen, (int)PAGE_SIZE); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == readbuf) { + FTS_ERROR("apk proc wirte buf zalloc fail"); + return -ENOMEM; + } + } else { + readbuf = tmpbuf; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + + switch (proc->opmode) { + case PROC_READ_REGISTER: + num_read_chars = 1; + ret = fts_read_reg(proc->cmd[0], &readbuf[0]); + if (ret < 0) { + FTS_ERROR("PROC_READ_REGISTER read error"); + goto proc_read_err; + } + break; + + case PROC_WRITE_REGISTER: + break; + + case PROC_READ_DATA: + num_read_chars = buflen; + ret = fts_read(NULL, 0, readbuf, num_read_chars); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA read error"); + goto proc_read_err; + } + break; + + case PROC_WRITE_DATA: + break; + + default: + break; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + + if (copy_to_user(buff, readbuf, num_read_chars)) { + FTS_ERROR("copy to user error"); + ret = -EFAULT; + goto proc_read_err; + } + + ret = num_read_chars; +proc_read_err: + if ((buflen > PROC_BUF_SIZE) && readbuf) { + kfree(readbuf); + readbuf = NULL; + } + return ret; +} + +static const struct proc_ops fts_proc_fops = { + .proc_read = fts_debug_read, + .proc_write = fts_debug_write, +}; +#else +static int fts_debug_write(struct file *filp, + const char __user *buff, unsigned long len, void *data) +{ + u8 *writebuf = NULL; + u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; + int buflen = count; + int writelen = 0; + int ret = 0; + char tmp[PROC_BUF_SIZE]; + struct fts_ts_data *ts_data = fts_data; + struct ftxxxx_proc *proc = &ts_data->proc; + + if ((buflen <= 1) || (buflen > PAGE_SIZE)) { + FTS_ERROR("apk proc wirte count(%d>%d) fail", buflen, (int)PAGE_SIZE); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + writebuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == writebuf) { + FTS_ERROR("apk proc wirte buf zalloc fail"); + return -ENOMEM; + } + } else { + writebuf = tmpbuf; + } + + if (copy_from_user(writebuf, buff, buflen)) { + FTS_ERROR("[APK]: copy from user error!!"); + ret = -EFAULT; + goto proc_write_err; + } + + proc->opmode = writebuf[0]; + switch (proc->opmode) { + case PROC_SET_TEST_FLAG: + FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x", writebuf[1]); + if (writebuf[1] == 0) { +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ENABLE); +#endif + } else { +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(DISABLE); +#endif + } + break; + + case PROC_READ_REGISTER: + proc->cmd[0] = writebuf[1]; + break; + + case PROC_WRITE_REGISTER: + ret = fts_write_reg(writebuf[1], writebuf[2]); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_REGISTER write error"); + goto proc_write_err; + } + break; + + case PROC_READ_DATA: + writelen = buflen - 1; + if (writelen >= FTX_MAX_COMMMAND_LENGTH) { + FTS_ERROR("cmd(PROC_READ_DATA) length(%d) fail", writelen); + goto proc_write_err; + } + memcpy(proc->cmd, writebuf + 1, writelen); + proc->cmd_len = writelen; + ret = fts_write(writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA write error"); + goto proc_write_err; + } + break; + + case PROC_WRITE_DATA: + writelen = buflen - 1; + ret = fts_write(writebuf + 1, writelen); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_DATA write error"); + goto proc_write_err; + } + break; + + case PROC_SET_SLAVE_ADDR: + break; + + case PROC_HW_RESET: + snprintf(tmp, PROC_BUF_SIZE, "%s", writebuf + 1); + tmp[buflen - 1] = '\0'; + if (strncmp(tmp, "focal_driver", 12) == 0) { + FTS_INFO("APK execute HW Reset"); + fts_reset_proc(0); + } + break; + + case PROC_SET_BOOT_MODE: + FTS_DEBUG("[APK]: PROC_SET_BOOT_MODE = %x", writebuf[1]); + if (0 == writebuf[1]) { + ts_data->fw_is_running = true; + } else { + ts_data->fw_is_running = false; + } + break; + + case PROC_ENTER_TEST_ENVIRONMENT: + FTS_DEBUG("[APK]: PROC_ENTER_TEST_ENVIRONMENT = %x", writebuf[1]); + if (0 == writebuf[1]) { + fts_enter_test_environment(0); + } else { + fts_enter_test_environment(1); + } + break; + + default: + break; + } + + ret = buflen; +proc_write_err: + if ((buflen > PROC_BUF_SIZE) && writebuf) { + kfree(writebuf); + writebuf = NULL; + } + return ret; +} + +static int fts_debug_read( + char *page, char **start, off_t off, int count, int *eof, void *data ) +{ + int ret = 0; + int num_read_chars = 0; + int buflen = count; + u8 *readbuf = NULL; + u8 tmpbuf[PROC_BUF_SIZE] = { 0 }; + struct fts_ts_data *ts_data = fts_data; + struct ftxxxx_proc *proc = &ts_data->proc; + + if ((buflen <= 0) || (buflen > PAGE_SIZE)) { + FTS_ERROR("apk proc read count(%d>%d) fail", buflen, (int)PAGE_SIZE); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == readbuf) { + FTS_ERROR("apk proc wirte buf zalloc fail"); + return -ENOMEM; + } + } else { + readbuf = tmpbuf; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + + switch (proc->opmode) { + case PROC_READ_REGISTER: + num_read_chars = 1; + ret = fts_read_reg(proc->cmd[0], &readbuf[0]); + if (ret < 0) { + FTS_ERROR("PROC_READ_REGISTER read error"); + goto proc_read_err; + } + break; + + case PROC_WRITE_REGISTER: + break; + + case PROC_READ_DATA: + num_read_chars = buflen; + ret = fts_read(NULL, 0, readbuf, num_read_chars); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA read error"); + goto proc_read_err; + } + break; + + case PROC_WRITE_DATA: + break; + + default: + break; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + + if (copy_to_user(buff, readbuf, num_read_chars)) { + FTS_ERROR("copy to user error"); + ret = -EFAULT; + goto proc_read_err; + } + + ret = num_read_chars; +proc_read_err: + if ((buflen > PROC_BUF_SIZE) && readbuf) { + kfree(readbuf); + readbuf = NULL; + } + return ret; +} +#endif + +int fts_create_apk_debug_channel(struct fts_ts_data *ts_data) +{ + struct ftxxxx_proc *proc = &ts_data->proc; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + proc->proc_entry = proc_create(PROC_NAME, 0777, NULL, &fts_proc_fops); + if (NULL == proc->proc_entry) { + FTS_ERROR("create proc entry fail"); + return -ENOMEM; + } +#else + proc->proc_entry = create_proc_entry(PROC_NAME, 0777, NULL); + if (NULL == proc->proc_entry) { + FTS_ERROR("create proc entry fail"); + return -ENOMEM; + } + proc->proc_entry->write_proc = fts_debug_write; + proc->proc_entry->read_proc = fts_debug_read; +#endif + + FTS_INFO("Create proc entry success!"); + return 0; +} + +void fts_release_apk_debug_channel(struct fts_ts_data *ts_data) +{ + struct ftxxxx_proc *proc = &ts_data->proc; + + if (proc->proc_entry) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + proc_remove(proc->proc_entry); +#else + remove_proc_entry(PROC_NAME, NULL); +#endif + } +} + +/************************************************************************ + * sysfs interface + ***********************************************************************/ +/* fts_hw_reset interface */ +static ssize_t fts_hw_reset_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + struct input_dev *input_dev = fts_data->input_dev; + ssize_t count = 0; + + mutex_lock(&input_dev->mutex); + fts_reset_proc(0); + count = snprintf(buf, PAGE_SIZE, "hw reset executed\n"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_hw_reset_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/* fts_irq interface */ +static ssize_t fts_irq_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t count = 0; + struct irq_desc *desc = irq_to_desc(fts_data->irq); + + count = snprintf(buf, PAGE_SIZE, "irq_depth:%d\n", desc->depth); + + return count; +} + +static ssize_t fts_irq_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_INFO("enable irq"); + fts_irq_enable(); + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_INFO("disable irq"); + fts_irq_disable(); + } + mutex_unlock(&input_dev->mutex); + return count; +} + +/* fts_boot_mode interface */ +static ssize_t fts_bootmode_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_INFO("[EX-FUN]set to boot mode"); + fts_data->fw_is_running = false; + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_INFO("[EX-FUN]set to fw mode"); + fts_data->fw_is_running = true; + } + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +static ssize_t fts_bootmode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t count = 0; + struct input_dev *input_dev = fts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + if (true == fts_data->fw_is_running) { + count = snprintf(buf, PAGE_SIZE, "tp is in fw mode\n"); + } else { + count = snprintf(buf, PAGE_SIZE, "tp is in boot mode\n"); + } + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +/* fts_tpfwver interface */ +static ssize_t fts_fw_version_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; + ssize_t num_read_chars = 0; + u8 fwver = 0; + + mutex_lock(&input_dev->mutex); + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + fts_read_reg(FTS_REG_FW_VER, &fwver); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + if ((fwver == 0xFF) || (fwver == 0x00)) + num_read_chars = snprintf(buf, PAGE_SIZE, "get tp fw version fail!\n"); + else + num_read_chars = snprintf(buf, PAGE_SIZE, "%02x\n", fwver); + + mutex_unlock(&input_dev->mutex); + return num_read_chars; +} + +static ssize_t fts_fw_version_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/* fts_rw_reg */ +static ssize_t fts_tprwreg_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + int i; + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + + if (rw_op.len < 0) { + count = snprintf(buf, PAGE_SIZE, "Invalid cmd line\n"); + } else if (rw_op.len == 1) { + if (RWREG_OP_READ == rw_op.type) { + if (rw_op.res == 0) { + count = snprintf(buf, PAGE_SIZE, "Read %02X: %02X\n", rw_op.reg, rw_op.val); + } else { + count = snprintf(buf, PAGE_SIZE, "Read %02X failed, ret: %d\n", rw_op.reg, rw_op.res); + } + } else { + if (rw_op.res == 0) { + count = snprintf(buf, PAGE_SIZE, "Write %02X, %02X success\n", rw_op.reg, rw_op.val); + } else { + count = snprintf(buf, PAGE_SIZE, "Write %02X failed, ret: %d\n", rw_op.reg, rw_op.res); + } + } + } else { + if (RWREG_OP_READ == rw_op.type) { + count = snprintf(buf, PAGE_SIZE, "Read Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len); + count += snprintf(buf + count, PAGE_SIZE, "Result: "); + if (rw_op.res) { + count += snprintf(buf + count, PAGE_SIZE, "failed, ret: %d\n", rw_op.res); + } else { + if (rw_op.opbuf) { + for (i = 0; i < rw_op.len; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]); + } + count += snprintf(buf + count, PAGE_SIZE, "\n"); + } + } + } else { + count = snprintf(buf, PAGE_SIZE, "Write Reg: [%02X]-[%02X]\n", rw_op.reg, rw_op.reg + rw_op.len - 1); + count += snprintf(buf + count, PAGE_SIZE, "Write Data: "); + if (rw_op.opbuf) { + for (i = 1; i < rw_op.len; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%02X ", rw_op.opbuf[i]); + } + count += snprintf(buf + count, PAGE_SIZE, "\n"); + } + if (rw_op.res) { + count += snprintf(buf + count, PAGE_SIZE, "Result: failed, ret: %d\n", rw_op.res); + } else { + count += snprintf(buf + count, PAGE_SIZE, "Result: success\n"); + } + } + /*if (rw_op.opbuf) { + * kfree(rw_op.opbuf); + * rw_op.opbuf = NULL; + *} + */ + } + mutex_unlock(&input_dev->mutex); + + return count; +} + +static int shex_to_int(const char *hex_buf, int size) +{ + int i; + int base = 1; + int value = 0; + char single; + + for (i = size - 1; i >= 0; i--) { + single = hex_buf[i]; + + if ((single >= '0') && (single <= '9')) { + value += (single - '0') * base; + } else if ((single >= 'a') && (single <= 'z')) { + value += (single - 'a' + 10) * base; + } else if ((single >= 'A') && (single <= 'Z')) { + value += (single - 'A' + 10) * base; + } else { + return -EINVAL; + } + + base *= 16; + } + + return value; +} + + +static u8 shex_to_u8(const char *hex_buf, int size) +{ + return (u8)shex_to_int(hex_buf, size); +} +/* + * Format buf: + * [0]: '0' write, '1' read(reserved) + * [1-2]: addr, hex + * [3-4]: length, hex + * [5-6]...[n-(n+1)]: data, hex + */ +static int fts_parse_buf(const char *buf, size_t cmd_len) +{ + int length; + int i; + char *tmpbuf; + + rw_op.reg = shex_to_u8(buf + 1, 2); + length = shex_to_int(buf + 3, 2); + + if (buf[0] == '1') { + rw_op.len = length; + rw_op.type = RWREG_OP_READ; + FTS_DEBUG("read %02X, %d bytes", rw_op.reg, rw_op.len); + } else { + if (cmd_len < (length * 2 + 5)) { + pr_err("data invalided!\n"); + return -EINVAL; + } + FTS_DEBUG("write %02X, %d bytes", rw_op.reg, length); + + /* first byte is the register addr */ + rw_op.type = RWREG_OP_WRITE; + rw_op.len = length + 1; + } + + if (rw_op.len > 0) { + tmpbuf = (char *)kzalloc(rw_op.len, GFP_KERNEL); + if (!tmpbuf) { + FTS_ERROR("allocate memory failed!\n"); + return -ENOMEM; + } + + if (RWREG_OP_WRITE == rw_op.type) { + tmpbuf[0] = rw_op.reg & 0xFF; + FTS_DEBUG("write buffer: "); + for (i = 1; i < rw_op.len; i++) { + tmpbuf[i] = shex_to_u8(buf + 5 + i * 2 - 2, 2); + FTS_DEBUG("buf[%d]: %02X", i, tmpbuf[i] & 0xFF); + } + } + rw_op.opbuf = tmpbuf; + } + + return rw_op.len; +} + +static ssize_t fts_tprwreg_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + ssize_t cmd_length = 0; + + mutex_lock(&input_dev->mutex); + cmd_length = count - 1; + + if (rw_op.opbuf) { + kfree(rw_op.opbuf); + rw_op.opbuf = NULL; + } + + FTS_DEBUG("cmd len: %d, buf: %s", (int)cmd_length, buf); + /* compatible old ops */ + if (2 == cmd_length) { + rw_op.type = RWREG_OP_READ; + rw_op.len = 1; + rw_op.reg = shex_to_int(buf, 2); + } else if (4 == cmd_length) { + rw_op.type = RWREG_OP_WRITE; + rw_op.len = 1; + rw_op.reg = shex_to_int(buf, 2); + rw_op.val = shex_to_int(buf + 2, 2); + } else if (cmd_length < 5) { + FTS_ERROR("Invalid cmd buffer"); + mutex_unlock(&input_dev->mutex); + return -EINVAL; + } else { + rw_op.len = fts_parse_buf(buf, cmd_length); + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + if (rw_op.len < 0) { + FTS_ERROR("cmd buffer error!"); + goto exit; + } + + if (RWREG_OP_READ == rw_op.type) { + if (rw_op.len == 1) { + u8 reg, val; + reg = rw_op.reg & 0xFF; + rw_op.res = fts_read_reg(reg, &val); + rw_op.val = val; + } else { + char reg; + reg = rw_op.reg & 0xFF; + + rw_op.res = fts_read(®, 1, rw_op.opbuf, rw_op.len); + } + + if (rw_op.res < 0) { + FTS_ERROR("Could not read 0x%02x", rw_op.reg); + } else { + FTS_INFO("read 0x%02x, %d bytes successful", rw_op.reg, rw_op.len); + rw_op.res = 0; + } + + } else { + if (rw_op.len == 1) { + u8 reg, val; + reg = rw_op.reg & 0xFF; + val = rw_op.val & 0xFF; + rw_op.res = fts_write_reg(reg, val); + } else { + rw_op.res = fts_write(rw_op.opbuf, rw_op.len); + } + if (rw_op.res < 0) { + FTS_ERROR("Could not write 0x%02x", rw_op.reg); + + } else { + FTS_INFO("Write 0x%02x, %d bytes successful", rw_op.val, rw_op.len); + rw_op.res = 0; + } + } + +exit: +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* fts_driver_info interface */ +static ssize_t fts_driverinfo_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = fts_data; + struct fts_ts_platform_data *pdata = ts_data->pdata; + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += scnprintf(buf + count, PAGE_SIZE, "Driver Ver:%s\n", FTS_DRIVER_VERSION); + + count += snprintf(buf + count, PAGE_SIZE, "Resolution:(%d,%d)~(%d,%d)\n", + pdata->x_min, pdata->y_min, pdata->x_max, pdata->y_max); + + count += snprintf(buf + count, PAGE_SIZE, "Max Touchs:%d\n", pdata->max_touch_number); + + count += scnprintf(buf + count, PAGE_SIZE, "reset gpio:%d,int gpio:%d,irq:%d\n", + pdata->reset_gpio, pdata->irq_gpio, ts_data->irq); + + count += scnprintf(buf + count, PAGE_SIZE, "IC ID:0x%02x%02x\n", + ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl); + if (ts_data->bus_type == BUS_TYPE_I2C) + count += scnprintf(buf + count, PAGE_SIZE, "BUS:%s,addr:0x%x\n", + "I2C", ts_data->client->addr); + else + count += scnprintf(buf + count, PAGE_SIZE, + "BUS:%s,mode:%d,max_freq:%d\n", "SPI", + ts_data->spi->mode, ts_data->spi->max_speed_hz); + + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_driverinfo_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/* fts_dump_reg interface */ +static ssize_t fts_dumpreg_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + fts_read_reg(FTS_REG_POWER_MODE, &val); + count += snprintf(buf + count, PAGE_SIZE, "Power Mode:0x%02x\n", val); + + fts_read_reg(FTS_REG_FW_VER, &val); + count += snprintf(buf + count, PAGE_SIZE, "FW Ver:0x%02x\n", val); + + fts_read_reg(FTS_REG_LIC_VER, &val); + count += snprintf(buf + count, PAGE_SIZE, "LCD Initcode Ver:0x%02x\n", val); + + fts_read_reg(FTS_REG_IDE_PARA_VER_ID, &val); + count += snprintf(buf + count, PAGE_SIZE, "Param Ver:0x%02x\n", val); + + fts_read_reg(FTS_REG_IDE_PARA_STATUS, &val); + count += snprintf(buf + count, PAGE_SIZE, "Param status:0x%02x\n", val); + + fts_read_reg(FTS_REG_VENDOR_ID, &val); + count += snprintf(buf + count, PAGE_SIZE, "Vendor ID:0x%02x\n", val); + + fts_read_reg(FTS_REG_LCD_BUSY_NUM, &val); + count += snprintf(buf + count, PAGE_SIZE, "LCD Busy Number:0x%02x\n", val); + + fts_read_reg(FTS_REG_GESTURE_EN, &val); + count += snprintf(buf + count, PAGE_SIZE, "Gesture Mode:0x%02x\n", val); + + fts_read_reg(FTS_REG_CHARGER_MODE_EN, &val); + count += snprintf(buf + count, PAGE_SIZE, "charge stat:0x%02x\n", val); + + fts_read_reg(FTS_REG_INT_CNT, &val); + count += snprintf(buf + count, PAGE_SIZE, "INT count:0x%02x\n", val); + + fts_read_reg(FTS_REG_FLOW_WORK_CNT, &val); + count += snprintf(buf + count, PAGE_SIZE, "ESD count:0x%02x\n", val); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_dumpreg_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/* fts_dump_reg interface */ +static ssize_t fts_tpbuf_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + int i = 0; + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "touch point buffer:\n"); + for (i = 0; i < fts_data->pnt_buf_size; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%02x ", + fts_data->point_buf[i]); + } + count += snprintf(buf + count, PAGE_SIZE, "\n"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_tpbuf_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +/* fts_log_level interface */ +static ssize_t fts_log_level_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "log level:%d\n", + fts_data->log_level); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_log_level_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + struct input_dev *input_dev = fts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + sscanf(buf, "%d", &value); + FTS_DEBUG("log level:%d->%d", fts_data->log_level, value); + fts_data->log_level = value; + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +#ifdef CONFIG_FTS_TRUSTED_TOUCH + +static ssize_t trusted_touch_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *info = fts_data; + + return scnprintf(buf, PAGE_SIZE, "%d", + atomic_read(&info->trusted_touch_enabled)); +} + +static ssize_t trusted_touch_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *info = fts_data; + unsigned long value; + int err = 0; + + if (count > 2) + return -EINVAL; + err = kstrtoul(buf, 10, &value); + if (err != 0) + return err; + + if (!atomic_read(&info->trusted_touch_initialized)) + return -EIO; + +#ifdef CONFIG_ARCH_QTI_VM + err = fts_ts_handle_trusted_touch_tvm(info, value); + if (err) { + pr_err("Failed to handle trusted touch in tvm\n"); + return -EINVAL; + } +#else + err = fts_ts_handle_trusted_touch_pvm(info, value); + if (err) { + pr_err("Failed to handle trusted touch in pvm\n"); + return -EINVAL; + } +#endif + err = count; + return err; +} + +static ssize_t trusted_touch_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *info = fts_data; + + return scnprintf(buf, PAGE_SIZE, "%d", + atomic_read(&info->trusted_touch_event)); +} + +static ssize_t trusted_touch_event_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *info = fts_data; + unsigned long value; + int err = 0; + + if (count > 2) + return -EINVAL; + + err = kstrtoul(buf, 10, &value); + if (err != 0) + return err; + + if (!atomic_read(&info->trusted_touch_initialized)) + return -EIO; + + if (value) + return -EIO; + + atomic_set(&info->trusted_touch_event, value); + + return count; +} + +static ssize_t trusted_touch_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *info = fts_data; + + return scnprintf(buf, PAGE_SIZE, "%s", info->vm_info->trusted_touch_type); +} + +#endif + +/* get the fw version example:cat fw_version */ +static DEVICE_ATTR_RW(fts_fw_version); + +/* read and write register(s) +* All data type is **HEX** +* Single Byte: +* read: echo 88 > rw_reg ---read register 0x88 +* write: echo 8807 > rw_reg ---write 0x07 into register 0x88 +* Multi-bytes: +* [0:rw-flag][1-2: reg addr, hex][3-4: length, hex][5-6...n-n+1: write data, hex] +* rw-flag: 0, write; 1, read +* read: echo 10005 > rw_reg ---read reg 0x00-0x05 +* write: echo 000050102030405 > rw_reg ---write reg 0x00-0x05 as 01,02,03,04,05 +* Get result: +* cat rw_reg +*/ +static DEVICE_ATTR(fts_rw_reg, S_IRUGO | S_IWUSR, fts_tprwreg_show, fts_tprwreg_store); +static DEVICE_ATTR(fts_driver_info, S_IRUGO | S_IWUSR, fts_driverinfo_show, fts_driverinfo_store); +static DEVICE_ATTR(fts_dump_reg, S_IRUGO | S_IWUSR, fts_dumpreg_show, fts_dumpreg_store); +static DEVICE_ATTR(fts_hw_reset, S_IRUGO | S_IWUSR, fts_hw_reset_show, fts_hw_reset_store); +static DEVICE_ATTR(fts_irq, S_IRUGO | S_IWUSR, fts_irq_show, fts_irq_store); +static DEVICE_ATTR(fts_boot_mode, S_IRUGO | S_IWUSR, fts_bootmode_show, fts_bootmode_store); +static DEVICE_ATTR(fts_touch_point, S_IRUGO | S_IWUSR, fts_tpbuf_show, fts_tpbuf_store); +static DEVICE_ATTR(fts_log_level, S_IRUGO | S_IWUSR, fts_log_level_show, fts_log_level_store); +#ifdef CONFIG_FTS_TRUSTED_TOUCH +static DEVICE_ATTR_RW(trusted_touch_enable); +static DEVICE_ATTR_RW(trusted_touch_event); +static DEVICE_ATTR_RO(trusted_touch_type); +#endif + +/* add your attr in here*/ +static struct attribute *fts_attributes[] = { + &dev_attr_fts_fw_version.attr, + &dev_attr_fts_rw_reg.attr, + &dev_attr_fts_dump_reg.attr, + &dev_attr_fts_driver_info.attr, + &dev_attr_fts_hw_reset.attr, + &dev_attr_fts_irq.attr, + &dev_attr_fts_boot_mode.attr, + &dev_attr_fts_touch_point.attr, + &dev_attr_fts_log_level.attr, +#ifdef CONFIG_FTS_TRUSTED_TOUCH + &dev_attr_trusted_touch_enable.attr, + &dev_attr_trusted_touch_event.attr, + &dev_attr_trusted_touch_type.attr, +#endif + NULL +}; + +static struct attribute_group fts_attribute_group = { + .attrs = fts_attributes +}; + +int fts_create_sysfs(struct fts_ts_data *ts_data) +{ + int ret = 0; + + ret = sysfs_create_group(&ts_data->dev->kobj, &fts_attribute_group); + if (ret) { + FTS_ERROR("[EX]: sysfs_create_group() failed!!"); + sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group); + return -ENOMEM; + } else { + FTS_INFO("[EX]: sysfs_create_group() succeeded!!"); + } + + return ret; +} + +int fts_remove_sysfs(struct fts_ts_data *ts_data) +{ + sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group); + return 0; +} diff --git a/focaltech_touch/focaltech_ex_mode.c b/focaltech_touch/focaltech_ex_mode.c new file mode 100644 index 0000000000..03cec68d1a --- /dev/null +++ b/focaltech_touch/focaltech_ex_mode.c @@ -0,0 +1,359 @@ +/* + * + * FocalTech ftxxxx TouchScreen driver. + * + * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_ex_mode.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-31 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* 2.Private constant and macro definitions using #define +*****************************************************************************/ + +/***************************************************************************** +* 3.Private enumerations, structures and unions using typedef +*****************************************************************************/ +enum _ex_mode { + MODE_GLOVE = 0, + MODE_COVER, + MODE_CHARGER, + REPORT_RATE, +}; + +/***************************************************************************** +* 4.Static variables +*****************************************************************************/ + +/***************************************************************************** +* 5.Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* 6.Static function prototypes +*******************************************************************************/ +static int fts_ex_mode_switch(enum _ex_mode mode, u8 value) +{ + int ret = 0; + + switch (mode) { + case MODE_GLOVE: + ret = fts_write_reg(FTS_REG_GLOVE_MODE_EN, value > 0 ? 1 : 0); + if (ret < 0) + FTS_ERROR("MODE_GLOVE switch to %d fail", value); + break; + + case MODE_COVER: + ret = fts_write_reg(FTS_REG_COVER_MODE_EN, value > 0 ? 1 : 0); + if (ret < 0) + FTS_ERROR("MODE_COVER switch to %d fail", value); + break; + + case MODE_CHARGER: + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, value > 0 ? 1 : 0); + if (ret < 0) + FTS_ERROR("MODE_CHARGER switch to %d fail", value); + break; + + case REPORT_RATE: + ret = fts_write_reg(FTS_REG_REPORT_RATE, value); + if (ret < 0) + FTS_ERROR("REPORT_RATE switch to %d fail", value); + break; + + default: + FTS_ERROR("mode(%d) unsupport", mode); + ret = -EINVAL; + break; + } + + return ret; +} + +static ssize_t fts_glove_mode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_GLOVE_MODE_EN, &val); + count = scnprintf(buf + count, PAGE_SIZE, "Glove Mode:%s\n", + ts_data->glove_mode ? "On" : "Off"); + count += scnprintf(buf + count, PAGE_SIZE - count, + "Glove Reg(0xC0):%d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_glove_mode_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + + if (FTS_SYSFS_ECHO_ON(buf)) { + if (!ts_data->glove_mode) { + FTS_DEBUG("enter glove mode"); + ret = fts_ex_mode_switch(MODE_GLOVE, ENABLE); + if (ret >= 0) { + ts_data->glove_mode = ENABLE; + } + } + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + if (ts_data->glove_mode) { + FTS_DEBUG("exit glove mode"); + ret = fts_ex_mode_switch(MODE_GLOVE, DISABLE); + if (ret >= 0) { + ts_data->glove_mode = DISABLE; + } + } + } + + FTS_DEBUG("glove mode:%d", ts_data->glove_mode); + return count; +} + + +static ssize_t fts_cover_mode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_COVER_MODE_EN, &val); + count = scnprintf(buf + count, PAGE_SIZE, "Cover Mode:%s\n", + ts_data->cover_mode ? "On" : "Off"); + count += scnprintf(buf + count, PAGE_SIZE - count, + "Cover Reg(0xC1):%d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_cover_mode_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + + if (FTS_SYSFS_ECHO_ON(buf)) { + if (!ts_data->cover_mode) { + FTS_DEBUG("enter cover mode"); + ret = fts_ex_mode_switch(MODE_COVER, ENABLE); + if (ret >= 0) { + ts_data->cover_mode = ENABLE; + } + } + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + if (ts_data->cover_mode) { + FTS_DEBUG("exit cover mode"); + ret = fts_ex_mode_switch(MODE_COVER, DISABLE); + if (ret >= 0) { + ts_data->cover_mode = DISABLE; + } + } + } + + FTS_DEBUG("cover mode:%d", ts_data->cover_mode); + return count; +} + +static ssize_t fts_charger_mode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_CHARGER_MODE_EN, &val); + count = scnprintf(buf + count, PAGE_SIZE, "Charger Mode:%s\n", + ts_data->charger_mode ? "On" : "Off"); + count += scnprintf(buf + count, PAGE_SIZE - count, + "Charger Reg(0x8B):%d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_charger_mode_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + + if (FTS_SYSFS_ECHO_ON(buf)) { + if (!ts_data->charger_mode) { + FTS_DEBUG("enter charger mode"); + ret = fts_ex_mode_switch(MODE_CHARGER, ENABLE); + if (ret >= 0) { + ts_data->charger_mode = ENABLE; + } + } + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + if (ts_data->charger_mode) { + FTS_DEBUG("exit charger mode"); + ret = fts_ex_mode_switch(MODE_CHARGER, DISABLE); + if (ret >= 0) { + ts_data->charger_mode = DISABLE; + } + } + } + + FTS_DEBUG("charger mode:%d", ts_data->glove_mode); + return count; +} + +static ssize_t fts_report_rate_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_REPORT_RATE, &val); + count = scnprintf(buf + count, PAGE_SIZE, + "Report Rate:%d\n", ts_data->report_rate); + count += scnprintf(buf + count, PAGE_SIZE - count, + "Report Rate Reg(0x88):%d\n", val); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_report_rate_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + int rate; + + ret = kstrtoint(buf, 16, &rate); + if (ret) + return ret; + + if (rate != ts_data->report_rate) { + ret = fts_ex_mode_switch(REPORT_RATE, (u8)rate); + if (ret >= 0) + ts_data->report_rate = rate; + } + + FTS_DEBUG("report rate:%d", ts_data->report_rate); + return count; +} + + +/* read and write charger mode + * read example: cat fts_glove_mode ---read glove mode + * write example:echo 1 > fts_glove_mode ---write glove mode to 01 + */ +static DEVICE_ATTR(fts_glove_mode, S_IRUGO | S_IWUSR, + fts_glove_mode_show, fts_glove_mode_store); + +static DEVICE_ATTR(fts_cover_mode, S_IRUGO | S_IWUSR, + fts_cover_mode_show, fts_cover_mode_store); + +static DEVICE_ATTR(fts_charger_mode, S_IRUGO | S_IWUSR, + fts_charger_mode_show, fts_charger_mode_store); + +static DEVICE_ATTR_RW(fts_report_rate); + +static struct attribute *fts_touch_mode_attrs[] = { + &dev_attr_fts_glove_mode.attr, + &dev_attr_fts_cover_mode.attr, + &dev_attr_fts_charger_mode.attr, + &dev_attr_fts_report_rate.attr, + NULL, +}; + +static struct attribute_group fts_touch_mode_group = { + .attrs = fts_touch_mode_attrs, +}; + +int fts_ex_mode_recovery(struct fts_ts_data *ts_data) +{ + if (ts_data->glove_mode) { + fts_ex_mode_switch(MODE_GLOVE, ENABLE); + } + + if (ts_data->cover_mode) { + fts_ex_mode_switch(MODE_COVER, ENABLE); + } + + if (ts_data->charger_mode) { + fts_ex_mode_switch(MODE_CHARGER, ENABLE); + } + + if (ts_data->report_rate > 0) + fts_ex_mode_switch(REPORT_RATE, ts_data->report_rate); + + return 0; +} + +int fts_ex_mode_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + + ts_data->glove_mode = DISABLE; + ts_data->cover_mode = DISABLE; + ts_data->charger_mode = DISABLE; + ts_data->report_rate = 0; + + ret = sysfs_create_group(&ts_data->dev->kobj, &fts_touch_mode_group); + if (ret < 0) { + FTS_ERROR("create sysfs(ex_mode) fail"); + sysfs_remove_group(&ts_data->dev->kobj, &fts_touch_mode_group); + return ret; + } else { + FTS_DEBUG("create sysfs(ex_mode) succeedfully"); + } + + return 0; +} + +int fts_ex_mode_exit(struct fts_ts_data *ts_data) +{ + sysfs_remove_group(&ts_data->dev->kobj, &fts_touch_mode_group); + return 0; +} diff --git a/focaltech_touch/focaltech_flash.c b/focaltech_touch/focaltech_flash.c new file mode 100644 index 0000000000..c1e54b10ff --- /dev/null +++ b/focaltech_touch/focaltech_flash.c @@ -0,0 +1,2079 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_flash.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" +#include "focaltech_flash.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_FW_REQUEST_SUPPORT 1 +/* Example: focaltech_ts_fw_tianma.bin */ +#define FTS_FW_NAME_PREX_WITH_REQUEST "focaltech_ts_fw_" + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +u8 fw_file[1] = { +0, +}; + +struct upgrade_module module_list[] = { + {FTS_MODULE_ID, FTS_MODULE_NAME, fw_file, sizeof(fw_file)}, +}; + +struct upgrade_func *upgrade_func_list[] = { + &upgrade_func_ft5452, + &upgrade_func_ft5652, +}; + +struct fts_upgrade *fwupgrade; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static bool fts_fwupg_check_state( + struct fts_upgrade *upg, enum FW_STATUS rstate); + +/************************************************************************ +* Name: fts_fwupg_get_boot_state +* Brief: read boot id(rom/pram/bootloader), confirm boot environment +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_fwupg_get_boot_state( + struct fts_upgrade *upg, + enum FW_STATUS *fw_sts) +{ + int ret = 0; + u8 cmd[4] = { 0 }; + u32 cmd_len = 0; + u8 val[2] = { 0 }; + struct ft_chip_t *ids = NULL; + + FTS_INFO("**********read boot id**********"); + if ((!upg) || (!upg->func) || (!upg->ts_data) || (!fw_sts)) { + FTS_ERROR("upg/func/ts_data/fw_sts is null"); + return -EINVAL; + } + + if (upg->func->hid_supported) + fts_hid2std(); + + cmd[0] = FTS_CMD_START1; + cmd[1] = FTS_CMD_START2; + if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0) + cmd_len = 1; + else + cmd_len = 2; + ret = fts_write(cmd, cmd_len); + if (ret < 0) { + FTS_ERROR("write 55 cmd fail"); + return ret; + } + + msleep(FTS_CMD_START_DELAY); + cmd[0] = FTS_CMD_READ_ID; + cmd[1] = cmd[2] = cmd[3] = 0x00; + if (fts_data->ic_info.is_incell || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) + cmd_len = FTS_CMD_READ_ID_LEN_INCELL; + else + cmd_len = FTS_CMD_READ_ID_LEN; + ret = fts_read(cmd, cmd_len, val, 2); + if (ret < 0) { + FTS_ERROR("write 90 cmd fail"); + return ret; + } + FTS_INFO("read boot id:0x%02x%02x", val[0], val[1]); + + ids = &upg->ts_data->ic_info.ids; + if ((val[0] == ids->rom_idh) && (val[1] == ids->rom_idl)) { + FTS_INFO("tp run in romboot"); + *fw_sts = FTS_RUN_IN_ROM; + } else if ((val[0] == ids->pb_idh) && (val[1] == ids->pb_idl)) { + FTS_INFO("tp run in pramboot"); + *fw_sts = FTS_RUN_IN_PRAM; + } else if ((val[0] == ids->bl_idh) && (val[1] == ids->bl_idl)) { + FTS_INFO("tp run in bootloader"); + *fw_sts = FTS_RUN_IN_BOOTLOADER; + } + + return 0; +} + +static int fts_fwupg_reset_to_boot(struct fts_upgrade *upg) +{ + int ret = 0; + u8 reg = FTS_REG_UPGRADE; + + FTS_INFO("send 0xAA and 0x55 to FW, reset to boot environment"); + if (upg && upg->func && upg->func->is_reset_register_BC) { + reg = FTS_REG_UPGRADE2; + } + + ret = fts_write_reg(reg, FTS_UPGRADE_AA); + if (ret < 0) { + FTS_ERROR("write FC=0xAA fail"); + return ret; + } + msleep(FTS_DELAY_UPGRADE_AA); + + ret = fts_write_reg(reg, FTS_UPGRADE_55); + if (ret < 0) { + FTS_ERROR("write FC=0x55 fail"); + return ret; + } + + msleep(FTS_DELAY_UPGRADE_RESET); + return 0; +} + +/************************************************************************ +* Name: fts_fwupg_reset_to_romboot +* Brief: reset to romboot, to load pramboot +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_fwupg_reset_to_romboot(struct fts_upgrade *upg) +{ + int ret = 0; + int i = 0; + u8 cmd = FTS_CMD_RESET; + enum FW_STATUS state = FTS_RUN_IN_ERROR; + + ret = fts_write(&cmd, 1); + if (ret < 0) { + FTS_ERROR("pram/rom/bootloader reset cmd write fail"); + return ret; + } + mdelay(10); + + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + ret = fts_fwupg_get_boot_state(upg, &state); + if (FTS_RUN_IN_ROM == state) + break; + mdelay(5); + } + if (i >= FTS_UPGRADE_LOOP) { + FTS_ERROR("reset to romboot fail"); + return -EIO; + } + + return 0; +} + +static u16 fts_crc16_calc_host(u8 *pbuf, u32 length) +{ + u16 ecc = 0; + u32 i = 0; + u32 j = 0; + + for ( i = 0; i < length; i += 2 ) { + ecc ^= ((pbuf[i] << 8) | (pbuf[i + 1])); + for (j = 0; j < 16; j ++) { + if (ecc & 0x01) + ecc = (u16)((ecc >> 1) ^ AL2_FCS_COEF); + else + ecc >>= 1; + } + } + + return ecc; +} + +static u16 fts_pram_ecc_calc_host(u8 *pbuf, u32 length) +{ + return fts_crc16_calc_host(pbuf, length); +} + +static int fts_pram_ecc_cal_algo( + struct fts_upgrade *upg, + u32 start_addr, + u32 ecc_length) +{ + int ret = 0; + int i = 0; + int ecc = 0; + u8 val[2] = { 0 }; + u8 tmp = 0; + u8 cmd[FTS_ROMBOOT_CMD_ECC_NEW_LEN] = { 0 }; + + FTS_INFO("read out pramboot checksum"); + if ((!upg) || (!upg->func)) { + FTS_ERROR("upg/func is null"); + return -EINVAL; + } + + cmd[0] = FTS_ROMBOOT_CMD_ECC; + cmd[1] = BYTE_OFF_16(start_addr); + cmd[2] = BYTE_OFF_8(start_addr); + cmd[3] = BYTE_OFF_0(start_addr); + cmd[4] = BYTE_OFF_16(ecc_length); + cmd[5] = BYTE_OFF_8(ecc_length); + cmd[6] = BYTE_OFF_0(ecc_length); + ret = fts_write(cmd, FTS_ROMBOOT_CMD_ECC_NEW_LEN); + if (ret < 0) { + FTS_ERROR("write pramboot ecc cal cmd fail"); + return ret; + } + + cmd[0] = FTS_ROMBOOT_CMD_ECC_FINISH; + for (i = 0; i < FTS_ECC_FINISH_TIMEOUT; i++) { + msleep(1); + ret = fts_read(cmd, 1, val, 1); + if (ret < 0) { + FTS_ERROR("ecc_finish read cmd fail"); + return ret; + } + if (upg->func->new_return_value_from_ic || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) { + tmp = FTS_ROMBOOT_CMD_ECC_FINISH_OK_A5; + } else { + tmp = FTS_ROMBOOT_CMD_ECC_FINISH_OK_00; + } + if (tmp == val[0]) + break; + } + if (i >= FTS_ECC_FINISH_TIMEOUT) { + FTS_ERROR("wait ecc finish fail"); + return -EIO; + } + + cmd[0] = FTS_ROMBOOT_CMD_ECC_READ; + ret = fts_read(cmd, 1, val, 2); + if (ret < 0) { + FTS_ERROR("read pramboot ecc fail"); + return ret; + } + + ecc = ((u16)(val[0] << 8) + val[1]) & 0x0000FFFF; + return ecc; +} + +static int fts_pram_ecc_cal_xor(void) +{ + int ret = 0; + u8 reg_val = 0; + + FTS_INFO("read out pramboot checksum"); + + ret = fts_read_reg(FTS_ROMBOOT_CMD_ECC, ®_val); + if (ret < 0) { + FTS_ERROR("read pramboot ecc fail"); + return ret; + } + + return (int)reg_val; +} + +static int fts_pram_ecc_cal(struct fts_upgrade *upg, u32 saddr, u32 len) +{ + if ((!upg) || (!upg->func)) { + FTS_ERROR("upg/func is null"); + return -EINVAL; + } + + if ((upg->func->pram_ecc_check_mode == ECC_CHECK_MODE_CRC16) || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) + return fts_pram_ecc_cal_algo(upg, saddr, len); + + return fts_pram_ecc_cal_xor(); +} + +static int fts_pram_write_buf(struct fts_upgrade *upg, u8 *buf, u32 len) +{ + int ret = 0; + u32 i = 0; + u32 j = 0; + u32 offset = 0; + u32 remainder = 0; + u32 packet_number; + u32 packet_len = 0; + u8 packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 }; + u8 ecc_tmp = 0; + int ecc_in_host = 0; + u32 cmdlen = 0; + + FTS_INFO("write pramboot to pram"); + if ((!upg) || (!upg->func) || !buf) { + FTS_ERROR("upg/func/buf is null"); + return -EINVAL; + } + + FTS_INFO("pramboot len=%d", len); + if ((len < PRAMBOOT_MIN_SIZE) || (len > PRAMBOOT_MAX_SIZE)) { + FTS_ERROR("pramboot length(%d) fail", len); + return -EINVAL; + } + + packet_number = len / FTS_FLASH_PACKET_LENGTH; + remainder = len % FTS_FLASH_PACKET_LENGTH; + if (remainder > 0) + packet_number++; + packet_len = FTS_FLASH_PACKET_LENGTH; + + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + if (upg->ts_data->bus_type == BUS_TYPE_SPI_V2) { + packet_buf[0] = FTS_ROMBOOT_CMD_SET_PRAM_ADDR; + packet_buf[1] = BYTE_OFF_16(offset); + packet_buf[2] = BYTE_OFF_8(offset); + packet_buf[3] = BYTE_OFF_0(offset); + + ret = fts_write(packet_buf, FTS_ROMBOOT_CMD_SET_PRAM_ADDR_LEN); + if (ret < 0) { + FTS_ERROR("pramboot set write address(%d) fail", i); + return ret; + } + + packet_buf[0] = FTS_ROMBOOT_CMD_WRITE; + cmdlen = 1; + } else { + packet_buf[0] = FTS_ROMBOOT_CMD_WRITE; + packet_buf[1] = BYTE_OFF_16(offset); + packet_buf[2] = BYTE_OFF_8(offset); + packet_buf[3] = BYTE_OFF_0(offset); + + packet_buf[4] = BYTE_OFF_8(packet_len); + packet_buf[5] = BYTE_OFF_0(packet_len); + cmdlen = 6; + } + + for (j = 0; j < packet_len; j++) { + packet_buf[cmdlen + j] = buf[offset + j]; + if (upg->func->pram_ecc_check_mode == ECC_CHECK_MODE_XOR) + ecc_tmp ^= packet_buf[cmdlen + j]; + } + + ret = fts_write(packet_buf, packet_len + cmdlen); + if (ret < 0) { + FTS_ERROR("pramboot write data(%d) fail", i); + return ret; + } + } + + if ((upg->func->pram_ecc_check_mode == ECC_CHECK_MODE_CRC16) || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) + ecc_in_host = (int)fts_pram_ecc_calc_host(buf, len); + else + ecc_in_host = (int)ecc_tmp; + + return ecc_in_host; +} + +static int fts_pram_start(void) +{ + u8 cmd = FTS_ROMBOOT_CMD_START_APP; + int ret = 0; + + FTS_INFO("remap to start pramboot"); + + ret = fts_write(&cmd, 1); + if (ret < 0) { + FTS_ERROR("write start pram cmd fail"); + return ret; + } + msleep(FTS_DELAY_PRAMBOOT_START); + + return 0; +} + +static int fts_pram_write_remap(struct fts_upgrade *upg) +{ + int ret = 0; + int ecc_in_host = 0; + int ecc_in_tp = 0; + u8 *pb_buf = NULL; + u32 pb_len = 0; + + FTS_INFO("write pram and remap"); + if (!upg || !upg->func || !upg->func->pramboot) { + FTS_ERROR("upg/func/pramboot is null"); + return -EINVAL; + } + + if (upg->func->pb_length < FTS_MIN_LEN) { + FTS_ERROR("pramboot length(%d) fail", upg->func->pb_length); + return -EINVAL; + } + + pb_buf = upg->func->pramboot; + pb_len = upg->func->pb_length; + + /* write pramboot to pram */ + ecc_in_host = fts_pram_write_buf(upg, pb_buf, pb_len); + if (ecc_in_host < 0) { + FTS_ERROR( "write pramboot fail"); + return ecc_in_host; + } + + /* read out checksum */ + ecc_in_tp = fts_pram_ecc_cal(upg, 0, pb_len); + if (ecc_in_tp < 0) { + FTS_ERROR( "read pramboot ecc fail"); + return ecc_in_tp; + } + + FTS_INFO("pram ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host); + /* pramboot checksum != fw checksum, upgrade fail */ + if (ecc_in_host != ecc_in_tp) { + FTS_ERROR("pramboot ecc check fail"); + return -EIO; + } + + /*start pram*/ + ret = fts_pram_start(); + if (ret < 0) { + FTS_ERROR("pram start fail"); + return ret; + } + + return 0; +} + +static int fts_pram_init(void) +{ + int ret = 0; + u8 reg_val = 0; + u8 wbuf[3] = { 0 }; + + FTS_INFO("pramboot initialization"); + + /* read flash ID */ + wbuf[0] = FTS_CMD_FLASH_TYPE; + ret = fts_read(wbuf, 1, ®_val, 1); + if (ret < 0) { + FTS_ERROR("read flash type fail"); + return ret; + } + + /* set flash clk */ + wbuf[0] = FTS_CMD_FLASH_TYPE; + wbuf[1] = reg_val; + wbuf[2] = 0x00; + ret = fts_write(wbuf, 3); + if (ret < 0) { + FTS_ERROR("write flash type fail"); + return ret; + } + + return 0; +} + +static int fts_pram_write_init(struct fts_upgrade *upg) +{ + int ret = 0; + bool state = 0; + enum FW_STATUS status = FTS_RUN_IN_ERROR; + + FTS_INFO("**********pram write and init**********"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("upgrade/func is null"); + return -EINVAL; + } + + if (!upg->func->pramboot_supported) { + FTS_ERROR("ic not support pram"); + return -EINVAL; + } + + FTS_DEBUG("check whether tp is in romboot or not "); + /* need reset to romboot when non-romboot state */ + ret = fts_fwupg_get_boot_state(upg, &status); + if (status != FTS_RUN_IN_ROM) { + if (FTS_RUN_IN_PRAM == status) { + FTS_INFO("tp is in pramboot, need send reset cmd before upgrade"); + ret = fts_pram_init(); + if (ret < 0) { + FTS_ERROR("pramboot(before) init fail"); + return ret; + } + } + + FTS_INFO("tp isn't in romboot, need send reset to romboot"); + ret = fts_fwupg_reset_to_romboot(upg); + if (ret < 0) { + FTS_ERROR("reset to romboot fail"); + return ret; + } + } + + /* check the length of the pramboot */ + ret = fts_pram_write_remap(upg); + if (ret < 0) { + FTS_ERROR("pram write fail, ret=%d", ret); + return ret; + } + + FTS_DEBUG("after write pramboot, confirm run in pramboot"); + state = fts_fwupg_check_state(upg, FTS_RUN_IN_PRAM); + if (!state) { + FTS_ERROR("not in pramboot"); + return -EIO; + } + + ret = fts_pram_init(); + if (ret < 0) { + FTS_ERROR("pramboot init fail"); + return ret; + } + + return 0; +} + +static bool fts_fwupg_check_fw_valid(void) +{ + int ret = 0; + + ret = fts_wait_tp_to_valid(); + if (ret < 0) { + FTS_INFO("tp fw invaild"); + return false; + } + + FTS_INFO("tp fw vaild"); + return true; +} + +/************************************************************************ +* Name: fts_fwupg_check_state +* Brief: confirm tp run in which mode: romboot/pramboot/bootloader +* Input: +* Output: +* Return: return true if state is match, otherwise return false +***********************************************************************/ +static bool fts_fwupg_check_state( + struct fts_upgrade *upg, enum FW_STATUS rstate) +{ + int ret = 0; + int i = 0; + enum FW_STATUS cstate = FTS_RUN_IN_ERROR; + + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + ret = fts_fwupg_get_boot_state(upg, &cstate); + /* FTS_DEBUG("fw state=%d, retries=%d", cstate, i); */ + if (cstate == rstate) + return true; + msleep(FTS_DELAY_READ_ID); + } + + return false; +} + +/************************************************************************ +* Name: fts_fwupg_reset_in_boot +* Brief: RST CMD(07), reset to romboot(bootloader) in boot environment +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_reset_in_boot(void) +{ + int ret = 0; + u8 cmd = FTS_CMD_RESET; + + FTS_INFO("reset in boot environment"); + ret = fts_write(&cmd, 1); + if (ret < 0) { + FTS_ERROR("pram/rom/bootloader reset cmd write fail"); + return ret; + } + + msleep(FTS_DELAY_UPGRADE_RESET); + return 0; +} + +static int fts_fwupg_enter_into_boot_old(struct fts_upgrade *upg) +{ + int ret = 0; + bool fwvalid = false; + bool state = false; + + FTS_INFO("***********enter into pramboot/bootloader***********"); + if ((!upg) || (NULL == upg->func)) { + FTS_ERROR("upgrade/func is null"); + return -EINVAL; + } + + fwvalid = fts_fwupg_check_fw_valid(); + if (fwvalid) { + ret = fts_fwupg_reset_to_boot(upg); + if (ret < 0) { + FTS_ERROR("enter into romboot/bootloader fail"); + return ret; + } + } else if (upg->func->read_boot_id_need_reset) { + ret = fts_fwupg_reset_in_boot(); + if (ret < 0) { + FTS_ERROR("reset before read boot id when fw invalid fail"); + return ret; + } + } + + if (upg->func->pramboot_supported) { + FTS_INFO("pram supported, write pramboot and init"); + /* pramboot */ + ret = fts_pram_write_init(upg); + if (ret < 0) { + FTS_ERROR("pram write_init fail"); + return ret; + } + } else { + FTS_DEBUG("pram not supported, confirm in bootloader"); + /* bootloader */ + state = fts_fwupg_check_state(upg, FTS_RUN_IN_BOOTLOADER); + if (!state) { + FTS_ERROR("fw not in bootloader, fail"); + return -EIO; + } + } + + return 0; +} + +static int fts_hardware_reset_to_boot(void) +{ + u8 cmd[2] = {0}; + int i = 0; + u8 chip_id[2] = { 0 }; + u8 id_cmd[4] = { 0 }; + int ret = 0; + + fts_reset_proc(0); + FTS_INFO("reset finish!!"); + usleep_range(10000, 11000); + + for (i = 0; i < 20; i++) { + FTS_INFO("start send 55 AA"); + cmd[0] = 0x55; + cmd[1] = 0xAA; + ret = fts_write(cmd, 2); + if (ret < 0) + FTS_ERROR("send 55 AA cmd fail"); + + usleep_range(8000, 9000); + FTS_INFO("start send 90 00 00 00"); + id_cmd[0] = 0x90; + id_cmd[1] = id_cmd[2] = id_cmd[3] = 0x00; + ret = fts_read(id_cmd, 1, chip_id, 2); + if (ret < 0) + FTS_ERROR("send 90 cmd fail"); + + FTS_INFO("read boot id = %x--%x", chip_id[0], chip_id[1]); + if (chip_id[1] == 0xB2) + break; + + usleep_range(1000, 2000); + + if (i == 10) { + fts_reset_proc(0); + FTS_INFO("reset again finish!!"); + usleep_range(10000, 11000); + } + + } + + if (i >= 20) { + FTS_INFO("enter into romboot fail"); + return -EIO; + } + return 0; + +} + +static int fts_fwupg_enter_into_boot_new(struct fts_upgrade *upg) +{ + int ret = 0; + bool state = false; + + FTS_INFO("***********enter into pramboot/bootloader***********"); + if ((!upg) || (upg->func == NULL)) { + FTS_ERROR("upgrade/func is null"); + return -EINVAL; + } + + ret = fts_hardware_reset_to_boot(); + if (!ret) { + FTS_INFO("tp run in bootloader success"); + return ret; + } + + if (upg->func->pramboot_supported) { + FTS_INFO("pram supported, write pramboot and init"); + /* pramboot */ + if (upg->func->write_pramboot_private) + ret = upg->func->write_pramboot_private(); + else + ret = fts_pram_write_init(upg); + + if (ret < 0) { + FTS_ERROR("pram write_init fail"); + return ret; + } + } else { + FTS_DEBUG("pram not supported, confirm in bootloader"); + /* bootloader */ + state = fts_fwupg_check_state(upg, FTS_RUN_IN_BOOTLOADER); + if (!state) { + FTS_ERROR("fw not in bootloader, fail"); + return -EIO; + } + } + + return 0; +} + +int fts_fwupg_enter_into_boot(void) +{ + struct fts_upgrade *upg = fwupgrade; + + if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0) + return fts_fwupg_enter_into_boot_new(upg); + + return fts_fwupg_enter_into_boot_old(upg); +} + +/************************************************************************ + * Name: fts_fwupg_check_flash_status + * Brief: read status from tp + * Input: flash_status: correct value from tp + * retries: read retry times + * retries_delay: retry delay + * Output: + * Return: return true if flash status check pass, otherwise return false + ***********************************************************************/ +static bool fts_fwupg_check_flash_status( + u16 flash_status, + int retries, + int retries_delay) +{ + int ret = 0; + int i = 0; + u8 cmd = 0; + u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 }; + u16 read_status = 0; + + for (i = 0; i < retries; i++) { + cmd = FTS_CMD_FLASH_STATUS; + ret = fts_read(&cmd , 1, val, FTS_CMD_FLASH_STATUS_LEN); + read_status = (((u16)val[0]) << 8) + val[1]; + if (flash_status == read_status) { + /* FTS_DEBUG("[UPGRADE]flash status ok"); */ + return true; + } + /* FTS_DEBUG("flash status fail,ok:%04x read:%04x, retries:%d", flash_status, read_status, i); */ + msleep(retries_delay); + } + + return false; +} + +/************************************************************************ + * Name: fts_fwupg_erase + * Brief: erase flash area + * Input: delay - delay after erase + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +int fts_fwupg_erase(u32 delay) +{ + int ret = 0; + u8 cmd = 0; + bool flag = false; + + FTS_INFO("**********erase now**********"); + + /*send to erase flash*/ + cmd = FTS_CMD_ERASE_APP; + ret = fts_write(&cmd, 1); + if (ret < 0) { + FTS_ERROR("erase cmd fail"); + return ret; + } + msleep(delay); + + /* read status 0xF0AA: success */ + flag = fts_fwupg_check_flash_status(FTS_CMD_FLASH_STATUS_ERASE_OK, + FTS_RETRIES_REASE, + FTS_RETRIES_DELAY_REASE); + if (!flag) { + FTS_ERROR("ecc flash status check fail"); + return -EIO; + } + + return 0; +} + +/************************************************************************ + * Name: fts_fwupg_ecc_cal + * Brief: calculate and get ecc from tp + * Input: saddr - start address need calculate ecc + * len - length need calculate ecc + * Output: + * Return: return data ecc of tp if success, otherwise return error code + ***********************************************************************/ +int fts_fwupg_ecc_cal(u32 saddr, u32 len) +{ + int ret = 0; + u32 i = 0; + u32 cmdlen = FTS_CMD_ECC_CAL_LEN; + u8 wbuf[FTS_CMD_ECC_CAL_LEN] = { 0 }; + u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 }; + int ecc = 0; + int ecc_len = 0; + u32 packet_num = 0; + u32 packet_len = 0; + u32 remainder = 0; + u32 addr = 0; + u32 offset = 0; + bool bflag = false; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO( "**********read out checksum**********"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("upgrade/func is null"); + return -EINVAL; + } + + /* check sum init */ + wbuf[0] = FTS_CMD_ECC_INIT; + ret = fts_write(wbuf, 1); + if (ret < 0) { + FTS_ERROR("ecc init cmd write fail"); + return ret; + } + + if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0) { + packet_num = 1; + remainder = 0; + packet_len = len; + } else { + packet_num = len / FTS_MAX_LEN_ECC_CALC; + remainder = len % FTS_MAX_LEN_ECC_CALC; + if (remainder) + packet_num++; + packet_len = FTS_MAX_LEN_ECC_CALC; + } + FTS_INFO("ecc calc num:%d, remainder:%d", packet_num, remainder); + + /* send commond to start checksum */ + wbuf[0] = FTS_CMD_ECC_CAL; + for (i = 0; i < packet_num; i++) { + offset = FTS_MAX_LEN_ECC_CALC * i; + addr = saddr + offset; + wbuf[1] = BYTE_OFF_16(addr); + wbuf[2] = BYTE_OFF_8(addr); + wbuf[3] = BYTE_OFF_0(addr); + + if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0) { + wbuf[4] = BYTE_OFF_16(packet_len); + wbuf[5] = BYTE_OFF_8(packet_len); + wbuf[6] = BYTE_OFF_0(packet_len); + cmdlen = FTS_CMD_ECC_CAL_LEN; + } else { + if ((i == (packet_num - 1)) && remainder) + packet_len = remainder; + wbuf[4] = BYTE_OFF_8(packet_len); + wbuf[5] = BYTE_OFF_0(packet_len); + cmdlen = FTS_CMD_ECC_CAL_LEN - 1; + } + + FTS_DEBUG("ecc calc startaddr:0x%04x, len:%d", addr, packet_len); + ret = fts_write(wbuf, cmdlen); + if (ret < 0) { + FTS_ERROR("ecc calc cmd write fail"); + return ret; + } + + msleep(packet_len / 256); + + /* read status if check sum is finished */ + bflag = fts_fwupg_check_flash_status(FTS_CMD_FLASH_STATUS_ECC_OK, + FTS_RETRIES_ECC_CAL, + FTS_RETRIES_DELAY_ECC_CAL); + if (!bflag) { + FTS_ERROR("ecc flash status read fail"); + return -EIO; + } + } + + ecc_len = 1; + if ((upg->func->fw_ecc_check_mode == ECC_CHECK_MODE_CRC16) || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) + ecc_len = 2; + + /* read out check sum */ + wbuf[0] = FTS_CMD_ECC_READ; + ret = fts_read(wbuf, 1, val, ecc_len); + if (ret < 0) { + FTS_ERROR( "ecc read cmd write fail"); + return ret; + } + + if ((upg->func->fw_ecc_check_mode == ECC_CHECK_MODE_CRC16) || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) + ecc = (int)((u16)(val[0] << 8) + val[1]); + else + ecc = (int)val[0]; + + return ecc; +} + +/************************************************************************ + * Name: fts_flash_write_buf + * Brief: write buf data to flash address + * Input: saddr - start address data write to flash + * buf - data buffer + * len - data length + * delay - delay after write + * Output: + * Return: return data ecc of host if success, otherwise return error code + ***********************************************************************/ +int fts_flash_write_buf( + u32 saddr, + u8 *buf, + u32 len, + u32 delay) +{ + int ret = 0; + u32 i = 0; + u32 j = 0; + u32 packet_number = 0; + u32 packet_len = 0; + u32 addr = 0; + u32 offset = 0; + u32 remainder = 0; + u32 cmdlen = 0; + u8 packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 }; + u8 ecc_tmp = 0; + int ecc_in_host = 0; + u8 cmd = 0; + u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 }; + u16 read_status = 0; + u16 wr_ok = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO( "**********write data to flash**********"); + if ((!upg) || (!upg->func || !buf || !len)) { + FTS_ERROR("upgrade/func/buf/len is invalid"); + return -EINVAL; + } + + FTS_INFO("data buf start addr=0x%x, len=0x%x", saddr, len); + packet_number = len / FTS_FLASH_PACKET_LENGTH; + remainder = len % FTS_FLASH_PACKET_LENGTH; + if (remainder > 0) + packet_number++; + packet_len = FTS_FLASH_PACKET_LENGTH; + FTS_INFO("write data, num:%d remainder:%d", packet_number, remainder); + + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + addr = saddr + offset; + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + if (upg->ts_data->bus_type == BUS_TYPE_SPI_V2) { + packet_buf[0] = FTS_CMD_SET_WFLASH_ADDR; + packet_buf[1] = BYTE_OFF_16(addr); + packet_buf[2] = BYTE_OFF_8(addr); + packet_buf[3] = BYTE_OFF_0(addr); + ret = fts_write(packet_buf, FTS_LEN_SET_ADDR); + if (ret < 0) { + FTS_ERROR("set flash address fail"); + return ret; + } + + packet_buf[0] = FTS_CMD_WRITE; + cmdlen = 1; + } else { + packet_buf[0] = FTS_CMD_WRITE; + packet_buf[1] = BYTE_OFF_16(addr); + packet_buf[2] = BYTE_OFF_8(addr); + packet_buf[3] = BYTE_OFF_0(addr); + packet_buf[4] = BYTE_OFF_8(packet_len); + packet_buf[5] = BYTE_OFF_0(packet_len); + cmdlen = 6; + } + + for (j = 0; j < packet_len; j++) { + packet_buf[cmdlen + j] = buf[offset + j]; + ecc_tmp ^= packet_buf[cmdlen + j]; + } + + ret = fts_write(packet_buf, packet_len + cmdlen); + if (ret < 0) { + FTS_ERROR("app write fail"); + return ret; + } + mdelay(delay); + + /* read status */ + wr_ok = FTS_CMD_FLASH_STATUS_WRITE_OK + addr / packet_len; + for (j = 0; j < FTS_RETRIES_WRITE; j++) { + cmd = FTS_CMD_FLASH_STATUS; + ret = fts_read(&cmd , 1, val, FTS_CMD_FLASH_STATUS_LEN); + read_status = (((u16)val[0]) << 8) + val[1]; + /* FTS_INFO("%x %x", wr_ok, read_status); */ + if (read_status == wr_ok) + break; + + mdelay(FTS_RETRIES_DELAY_WRITE); + } + } + + ecc_in_host = (int)ecc_tmp; + if ((upg->func->fw_ecc_check_mode == ECC_CHECK_MODE_CRC16) || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) + ecc_in_host = (int)fts_crc16_calc_host(buf, len); + + return ecc_in_host; +} + +/************************************************************************ + * Name: fts_flash_read_buf + * Brief: read data from flash + * Input: saddr - start address data write to flash + * buf - buffer to store data read from flash + * len - read length + * Output: + * Return: return 0 if success, otherwise return error code + * + * Warning: can't call this function directly, need call in boot environment + ***********************************************************************/ +static int fts_flash_read_buf(u32 saddr, u8 *buf, u32 len) +{ + int ret = 0; + u32 i = 0; + u32 packet_number = 0; + u32 packet_len = 0; + u32 addr = 0; + u32 offset = 0; + u32 remainder = 0; + u8 wbuf[FTS_CMD_READ_LEN_SPI] = { 0 }; + struct fts_upgrade *upg = fwupgrade; + + if (!upg || !buf || !len) { + FTS_ERROR("upgrade/buf is NULL or len is 0"); + return -EINVAL; + } + + packet_number = len / FTS_FLASH_PACKET_LENGTH; + remainder = len % FTS_FLASH_PACKET_LENGTH; + if (remainder > 0) { + packet_number++; + } + packet_len = FTS_FLASH_PACKET_LENGTH; + FTS_INFO("read packet_number:%d, remainder:%d", packet_number, remainder); + + + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + addr = saddr + offset; + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + if (upg->ts_data->bus_type == BUS_TYPE_I2C) { + wbuf[0] = FTS_CMD_READ; + wbuf[1] = BYTE_OFF_16(addr); + wbuf[2] = BYTE_OFF_8(addr); + wbuf[3] = BYTE_OFF_0(addr); + ret = fts_write(wbuf, FTS_CMD_READ_LEN); + if (ret < 0) { + FTS_ERROR("pram/bootloader write 03 command fail"); + return ret; + } + + msleep(FTS_CMD_READ_DELAY); /* must wait, otherwise read wrong data */ + ret = fts_read(NULL, 0, buf + offset, packet_len); + if (ret < 0) { + FTS_ERROR("pram/bootloader read 03 command fail"); + return ret; + } + } else if (upg->ts_data->bus_type == BUS_TYPE_SPI_V2) { + wbuf[0] = FTS_CMD_SET_RFLASH_ADDR; + wbuf[1] = BYTE_OFF_16(addr); + wbuf[2] = BYTE_OFF_8(addr); + wbuf[3] = BYTE_OFF_0(addr); + ret = fts_write(wbuf, FTS_LEN_SET_ADDR); + if (ret < 0) { + FTS_ERROR("set flash address fail"); + return ret; + } + + msleep(FTS_CMD_READ_DELAY); + wbuf[0] = FTS_CMD_READ; + ret = fts_read(wbuf, 1, buf + offset, packet_len); + if (ret < 0) { + FTS_ERROR("pram/bootloader read 03(SPI_V2) command fail"); + return ret; + } + } else if (upg->ts_data->bus_type == BUS_TYPE_SPI) { + wbuf[0] = FTS_CMD_READ; + wbuf[1] = BYTE_OFF_16(addr); + wbuf[2] = BYTE_OFF_8(addr); + wbuf[3] = BYTE_OFF_0(addr); + wbuf[4] = BYTE_OFF_8(packet_len); + wbuf[5] = BYTE_OFF_0(packet_len); + ret = fts_read(wbuf, FTS_CMD_READ_LEN_SPI, + buf + offset, packet_len); + if (ret < 0) { + FTS_ERROR("pram/bootloader read 03(SPI) command fail"); + return ret; + } + } + } + + return 0; +} + +/************************************************************************ + * Name: fts_flash_read + * Brief: + * Input: addr - address of flash + * len - length of read + * Output: buf - data read from flash + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_flash_read(u32 addr, u8 *buf, u32 len) +{ + int ret = 0; + + FTS_INFO("***********read flash***********"); + if ((NULL == buf) || (0 == len)) { + FTS_ERROR("buf is NULL or len is 0"); + return -EINVAL; + } + + ret = fts_fwupg_enter_into_boot(); + if (ret < 0) { + FTS_ERROR("enter into pramboot/bootloader fail"); + goto read_flash_err; + } + + ret = fts_flash_read_buf(addr, buf, len); + if (ret < 0) { + FTS_ERROR("read flash fail"); + goto read_flash_err; + } + +read_flash_err: + /* reset to normal boot */ + ret = fts_fwupg_reset_in_boot(); + if (ret < 0) + FTS_ERROR("reset to normal boot fail"); + + return ret; +} + +int fts_enter_test_environment(bool test_state) +{ + return 0; +} +#if FTS_AUTO_LIC_UPGRADE_EN +static int fts_lic_get_vid_in_tp(u16 *vid) +{ + int ret = 0; + u8 val[2] = { 0 }; + + if (NULL == vid) { + FTS_ERROR("vid is NULL"); + return -EINVAL; + } + + ret = fts_read_reg(FTS_REG_VENDOR_ID, &val[0]); + if (fts_data->ic_info.is_incell) + ret = fts_read_reg(FTS_REG_MODULE_ID, &val[1]); + if (ret < 0) { + FTS_ERROR("read vid from tp fail"); + return ret; + } + + *vid = *(u16 *)val; + return 0; +} + +static int fts_lic_get_vid_in_host(struct fts_upgrade *upg, u16 *vid) +{ + u8 val[2] = { 0 }; + u8 *licbuf = NULL; + u32 conf_saddr = 0; + + if (!upg || !upg->func || !upg->lic || !vid) { + FTS_ERROR("upgrade/func/get_hlic_ver/lic/vid is null"); + return -EINVAL; + } + + if (upg->lic_length < FTS_MAX_LEN_SECTOR) { + FTS_ERROR("lic length(%x) fail", upg->lic_length); + return -EINVAL; + } + + licbuf = upg->lic; + conf_saddr = upg->func->fwcfgoff; + val[0] = licbuf[conf_saddr + FTS_CONIFG_VENDORID_OFF]; + if (fts_data->ic_info.is_incell) + val[1] = licbuf[conf_saddr + FTS_CONIFG_MODULEID_OFF]; + + *vid = *(u16 *)val; + return 0; +} + +static int fts_lic_get_ver_in_tp(u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("ver is NULL"); + return -EINVAL; + } + + ret = fts_read_reg(FTS_REG_LIC_VER, ver); + if (ret < 0) { + FTS_ERROR("read lcd initcode ver from tp fail"); + return ret; + } + + return 0; +} + +static int fts_lic_get_ver_in_host(struct fts_upgrade *upg, u8 *ver) +{ + int ret = 0; + + if (!upg || !upg->func || !upg->func->get_hlic_ver || !upg->lic) { + FTS_ERROR("upgrade/func/get_hlic_ver/lic is null"); + return -EINVAL; + } + + ret = upg->func->get_hlic_ver(upg->lic); + if (ret < 0) { + FTS_ERROR("get host lcd initial code version fail"); + return ret; + } + + *ver = (u8)ret; + return ret; +} + +static bool fts_lic_need_upgrade(struct fts_upgrade *upg) +{ + int ret = 0; + u8 initcode_ver_in_tp = 0; + u8 initcode_ver_in_host = 0; + u16 vid_in_tp = 0; + u16 vid_in_host = 0; + bool fwvalid = false; + + fwvalid = fts_fwupg_check_fw_valid(); + if ( !fwvalid) { + FTS_INFO("fw is invalid, no upgrade lcd init code"); + return false; + } + + ret = fts_lic_get_vid_in_host(upg, &vid_in_host); + if (ret < 0) { + FTS_ERROR("vendor id in host invalid"); + return false; + } + + ret = fts_lic_get_vid_in_tp(&vid_in_tp); + if (ret < 0) { + FTS_ERROR("vendor id in tp invalid"); + return false; + } + + FTS_DEBUG("vid in tp:0x%04x, host:0x%04x", vid_in_tp, vid_in_host); + if (vid_in_tp != vid_in_host) { + FTS_INFO("vendor id in tp&host are different, no upgrade lic"); + return false; + } + + ret = fts_lic_get_ver_in_host(upg, &initcode_ver_in_host); + if (ret < 0) { + FTS_ERROR("init code in host invalid"); + return false; + } + + ret = fts_lic_get_ver_in_tp(&initcode_ver_in_tp); + if (ret < 0) { + FTS_ERROR("read reg0xE4 fail"); + return false; + } + + FTS_DEBUG("lcd initial code version in tp:%x, host:%x", + initcode_ver_in_tp, initcode_ver_in_host); + if (0xA5 == initcode_ver_in_tp) { + FTS_INFO("lcd init code ver is 0xA5, don't upgade init code"); + return false; + } else if (0xFF == initcode_ver_in_tp) { + FTS_DEBUG("lcd init code in tp is invalid, need upgrade init code"); + return true; + } else if (initcode_ver_in_tp < initcode_ver_in_host) + return true; + else + return false; +} + +static int fts_lic_upgrade(struct fts_upgrade *upg) +{ + int ret = 0; + bool hlic_upgrade = false; + int upgrade_count = 0; + u8 ver = 0; + + FTS_INFO("lcd initial code auto upgrade function"); + if ((!upg) || (!upg->func) || (!upg->func->lic_upgrade)) { + FTS_ERROR("lcd upgrade function is null"); + return -EINVAL; + } + + hlic_upgrade = fts_lic_need_upgrade(upg); + FTS_INFO("lcd init code upgrade flag:%d", hlic_upgrade); + if (hlic_upgrade) { + FTS_INFO("lcd initial code need upgrade, upgrade begin..."); + do { + FTS_INFO("lcd initial code upgrade times:%d", upgrade_count); + upgrade_count++; + + ret = upg->func->lic_upgrade(upg->lic, upg->lic_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(); + } else { + fts_lic_get_ver_in_tp(&ver); + FTS_INFO("success upgrade to lcd initcode ver:%02x", ver); + break; + } + } while (upgrade_count < 2); + } else { + FTS_INFO("lcd initial code don't need upgrade"); + } + + return ret; +} +#endif /* FTS_AUTO_LIC_UPGRADE_EN */ + + +static int fts_param_get_ver_in_tp(u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("ver is NULL"); + return -EINVAL; + } + + ret = fts_read_reg(FTS_REG_IDE_PARA_VER_ID, ver); + if (ret < 0) { + FTS_ERROR("read fw param ver from tp fail"); + return ret; + } + + if ((0x00 == *ver) || (0xFF == *ver)) { + FTS_INFO("param version in tp invalid"); + return -EIO; + } + + return 0; +} + +static int fts_param_get_ver_in_host(struct fts_upgrade *upg, u8 *ver) +{ + if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) { + FTS_ERROR("fts_data/upgrade/func/fw/ver is NULL"); + return -EINVAL; + } + + if (upg->fw_length < upg->func->paramcfgveroff) { + FTS_ERROR("fw len(%x) < paramcfg ver offset(%x)", + upg->fw_length, upg->func->paramcfgveroff); + return -EINVAL; + } + + FTS_INFO("fw paramcfg version offset:%x", upg->func->paramcfgveroff); + *ver = upg->fw[upg->func->paramcfgveroff]; + + if ((0x00 == *ver) || (0xFF == *ver)) { + FTS_INFO("param version in host invalid"); + return -EIO; + } + + return 0; +} + +/* + * return: < 0 : error + * == 0: no ide + * == 1: ide + */ +static int fts_param_ide_in_host(struct fts_upgrade *upg) +{ + u32 off = 0; + + if ((!upg) || (!upg->func) || (!upg->fw)) { + FTS_ERROR("fts_data/upgrade/func/fw is NULL"); + return -EINVAL; + } + + if (upg->fw_length < upg->func->paramcfgoff + FTS_FW_IDE_SIG_LEN) { + FTS_INFO("fw len(%x) < paramcfg offset(%x), no IDE", + upg->fw_length, upg->func->paramcfgoff + FTS_FW_IDE_SIG_LEN); + return 0; + } + + off = upg->func->paramcfgoff; + if (0 == memcmp(&upg->fw[off], FTS_FW_IDE_SIG, FTS_FW_IDE_SIG_LEN)) { + FTS_INFO("fw in host is IDE version"); + return 1; + } + + FTS_INFO("fw in host isn't IDE version"); + return 0; +} + +/* + * return: < 0 : error + * 0 : no ide + * 1 : ide + */ +static int fts_param_ide_in_tp(u8 *val) +{ + int ret = 0; + + ret = fts_read_reg(FTS_REG_IDE_PARA_STATUS, val); + if (ret < 0) { + FTS_ERROR("read IDE PARAM STATUS in tp fail"); + return ret; + } + + if ((*val != 0xFF) && ((*val & 0x80) == 0x80)) { + FTS_INFO("fw in tp is IDE version"); + return 1; + } + + FTS_INFO("fw in tp isn't IDE version"); + return 0; +} + +/************************************************************************ + * fts_param_need_upgrade - check fw paramcfg need upgrade or not + * + * Return: < 0 : error if paramcfg need upgrade + * 0 : no need upgrade + * 1 : need upgrade app + param + * 2 : need upgrade param + ***********************************************************************/ +static int fts_param_need_upgrade(struct fts_upgrade *upg) +{ + int ret = 0; + u8 val = 0; + int ide_in_host = 0; + int ide_in_tp = 0; + u8 ver_in_host = 0; + u8 ver_in_tp = 0; + bool fwvalid = false; + + fwvalid = fts_fwupg_check_fw_valid(); + if ( !fwvalid) { + FTS_INFO("fw is invalid, upgrade app+param"); + return 1; + } + + ide_in_host = fts_param_ide_in_host(upg); + if (ide_in_host < 0) { + FTS_INFO("fts_param_ide_in_host fail"); + return ide_in_host; + } + + ide_in_tp = fts_param_ide_in_tp(&val); + if (ide_in_tp < 0) { + FTS_INFO("fts_param_ide_in_tp fail"); + return ide_in_tp; + } + + if ((0 == ide_in_host) && (0 == ide_in_tp)) { + FTS_INFO("fw in host&tp are both no ide"); + return 0; + } else if (ide_in_host != ide_in_tp) { + FTS_INFO("fw in host&tp not equal, need upgrade app+param"); + return 1; + } else if ((1 == ide_in_host) && (1 == ide_in_tp)) { + FTS_INFO("fw in host&tp are both ide"); + if ((val & 0x7F) != 0x00) { + FTS_INFO("param invalid, need upgrade param"); + return 2; + } + + ret = fts_param_get_ver_in_host(upg, &ver_in_host); + if (ret < 0) { + FTS_ERROR("param version in host invalid"); + return ret; + } + + ret = fts_param_get_ver_in_tp(&ver_in_tp); + if (ret < 0) { + FTS_ERROR("get IDE param ver in tp fail"); + return ret; + } + + FTS_INFO("fw paramcfg version in tp:%x, host:%x", + ver_in_tp, ver_in_host); + if (ver_in_tp != ver_in_host) { + return 2; + } + } + + return 0; +} + +static int fts_fwupg_get_ver_in_tp(u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("ver is NULL"); + return -EINVAL; + } + + ret = fts_read_reg(FTS_REG_FW_VER, ver); + if (ret < 0) { + FTS_ERROR("read fw ver from tp fail"); + return ret; + } + + return 0; +} + +static int fts_fwupg_get_ver_in_host(struct fts_upgrade *upg, u8 *ver) +{ + if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) { + FTS_ERROR("fts_data/upgrade/func/fw/ver is NULL"); + return -EINVAL; + } + + if (upg->fw_length < upg->func->fwveroff) { + FTS_ERROR("fw len(0x%0x) < fw ver offset(0x%x)", + upg->fw_length, upg->func->fwveroff); + return -EINVAL; + } + + FTS_INFO("fw version offset:0x%x", upg->func->fwveroff); + *ver = upg->fw[upg->func->fwveroff]; + return 0; +} + +static bool fts_fwupg_need_upgrade(struct fts_upgrade *upg) +{ + int ret = 0; + bool fwvalid = false; + u8 fw_ver_in_host = 0; + u8 fw_ver_in_tp = 0; + + fwvalid = fts_fwupg_check_fw_valid(); + if (fwvalid) { + ret = fts_fwupg_get_ver_in_host(upg, &fw_ver_in_host); + if (ret < 0) { + FTS_ERROR("get fw ver in host fail"); + return false; + } + + ret = fts_fwupg_get_ver_in_tp(&fw_ver_in_tp); + if (ret < 0) { + FTS_ERROR("get fw ver in tp fail"); + return false; + } + + FTS_INFO("fw version in tp:%x, host:%x", fw_ver_in_tp, fw_ver_in_host); + if (fw_ver_in_tp != fw_ver_in_host) { + return true; + } + } else { + FTS_INFO("fw invalid, need upgrade fw"); + return true; + } + + return false; +} + +/************************************************************************ + * Name: fts_fw_upgrade + * Brief: fw upgrade main entry, run in following steps + * 1. check fw version(A6), not equal, will upgrade app(+param) + * 2. if fw version equal, will check ide, will upgrade app(+param) + * in the follow situation + * a. host&tp IDE's type are not equal, will upgrade app+param + * b. host&tp are both IDE's type, and param's version are not + * equal, will upgrade param + * Input: + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +int fts_fwupg_upgrade(struct fts_upgrade *upg) +{ + int ret = 0; + bool upgrade_flag = false; + int upgrade_count = 0; + u8 ver = 0; + + FTS_INFO("fw auto upgrade function"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("upg/upg->func is null"); + return -EINVAL; + } + + upgrade_flag = fts_fwupg_need_upgrade(upg); + FTS_INFO("fw upgrade flag:%d", upgrade_flag); + do { + upgrade_count++; + if (upgrade_flag) { + FTS_INFO("upgrade fw app(times:%d)", upgrade_count); + if (upg->func->upgrade) { + ret = upg->func->upgrade(upg->fw, upg->fw_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(); + } else { + fts_fwupg_get_ver_in_tp(&ver); + FTS_INFO("success upgrade to fw version %02x", ver); + break; + } + } else { + FTS_ERROR("upgrade func/upgrade is null, return immediately"); + ret = -ENODATA; + break; + } + } else { + if (upg->func->param_upgrade) { + ret = fts_param_need_upgrade(upg); + if (ret <= 0) { + FTS_INFO("param don't need upgrade"); + break; + } else if (1 == ret) { + FTS_INFO("force upgrade fw app(times:%d)", upgrade_count); + if (upg->func->upgrade) { + ret = upg->func->upgrade(upg->fw, upg->fw_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(); + } else { + break; + } + } + } else if (2 == ret) { + FTS_INFO("upgrade param area(times:%d)", upgrade_count); + ret = upg->func->param_upgrade(upg->fw, upg->fw_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(); + } else { + fts_param_get_ver_in_tp(&ver); + FTS_INFO("success upgrade to fw param version %02x", ver); + break; + } + } else + break; + } else { + break; + } + } + } while (upgrade_count < 2); + + return ret; +} + +/************************************************************************ + * fts_fwupg_auto_upgrade - upgrade main entry + ***********************************************************************/ +static void fts_fwupg_auto_upgrade(struct fts_upgrade *upg) +{ + int ret = 0; + + FTS_INFO("********************FTS enter upgrade********************"); + if (!upg || !upg->ts_data) { + FTS_ERROR("upg/ts_data is null"); + return ; + } + + ret = fts_fwupg_upgrade(upg); + if (ret < 0) + FTS_ERROR("**********tp fw(app/param) upgrade failed**********"); + else + FTS_INFO("**********tp fw(app/param) no upgrade/upgrade success**********"); + +#if FTS_AUTO_LIC_UPGRADE_EN + ret = fts_lic_upgrade(upg); + if (ret < 0) + FTS_ERROR("**********lcd init code upgrade failed**********"); + else + FTS_INFO("**********lcd init code no upgrade/upgrade success**********"); +#endif + + FTS_INFO("********************FTS exit upgrade********************"); +} + +static int fts_fwupg_get_vendorid(struct fts_upgrade *upg, int *vid) +{ + int ret = 0; + bool fwvalid = false; + u8 vendor_id = 0; + u8 module_id = 0; + u32 fwcfg_addr = 0; + u8 cmd = 0; + u8 cfgbuf[FTS_HEADER_LEN] = { 0 }; + + FTS_INFO("read vendor id from tp"); + if ((!upg) || (!upg->func) || (!upg->ts_data) || (!vid)) { + FTS_ERROR("upgrade/func/ts_data/vid is null"); + return -EINVAL; + } + + fwvalid = fts_fwupg_check_fw_valid(); + if (fwvalid) { + ret = fts_read_reg(FTS_REG_VENDOR_ID, &vendor_id); + if (upg->ts_data->ic_info.is_incell) + ret = fts_read_reg(FTS_REG_MODULE_ID, &module_id); + } else { + if (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0) { + cmd = FTS_CMD_READ_FW_CONF; + ret = fts_read(&cmd, 1, cfgbuf, FTS_HEADER_LEN); + } else { + fwcfg_addr = upg->func->fwcfgoff; + ret = fts_flash_read(fwcfg_addr, cfgbuf, FTS_HEADER_LEN); + } + + if ((cfgbuf[FTS_CONIFG_VENDORID_OFF] + + cfgbuf[FTS_CONIFG_VENDORID_OFF + 1]) == 0xFF) + vendor_id = cfgbuf[FTS_CONIFG_VENDORID_OFF]; + if (upg->ts_data->ic_info.is_incell) { + if ((cfgbuf[FTS_CONIFG_MODULEID_OFF] + + cfgbuf[FTS_CONIFG_MODULEID_OFF + 1]) == 0xFF) + module_id = cfgbuf[FTS_CONIFG_MODULEID_OFF]; + } + } + + if (ret < 0) { + FTS_ERROR("fail to get vendor id from tp"); + return ret; + } + + *vid = (int)((module_id << 8) + vendor_id); + return 0; +} + +static int fts_fwupg_get_module_info(struct fts_upgrade *upg) +{ + int ret = 0; + int i = 0; + struct upgrade_module *info = &module_list[0]; + + if (!upg || !upg->ts_data) { + FTS_ERROR("upg/ts_data is null"); + return -EINVAL; + } + + if (FTS_GET_MODULE_NUM > 1) { + /* support multi modules, must read correct module id(vendor id) */ + ret = fts_fwupg_get_vendorid(upg, &upg->module_id); + if (ret < 0) { + FTS_ERROR("get vendor id failed"); + return ret; + } + FTS_INFO("module id:%04x", upg->module_id); + for (i = 0; i < FTS_GET_MODULE_NUM; i++) { + info = &module_list[i]; + if (upg->module_id == info->id) { + FTS_INFO("module id match, get module info pass"); + break; + } + } + if (i >= FTS_GET_MODULE_NUM) { + FTS_ERROR("no module id match, don't get file"); + return -ENODATA; + } + } + + upg->module_info = info; + return 0; +} + +static int fts_get_fw_file_via_request_firmware(struct fts_upgrade *upg) +{ + int ret = 0; + const struct firmware *fw = NULL; + u8 *tmpbuf = NULL; + char fwname[FILE_NAME_LENGTH] = { 0 }; + + if (!upg || !upg->ts_data || !upg->ts_data->dev) { + FTS_ERROR("upg/ts_data/dev is null"); + return -EINVAL; + } + + if (upg->ts_data->pdata->type == _FT3658U) + snprintf(fwname, FILE_NAME_LENGTH, "%s%s_ft3658.bin", + FTS_FW_NAME_PREX_WITH_REQUEST, + upg->module_info->vendor_name); + else + snprintf(fwname, FILE_NAME_LENGTH, "%s%s.bin", + FTS_FW_NAME_PREX_WITH_REQUEST, + upg->module_info->vendor_name); + + ret = request_firmware(&fw, fwname, upg->ts_data->dev); + if (ret == 0) { + FTS_INFO("firmware(%s) request successfully", fwname); + tmpbuf = vmalloc(fw->size); + if (NULL == tmpbuf) { + FTS_ERROR("fw buffer vmalloc fail"); + ret = -ENOMEM; + } else { + memcpy(tmpbuf, fw->data, fw->size); + upg->fw = tmpbuf; + upg->fw_length = fw->size; + upg->fw_from_request = 1; + } + } else { + FTS_INFO("firmware(%s) request fail,ret=%d", fwname, ret); + } + + if (fw != NULL) { + release_firmware(fw); + fw = NULL; + } + + return ret; +} + +static int fts_get_fw_file_via_i(struct fts_upgrade *upg) +{ + upg->fw = upg->module_info->fw_file; + upg->fw_length = upg->module_info->fw_len; + upg->fw_from_request = 0; + + return 0; +} + +/***************************************************************************** + * Name: fts_fwupg_get_fw_file + * Brief: get fw image/file, + * If support muitl modules, please set FTS_GET_MODULE_NUM, and FTS_- + * MODULE_ID/FTS_MODULE_NAME; + * If get fw via .i file, please set FTS_FW_REQUEST_SUPPORT=0, and F- + * TS_MODULE_ID; will use module id to distingwish different modules; + * If get fw via reques_firmware(), please set FTS_FW_REQUEST_SUPPORT + * =1, and FTS_MODULE_NAME; fw file name will be composed of "focalt- + * ech_ts_fw_" & FTS_VENDOR_NAME; + * + * If have flash, module_id=vendor_id, If non-flash,module_id need + * transfer from LCD driver(gpio or lcm_id or ...); + * Input: + * Output: + * Return: return 0 if success, otherwise return error code + *****************************************************************************/ +static int fts_fwupg_get_fw_file(struct fts_upgrade *upg) +{ + int ret = 0; + bool get_fw_i_flag = false; + + FTS_DEBUG("get upgrade fw file"); + if (!upg || !upg->ts_data) { + FTS_ERROR("upg/ts_data is null"); + return -EINVAL; + } + + ret = fts_fwupg_get_module_info(upg); + if ((ret < 0) || (!upg->module_info)) { + FTS_ERROR("get module info fail"); + return ret; + } + + if (FTS_FW_REQUEST_SUPPORT) { + ret = fts_get_fw_file_via_request_firmware(upg); + if (ret != 0) { + get_fw_i_flag = true; + } + } else { + get_fw_i_flag = true; + } + + if (get_fw_i_flag) { + ret = fts_get_fw_file_via_i(upg); + } + + upg->lic = upg->fw; + upg->lic_length = upg->fw_length; + + FTS_INFO("upgrade fw file len:%d", upg->fw_length); + if ((upg->fw_length < FTS_MIN_LEN) + || (upg->fw_length > FTS_MAX_LEN_FILE)) { + FTS_ERROR("fw file len(%d) fail", upg->fw_length); + return -ENODATA; + } + + return ret; +} + +static void fts_fwupg_init_ic_detail(struct fts_upgrade *upg) +{ + if (upg && upg->func && upg->func->init) { + upg->func->init(upg->fw, upg->fw_length); + } +} + +/***************************************************************************** + * Name: fts_fwupg_work + * Brief: 1. get fw image/file + * 2. ic init if have + * 3. call upgrade main function(fts_fwupg_auto_upgrade) + * Input: + * Output: + * Return: + *****************************************************************************/ +static void fts_fwupg_work(struct work_struct *work) +{ + int ret = 0; + struct fts_upgrade *upg = fwupgrade; + +#if !FTS_AUTO_UPGRADE_EN + FTS_INFO("FTS_AUTO_UPGRADE_EN is disabled, not upgrade when power on"); + return ; +#endif + + FTS_INFO("fw upgrade work function"); + if (!upg || !upg->ts_data) { + FTS_ERROR("upg/ts_data is null"); + return ; + } + + upg->ts_data->fw_loading = 1; + fts_irq_disable(); +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(DISABLE); +#endif + + /* get fw */ + ret = fts_fwupg_get_fw_file(upg); + if (ret < 0) { + FTS_ERROR("get file fail, can't upgrade"); + } else { + /* ic init if have */ + fts_fwupg_init_ic_detail(upg); + /* run auto upgrade */ + fts_fwupg_auto_upgrade(upg); + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ENABLE); +#endif + fts_irq_enable(); + upg->ts_data->fw_loading = 0; +} + +int fts_fwupg_init(struct fts_ts_data *ts_data) +{ + int i = 0; + int j = 0; + int ic_stype = 0; + struct upgrade_func *func = upgrade_func_list[0]; + int func_count = sizeof(upgrade_func_list) / sizeof(upgrade_func_list[0]); + + FTS_INFO("fw upgrade init function"); + + if (!ts_data || !ts_data->ts_workqueue) { + FTS_ERROR("ts_data/workqueue is NULL, can't run upgrade function"); + return -EINVAL; + } + + if (0 == func_count) { + FTS_ERROR("no upgrade function in tp driver"); + return -ENODATA; + } + + fwupgrade = (struct fts_upgrade *)kzalloc(sizeof(*fwupgrade), GFP_KERNEL); + if (NULL == fwupgrade) { + FTS_ERROR("malloc memory for upgrade fail"); + return -ENOMEM; + } + + ic_stype = ts_data->ic_info.ids.type; + if (1 == func_count) { + fwupgrade->func = func; + } else { + for (i = 0; i < func_count; i++) { + func = upgrade_func_list[i]; + for (j = 0; j < FTX_MAX_COMPATIBLE_TYPE; j++) { + if (0 == func->ctype[j]) + break; + else if (func->ctype[j] == ic_stype) { + FTS_INFO("match upgrade function,type:%x", (int)func->ctype[j]); + fwupgrade->func = func; + } + } + } + } + + if (NULL == fwupgrade->func) { + FTS_ERROR("no upgrade function match, can't upgrade"); + kfree(fwupgrade); + fwupgrade = NULL; + return -ENODATA; + } + + fwupgrade->ts_data = ts_data; + INIT_WORK(&ts_data->fwupg_work, fts_fwupg_work); + queue_work(ts_data->ts_workqueue, &ts_data->fwupg_work); + + return 0; +} + +int fts_fwupg_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + if (fwupgrade) { + if (fwupgrade->fw_from_request) { + vfree(fwupgrade->fw); + fwupgrade->fw = NULL; + } + + kfree(fwupgrade); + fwupgrade = NULL; + } + FTS_FUNC_EXIT(); + return 0; +} diff --git a/focaltech_touch/focaltech_flash.h b/focaltech_touch/focaltech_flash.h new file mode 100644 index 0000000000..8518ef460a --- /dev/null +++ b/focaltech_touch/focaltech_flash.h @@ -0,0 +1,216 @@ +/************************************************************************ +* Copyright (C) 2012-2019, Focaltech Systems (R)£¬All Rights Reserved. +* +* File Name: focaltech_flash.h +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-07 +* +* Abstract: +* +************************************************************************/ +#ifndef __LINUX_FOCALTECH_FLASH_H__ +#define __LINUX_FOCALTECH_FLASH_H__ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_CMD_RESET 0x07 +#define FTS_ROMBOOT_CMD_SET_PRAM_ADDR 0xAD +#define FTS_ROMBOOT_CMD_SET_PRAM_ADDR_LEN 4 +#define FTS_ROMBOOT_CMD_WRITE 0xAE +#define FTS_ROMBOOT_CMD_START_APP 0x08 +#define FTS_DELAY_PRAMBOOT_START 100 +#define FTS_ROMBOOT_CMD_ECC 0xCC +#define FTS_PRAM_SADDR 0x000000 +#define FTS_DRAM_SADDR 0xD00000 + +#define FTS_CMD_READ 0x03 +#define FTS_CMD_READ_DELAY 1 +#define FTS_CMD_READ_LEN 4 +#define FTS_CMD_READ_LEN_SPI 6 +#define FTS_CMD_FLASH_TYPE 0x05 +#define FTS_CMD_FLASH_MODE 0x09 +#define FLASH_MODE_WRITE_FLASH_VALUE 0x0A +#define FLASH_MODE_UPGRADE_VALUE 0x0B +#define FLASH_MODE_LIC_VALUE 0x0C +#define FLASH_MODE_PARAM_VALUE 0x0D +#define FTS_CMD_ERASE_APP 0x61 +#define FTS_REASE_APP_DELAY 1350 +#define FTS_ERASE_SECTOR_DELAY 60 +#define FTS_RETRIES_REASE 50 +#define FTS_RETRIES_DELAY_REASE 400 +#define FTS_CMD_FLASH_STATUS 0x6A +#define FTS_CMD_FLASH_STATUS_LEN 2 +#define FTS_CMD_FLASH_STATUS_NOP 0x0000 +#define FTS_CMD_FLASH_STATUS_ECC_OK 0xF055 +#define FTS_CMD_FLASH_STATUS_ERASE_OK 0xF0AA +#define FTS_CMD_FLASH_STATUS_WRITE_OK 0x1000 +#define FTS_CMD_ECC_INIT 0x64 +#define FTS_CMD_ECC_CAL 0x65 +#define FTS_CMD_ECC_CAL_LEN 7 +#define FTS_RETRIES_ECC_CAL 10 +#define FTS_RETRIES_DELAY_ECC_CAL 50 +#define FTS_CMD_ECC_READ 0x66 +#define FTS_CMD_SET_WFLASH_ADDR 0xAB +#define FTS_CMD_SET_RFLASH_ADDR 0xAC +#define FTS_LEN_SET_ADDR 4 +#define FTS_CMD_DATA_LEN 0xB0 +#define FTS_CMD_APP_DATA_LEN_INCELL 0x7A +#define FTS_CMD_DATA_LEN_LEN 4 +#define FTS_CMD_WRITE 0xBF +#define FTS_RETRIES_WRITE 100 +#define FTS_RETRIES_DELAY_WRITE 1 +#define FTS_CMD_WRITE_LEN 6 +#define FTS_DELAY_READ_ID 20 +#define FTS_DELAY_UPGRADE_RESET 80 +#define PRAMBOOT_MIN_SIZE 0x120 +#define PRAMBOOT_MAX_SIZE (64*1024) +#define FTS_FLASH_PACKET_LENGTH 32 /* max=128 */ +#define FTS_MAX_LEN_ECC_CALC 0xFFFE /* must be even */ +#define FTS_MIN_LEN 0x120 +#define FTS_MAX_LEN_FILE (128 * 1024) +#define FTS_MAX_LEN_APP (64 * 1024) +#define FTS_MAX_LEN_SECTOR (4 * 1024) +#define FTS_CONIFG_VENDORID_OFF 0x04 +#define FTS_CONIFG_MODULEID_OFF 0x1E +#define FTS_CONIFG_PROJECTID_OFF 0x20 +#define FTS_APPINFO_OFF 0x100 +#define FTS_APPINFO_APPLEN_OFF 0x00 +#define FTS_APPINFO_APPLEN2_OFF 0x12 +#define FTS_REG_UPGRADE 0xFC +#define FTS_REG_UPGRADE2 0xBC +#define FTS_UPGRADE_AA 0xAA +#define FTS_UPGRADE_55 0x55 +#define FTS_DELAY_UPGRADE_AA 10 +#define FTS_UPGRADE_LOOP 30 +#define FTS_HEADER_LEN 32 +#define FTS_FW_BIN_FILEPATH "/sdcard/" +#define FTS_FW_IDE_SIG "IDE_" +#define FTS_FW_IDE_SIG_LEN 4 +#define MAX_MODULE_VENDOR_NAME_LEN 16 + +#define FTS_ROMBOOT_CMD_ECC_NEW_LEN 7 +#define FTS_ECC_FINISH_TIMEOUT 100 +#define FTS_ROMBOOT_CMD_ECC_FINISH 0xCE +#define FTS_ROMBOOT_CMD_ECC_FINISH_OK_A5 0xA5 +#define FTS_ROMBOOT_CMD_ECC_FINISH_OK_00 0x00 +#define FTS_ROMBOOT_CMD_ECC_READ 0xCD +#define AL2_FCS_COEF ((1 << 15) + (1 << 10) + (1 << 3)) + +#define FTS_APP_INFO_OFFSET 0x100 + +enum FW_STATUS { + FTS_RUN_IN_ERROR, + FTS_RUN_IN_APP, + FTS_RUN_IN_ROM, + FTS_RUN_IN_PRAM, + FTS_RUN_IN_BOOTLOADER, +}; + +enum FW_FLASH_MODE { + FLASH_MODE_APP, + FLASH_MODE_LIC, + FLASH_MODE_PARAM, + FLASH_MODE_ALL, +}; + +enum ECC_CHECK_MODE { + ECC_CHECK_MODE_XOR, + ECC_CHECK_MODE_CRC16, +}; + +enum UPGRADE_SPEC { + UPGRADE_SPEC_V_1_0 = 0x0100, +}; + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +/* IC info */ +struct upgrade_func { + u64 ctype[FTX_MAX_COMPATIBLE_TYPE]; + u32 fwveroff; + u32 fwcfgoff; + u32 appoff; + u32 licoff; + u32 paramcfgoff; + u32 paramcfgveroff; + u32 paramcfg2off; + int pram_ecc_check_mode; + int fw_ecc_check_mode; + int upgspec_version; + bool new_return_value_from_ic; + bool appoff_handle_in_ic; + bool is_reset_register_BC; + bool read_boot_id_need_reset; + bool hid_supported; + bool pramboot_supported; + u8 *pramboot; + u32 pb_length; + int (*init)(u8 *, u32); + int (*write_pramboot_private)(void); + int (*upgrade)(u8 *, u32); + int (*get_hlic_ver)(u8 *); + int (*lic_upgrade)(u8 *, u32); + int (*param_upgrade)(u8 *, u32); + int (*force_upgrade)(u8 *, u32); +}; + +struct upgrade_setting_nf { + u8 rom_idh; + u8 rom_idl; + u16 reserved; + u32 app2_offset; + u32 ecclen_max; + u8 eccok_val; + u8 upgsts_boot; + u8 delay_init; + bool spi_pe; + bool half_length; + bool fd_check; + bool drwr_support; +}; + +struct upgrade_module { + int id; + char vendor_name[MAX_MODULE_VENDOR_NAME_LEN]; + u8 *fw_file; + u32 fw_len; +}; + +struct fts_upgrade { + struct fts_ts_data *ts_data; + struct upgrade_module *module_info; + struct upgrade_func *func; + struct upgrade_setting_nf *setting_nf; + int module_id; + bool fw_from_request; + u8 *fw; + u32 fw_length; + u8 *lic; + u32 lic_length; +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +extern struct upgrade_func upgrade_func_ft5452; +extern struct upgrade_func upgrade_func_ft5652; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +int fts_fwupg_reset_in_boot(void); +int fts_fwupg_enter_into_boot(void); +int fts_fwupg_erase(u32 delay); +int fts_fwupg_ecc_cal(u32 saddr, u32 len); +int fts_flash_write_buf(u32 saddr, u8 *buf, u32 len, u32 delay); +int fts_fwupg_upgrade(struct fts_upgrade *upg); +#endif diff --git a/focaltech_touch/focaltech_flash/focaltech_upgrade_ft3518.c b/focaltech_touch/focaltech_flash/focaltech_upgrade_ft3518.c new file mode 100644 index 0000000000..c42e7b5ad9 --- /dev/null +++ b/focaltech_touch/focaltech_flash/focaltech_upgrade_ft3518.c @@ -0,0 +1,292 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_upgrade_ft5452.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-15 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "../focaltech_flash.h" + +/************************************************************************ +* Name: fts_ft5452_upgrade +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_ft5452_upgrade(u8 *buf, u32 len) +{ + int ret = 0; + u32 start_addr = 0; + u8 cmd[4] = { 0 }; + int ecc_in_host = 0; + int ecc_in_tp = 0; + + int i = 0; + u8 wbuf[7] = { 0 }; + u8 reg_val[4] = {0}; + + if (NULL == buf) { + FTS_ERROR("fw buf is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > (60 * 1024))) { + FTS_ERROR("fw buffer len(%x) fail", len); + return -EINVAL; + } + + /* enter into upgrade environment */ + ret = fts_fwupg_enter_into_boot(); + if (ret < 0) { + FTS_ERROR("enter into pramboot/bootloader fail,ret=%d", ret); + goto fw_reset; + } + + cmd[0] = FTS_CMD_FLASH_MODE; + cmd[1] = FLASH_MODE_UPGRADE_VALUE; + ret = fts_write(cmd, 2); + if (ret < 0) { + FTS_ERROR("upgrade mode(09) cmd write fail"); + goto fw_reset; + } + + cmd[0] = FTS_CMD_DATA_LEN; + cmd[1] = BYTE_OFF_16(len); + cmd[2] = BYTE_OFF_8(len); + cmd[3] = BYTE_OFF_0(len); + ret = fts_write(cmd, FTS_CMD_DATA_LEN_LEN); + if (ret < 0) { + FTS_ERROR("data len cmd write fail"); + goto fw_reset; + } + + ret = fts_fwupg_erase(FTS_REASE_APP_DELAY); + if (ret < 0) { + FTS_ERROR("erase cmd write fail"); + goto fw_reset; + } + + /* write app */ + start_addr = upgrade_func_ft5452.appoff; + ecc_in_host = fts_flash_write_buf(start_addr, buf, len, 1); + if (ecc_in_host < 0 ) { + FTS_ERROR("lcd initial code write fail"); + goto fw_reset; + } + + FTS_INFO( "**********read out checksum**********"); + + /* check sum init */ + wbuf[0] = FTS_CMD_ECC_INIT; + ret = fts_write(wbuf, 1); + if (ret < 0) { + FTS_ERROR("ecc init cmd write fail"); + return ret; + } + + /* send commond to start checksum */ + wbuf[0] = FTS_CMD_ECC_CAL; + wbuf[1] = BYTE_OFF_16(start_addr); + wbuf[2] = BYTE_OFF_8(start_addr); + wbuf[3] = BYTE_OFF_0(start_addr); + + wbuf[4] = BYTE_OFF_16(len); + wbuf[5] = BYTE_OFF_8(len); + wbuf[6] = BYTE_OFF_0(len); + + FTS_DEBUG("ecc calc startaddr:0x%04x, len:%d", start_addr, len); + ret = fts_write(wbuf, 7); + if (ret < 0) { + FTS_ERROR("ecc calc cmd write fail"); + return ret; + } + + msleep(len / 256); + + /* read status if check sum is finished */ + for (i = 0; i < FTS_RETRIES_ECC_CAL; i++) { + wbuf[0] = FTS_CMD_FLASH_STATUS; + reg_val[0] = reg_val[1] = 0x00; + fts_read(wbuf, 1, reg_val, 2); + FTS_DEBUG("[UPGRADE]: reg_val[0]=%02x reg_val[0]=%02x!!", reg_val[0], reg_val[1]); + if ((0xF0 == reg_val[0]) && (0x55 == reg_val[1])) { + break; + } + msleep(FTS_RETRIES_DELAY_ECC_CAL); + } + + /* read out check sum */ + wbuf[0] = FTS_CMD_ECC_READ; + ret = fts_read(wbuf, 1, reg_val, 1); + if (ret < 0) { + FTS_ERROR( "ecc read cmd write fail"); + return ret; + } + ecc_in_tp = reg_val[0]; + + FTS_INFO("ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host); + if (ecc_in_tp != ecc_in_host) { + FTS_ERROR("ecc check fail"); + goto fw_reset; + } + + FTS_INFO("upgrade success, reset to normal boot"); + ret = fts_fwupg_reset_in_boot(); + if (ret < 0) { + FTS_ERROR("reset to normal boot fail"); + } + + msleep(200); + return 0; + +fw_reset: + FTS_INFO("upgrade fail, reset to normal boot"); + ret = fts_fwupg_reset_in_boot(); + if (ret < 0) { + FTS_ERROR("reset to normal boot fail"); + } + return -EIO; +} + +struct upgrade_func upgrade_func_ft5452 = { + .ctype = {0x81}, + .fwveroff = 0x010E, + .fwcfgoff = 0x1FFB0, + .appoff = 0x0000, + .pramboot_supported = false, + .hid_supported = true, + .upgrade = fts_ft5452_upgrade, +}; + + +#define FTS_DELAY_ERASE_PAGE_2K 80 +#define FTS_SIZE_PAGE_2K 2048 + +/************************************************************************ + * Name: fts_ft5652_upgrade + * Brief: + * Input: + * Output: + * Return: return 0 if success, otherwise return error code + **********************************************************************/ +static int fts_ft5652_upgrade(u8 *buf, u32 len) +{ + int ret = 0; + u32 start_addr = 0; + u8 cmd[4] = { 0 }; + u32 delay = 0; + int ecc_in_host = 0; + int ecc_in_tp = 0; + + if ((buf == NULL) || (len < FTS_MIN_LEN)) { + FTS_ERROR("buffer/len(%x) is invalid", len); + return -EINVAL; + } + + /* enter into upgrade environment */ + ret = fts_fwupg_enter_into_boot(); + if (ret < 0) { + FTS_ERROR("enter into pramboot/bootloader fail,ret=%d", ret); + goto fw_reset; + } + + cmd[0] = FTS_CMD_APP_DATA_LEN_INCELL; + cmd[1] = BYTE_OFF_16(len); + cmd[2] = BYTE_OFF_8(len); + cmd[3] = BYTE_OFF_0(len); + ret = fts_write(cmd, FTS_CMD_DATA_LEN_LEN); + if (ret < 0) { + FTS_ERROR("data len cmd write fail"); + goto fw_reset; + } + + cmd[0] = FTS_CMD_FLASH_MODE; + cmd[1] = FLASH_MODE_UPGRADE_VALUE; + ret = fts_write(cmd, 2); + if (ret < 0) { + FTS_ERROR("upgrade mode(09) cmd write fail"); + goto fw_reset; + } + + delay = FTS_DELAY_ERASE_PAGE_2K * (len / FTS_SIZE_PAGE_2K); + ret = fts_fwupg_erase(delay); + if (ret < 0) { + FTS_ERROR("erase cmd write fail"); + goto fw_reset; + } + + /* write app */ + start_addr = upgrade_func_ft5652.appoff; + ecc_in_host = fts_flash_write_buf(start_addr, buf, len, 1); + if (ecc_in_host < 0) { + FTS_ERROR("flash write fail"); + goto fw_reset; + } + + /* ecc */ + ecc_in_tp = fts_fwupg_ecc_cal(start_addr, len); + if (ecc_in_tp < 0) { + FTS_ERROR("ecc read fail"); + goto fw_reset; + } + + FTS_INFO("ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host); + if (ecc_in_tp != ecc_in_host) { + FTS_ERROR("ecc check fail"); + goto fw_reset; + } + + FTS_INFO("upgrade success, reset to normal boot"); + ret = fts_fwupg_reset_in_boot(); + if (ret < 0) + FTS_ERROR("reset to normal boot fail"); + + msleep(200); + return 0; + +fw_reset: + FTS_INFO("upgrade fail, reset to normal boot"); + ret = fts_fwupg_reset_in_boot(); + if (ret < 0) + FTS_ERROR("reset to normal boot fail"); + + return -EIO; +} + +struct upgrade_func upgrade_func_ft5652 = { + .ctype = {0x88}, + .fwveroff = 0x010E, + .fwcfgoff = 0x1F80, + .appoff = 0x0000, + .upgspec_version = UPGRADE_SPEC_V_1_0, + .pramboot_supported = false, + .hid_supported = true, + .upgrade = fts_ft5652_upgrade, +}; diff --git a/focaltech_touch/focaltech_gesture.c b/focaltech_touch/focaltech_gesture.c new file mode 100644 index 0000000000..92fe990967 --- /dev/null +++ b/focaltech_touch/focaltech_gesture.c @@ -0,0 +1,477 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_gestrue.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-08 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/****************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define KEY_GESTURE_U KEY_U +#define KEY_GESTURE_UP KEY_UP +#define KEY_GESTURE_DOWN KEY_DOWN +#define KEY_GESTURE_LEFT KEY_LEFT +#define KEY_GESTURE_RIGHT KEY_RIGHT +#define KEY_GESTURE_O KEY_O +#define KEY_GESTURE_E KEY_E +#define KEY_GESTURE_M KEY_M +#define KEY_GESTURE_L KEY_L +#define KEY_GESTURE_W KEY_W +#define KEY_GESTURE_S KEY_S +#define KEY_GESTURE_V KEY_V +#define KEY_GESTURE_C KEY_C +#define KEY_GESTURE_Z KEY_Z + +#define GESTURE_LEFT 0x20 +#define GESTURE_RIGHT 0x21 +#define GESTURE_UP 0x22 +#define GESTURE_DOWN 0x23 +#define GESTURE_DOUBLECLICK 0x24 +#define GESTURE_O 0x30 +#define GESTURE_W 0x31 +#define GESTURE_M 0x32 +#define GESTURE_E 0x33 +#define GESTURE_L 0x44 +#define GESTURE_S 0x46 +#define GESTURE_V 0x54 +#define GESTURE_Z 0x41 +#define GESTURE_C 0x34 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +/* +* gesture_id - mean which gesture is recognised +* point_num - points number of this gesture +* coordinate_x - All gesture point x coordinate +* coordinate_y - All gesture point y coordinate +* mode - gesture enable/disable, need enable by host +* - 1:enable gesture function(default) 0:disable +* active - gesture work flag, +* always set 1 when suspend, set 0 when resume +*/ +struct fts_gesture_st { + u8 gesture_id; + u8 point_num; + u16 coordinate_x[FTS_GESTURE_POINTS_MAX]; + u16 coordinate_y[FTS_GESTURE_POINTS_MAX]; +}; + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static struct fts_gesture_st fts_gesture_data; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static ssize_t fts_gesture_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + u8 val = 0; + struct fts_ts_data *ts_data = fts_data; + + mutex_lock(&ts_data->input_dev->mutex); + fts_read_reg(FTS_REG_GESTURE_EN, &val); + count = snprintf(buf, PAGE_SIZE, "Gesture Mode:%s\n", + ts_data->gesture_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "Reg(0xD0)=%d\n", val); + mutex_unlock(&ts_data->input_dev->mutex); + + return count; +} + +static ssize_t fts_gesture_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *ts_data = fts_data; + + mutex_lock(&ts_data->input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_DEBUG("enable gesture"); + ts_data->gesture_mode = ENABLE; + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_DEBUG("disable gesture"); + ts_data->gesture_mode = DISABLE; + } + mutex_unlock(&ts_data->input_dev->mutex); + + return count; +} + +static ssize_t fts_gesture_buf_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + int i = 0; + struct input_dev *input_dev = fts_data->input_dev; + struct fts_gesture_st *gesture = &fts_gesture_data; + + mutex_lock(&input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "Gesture ID:%d\n", gesture->gesture_id); + count += snprintf(buf + count, PAGE_SIZE, "Gesture PointNum:%d\n", + gesture->point_num); + count += snprintf(buf + count, PAGE_SIZE, "Gesture Points Buffer:\n"); + + /* save point data,max:6 */ + for (i = 0; i < FTS_GESTURE_POINTS_MAX; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%3d(%4d,%4d) ", i, + gesture->coordinate_x[i], gesture->coordinate_y[i]); + + if ((i + 1) % 4 == 0) + count += snprintf(buf + count, PAGE_SIZE, "\n"); + } + count += snprintf(buf + count, PAGE_SIZE, "\n"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_gesture_buf_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + + +/* sysfs gesture node + * read example: cat fts_gesture_mode ---read gesture mode + * write example:echo 1 > fts_gesture_mode --- write gesture mode to 1 + * + */ +static DEVICE_ATTR(fts_gesture_mode, S_IRUGO | S_IWUSR, fts_gesture_show, + fts_gesture_store); +/* + * read example: cat fts_gesture_buf --- read gesture buf + */ +static DEVICE_ATTR(fts_gesture_buf, S_IRUGO | S_IWUSR, + fts_gesture_buf_show, fts_gesture_buf_store); + +static struct attribute *fts_gesture_mode_attrs[] = { + &dev_attr_fts_gesture_mode.attr, + &dev_attr_fts_gesture_buf.attr, + NULL, +}; + +static struct attribute_group fts_gesture_group = { + .attrs = fts_gesture_mode_attrs, +}; + +static int fts_create_gesture_sysfs(struct device *dev) +{ + int ret = 0; + + ret = sysfs_create_group(&dev->kobj, &fts_gesture_group); + if (ret) { + FTS_ERROR("gesture sys node create fail"); + sysfs_remove_group(&dev->kobj, &fts_gesture_group); + return ret; + } + + return 0; +} + +static void fts_gesture_report(struct input_dev *input_dev, int gesture_id) +{ + int gesture; + + FTS_DEBUG("gesture_id:0x%x", gesture_id); + switch (gesture_id) { + case GESTURE_LEFT: + gesture = KEY_GESTURE_LEFT; + break; + + case GESTURE_RIGHT: + gesture = KEY_GESTURE_RIGHT; + break; + + case GESTURE_UP: + gesture = KEY_GESTURE_UP; + break; + + case GESTURE_DOWN: + gesture = KEY_GESTURE_DOWN; + break; + + case GESTURE_DOUBLECLICK: + gesture = KEY_POWER; + + break; + + case GESTURE_O: + gesture = KEY_GESTURE_O; + break; + + case GESTURE_W: + gesture = KEY_GESTURE_W; + break; + + case GESTURE_M: + gesture = KEY_GESTURE_M; + break; + + case GESTURE_E: + gesture = KEY_GESTURE_E; + break; + + case GESTURE_L: + gesture = KEY_GESTURE_L; + break; + + case GESTURE_S: + gesture = KEY_GESTURE_S; + break; + + case GESTURE_V: + gesture = KEY_GESTURE_V; + break; + + case GESTURE_Z: + gesture = KEY_GESTURE_Z; + break; + + case GESTURE_C: + gesture = KEY_GESTURE_C; + break; + + default: + gesture = -1; + break; + } + + /* report event key */ + if (gesture != -1) { + FTS_DEBUG("Gesture Code=%d", gesture); + input_report_key(input_dev, gesture, 1); + input_sync(input_dev); + input_report_key(input_dev, gesture, 0); + input_sync(input_dev); + } +} + +/***************************************************************************** +* Name: fts_gesture_readdata +* Brief: Read information about gesture: enable flag/gesture points..., if ges- +* ture enable, save gesture points' information, and report to OS. +* It will be called this function every intrrupt when FTS_GESTURE_EN = 1 +* +* gesture data length: 1(enable) + 1(reserve) + 2(header) + 6 * 4 +* Input: ts_data - global struct data +* data - gesture data buffer if non-flash, else NULL +* Output: +* Return: 0 - read gesture data successfully, the report data is gesture data +* 1 - tp not in suspend/gesture not enable in TP FW +* -Exx - error +*****************************************************************************/ +int fts_gesture_readdata(struct fts_ts_data *ts_data, u8 *data) +{ + int ret = 0; + int i = 0; + int index = 0; + u8 buf[FTS_GESTURE_DATA_LEN] = { 0 }; + struct input_dev *input_dev = ts_data->input_dev; + struct fts_gesture_st *gesture = &fts_gesture_data; + + if (!ts_data->suspended || !ts_data->gesture_mode) { + return 1; + } + + + ret = fts_read_reg(FTS_REG_GESTURE_EN, &buf[0]); + if ((ret < 0) || (buf[0] != ENABLE)) { + FTS_DEBUG("gesture not enable in fw, don't process gesture"); + return 1; + } + + buf[2] = FTS_REG_GESTURE_OUTPUT_ADDRESS; + ret = fts_read(&buf[2], 1, &buf[2], FTS_GESTURE_DATA_LEN - 2); + if (ret < 0) { + FTS_ERROR("read gesture header data fail"); + return ret; + } + + /* init variable before read gesture point */ + memset(gesture->coordinate_x, 0, FTS_GESTURE_POINTS_MAX * sizeof(u16)); + memset(gesture->coordinate_y, 0, FTS_GESTURE_POINTS_MAX * sizeof(u16)); + gesture->gesture_id = buf[2]; + gesture->point_num = buf[3]; + FTS_DEBUG("gesture_id=%d, point_num=%d", + gesture->gesture_id, gesture->point_num); + + /* save point data,max:6 */ + for (i = 0; i < FTS_GESTURE_POINTS_MAX; i++) { + index = 4 * i + 4; + gesture->coordinate_x[i] = (u16)(((buf[0 + index] & 0x0F) << 8) + + buf[1 + index]); + gesture->coordinate_y[i] = (u16)(((buf[2 + index] & 0x0F) << 8) + + buf[3 + index]); + } + + /* report gesture to OS */ + fts_gesture_report(input_dev, gesture->gesture_id); + return 0; +} + +void fts_gesture_recovery(struct fts_ts_data *ts_data) +{ + if (ts_data->gesture_mode && ts_data->suspended) { + FTS_DEBUG("gesture recovery..."); + fts_write_reg(0xD1, 0xFF); + fts_write_reg(0xD2, 0xFF); + fts_write_reg(0xD5, 0xFF); + fts_write_reg(0xD6, 0xFF); + fts_write_reg(0xD7, 0xFF); + fts_write_reg(0xD8, 0xFF); + fts_write_reg(FTS_REG_GESTURE_EN, ENABLE); + } +} + +int fts_gesture_suspend(struct fts_ts_data *ts_data) +{ + int i = 0; + u8 state = 0xFF; + + FTS_FUNC_ENTER(); + if (enable_irq_wake(ts_data->irq)) { + FTS_DEBUG("enable_irq_wake(irq:%d) fail", ts_data->irq); + } + + for (i = 0; i < 5; i++) { + fts_write_reg(0xD1, 0xFF); + fts_write_reg(0xD2, 0xFF); + fts_write_reg(0xD5, 0xFF); + fts_write_reg(0xD6, 0xFF); + fts_write_reg(0xD7, 0xFF); + fts_write_reg(0xD8, 0xFF); + fts_write_reg(FTS_REG_GESTURE_EN, ENABLE); + msleep(1); + fts_read_reg(FTS_REG_GESTURE_EN, &state); + if (state == ENABLE) + break; + } + + if (i >= 5) + FTS_ERROR("make IC enter into gesture(suspend) fail,state:%x", state); + else + FTS_INFO("Enter into gesture(suspend) successfully"); + + FTS_FUNC_EXIT(); + return 0; +} + +int fts_gesture_resume(struct fts_ts_data *ts_data) +{ + int i = 0; + u8 state = 0xFF; + + FTS_FUNC_ENTER(); + if (disable_irq_wake(ts_data->irq)) { + FTS_DEBUG("disable_irq_wake(irq:%d) fail", ts_data->irq); + } + + for (i = 0; i < 5; i++) { + fts_write_reg(FTS_REG_GESTURE_EN, DISABLE); + msleep(1); + fts_read_reg(FTS_REG_GESTURE_EN, &state); + if (state == DISABLE) + break; + } + + if (i >= 5) + FTS_ERROR("make IC exit gesture(resume) fail,state:%x", state); + else + FTS_INFO("resume from gesture successfully"); + + FTS_FUNC_EXIT(); + return 0; +} + +int fts_gesture_init(struct fts_ts_data *ts_data) +{ + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + input_set_capability(input_dev, EV_KEY, KEY_POWER); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_U); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_UP); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_DOWN); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_LEFT); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_RIGHT); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_O); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_E); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_M); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_L); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_W); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_S); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_V); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_Z); + input_set_capability(input_dev, EV_KEY, KEY_GESTURE_C); + + __set_bit(KEY_GESTURE_RIGHT, input_dev->keybit); + __set_bit(KEY_GESTURE_LEFT, input_dev->keybit); + __set_bit(KEY_GESTURE_UP, input_dev->keybit); + __set_bit(KEY_GESTURE_DOWN, input_dev->keybit); + __set_bit(KEY_GESTURE_U, input_dev->keybit); + __set_bit(KEY_GESTURE_O, input_dev->keybit); + __set_bit(KEY_GESTURE_E, input_dev->keybit); + __set_bit(KEY_GESTURE_M, input_dev->keybit); + __set_bit(KEY_GESTURE_W, input_dev->keybit); + __set_bit(KEY_GESTURE_L, input_dev->keybit); + __set_bit(KEY_GESTURE_S, input_dev->keybit); + __set_bit(KEY_GESTURE_V, input_dev->keybit); + __set_bit(KEY_GESTURE_C, input_dev->keybit); + __set_bit(KEY_GESTURE_Z, input_dev->keybit); + + fts_create_gesture_sysfs(ts_data->dev); + + memset(&fts_gesture_data, 0, sizeof(struct fts_gesture_st)); + ts_data->gesture_mode = FTS_GESTURE_EN; + + FTS_FUNC_EXIT(); + return 0; +} + +int fts_gesture_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + sysfs_remove_group(&ts_data->dev->kobj, &fts_gesture_group); + FTS_FUNC_EXIT(); + return 0; +} diff --git a/focaltech_touch/focaltech_i2c.c b/focaltech_touch/focaltech_i2c.c new file mode 100644 index 0000000000..8d439d3ac7 --- /dev/null +++ b/focaltech_touch/focaltech_i2c.c @@ -0,0 +1,548 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/************************************************************************ +* +* File Name: focaltech_i2c.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-04 +* +* Abstract: i2c communication with TP +* +* Version: v1.0 +* +* Revision History: +* +************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" +#include + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define I2C_RETRY_NUMBER 3 +#define I2C_BUF_LENGTH 256 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static struct fts_ts_data *ts_data; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* functions body +*****************************************************************************/ +static int fts_i2c_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen) +{ + int ret = 0; + int i = 0; + struct i2c_msg msg_list[2]; + struct i2c_msg *msg = NULL; + int msg_num = 0; + + /* must have data when read */ + if (!ts_data || !ts_data->client || !data || !datalen + || (datalen >= I2C_BUF_LENGTH) || (cmdlen >= I2C_BUF_LENGTH)) { + FTS_ERROR("fts_data/client/cmdlen(%d)/data/datalen(%d) is invalid", + cmdlen, datalen); + return -EINVAL; + } + + mutex_lock(&ts_data->bus_lock); + memset(&msg_list[0], 0, sizeof(struct i2c_msg)); + memset(&msg_list[1], 0, sizeof(struct i2c_msg)); + memcpy(ts_data->bus_tx_buf, cmd, cmdlen); + msg_list[0].addr = ts_data->client->addr; + msg_list[0].flags = 0; + msg_list[0].len = cmdlen; + msg_list[0].buf = ts_data->bus_tx_buf; + msg_list[1].addr = ts_data->client->addr; + msg_list[1].flags = I2C_M_RD; + msg_list[1].len = datalen; + msg_list[1].buf = ts_data->bus_rx_buf; + if (cmd && cmdlen) { + msg = &msg_list[0]; + msg_num = 2; + } else { + msg = &msg_list[1]; + msg_num = 1; + } + + for (i = 0; i < I2C_RETRY_NUMBER; i++) { + ret = i2c_transfer(ts_data->client->adapter, msg, msg_num); + if (ret < 0) { +#ifdef CONFIG_FTS_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + if (atomic_read(&ts_data->trusted_touch_enabled) && + ret == -ECONNRESET) { + pr_err("failed i2c read reacquiring session\n"); + pm_runtime_put_sync( + ts_data->client->adapter->dev.parent); + pm_runtime_get_sync( + ts_data->client->adapter->dev.parent); + } +#endif +#endif + FTS_ERROR("i2c_transfer(read) fail,ret:%d", ret); + } else { + memcpy(data, ts_data->bus_rx_buf, datalen); + break; + } + } + + if (ret < 0) { +#ifdef CONFIG_FTS_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + pr_err("initiating abort due to i2c xfer failure\n"); + fts_ts_trusted_touch_tvm_i2c_failure_report(ts_data); +#endif +#endif + } + + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +static int fts_i2c_write(u8 *writebuf, u32 writelen) +{ + int ret = 0; + int i = 0; + struct i2c_msg msgs; + + if (!ts_data || !ts_data->client || !writebuf || !writelen + || (writelen >= I2C_BUF_LENGTH)) { + FTS_ERROR("fts_data/client/data/datalen(%d) is invalid", writelen); + return -EINVAL; + } + + mutex_lock(&ts_data->bus_lock); + memset(&msgs, 0, sizeof(struct i2c_msg)); + memcpy(ts_data->bus_tx_buf, writebuf, writelen); + msgs.addr = ts_data->client->addr; + msgs.flags = 0; + msgs.len = writelen; + msgs.buf = ts_data->bus_tx_buf; + for (i = 0; i < I2C_RETRY_NUMBER; i++) { + ret = i2c_transfer(ts_data->client->adapter, &msgs, 1); + if (ret < 0) { +#ifdef CONFIG_FTS_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + if (atomic_read(&ts_data->trusted_touch_enabled) && + ret == -ECONNRESET){ + pr_err("failed i2c write reacquiring session\n"); + pm_runtime_put_sync( + ts_data->client->adapter->dev.parent); + pm_runtime_get_sync( + ts_data->client->adapter->dev.parent); + } +#endif +#endif + FTS_ERROR("i2c_transfer(write) fail,ret:%d", ret); + } else { + break; + } + } + + if (ret < 0) { +#ifdef CONFIG_FTS_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + pr_err("initiating abort due to i2c xfer failure\n"); + fts_ts_trusted_touch_tvm_i2c_failure_report(ts_data); +#endif +#endif + } + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +static int fts_i2c_init(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + ts_data->bus_tx_buf = kzalloc(I2C_BUF_LENGTH, GFP_KERNEL); + if (ts_data->bus_tx_buf == NULL) { + FTS_ERROR("failed to allocate memory for bus_tx_buf"); + return -ENOMEM; + } + + ts_data->bus_rx_buf = kzalloc(I2C_BUF_LENGTH, GFP_KERNEL); + if (ts_data->bus_rx_buf == NULL) { + FTS_ERROR("failed to allocate memory for bus_rx_buf"); + return -ENOMEM; + } + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_i2c_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + if (ts_data && ts_data->bus_tx_buf) { + kfree(ts_data->bus_tx_buf); + ts_data->bus_tx_buf = NULL; + } + + if (ts_data && ts_data->bus_rx_buf) { + kfree(ts_data->bus_rx_buf); + ts_data->bus_rx_buf = NULL; + } + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** + * Private constant and macro definitions using #define + ****************************************************************************/ +#define SPI_RETRY_NUMBER 3 +#define CS_HIGH_DELAY 150 /* unit: us */ +#define SPI_BUF_LENGTH 256 + +#define DATA_CRC_EN 0x20 +#define WRITE_CMD 0x00 +#define READ_CMD (0x80 | DATA_CRC_EN) + +#define SPI_DUMMY_BYTE 3 +#define SPI_HEADER_LENGTH 6 /*CRC*/ + +/***************************************************************************** + * functions body + ****************************************************************************/ +/* spi interface */ +static int fts_spi_transfer(u8 *tx_buf, u8 *rx_buf, u32 len) +{ + int ret = 0; + struct spi_device *spi = fts_data->spi; + struct spi_message msg; + struct spi_transfer xfer = { + .tx_buf = tx_buf, + .rx_buf = rx_buf, + .len = len, + }; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + ret = spi_sync(spi, &msg); + if (ret) { + FTS_ERROR("spi_sync fail,ret:%d", ret); + return ret; + } + + return ret; +} + +static void crckermit(u8 *data, u32 len, u16 *crc_out) +{ + u32 i = 0; + u32 j = 0; + u16 crc = 0xFFFF; + + for (i = 0; i < len; i++) { + crc ^= data[i]; + for (j = 0; j < 8; j++) { + if (crc & 0x01) + crc = (crc >> 1) ^ 0x8408; + else + crc = (crc >> 1); + } + } + + *crc_out = crc; +} + +static int rdata_check(u8 *rdata, u32 rlen) +{ + u16 crc_calc = 0; + u16 crc_read = 0; + + crckermit(rdata, rlen - 2, &crc_calc); + crc_read = (u16)(rdata[rlen - 1] << 8) + rdata[rlen - 2]; + if (crc_calc != crc_read) + return -EIO; + + return 0; +} + +static int fts_spi_write(u8 *writebuf, u32 writelen) +{ + int ret = 0; + int i = 0; + struct fts_ts_data *ts_data = fts_data; + u8 *txbuf = NULL; + u8 *rxbuf = NULL; + u32 txlen = 0; + u32 txlen_need = writelen + SPI_HEADER_LENGTH + ts_data->dummy_byte; + u32 datalen = writelen - 1; + + if (!writebuf || !writelen) { + FTS_ERROR("writebuf/len is invalid"); + return -EINVAL; + } + + mutex_lock(&ts_data->bus_lock); + if (txlen_need > SPI_BUF_LENGTH) { + txbuf = kzalloc(txlen_need, GFP_KERNEL); + if (txbuf == NULL) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto err_write; + } + + rxbuf = kzalloc(txlen_need, GFP_KERNEL); + if (rxbuf == NULL) { + FTS_ERROR("rxbuf malloc fail"); + ret = -ENOMEM; + goto err_write; + } + } else { + txbuf = ts_data->bus_tx_buf; + rxbuf = ts_data->bus_rx_buf; + memset(txbuf, 0x0, SPI_BUF_LENGTH); + memset(rxbuf, 0x0, SPI_BUF_LENGTH); + } + + txbuf[txlen++] = writebuf[0]; + txbuf[txlen++] = WRITE_CMD; + txbuf[txlen++] = (datalen >> 8) & 0xFF; + txbuf[txlen++] = datalen & 0xFF; + if (datalen > 0) { + txlen = txlen + SPI_DUMMY_BYTE; + memcpy(&txbuf[txlen], &writebuf[1], datalen); + txlen = txlen + datalen; + } + + for (i = 0; i < SPI_RETRY_NUMBER; i++) { + ret = fts_spi_transfer(txbuf, rxbuf, txlen); + if ((ret == 0) && ((rxbuf[3] & 0xA0) == 0)) + break; + + FTS_DEBUG("data write(addr:%x),status:%x,retry:%d,ret:%d", + writebuf[0], rxbuf[3], i, ret); + ret = -EIO; + udelay(CS_HIGH_DELAY); + } + + if (ret < 0) { + FTS_ERROR("data write(addr:%x) fail,status:%x,ret:%d", + writebuf[0], rxbuf[3], ret); + } + +err_write: + if (txlen_need > SPI_BUF_LENGTH) { + kfree(txbuf); + kfree(rxbuf); + } + + udelay(CS_HIGH_DELAY); + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +static int fts_spi_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen) +{ + int ret = 0; + int i = 0; + u8 *txbuf = NULL; + u8 *rxbuf = NULL; + u32 txlen = 0; + u32 txlen_need = datalen + SPI_HEADER_LENGTH + ts_data->dummy_byte; + u8 ctrl = READ_CMD; + u32 dp = 0; + + if (!cmd || !cmdlen || !data || !datalen) { + FTS_ERROR("cmd/cmdlen/data/datalen is invalid"); + return -EINVAL; + } + + mutex_lock(&ts_data->bus_lock); + if (txlen_need > SPI_BUF_LENGTH) { + txbuf = kzalloc(txlen_need, GFP_KERNEL); + if (txbuf == NULL) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto err_read; + } + + rxbuf = kzalloc(txlen_need, GFP_KERNEL); + if (rxbuf == NULL) { + FTS_ERROR("rxbuf malloc fail"); + ret = -ENOMEM; + goto err_read; + } + } else { + txbuf = ts_data->bus_tx_buf; + rxbuf = ts_data->bus_rx_buf; + memset(txbuf, 0x0, SPI_BUF_LENGTH); + memset(rxbuf, 0x0, SPI_BUF_LENGTH); + } + + txbuf[txlen++] = cmd[0]; + txbuf[txlen++] = ctrl; + txbuf[txlen++] = (datalen >> 8) & 0xFF; + txbuf[txlen++] = datalen & 0xFF; + dp = txlen + SPI_DUMMY_BYTE; + txlen = dp + datalen; + if (ctrl & DATA_CRC_EN) + txlen = txlen + 2; + + for (i = 0; i < SPI_RETRY_NUMBER; i++) { + ret = fts_spi_transfer(txbuf, rxbuf, txlen); + if ((ret == 0) && ((rxbuf[3] & 0xA0) == 0)) { + memcpy(data, &rxbuf[dp], datalen); + /* crc check */ + if (ctrl & DATA_CRC_EN) { + ret = rdata_check(&rxbuf[dp], txlen - dp); + if (ret < 0) { + FTS_DEBUG("data read(addr:%x) crc abnormal,retry:%d", + cmd[0], i); + udelay(CS_HIGH_DELAY); + continue; + } + } + break; + } + + FTS_DEBUG("data read(addr:%x) status:%x,retry:%d,ret:%d", + cmd[0], rxbuf[3], i, ret); + ret = -EIO; + udelay(CS_HIGH_DELAY); + } + + if (ret < 0) { + FTS_ERROR("data read(addr:%x) %s,status:%x,ret:%d", cmd[0], + (i >= SPI_RETRY_NUMBER) ? "crc abnormal" : "fail", + rxbuf[3], ret); + } + +err_read: + if (txlen_need > SPI_BUF_LENGTH) { + kfree(txbuf); + kfree(rxbuf); + } + + udelay(CS_HIGH_DELAY); + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +static int fts_spi_init(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + ts_data->bus_tx_buf = kzalloc(SPI_BUF_LENGTH, GFP_KERNEL); + if (ts_data->bus_tx_buf == NULL) { + FTS_ERROR("failed to allocate memory for bus_tx_buf"); + return -ENOMEM; + } + + ts_data->bus_rx_buf = kzalloc(SPI_BUF_LENGTH, GFP_KERNEL); + if (ts_data->bus_rx_buf == NULL) { + FTS_ERROR("failed to allocate memory for bus_rx_buf"); + return -ENOMEM; + } + + ts_data->dummy_byte = SPI_DUMMY_BYTE; + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_spi_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + if (ts_data && ts_data->bus_tx_buf) { + kfree(ts_data->bus_tx_buf); + ts_data->bus_tx_buf = NULL; + } + + if (ts_data && ts_data->bus_rx_buf) { + kfree(ts_data->bus_rx_buf); + ts_data->bus_rx_buf = NULL; + } + FTS_FUNC_EXIT(); + return 0; +} + +int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen) +{ + int ret = 0; + + if (ts_data->bus_type == BUS_TYPE_I2C) + ret = fts_i2c_read(cmd, cmdlen, data, datalen); + else + ret = fts_spi_read(cmd, cmdlen, data, datalen); + + return ret; +} + +int fts_write(u8 *writebuf, u32 writelen) +{ + int ret = 0; + + if (ts_data->bus_type == BUS_TYPE_I2C) + ret = fts_i2c_write(writebuf, writelen); + else + ret = fts_spi_write(writebuf, writelen); + + return ret; +} + +int fts_read_reg(u8 addr, u8 *value) +{ + return fts_read(&addr, 1, value, 1); +} + +int fts_write_reg(u8 addr, u8 value) +{ + u8 buf[2] = { 0 }; + + buf[0] = addr; + buf[1] = value; + return fts_write(buf, sizeof(buf)); +} + +int fts_bus_init(struct fts_ts_data *_ts_data) +{ + ts_data = _ts_data; + + if (ts_data->bus_type == BUS_TYPE_I2C) + return fts_i2c_init(ts_data); + + return fts_spi_init(ts_data); +} + +int fts_bus_exit(struct fts_ts_data *ts_data) +{ + if (ts_data->bus_type == BUS_TYPE_I2C) + return fts_i2c_exit(ts_data); + + return fts_spi_exit(ts_data); +} \ No newline at end of file diff --git a/focaltech_touch/focaltech_point_report_check.c b/focaltech_touch/focaltech_point_report_check.c new file mode 100644 index 0000000000..0f626408a9 --- /dev/null +++ b/focaltech_touch/focaltech_point_report_check.c @@ -0,0 +1,135 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2019, FocalTech Systems, Ltd., all rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/***************************************************************************** +* +* File Name: focaltech_point_report_check.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-11-16 +* +* Abstract: point report check function +* +* Version: v1.0 +* +* Revision History: +* +*****************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +#if FTS_POINT_REPORT_CHECK_EN +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define POINT_REPORT_CHECK_WAIT_TIME 200 /* unit:ms */ + +/***************************************************************************** +* functions body +*****************************************************************************/ +/***************************************************************************** +* Name: fts_prc_func +* Brief: fts point report check work func, report whole up of points +* Input: +* Output: +* Return: +*****************************************************************************/ +static void fts_prc_func(struct work_struct *work) +{ + struct fts_ts_data *ts_data = container_of(work, + struct fts_ts_data, prc_work.work); + struct input_dev *input_dev = ts_data->input_dev; +#if FTS_MT_PROTOCOL_B_EN + u32 finger_count = 0; + u32 max_touches = fts_data->pdata->max_touch_number; +#endif + + FTS_FUNC_ENTER(); + mutex_lock(&ts_data->report_mutex); + +#if FTS_MT_PROTOCOL_B_EN + for (finger_count = 0; finger_count < max_touches; finger_count++) { + input_mt_slot(input_dev, finger_count); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } +#else + input_mt_sync(input_dev); +#endif + input_report_key(input_dev, BTN_TOUCH, 0); + input_sync(input_dev); + + mutex_unlock(&ts_data->report_mutex); + + FTS_FUNC_EXIT(); +} + +/***************************************************************************** +* Name: fts_prc_queue_work +* Brief: fts point report check queue work, call it when interrupt comes +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_prc_queue_work(struct fts_ts_data *ts_data) +{ + cancel_delayed_work_sync(&ts_data->prc_work); + queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work, + msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME)); +} + +/***************************************************************************** +* Name: fts_point_report_check_init +* Brief: +* Input: +* Output: +* Return: < 0: Fail to create esd check queue +*****************************************************************************/ +int fts_point_report_check_init(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + + if (ts_data->ts_workqueue) { + INIT_DELAYED_WORK(&ts_data->prc_work, fts_prc_func); + } else { + FTS_ERROR("fts workqueue is NULL, can't run point report check function"); + return -EINVAL; + } + + FTS_FUNC_EXIT(); + return 0; +} + +/***************************************************************************** +* Name: fts_point_report_check_exit +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +int fts_point_report_check_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + + FTS_FUNC_EXIT(); + return 0; +} +#endif /* FTS_POINT_REPORT_CHECK_EN */ + diff --git a/nt36xxx/nt36xxx.c b/nt36xxx/nt36xxx.c new file mode 100644 index 0000000000..fa3e6a3fd1 --- /dev/null +++ b/nt36xxx/nt36xxx.c @@ -0,0 +1,4601 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 47247 $ + * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $ + * + * 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. + * + */ + +#if !defined(NVT_NT36XXX_SPI) /* NT36XXX I2C */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_DRM) +#include +#endif + +#if defined(CONFIG_DRM_PANEL) +#include +#elif defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#endif + +#include "nt36xxx.h" +#if NVT_TOUCH_ESD_PROTECT +#include +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#if NVT_TOUCH_ESD_PROTECT +static struct delayed_work nvt_esd_check_work; +static struct workqueue_struct *nvt_esd_check_wq; +static unsigned long irq_timer = 0; +uint8_t esd_check = false; +uint8_t esd_retry = 0; +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#if NVT_TOUCH_EXT_PROC +extern int32_t nvt_extra_proc_init(void); +extern void nvt_extra_proc_deinit(void); +#endif + +#if NVT_TOUCH_MP +extern int32_t nvt_mp_proc_init(void); +extern void nvt_mp_proc_deinit(void); +#endif + +struct nvt_ts_data *ts; + +#if BOOT_UPDATE_FIRMWARE +static struct workqueue_struct *nvt_fwu_wq; +extern void Boot_Update_Firmware(struct work_struct *work); +#endif + +#if defined(CONFIG_DRM) +static struct drm_panel *active_panel; +static void nvt_i2c_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data); + +#elif defined(_MSM_DRM_NOTIFY_H_) +static int nvt_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data); + +#else +static int nvt_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#endif + +#if TOUCH_KEY_NUM > 0 +const uint16_t touch_key_array[TOUCH_KEY_NUM] = { + KEY_BACK, + KEY_HOME, + KEY_MENU +}; +#endif + +#if WAKEUP_GESTURE +const uint16_t gesture_key_array[] = { + KEY_POWER, //GESTURE_WORD_C + KEY_POWER, //GESTURE_WORD_W + KEY_POWER, //GESTURE_WORD_V + KEY_POWER, //GESTURE_DOUBLE_CLICK + KEY_POWER, //GESTURE_WORD_Z + KEY_POWER, //GESTURE_WORD_M + KEY_POWER, //GESTURE_WORD_O + KEY_POWER, //GESTURE_WORD_e + KEY_POWER, //GESTURE_WORD_S + KEY_POWER, //GESTURE_SLIDE_UP + KEY_POWER, //GESTURE_SLIDE_DOWN + KEY_POWER, //GESTURE_SLIDE_LEFT + KEY_POWER, //GESTURE_SLIDE_RIGHT +}; +#endif + +static uint8_t bTouchIsAwake = 0; + +#if defined(CONFIG_DRM) +static void nvt_i2c_register_for_panel_events(struct device_node *dp, + struct nvt_ts_data *ts) +{ + void *cookie = NULL; + + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, active_panel, + &nvt_i2c_panel_notifier_callback, ts); + if (!cookie) { + pr_err("Failed to register for panel events\n"); + return; + } + ts->notifier_cookie = cookie; +} +#endif +/******************************************************* + * Description: + * Novatek touchscreen irq enable/disable function. + * + * return: + * n.a. + *******************************************************/ +static void nvt_irq_enable(bool enable) +{ + struct irq_desc *desc; + + if (enable) { + if (!ts->irq_enabled) { + enable_irq(ts->client->irq); + ts->irq_enabled = true; + } + } else { + if (ts->irq_enabled) { + disable_irq(ts->client->irq); + ts->irq_enabled = false; + } + } + + desc = irq_to_desc(ts->client->irq); + NVT_LOG("enable=%d, desc->depth=%d\n", enable, desc->depth); +} + +/******************************************************* + * Description: + * Novatek touchscreen i2c read function. + * + * return: + * Executive outcomes. 2---succeed. -5---I/O error + *******************************************************/ +int32_t CTP_I2C_READ(struct i2c_client *client, uint16_t address, uint8_t *buf, + uint16_t len) +{ + struct i2c_msg msgs[2]; + int32_t ret = -1; + int32_t retries = 0; + + mutex_lock(&ts->xbuf_lock); + + msgs[0].flags = !I2C_M_RD; + msgs[0].addr = address; + msgs[0].len = 1; + msgs[0].buf = &buf[0]; + + msgs[1].flags = I2C_M_RD; + msgs[1].addr = address; + msgs[1].len = len - 1; + msgs[1].buf = ts->xbuf; + + while (retries < 5) { + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret == 2) break; + retries++; + } + + if (unlikely(retries == 5)) { + NVT_ERR("error, ret=%d\n", ret); + ret = -EIO; + } + + memcpy(buf + 1, ts->xbuf, len - 1); + + mutex_unlock(&ts->xbuf_lock); + + return ret; +} + +/******************************************************* + * Description: + * Novatek touchscreen i2c write function. + * + * return: + * Executive outcomes. 1---succeed. -5---I/O error + *******************************************************/ +int32_t CTP_I2C_WRITE(struct i2c_client *client, uint16_t address, uint8_t *buf, + uint16_t len) +{ + struct i2c_msg msg; + int32_t ret = -1; + int32_t retries = 0; + + mutex_lock(&ts->xbuf_lock); + + msg.flags = !I2C_M_RD; + msg.addr = address; + msg.len = len; + memcpy(ts->xbuf, buf, len); + msg.buf = ts->xbuf; + + while (retries < 5) { + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret == 1) break; + retries++; + } + + if (unlikely(retries == 5)) { + NVT_ERR("error, ret=%d\n", ret); + ret = -EIO; + } + + mutex_unlock(&ts->xbuf_lock); + + return ret; +} + +/******************************************************* + * Description: + * Novatek touchscreen set index/page/addr address. + * + * return: + * Executive outcomes. 0---succeed. -5---access fail. + ********************************************************/ +int32_t nvt_set_page(uint16_t i2c_addr, uint32_t addr) +{ + uint8_t buf[4] = {0}; + + buf[0] = 0xFF; //set index/page/addr command + buf[1] = (addr >> 16) & 0xFF; + buf[2] = (addr >> 8) & 0xFF; + + return CTP_I2C_WRITE(ts->client, i2c_addr, buf, 3); +} + +/******************************************************* + * Description: + * Novatek touchscreen reset MCU then into idle mode + * function. + * + * return: + * n.a. + *******************************************************/ +void nvt_sw_reset_idle(void) +{ + uint8_t buf[4]={0}; + + //---write i2c cmds to reset idle--- + buf[0]=0x00; + buf[1]=0xA5; + CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + + msleep(15); +} + +/******************************************************* + * Description: + * Novatek touchscreen reset MCU (boot) function. + * + * return: + * n.a. + *******************************************************/ +void nvt_bootloader_reset(void) +{ + uint8_t buf[8] = {0}; + + NVT_LOG("start\n"); + + //---write i2c cmds to reset--- + buf[0] = 0x00; + buf[1] = 0x69; + CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + + // need 35ms delay after bootloader reset + msleep(35); + + NVT_LOG("end\n"); +} + +/******************************************************* + * Description: + * Novatek touchscreen clear FW status function. + * + * return: + * Executive outcomes. 0---succeed. -1---fail. + *******************************************************/ +int32_t nvt_clear_fw_status(void) +{ + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 20; + + for (i = 0; i < retry; i++) { + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE); + + //---clear fw status--- + buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0x00; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); + + //---read fw status--- + buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0xFF; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2); + + if (buf[1] == 0x00) + break; + + usleep_range(10000, 10000); + } + + if (i >= retry) { + NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); + return -1; + } else { + return 0; + } +} + +/******************************************************* + * Description: + * Novatek touchscreen check FW status function. + * + * return: + * Executive outcomes. 0---succeed. -1---failed. + *******************************************************/ +int32_t nvt_check_fw_status(void) +{ + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 50; + + for (i = 0; i < retry; i++) { + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE); + + //---read fw status--- + buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0x00; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2); + + if ((buf[1] & 0xF0) == 0xA0) + break; + + usleep_range(10000, 10000); + } + + if (i >= retry) { + NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); + return -1; + } else { + return 0; + } +} + +/******************************************************* + * Description: + * Novatek touchscreen check FW reset state function. + * + * return: + * Executive outcomes. 0---succeed. -1---failed. + ******************************************************/ +int32_t nvt_check_fw_reset_state(RST_COMPLETE_STATE check_reset_state) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + int32_t retry = 0; + + while (1) { + usleep_range(10000, 10000); + + //---read reset state--- + buf[0] = EVENT_MAP_RESET_COMPLETE; + buf[1] = 0x00; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 6); + + if ((buf[1] >= check_reset_state) && (buf[1] <= RESET_STATE_MAX)) { + ret = 0; + break; + } + + retry++; + if(unlikely(retry > 100)) { + NVT_ERR("error, retry=%d, buf[1]=0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", + retry, buf[1], buf[2], buf[3], buf[4], buf[5]); + ret = -1; + break; + } + } + + return ret; +} + +/******************************************************* + * Description: + * Novatek touchscreen get novatek project id information + * function. + * + * return: + * Executive outcomes. 0---success. -1---fail. + *******************************************************/ +int32_t nvt_read_pid(void) +{ + uint8_t buf[3] = {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_PROJECTID); + + //---read project id--- + buf[0] = EVENT_MAP_PROJECTID; + buf[1] = 0x00; + buf[2] = 0x00; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 3); + + ts->nvt_pid = (buf[2] << 8) + buf[1]; + + NVT_LOG("PID=%04X\n", ts->nvt_pid); + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen get firmware related information + function. + +return: + Executive outcomes. 0---success. -1---fail. +*******************************************************/ +int32_t nvt_get_fw_info(void) +{ + uint8_t buf[64] = {0}; + uint32_t retry_count = 0; + int32_t ret = 0; + +info_retry: + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO); + + //---read fw info--- + buf[0] = EVENT_MAP_FWINFO; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 17); + ts->fw_ver = buf[1]; + ts->x_num = buf[3]; + ts->y_num = buf[4]; + ts->abs_x_max = (uint16_t)((buf[5] << 8) | buf[6]); + ts->abs_y_max = (uint16_t)((buf[7] << 8) | buf[8]); + ts->max_button_num = buf[11]; + + //---clear x_num, y_num if fw info is broken--- + if ((buf[1] + buf[2]) != 0xFF) { + NVT_ERR("FW info is broken! fw_ver=0x%02X, ~fw_ver=0x%02X\n", buf[1], buf[2]); + ts->fw_ver = 0; + ts->x_num = 18; + ts->y_num = 32; + ts->abs_x_max = TOUCH_DEFAULT_MAX_WIDTH; + ts->abs_y_max = TOUCH_DEFAULT_MAX_HEIGHT; + ts->max_button_num = TOUCH_KEY_NUM; + + if(retry_count < 3) { + retry_count++; + NVT_ERR("retry_count=%d\n", retry_count); + goto info_retry; + } else { + NVT_ERR("Set default fw_ver=%d, x_num=%d, y_num=%d, " + "abs_x_max=%d, abs_y_max=%d, max_button_num=%d!\n", + ts->fw_ver, ts->x_num, ts->y_num, + ts->abs_x_max, ts->abs_y_max, ts->max_button_num); + ret = -1; + } + } else { + ret = 0; + } + + //---Get Novatek PID--- + nvt_read_pid(); + + return ret; +} + +/******************************************************* + * Create Device Node (Proc Entry) + *******************************************************/ +#if NVT_TOUCH_PROC +static struct proc_dir_entry *NVT_proc_entry; +#define DEVICE_NAME "NVTflash" + +/******************************************************* + * Description: + * Novatek touchscreen /proc/NVTflash read function. + * + * return: + * Executive outcomes. 2---succeed. -5,-14---failed. + *******************************************************/ +static ssize_t nvt_flash_read(struct file *file, char __user *buff, + size_t count, loff_t *offp) +{ + uint8_t str[68] = {0}; + int32_t ret = -1; + int32_t retries = 0; + int8_t i2c_wr = 0; + + if (count > sizeof(str)) { + NVT_ERR("error count=%zu\n", count); + return -EFAULT; + } + + if (copy_from_user(str, buff, count)) { + NVT_ERR("copy from user error\n"); + return -EFAULT; + } + +#if NVT_TOUCH_ESD_PROTECT + /* + * stop esd check work to avoid case that 0x77 report righ after here to enable esd check again + * finally lead to trigger esd recovery bootloader reset + */ + cancel_delayed_work_sync(&nvt_esd_check_work); + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + i2c_wr = str[0] >> 7; + + if (i2c_wr == 0) { //I2C write + while (retries < 20) { + ret = CTP_I2C_WRITE(ts->client, (str[0] & 0x7F), &str[2], str[1]); + if (ret == 1) + break; + else + NVT_ERR("error, retries=%d, ret=%d\n", retries, ret); + + retries++; + } + + if (unlikely(retries == 20)) { + NVT_ERR("error, ret = %d\n", ret); + return -EIO; + } + + return ret; + } else if (i2c_wr == 1) { //I2C read + while (retries < 20) { + ret = CTP_I2C_READ(ts->client, (str[0] & 0x7F), &str[2], str[1]); + if (ret == 2) + break; + else + NVT_ERR("error, retries=%d, ret=%d\n", retries, ret); + + retries++; + } + + // copy buff to user if i2c transfer + if (retries < 20) { + if (copy_to_user(buff, str, count)) + return -EFAULT; + } + + if (unlikely(retries == 20)) { + NVT_ERR("error, ret = %d\n", ret); + return -EIO; + } + + return ret; + } else { + NVT_ERR("Call error, str[0]=%d\n", str[0]); + return -EFAULT; + } +} + +/******************************************************* + * Description: + * Novatek touchscreen /proc/NVTflash open function. + * + * return: + * Executive outcomes. 0---succeed. -12---failed. + *******************************************************/ +static int32_t nvt_flash_open(struct inode *inode, struct file *file) +{ + struct nvt_flash_data *dev; + + dev = kmalloc(sizeof(struct nvt_flash_data), GFP_KERNEL); + if (dev == NULL) { + NVT_ERR("Failed to allocate memory for nvt flash data\n"); + return -ENOMEM; + } + + rwlock_init(&dev->lock); + file->private_data = dev; + + return 0; +} + +/******************************************************* + * Description: + * Novatek touchscreen /proc/NVTflash close function. + * + * return: + * Executive outcomes. 0---succeed. + *******************************************************/ +static int32_t nvt_flash_close(struct inode *inode, struct file *file) +{ + struct nvt_flash_data *dev = file->private_data; + + kfree(dev); + + return 0; +} + +static const struct proc_ops nvt_flash_fops = { + .proc_open = nvt_flash_open, + .proc_release = nvt_flash_close, + .proc_read = nvt_flash_read, +}; + +/******************************************************* + * Description: + * Novatek touchscreen /proc/NVTflash initial function. + * + * return: + * Executive outcomes. 0---succeed. -12---failed. + *******************************************************/ +static int32_t nvt_flash_proc_init(void) +{ + NVT_proc_entry = proc_create(DEVICE_NAME, 0444, NULL,&nvt_flash_fops); + if (NVT_proc_entry == NULL) { + NVT_ERR("Failed!\n"); + return -ENOMEM; + } else { + NVT_LOG("Succeeded!\n"); + } + + NVT_LOG("==========================================================\n"); + NVT_LOG("Create /proc/%s\n", DEVICE_NAME); + NVT_LOG("==========================================================\n"); + + return 0; +} + +/******************************************************* + * Description: + * Novatek touchscreen /proc/NVTflash deinitial function. + * + * return: + * n.a. + *******************************************************/ +static void nvt_flash_proc_deinit(void) +{ + if (NVT_proc_entry != NULL) { + remove_proc_entry(DEVICE_NAME, NULL); + NVT_proc_entry = NULL; + NVT_LOG("Removed /proc/%s\n", DEVICE_NAME); + } +} +#endif + +#if WAKEUP_GESTURE +#define GESTURE_WORD_C 12 +#define GESTURE_WORD_W 13 +#define GESTURE_WORD_V 14 +#define GESTURE_DOUBLE_CLICK 15 +#define GESTURE_WORD_Z 16 +#define GESTURE_WORD_M 17 +#define GESTURE_WORD_O 18 +#define GESTURE_WORD_e 19 +#define GESTURE_WORD_S 20 +#define GESTURE_SLIDE_UP 21 +#define GESTURE_SLIDE_DOWN 22 +#define GESTURE_SLIDE_LEFT 23 +#define GESTURE_SLIDE_RIGHT 24 +/* customized gesture id */ +#define DATA_PROTOCOL 30 + +/* function page definition */ +#define FUNCPAGE_GESTURE 1 + +/******************************************************* + * Description: + * Novatek touchscreen wake up gesture key report function. + * + * return: + * n.a. + *******************************************************/ +void nvt_ts_wakeup_gesture_report(uint8_t gesture_id, uint8_t *data) +{ + uint32_t keycode = 0; + uint8_t func_type = data[2]; + uint8_t func_id = data[3]; + + /* support fw specifal data protocol */ + if ((gesture_id == DATA_PROTOCOL) && (func_type == FUNCPAGE_GESTURE)) { + gesture_id = func_id; + } else if (gesture_id > DATA_PROTOCOL) { + NVT_ERR("gesture_id %d is invalid, func_type=%d, func_id=%d\n", gesture_id, func_type, func_id); + return; + } + + NVT_LOG("gesture_id = %d\n", gesture_id); + + switch (gesture_id) { + case GESTURE_WORD_C: + NVT_LOG("Gesture : Word-C.\n"); + keycode = gesture_key_array[0]; + break; + case GESTURE_WORD_W: + NVT_LOG("Gesture : Word-W.\n"); + keycode = gesture_key_array[1]; + break; + case GESTURE_WORD_V: + NVT_LOG("Gesture : Word-V.\n"); + keycode = gesture_key_array[2]; + break; + case GESTURE_DOUBLE_CLICK: + NVT_LOG("Gesture : Double Click.\n"); + keycode = gesture_key_array[3]; + break; + case GESTURE_WORD_Z: + NVT_LOG("Gesture : Word-Z.\n"); + keycode = gesture_key_array[4]; + break; + case GESTURE_WORD_M: + NVT_LOG("Gesture : Word-M.\n"); + keycode = gesture_key_array[5]; + break; + case GESTURE_WORD_O: + NVT_LOG("Gesture : Word-O.\n"); + keycode = gesture_key_array[6]; + break; + case GESTURE_WORD_e: + NVT_LOG("Gesture : Word-e.\n"); + keycode = gesture_key_array[7]; + break; + case GESTURE_WORD_S: + NVT_LOG("Gesture : Word-S.\n"); + keycode = gesture_key_array[8]; + break; + case GESTURE_SLIDE_UP: + NVT_LOG("Gesture : Slide UP.\n"); + keycode = gesture_key_array[9]; + break; + case GESTURE_SLIDE_DOWN: + NVT_LOG("Gesture : Slide DOWN.\n"); + keycode = gesture_key_array[10]; + break; + case GESTURE_SLIDE_LEFT: + NVT_LOG("Gesture : Slide LEFT.\n"); + keycode = gesture_key_array[11]; + break; + case GESTURE_SLIDE_RIGHT: + NVT_LOG("Gesture : Slide RIGHT.\n"); + keycode = gesture_key_array[12]; + break; + default: + break; + } + + if (keycode > 0) { + input_report_key(ts->input_dev, keycode, 1); + input_sync(ts->input_dev); + input_report_key(ts->input_dev, keycode, 0); + input_sync(ts->input_dev); + } +} +#endif + +/******************************************************* + * Description: + * Novatek touchscreen parse device tree function. + * + * return: + * n.a. + *******************************************************/ +#ifdef CONFIG_OF +static void nvt_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + +#if NVT_TOUCH_SUPPORT_HW_RST + ts->reset_gpio = of_get_named_gpio_flags(np, "novatek,reset-gpio", 0, &ts->reset_flags); + NVT_LOG("novatek,reset-gpio=%d\n", ts->reset_gpio); +#endif + ts->irq_gpio = of_get_named_gpio_flags(np, "novatek,irq-gpio", 0, &ts->irq_flags); + NVT_LOG("novatek,irq-gpio=%d\n", ts->irq_gpio); + +} +#else +static void nvt_parse_dt(struct device *dev) +{ +#if NVT_TOUCH_SUPPORT_HW_RST + ts->reset_gpio = NVTTOUCH_RST_PIN; +#endif + ts->irq_gpio = NVTTOUCH_INT_PIN; +} +#endif + +/******************************************************* + * Description: + * Novatek touchscreen config and request gpio + * + * return: + * Executive outcomes. 0---succeed. not 0---failed. + *******************************************************/ +static int nvt_gpio_config(struct nvt_ts_data *ts) +{ + int32_t ret = 0; + +#if NVT_TOUCH_SUPPORT_HW_RST + /* request RST-pin (Output/High) */ + if (gpio_is_valid(ts->reset_gpio)) { + ret = gpio_request_one(ts->reset_gpio, GPIOF_OUT_INIT_HIGH, "NVT-tp-rst"); + if (ret) { + NVT_ERR("Failed to request NVT-tp-rst GPIO\n"); + goto err_request_reset_gpio; + } + } +#endif + + /* request INT-pin (Input) */ + if (gpio_is_valid(ts->irq_gpio)) { + ret = gpio_request_one(ts->irq_gpio, GPIOF_IN, "NVT-int"); + if (ret) { + NVT_ERR("Failed to request NVT-int GPIO\n"); + goto err_request_irq_gpio; + } + } + + return ret; + +err_request_irq_gpio: +#if NVT_TOUCH_SUPPORT_HW_RST + gpio_free(ts->reset_gpio); +err_request_reset_gpio: +#endif + return ret; +} + +/******************************************************* + * Description: + * Novatek touchscreen deconfig gpio + * + * return: + * n.a. + *******************************************************/ +static void nvt_gpio_deconfig(struct nvt_ts_data *ts) +{ + if (gpio_is_valid(ts->irq_gpio)) + gpio_free(ts->irq_gpio); +#if NVT_TOUCH_SUPPORT_HW_RST + if (gpio_is_valid(ts->reset_gpio)) + gpio_free(ts->reset_gpio); +#endif +} + +static uint8_t nvt_fw_recovery(uint8_t *point_data) +{ + uint8_t i = 0; + uint8_t detected = true; + + /* check pattern */ + for (i=1 ; i<7 ; i++) { + if (point_data[i] != 0x77) { + detected = false; + break; + } + } + + return detected; +} + +#if NVT_TOUCH_ESD_PROTECT +void nvt_esd_check_enable(uint8_t enable) +{ + /* update interrupt timer */ + irq_timer = jiffies; + /* clear esd_retry counter, if protect function is enabled */ + esd_retry = enable ? 0 : esd_retry; + /* enable/disable esd check flag */ + esd_check = enable; +} + +static void nvt_esd_check_func(struct work_struct *work) +{ + unsigned int timer = jiffies_to_msecs(jiffies - irq_timer); + + //NVT_ERR("esd_check = %d (retry %d)\n", esd_check, esd_retry); //DEBUG + + if ((timer > NVT_TOUCH_ESD_CHECK_PERIOD) && esd_check) { + mutex_lock(&ts->lock); + NVT_ERR("do ESD recovery, timer = %d, retry = %d\n", timer, esd_retry); + /* do esd recovery, bootloader reset */ + nvt_bootloader_reset(); + mutex_unlock(&ts->lock); + /* update interrupt timer */ + irq_timer = jiffies; + /* update esd_retry counter */ + esd_retry++; + } + + queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work, + msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD)); +} +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#define POINT_DATA_LEN 65 +/******************************************************* + * Description: + * Novatek touchscreen work function. + * + * return: + * n.a. + *******************************************************/ +static irqreturn_t nvt_ts_work_func(int irq, void *data) +{ + int32_t ret = -1; + uint8_t point_data[POINT_DATA_LEN + 1] = {0}; + uint32_t position = 0; + uint32_t input_x = 0; + uint32_t input_y = 0; + uint32_t input_w = 0; + uint32_t input_p = 0; + uint8_t input_id = 0; +#if MT_PROTOCOL_B + uint8_t press_id[TOUCH_MAX_FINGER_NUM] = {0}; +#endif /* MT_PROTOCOL_B */ + int32_t i = 0; + int32_t finger_cnt = 0; + +#if WAKEUP_GESTURE + if (bTouchIsAwake == 0) { + pm_wakeup_event(&ts->input_dev->dev, 5000); + } +#endif + + mutex_lock(&ts->lock); + + ret = CTP_I2C_READ(ts->client, I2C_FW_Address, point_data, POINT_DATA_LEN + 1); + if (ret < 0) { + NVT_ERR("CTP_I2C_READ failed.(%d)\n", ret); + goto XFER_ERROR; + } + + if (nvt_fw_recovery(point_data)) { +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(true); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + goto XFER_ERROR; + } + +#if WAKEUP_GESTURE + if (bTouchIsAwake == 0) { + input_id = (uint8_t)(point_data[1] >> 3); + nvt_ts_wakeup_gesture_report(input_id, point_data); + mutex_unlock(&ts->lock); + return IRQ_HANDLED; + } +#endif + + finger_cnt = 0; + + for (i = 0; i < ts->max_touch_num; i++) { + position = 1 + 6 * i; + input_id = (uint8_t)(point_data[position + 0] >> 3); + if ((input_id == 0) || (input_id > ts->max_touch_num)) + continue; + + if (((point_data[position] & 0x07) == 0x01) || ((point_data[position] & 0x07) == 0x02)) { //finger down (enter & moving) +#if NVT_TOUCH_ESD_PROTECT + /* update interrupt timer */ + irq_timer = jiffies; +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + input_x = (uint32_t)(point_data[position + 1] << 4) + (uint32_t) (point_data[position + 3] >> 4); + input_y = (uint32_t)(point_data[position + 2] << 4) + (uint32_t) (point_data[position + 3] & 0x0F); + if ((input_x < 0) || (input_y < 0)) + continue; + if ((input_x > ts->abs_x_max) || (input_y > ts->abs_y_max)) + continue; + input_w = (uint32_t)(point_data[position + 4]); + if (input_w == 0) + input_w = 1; + if (i < 2) { + input_p = (uint32_t)(point_data[position + 5]) + (uint32_t)(point_data[i + 63] << 8); + if (input_p > TOUCH_FORCE_NUM) + input_p = TOUCH_FORCE_NUM; + } else { + input_p = (uint32_t)(point_data[position + 5]); + } + if (input_p == 0) + input_p = 1; + +#if MT_PROTOCOL_B + press_id[input_id - 1] = 1; + input_mt_slot(ts->input_dev, input_id - 1); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); +#else /* MT_PROTOCOL_B */ + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, input_id - 1); + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif /* MT_PROTOCOL_B */ + + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, input_p); + +#if MT_PROTOCOL_B +#else /* MT_PROTOCOL_B */ + input_mt_sync(ts->input_dev); +#endif /* MT_PROTOCOL_B */ + + finger_cnt++; + } + } + +#if MT_PROTOCOL_B + for (i = 0; i < ts->max_touch_num; i++) { + if (press_id[i] != 1) { + input_mt_slot(ts->input_dev, i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); + } + } + + input_report_key(ts->input_dev, BTN_TOUCH, (finger_cnt > 0)); +#else /* MT_PROTOCOL_B */ + if (finger_cnt == 0) { + input_report_key(ts->input_dev, BTN_TOUCH, 0); + input_mt_sync(ts->input_dev); + } +#endif /* MT_PROTOCOL_B */ + +#if TOUCH_KEY_NUM > 0 + if (point_data[61] == 0xF8) { +#if NVT_TOUCH_ESD_PROTECT + /* update interrupt timer */ + irq_timer = jiffies; +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + for (i = 0; i < ts->max_button_num; i++) { + input_report_key(ts->input_dev, touch_key_array[i], ((point_data[62] >> i) & 0x01)); + } + } else { + for (i = 0; i < ts->max_button_num; i++) { + input_report_key(ts->input_dev, touch_key_array[i], 0); + } + } +#endif + + input_sync(ts->input_dev); + +XFER_ERROR: + + mutex_unlock(&ts->lock); + + return IRQ_HANDLED; +} + +/******************************************************* + * Description: + * Novatek touchscreen check and stop crc reboot loop. + * + * return: + * n.a. +*******************************************************/ +void nvt_stop_crc_reboot(void) +{ + uint8_t buf[8] = {0}; + int32_t retry = 0; + + //read dummy buffer to check CRC fail reboot is happening or not + + //---change I2C index to prevent geting 0xFF, but not 0xFC--- + nvt_set_page(I2C_BLDR_Address, 0x1F64E); + + //---read to check if buf is 0xFC which means IC is in CRC reboot --- + buf[0] = 0x4E; + CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 4); + + if ((buf[1] == 0xFC) || + ((buf[1] == 0xFF) && (buf[2] == 0xFF) && (buf[3] == 0xFF))) { + + //IC is in CRC fail reboot loop, needs to be stopped! + for (retry = 5; retry > 0; retry--) { + + //---write i2c cmds to reset idle : 1st--- + buf[0]=0x00; + buf[1]=0xA5; + CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + + //---write i2c cmds to reset idle : 2rd--- + buf[0]=0x00; + buf[1]=0xA5; + CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + msleep(1); + + //---clear CRC_ERR_FLAG--- + nvt_set_page(I2C_BLDR_Address, 0x3F135); + + buf[0] = 0x35; + buf[1] = 0xA5; + CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 2); + + //---check CRC_ERR_FLAG--- + nvt_set_page(I2C_BLDR_Address, 0x3F135); + + buf[0] = 0x35; + buf[1] = 0x00; + CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 2); + + if (buf[1] == 0xA5) + break; + } + if (retry == 0) + NVT_ERR("CRC auto reboot is not able to be stopped!\n"); + } + + return; +} + +/******************************************************* + * Description: + * Novatek touchscreen check chip version trim function. + * + * return: + * Executive outcomes. 0---NVT IC. -1---not NVT IC. +*******************************************************/ +static int8_t nvt_ts_check_chip_ver_trim(uint32_t chip_ver_trim_addr) +{ + uint8_t buf[8] = {0}; + int32_t retry = 0; + int32_t list = 0; + int32_t i = 0; + int32_t found_nvt_chip = 0; + int32_t ret = -1; + + nvt_bootloader_reset(); // NOT in retry loop + + //---Check for 5 times--- + for (retry = 5; retry > 0; retry--) { + nvt_sw_reset_idle(); + + buf[0] = 0x00; + buf[1] = 0x35; + CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + msleep(10); + + nvt_set_page(I2C_BLDR_Address, chip_ver_trim_addr); + + buf[0] = chip_ver_trim_addr & 0xFF; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = 0x00; + buf[6] = 0x00; + CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 7); + NVT_LOG("buf[1]=0x%02X, buf[2]=0x%02X, buf[3]=0x%02X, buf[4]=0x%02X, buf[5]=0x%02X, buf[6]=0x%02X\n", + buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); + + //---Stop CRC check to prevent IC auto reboot--- + if ((buf[1] == 0xFC) || + ((buf[1] == 0xFF) && (buf[2] == 0xFF) && (buf[3] == 0xFF))) { + nvt_stop_crc_reboot(); + continue; + } + + // compare read chip id on supported list + for (list = 0; list < (sizeof(trim_id_table) / sizeof(struct nvt_ts_trim_id_table)); list++) { + found_nvt_chip = 0; + + // compare each byte + for (i = 0; i < NVT_ID_BYTE_MAX; i++) { + if (trim_id_table[list].mask[i]) { + if (buf[i + 1] != trim_id_table[list].id[i]) + break; + } + } + + if (i == NVT_ID_BYTE_MAX) { + found_nvt_chip = 1; + } + + if (found_nvt_chip) { + NVT_LOG("This is NVT touch IC\n"); + ts->mmap = trim_id_table[list].mmap; + ts->carrier_system = trim_id_table[list].hwinfo->carrier_system; + ret = 0; + goto out; + } else { + ts->mmap = NULL; + ret = -1; + } + } + + msleep(10); + } + +out: + return ret; +} + +#if defined(CONFIG_DRM) +static int nvt_ts_check_dt(struct device_node *np) +{ + int i; + int count; + struct device_node *node; + struct drm_panel *panel; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) + return 0; + + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + panel = of_drm_find_panel(node); + of_node_put(node); + if (!IS_ERR(panel)) { + active_panel = panel; + NVT_LOG(" %s:find\n", __func__); + return 0; + } + } + + NVT_ERR(" %s: not find\n", __func__); + return -ENODEV; +} + +static int nvt_ts_check_default_tp(struct device_node *dt, const char *prop) +{ + const char **active_tp = NULL; + int count, tmp, score = 0; + const char *active; + int ret, i; + + count = of_property_count_strings(dt->parent, prop); + if (count <= 0 || count > 3) + return -ENODEV; + + active_tp = kcalloc(count, sizeof(char *), GFP_KERNEL); + if (!active_tp) { + NVT_ERR("FTS alloc failed\n"); + return -ENOMEM; + } + + ret = of_property_read_string_array(dt->parent, prop, + active_tp, count); + if (ret < 0) { + NVT_ERR("fail to read %s %d\n", prop, ret); + ret = -ENODEV; + goto out; + } + + for (i = 0; i < count; i++) { + active = active_tp[i]; + if (active != NULL) { + tmp = of_device_is_compatible(dt, active); + if (tmp > 0) + score++; + } + } + + if (score <= 0) { + NVT_ERR("not match this driver\n"); + ret = -ENODEV; + goto out; + } + ret = 0; +out: + kfree(active_tp); + return ret; +} +#endif + +/******************************************************* + * Description: + * Novatek touchscreen driver probe function. + * + * return: + * Executive outcomes. 0---succeed. negative---failed + *******************************************************/ +static int32_t nvt_ts_late_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int32_t ret = 0; +#if ((TOUCH_KEY_NUM > 0) || WAKEUP_GESTURE) + int32_t retry = 0; +#endif + //---request and config GPIOs--- + ret = nvt_gpio_config(ts); + if (ret) { + NVT_ERR("gpio config error!\n"); + goto err_gpio_config_failed; + } + + //---check chip version trim--- + ret = nvt_ts_check_chip_ver_trim(CHIP_VER_TRIM_ADDR); + if (ret) { + NVT_LOG("try to check from old chip ver trim address\n"); + ret = nvt_ts_check_chip_ver_trim(CHIP_VER_TRIM_OLD_ADDR); + if (ret) { + NVT_ERR("chip is not identified\n"); + ret = -EINVAL; + goto err_chipvertrim_failed; + } + } + + nvt_bootloader_reset(); + nvt_check_fw_reset_state(RESET_STATE_INIT); + nvt_get_fw_info(); + + //---allocate input device--- + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) { + NVT_ERR("allocate input device failed\n"); + ret = -ENOMEM; + goto err_input_dev_alloc_failed; + } + + ts->max_touch_num = TOUCH_MAX_FINGER_NUM; + +#if TOUCH_KEY_NUM > 0 + ts->max_button_num = TOUCH_KEY_NUM; +#endif + + ts->int_trigger_type = INT_TRIGGER_TYPE; + + + //---set input device info.--- + ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ; + ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + ts->input_dev->propbit[0] = BIT(INPUT_PROP_DIRECT); + +#if MT_PROTOCOL_B + input_mt_init_slots(ts->input_dev, ts->max_touch_num, 0); +#endif + + input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, TOUCH_FORCE_NUM, 0, 0); //pressure = TOUCH_FORCE_NUM + +#if TOUCH_MAX_FINGER_NUM > 1 + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); //area = 255 + + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0); +#if MT_PROTOCOL_B + // no need to set ABS_MT_TRACKING_ID, input_mt_init_slots() already set it +#else + input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, ts->max_touch_num, 0, 0); +#endif //MT_PROTOCOL_B +#endif //TOUCH_MAX_FINGER_NUM > 1 + +#if TOUCH_KEY_NUM > 0 + for (retry = 0; retry < ts->max_button_num; retry++) { + input_set_capability(ts->input_dev, EV_KEY, touch_key_array[retry]); + } +#endif + +#if WAKEUP_GESTURE + for (retry = 0; retry < ARRAY_SIZE(gesture_key_array); retry++) { + input_set_capability(ts->input_dev, EV_KEY, gesture_key_array[retry]); + } +#endif + + snprintf(ts->phys, sizeof(ts->phys), "input/ts"); + ts->input_dev->name = NVT_TS_NAME; + ts->input_dev->phys = ts->phys; + ts->input_dev->id.bustype = BUS_I2C; + + //---register input device--- + ret = input_register_device(ts->input_dev); + if (ret) { + NVT_ERR("register input device (%s) failed. ret=%d\n", ts->input_dev->name, ret); + goto err_input_register_device_failed; + } + + //---set int-pin & request irq--- + client->irq = gpio_to_irq(ts->irq_gpio); + if (client->irq) { + NVT_LOG("int_trigger_type=%d\n", ts->int_trigger_type); + ts->irq_enabled = true; + ret = request_threaded_irq(client->irq, NULL, nvt_ts_work_func, + ts->int_trigger_type | IRQF_ONESHOT, NVT_I2C_NAME, ts); + if (ret != 0) { + NVT_ERR("request irq failed. ret=%d\n", ret); + goto err_int_request_failed; + } else { + nvt_irq_enable(false); + NVT_LOG("request irq %d succeed\n", client->irq); + } + } + +#if WAKEUP_GESTURE + device_init_wakeup(&ts->input_dev->dev, 1); +#endif + +#if BOOT_UPDATE_FIRMWARE + nvt_fwu_wq = alloc_workqueue("nvt_fwu_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + if (!nvt_fwu_wq) { + NVT_ERR("nvt_fwu_wq create workqueue failed\n"); + ret = -ENOMEM; + goto err_create_nvt_fwu_wq_failed; + } + INIT_DELAYED_WORK(&ts->nvt_fwu_work, Boot_Update_Firmware); + // please make sure boot update start after display reset(RESX) sequence + queue_delayed_work(nvt_fwu_wq, &ts->nvt_fwu_work, msecs_to_jiffies(14000)); +#endif + + NVT_LOG("NVT_TOUCH_ESD_PROTECT is %d\n", NVT_TOUCH_ESD_PROTECT); +#if NVT_TOUCH_ESD_PROTECT + INIT_DELAYED_WORK(&nvt_esd_check_work, nvt_esd_check_func); + nvt_esd_check_wq = alloc_workqueue("nvt_esd_check_wq", WQ_MEM_RECLAIM, 1); + if (!nvt_esd_check_wq) { + NVT_ERR("nvt_esd_check_wq create workqueue failed\n"); + ret = -ENOMEM; + goto err_create_nvt_esd_check_wq_failed; + } + queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work, + msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD)); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + //---set device node--- +#if NVT_TOUCH_PROC + ret = nvt_flash_proc_init(); + if (ret != 0) { + NVT_ERR("nvt flash proc init failed. ret=%d\n", ret); + goto err_flash_proc_init_failed; + } +#endif + +#if NVT_TOUCH_EXT_PROC + ret = nvt_extra_proc_init(); + if (ret != 0) { + NVT_ERR("nvt extra proc init failed. ret=%d\n", ret); + goto err_extra_proc_init_failed; + } +#endif + +#if NVT_TOUCH_MP + ret = nvt_mp_proc_init(); + if (ret != 0) { + NVT_ERR("nvt mp proc init failed. ret=%d\n", ret); + goto err_mp_proc_init_failed; + } +#endif + + bTouchIsAwake = 1; + NVT_LOG("end\n"); + + nvt_irq_enable(true); + + return 0; + +#if NVT_TOUCH_MP +nvt_mp_proc_deinit(); +err_mp_proc_init_failed: +#endif +#if NVT_TOUCH_EXT_PROC +nvt_extra_proc_deinit(); +err_extra_proc_init_failed: +#endif +#if NVT_TOUCH_PROC +nvt_flash_proc_deinit(); +err_flash_proc_init_failed: +#endif +#if NVT_TOUCH_ESD_PROTECT + if (nvt_esd_check_wq) { + cancel_delayed_work_sync(&nvt_esd_check_work); + destroy_workqueue(nvt_esd_check_wq); + nvt_esd_check_wq = NULL; + } +err_create_nvt_esd_check_wq_failed: +#endif +#if BOOT_UPDATE_FIRMWARE + if (nvt_fwu_wq) { + cancel_delayed_work_sync(&ts->nvt_fwu_work); + destroy_workqueue(nvt_fwu_wq); + nvt_fwu_wq = NULL; + } +err_create_nvt_fwu_wq_failed: +#endif +#if WAKEUP_GESTURE + device_init_wakeup(&ts->input_dev->dev, 0); +#endif + free_irq(client->irq, ts); +err_int_request_failed: + input_unregister_device(ts->input_dev); + ts->input_dev = NULL; +err_input_register_device_failed: + if (ts->input_dev) { + input_free_device(ts->input_dev); + ts->input_dev = NULL; + } +err_input_dev_alloc_failed: +err_chipvertrim_failed: + nvt_gpio_deconfig(ts); +err_gpio_config_failed: + NVT_ERR("ret = %d\n", ret); + return ret; +} + +/******************************************************* + * Description: + * Novatek touchscreen driver probe function. + * + * return: + * Executive outcomes. 0---succeed. negative---failed + *******************************************************/ +static int32_t nvt_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int32_t ret = 0; +#if defined(CONFIG_DRM) + struct device_node *dp = client->dev.of_node; +#endif + + NVT_LOG("start\n"); + +#if defined(CONFIG_DRM) + if (nvt_ts_check_dt(dp)) { + if (!nvt_ts_check_default_tp(dp, "qcom,i2c-touch-active")) + ret = -EPROBE_DEFER; + else + ret = -ENODEV; + + return ret; + } +#endif + + ts = devm_kzalloc(&client->dev, sizeof(struct nvt_ts_data), GFP_KERNEL); + if (ts == NULL) { + NVT_ERR("failed to allocated memory for nvt ts data\n"); + return -ENOMEM; + } + + ts->client = client; + i2c_set_clientdata(client, ts); + + //---parse dts--- + nvt_parse_dt(&client->dev); + + //---check i2c func.--- + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + NVT_ERR("i2c_check_functionality failed. (no I2C_FUNC_I2C)\n"); + return -ENODEV; + } + + mutex_init(&ts->lock); + mutex_init(&ts->xbuf_lock); + + ts->id = id; + +#if defined(CONFIG_DRM) + nvt_i2c_register_for_panel_events(client->dev.of_node, ts); +#elif defined(_MSM_DRM_NOTIFY_H_) + ts->drm_notif.notifier_call = nvt_drm_notifier_callback; + ret = msm_drm_register_client(&ts->drm_notif); + if (ret) { + NVT_ERR("register drm_notifier failed. ret=%d\n", ret); + goto err_register_drm_notif_failed; + } +#else + ts->fb_notif.notifier_call = nvt_fb_notifier_callback; + ts->fb_notif.notifier_call = nvt_fb_notifier_callback; + if (ret) { + NVT_ERR("register fb_notifier failed. ret=%d\n", ret); + goto err_register_fb_notif_failed; + } +#endif + NVT_LOG("end\n"); + return 0; +#if defined(CONFIG_DRM) + +#elif defined(_MSM_DRM_NOTIFY_H_) +err_register_drm_notif_failed: +#else +err_register_fb_notif_failed: +#endif + return -ENODEV; +} + +/******************************************************* + * Description: + * Novatek touchscreen driver release function. + * + * return: + * Executive outcomes. 0---succeed. + *******************************************************/ +static int32_t nvt_ts_remove(struct i2c_client *client) +{ + NVT_LOG("Removing driver...\n"); + +#if defined(CONFIG_DRM) + if (active_panel && ts->notifier_cookie) + panel_event_notifier_unregister(ts->notifier_cookie); +#elif defined(_MSM_DRM_NOTIFY_H_) + if (msm_drm_unregister_client(&ts->drm_notif)) + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); +#else + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier.\n"); +#endif + +#if NVT_TOUCH_MP + nvt_mp_proc_deinit(); +#endif +#if NVT_TOUCH_EXT_PROC + nvt_extra_proc_deinit(); +#endif +#if NVT_TOUCH_PROC + nvt_flash_proc_deinit(); +#endif + +#if NVT_TOUCH_ESD_PROTECT + if (nvt_esd_check_wq) { + cancel_delayed_work_sync(&nvt_esd_check_work); + nvt_esd_check_enable(false); + destroy_workqueue(nvt_esd_check_wq); + nvt_esd_check_wq = NULL; + } +#endif + +#if BOOT_UPDATE_FIRMWARE + if (nvt_fwu_wq) { + cancel_delayed_work_sync(&ts->nvt_fwu_work); + destroy_workqueue(nvt_fwu_wq); + nvt_fwu_wq = NULL; + } +#endif + +#if WAKEUP_GESTURE + if (ts->input_dev) + device_init_wakeup(&ts->input_dev->dev, 0); +#endif + + nvt_irq_enable(false); + free_irq(client->irq, ts); + + mutex_destroy(&ts->xbuf_lock); + mutex_destroy(&ts->lock); + + nvt_gpio_deconfig(ts); + + if (ts->input_dev) { + input_unregister_device(ts->input_dev); + ts->input_dev = NULL; + } + + i2c_set_clientdata(client, NULL); + + if (ts) { + kfree(ts); + ts = NULL; + } + + return 0; +} + +static void nvt_ts_shutdown(struct i2c_client *client) +{ + NVT_LOG("Shutdown driver...\n"); + + nvt_irq_enable(false); + +#if defined(CONFIG_DRM) + if (active_panel && ts->notifier_cookie) + panel_event_notifier_unregister(ts->notifier_cookie); +#elif defined(_MSM_DRM_NOTIFY_H_) + if (msm_drm_unregister_client(&ts->drm_notif)) + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); +#else + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier.\n"); +#endif + +#if NVT_TOUCH_MP + nvt_mp_proc_deinit(); +#endif +#if NVT_TOUCH_EXT_PROC + nvt_extra_proc_deinit(); +#endif +#if NVT_TOUCH_PROC + nvt_flash_proc_deinit(); +#endif + +#if NVT_TOUCH_ESD_PROTECT + if (nvt_esd_check_wq) { + cancel_delayed_work_sync(&nvt_esd_check_work); + nvt_esd_check_enable(false); + destroy_workqueue(nvt_esd_check_wq); + nvt_esd_check_wq = NULL; + } +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#if BOOT_UPDATE_FIRMWARE + if (nvt_fwu_wq) { + cancel_delayed_work_sync(&ts->nvt_fwu_work); + destroy_workqueue(nvt_fwu_wq); + nvt_fwu_wq = NULL; + } +#endif + +#if WAKEUP_GESTURE + if (ts->input_dev) + device_init_wakeup(&ts->input_dev->dev, 0); +#endif +} + +/******************************************************* + * Description: + * Novatek touchscreen driver suspend function. + * + * return: + * Executive outcomes. 0---succeed. + *******************************************************/ +static int32_t nvt_ts_suspend(struct device *dev) +{ + uint8_t buf[4] = {0}; +#if MT_PROTOCOL_B + uint32_t i = 0; +#endif + + if (!bTouchIsAwake) { + NVT_LOG("Touch is already suspend\n"); + return 0; + } + +#if !WAKEUP_GESTURE + nvt_irq_enable(false); +#endif + +#if NVT_TOUCH_ESD_PROTECT + NVT_LOG("cancel delayed work sync\n"); + cancel_delayed_work_sync(&nvt_esd_check_work); + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + mutex_lock(&ts->lock); + + NVT_LOG("start\n"); + + bTouchIsAwake = 0; + +#if WAKEUP_GESTURE + //---write command to enter "wakeup gesture mode"--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x13; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); + + enable_irq_wake(ts->client->irq); + + NVT_LOG("Enabled touch wakeup gesture\n"); + +#else // WAKEUP_GESTURE + //---write command to enter "deep sleep mode"--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x11; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); +#endif // WAKEUP_GESTURE + + mutex_unlock(&ts->lock); + + /* release all touches */ +#if MT_PROTOCOL_B + for (i = 0; i < ts->max_touch_num; i++) { + input_mt_slot(ts->input_dev, i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); + } +#endif + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#if !MT_PROTOCOL_B + input_mt_sync(ts->input_dev); +#endif + input_sync(ts->input_dev); + + msleep(50); + + NVT_LOG("end\n"); + + return 0; +} + +/******************************************************* + * Description: + * Novatek touchscreen driver resume function. + * + * return: + * Executive outcomes. 0---succeed. + *******************************************************/ +static int32_t nvt_ts_resume(struct device *dev) +{ + NVT_LOG("start\n"); + + if (bTouchIsAwake || ts->fw_ver == 0) { + nvt_ts_late_probe(ts->client, ts->id); + NVT_LOG("nvt_ts_late_probe\n"); + return 0; + } + + mutex_lock(&ts->lock); + + // make sure display reset(RESX) sequence and dsi cmds sent before this +#if NVT_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 1); +#endif + + // NT36772 IC due to no boot-load when RESX/TP_RESX + // nvt_bootloader_reset(); + if (nvt_check_fw_reset_state(RESET_STATE_REK)) { + NVT_ERR("FW is not ready! Try to bootloader reset...\n"); + nvt_bootloader_reset(); + nvt_check_fw_reset_state(RESET_STATE_REK); + } + +#if !WAKEUP_GESTURE + nvt_irq_enable(true); +#endif + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); + queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work, + msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD)); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + bTouchIsAwake = 1; + + mutex_unlock(&ts->lock); + + NVT_LOG("end\n"); + + return 0; +} + +#if defined(CONFIG_DRM) + +static void nvt_i2c_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data) +{ + struct nvt_ts_data *ts = client_data; + + if (!notification) { + pr_err("Invalid notification\n"); + return; + } + + NVT_LOG("Notification type:%d, early_trigger:%d", + notification->notif_type, + notification->notif_data.early_trigger); + + switch (notification->notif_type) { + case DRM_PANEL_EVENT_UNBLANK: + if (notification->notif_data.early_trigger) + NVT_LOG("resume notification pre commit\n"); + else + nvt_ts_resume(&ts->client->dev); + break; + + case DRM_PANEL_EVENT_BLANK: + if (notification->notif_data.early_trigger) + nvt_ts_suspend(&ts->client->dev); + else + NVT_LOG("suspend notification post commit\n"); + break; + + case DRM_PANEL_EVENT_BLANK_LP: + NVT_LOG("received lp event\n"); + break; + + case DRM_PANEL_EVENT_FPS_CHANGE: + NVT_LOG("Received fps change old fps:%d new fps:%d\n", + notification->notif_data.old_fps, + notification->notif_data.new_fps); + break; + default: + NVT_LOG("notification serviced :%d\n", + notification->notif_type); + break; + } +} + +#elif defined(_MSM_DRM_NOTIFY_H_) +static int nvt_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank; + struct nvt_ts_data *ts = + container_of(self, struct nvt_ts_data, drm_notif); + + if (!evdata || (evdata->id != 0)) + return 0; + + if (evdata->data && ts) { + blank = evdata->data; + if (event == MSM_DRM_EARLY_EVENT_BLANK) { + if (*blank == MSM_DRM_BLANK_POWERDOWN) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_ts_suspend(&ts->client->dev); + } + } else if (event == MSM_DRM_EVENT_BLANK) { + if (*blank == MSM_DRM_BLANK_UNBLANK) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_ts_resume(&ts->client->dev); + } + } + } + + return 0; +} + +#else +static int nvt_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct nvt_ts_data *ts = + container_of(self, struct nvt_ts_data, fb_notif); + + if (evdata && evdata->data && event == FB_EARLY_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_POWERDOWN) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_ts_suspend(&ts->client->dev); + } + } else if (evdata && evdata->data && event == FB_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_ts_resume(&ts->client->dev); + } + } + + return 0; +} +#endif + +static const struct i2c_device_id nvt_ts_id[] = { + { NVT_I2C_NAME, 0 }, + { } +}; + +#ifdef CONFIG_OF +static struct of_device_id nvt_match_table[] = { + { .compatible = "novatek,NVT-ts",}, + { }, +}; +#endif + +static struct i2c_driver nvt_i2c_driver = { + .probe = nvt_ts_probe, + .remove = nvt_ts_remove, + .shutdown = nvt_ts_shutdown, + .id_table = nvt_ts_id, + .driver = { + .name = NVT_I2C_NAME, +#ifdef CONFIG_OF + .of_match_table = nvt_match_table, +#endif + }, +}; + +/******************************************************* + * Description: + * Driver Install function. + * + * return: + * Executive Outcomes. 0---succeed. not 0---failed. + ********************************************************/ +static int32_t __init nvt_driver_init(void) +{ + int32_t ret = 0; + + NVT_LOG("start\n"); + //---add i2c driver--- + ret = i2c_add_driver(&nvt_i2c_driver); + if (ret) { + NVT_ERR("failed to add i2c driver"); + goto err_driver; + } + + NVT_LOG("finished\n"); + +err_driver: + return ret; +} + +/******************************************************* + * Description: + * Driver uninstall function. + * + * return: + * n.a. + ********************************************************/ +static void __exit nvt_driver_exit(void) +{ + i2c_del_driver(&nvt_i2c_driver); +} + +//late_initcall(nvt_driver_init); +module_init(nvt_driver_init); +module_exit(nvt_driver_exit); + +MODULE_DESCRIPTION("Novatek Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); + +#else /* NT36XXX_SPI */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_DRM) +#include +#endif + +#include "nt36xxx.h" +#if NVT_SPI_TOUCH_ESD_PROTECT +#include +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + +#if NVT_SPI_TOUCH_ESD_PROTECT +static struct delayed_work nvt_spi_esd_check_work; +static struct workqueue_struct *nvt_spi_esd_check_wq; +static unsigned long nvt_spi_irq_timer; +uint8_t nvt_spi_esd_check; +uint8_t nvt_spi_esd_retry; +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + +struct nvt_spi_data_t *nvt_spi_data; + +#if NVT_SPI_BOOT_UPDATE_FIRMWARE +static struct workqueue_struct *nvt_spi_fwu_wq; +#endif + +#if defined(CONFIG_DRM) +static struct drm_panel *active_spi_panel; +static void nvt_spi_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *event, void *client_data); + +#elif defined(_MSM_DRM_NOTIFY_H_) +static int nvt_drm_notifier_callback( + struct notifier_block *self, unsigned long event, void *data); +#else +static int nvt_fb_notifier_callback( + struct notifier_block *self, unsigned long event, void *data); +#endif + +static uint32_t NVT_SPI_ENG_RST_ADDR = 0x7FFF80; +uint32_t NVT_SPI_SWRST_N8_ADDR; //read from dtsi +uint32_t NVT_SPI_RD_FAST_ADDR; //read from dtsi + +#if NVT_SPI_TOUCH_KEY_NUM > 0 +const uint16_t nvt_spi_touch_key_array[NVT_SPI_TOUCH_KEY_NUM] = { + KEY_BACK, + KEY_HOME, + KEY_MENU +}; +#endif + +#if NVT_SPI_WAKEUP_GESTURE +const uint16_t nvt_spi_gesture_key_array[] = { + KEY_POWER, //GESTURE_WORD_C + KEY_POWER, //GESTURE_WORD_W + KEY_POWER, //GESTURE_WORD_V + KEY_POWER, //GESTURE_DOUBLE_CLICK + KEY_POWER, //GESTURE_WORD_Z + KEY_POWER, //GESTURE_WORD_M + KEY_POWER, //GESTURE_WORD_O + KEY_POWER, //GESTURE_WORD_e + KEY_POWER, //GESTURE_WORD_S + KEY_POWER, //GESTURE_SLIDE_UP + KEY_POWER, //GESTURE_SLIDE_DOWN + KEY_POWER, //GESTURE_SLIDE_LEFT + KEY_POWER, //GESTURE_SLIDE_RIGHT +}; +#endif + +static uint8_t bTouchIsAwake; + +#if defined(CONFIG_DRM) +static void nvt_spi_register_for_panel_events(struct device_node *dp, + struct nvt_spi_data_t *ts) +{ + void *cookie = NULL; + + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, active_spi_panel, + &nvt_spi_panel_notifier_callback, ts); + if (!cookie) { + pr_err("Failed to register for panel events\n"); + return; + } + + ts->notifier_cookie = cookie; +} +#endif + +/* + ******************************************************* + * Description: + * Novatek touchscreen irq enable/disable function. + * + * return: + * n.a. + ****************************************************** + */ +static void nvt_spi_irq_enable(bool enable) +{ + struct irq_desc *desc; + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (enable) { + if (!ts->irq_enabled) { + enable_irq(ts->client->irq); + ts->irq_enabled = true; + } + } else { + if (ts->irq_enabled) { + disable_irq(ts->client->irq); + ts->irq_enabled = false; + } + } + + desc = irq_to_desc(ts->client->irq); + NVT_LOG("enable=%d, desc->depth=%d\n", enable, desc->depth); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen spi read/write core function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static inline int32_t nvt_spi_read_write( + struct spi_device *client, uint8_t *buf, size_t len, enum NVT_SPI_RW rw) +{ + struct spi_message m; + struct spi_transfer t = { + .len = len, + }; + struct nvt_spi_data_t *ts = nvt_spi_data; + + memset(ts->xbuf, 0, len + NVT_SPI_DUMMY_BYTES); + memcpy(ts->xbuf, buf, len); + + switch (rw) { + case NVT_SPI_READ: + t.tx_buf = ts->xbuf; + t.rx_buf = ts->rbuf; + t.len = (len + NVT_SPI_DUMMY_BYTES); + break; + + case NVT_SPI_WRITE: + t.tx_buf = ts->xbuf; + break; + } + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + return spi_sync(client, &m); +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen spi read function. + * + * return: + * Executive outcomes. 2---succeed. -5---I/O error + ****************************************************** + */ +int32_t nvt_spi_read(uint8_t *buf, uint16_t len) +{ + int32_t ret = -1; + int32_t retries = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + + mutex_lock(&ts->xbuf_lock); + + buf[0] = NVT_SPI_READ_MASK(buf[0]); + + while (retries < 5) { + ret = nvt_spi_read_write(ts->client, buf, len, NVT_SPI_READ); + if (ret == 0) + break; + retries++; + } + + if (unlikely(retries == 5)) { + NVT_ERR("read error, ret=%d\n", ret); + ret = -EIO; + } else + memcpy((buf+1), (ts->rbuf+2), (len-1)); + + mutex_unlock(&ts->xbuf_lock); + + return ret; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen spi write function. + * + * return: + * Executive outcomes. 1---succeed. -5---I/O error + ****************************************************** + */ +int32_t nvt_spi_write(uint8_t *buf, uint16_t len) +{ + int32_t ret = -1; + int32_t retries = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + + mutex_lock(&ts->xbuf_lock); + + buf[0] = NVT_SPI_WRITE_MASK(buf[0]); + + while (retries < 5) { + ret = nvt_spi_read_write(ts->client, buf, len, NVT_SPI_WRITE); + if (ret == 0) + break; + retries++; + } + + if (unlikely(retries == 5)) { + NVT_ERR("error, ret=%d\n", ret); + ret = -EIO; + } + + mutex_unlock(&ts->xbuf_lock); + + return ret; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen set index/page/addr address. + * + * return: + * Executive outcomes. 0---succeed. -5---access fail. + ****************************************************** + */ +int32_t nvt_spi_set_page(uint32_t addr) +{ + uint8_t buf[4] = {0}; + + buf[0] = 0xFF; //set index/page/addr command + buf[1] = (addr >> 15) & 0xFF; + buf[2] = (addr >> 7) & 0xFF; + + return nvt_spi_write(buf, 3); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen write data to specify address. + * + * return: + * Executive outcomes. 0---succeed. -5---access fail. + ******************************************************* + */ +int32_t nvt_spi_write_addr(uint32_t addr, uint8_t data) +{ + int32_t ret = 0; + uint8_t buf[4] = {0}; + + //---set xdata index--- + buf[0] = 0xFF; //set index/page/addr command + buf[1] = (addr >> 15) & 0xFF; + buf[2] = (addr >> 7) & 0xFF; + ret = nvt_spi_write(buf, 3); + if (ret) { + NVT_ERR("set page 0x%06X failed, ret = %d\n", addr, ret); + return ret; + } + + //---write data to index--- + buf[0] = addr & (0x7F); + buf[1] = data; + ret = nvt_spi_write(buf, 2); + if (ret) { + NVT_ERR("write data to 0x%06X failed, ret = %d\n", addr, ret); + return ret; + } + + return ret; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen enable hw bld crc function. + * + * return: + * N/A. + ****************************************************** + */ +void nvt_spi_bld_crc_enable(void) +{ + uint8_t buf[4] = {0}; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata index to BLD_CRC_EN_ADDR--- + nvt_spi_set_page(ts->mmap->BLD_CRC_EN_ADDR); + + //---read data from index--- + buf[0] = ts->mmap->BLD_CRC_EN_ADDR & (0x7F); + buf[1] = 0xFF; + nvt_spi_read(buf, 2); + + //---write data to index--- + buf[0] = ts->mmap->BLD_CRC_EN_ADDR & (0x7F); + buf[1] = buf[1] | (0x01 << 7); + nvt_spi_write(buf, 2); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen clear status & enable fw crc function. + * + * return: + * N/A. + ****************************************************** + */ +void nvt_spi_fw_crc_enable(void) +{ + uint8_t buf[4] = {0}; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR); + + //---clear fw reset status--- + buf[0] = NVT_SPI_EVENT_MAP_RESET_COMPLETE & (0x7F); + buf[1] = 0x00; + nvt_spi_write(buf, 2); + + //---enable fw crc--- + buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD & (0x7F); + buf[1] = 0xAE; //enable fw crc command + nvt_spi_write(buf, 2); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen set boot ready function. + * + * return: + * N/A. + ****************************************************** + */ +void nvt_spi_boot_ready(void) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---write BOOT_RDY status cmds--- + nvt_spi_write_addr(ts->mmap->BOOT_RDY_ADDR, 1); + + mdelay(5); + + if (!ts->hw_crc) { + //---write BOOT_RDY status cmds--- + nvt_spi_write_addr(ts->mmap->BOOT_RDY_ADDR, 0); + + //---write POR_CD cmds--- + nvt_spi_write_addr(ts->mmap->POR_CD_ADDR, 0xA0); + } +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen enable auto copy mode function. + * + * return: + * N/A. + ****************************************************** + */ +void nvt_spi_tx_auto_copy_mode(void) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---write TX_AUTO_COPY_EN cmds--- + nvt_spi_write_addr(ts->mmap->TX_AUTO_COPY_EN, 0x69); + + NVT_ERR("tx auto copy mode enable\n"); +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen check spi dma tx info function. + * + * return: + * N/A. + ****************************************************** + */ +int32_t nvt_spi_check_spi_dma_tx_info(void) +{ + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 200; + struct nvt_spi_data_t *ts = nvt_spi_data; + + for (i = 0; i < retry; i++) { + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->SPI_DMA_TX_INFO); + + //---read fw status--- + buf[0] = ts->mmap->SPI_DMA_TX_INFO & 0x7F; + buf[1] = 0xFF; + nvt_spi_read(buf, 2); + + if (buf[1] == 0x00) + break; + + usleep_range(1000, 1100); + } + + if (i >= retry) { + NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); + return -EIO; + } + + return 0; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen eng reset cmd function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_eng_reset(void) +{ + //---eng reset cmds to ENG_RST_ADDR--- + nvt_spi_write_addr(NVT_SPI_ENG_RST_ADDR, 0x5A); + + mdelay(1); //wait tMCU_Idle2TP_REX_Hi after TP_RST +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen reset MCU function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_sw_reset(void) +{ + //---software reset cmds to SWRST_N8_ADDR--- + nvt_spi_write_addr(NVT_SPI_SWRST_N8_ADDR, 0x55); + + msleep(20); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen reset MCU then into idle mode + * function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_sw_reset_idle(void) +{ + //---MCU idle cmds to SWRST_N8_ADDR--- + nvt_spi_write_addr(NVT_SPI_SWRST_N8_ADDR, 0xAA); + + msleep(20); +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen reset MCU (boot) function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_bootloader_reset(void) +{ + //---reset cmds to SWRST_N8_ADDR--- + nvt_spi_write_addr(NVT_SPI_SWRST_N8_ADDR, 0x69); + + mdelay(5); //wait tBRST2FR after Bootload RST + + if (NVT_SPI_RD_FAST_ADDR) { + /* disable SPI_RD_FAST */ + nvt_spi_write_addr(NVT_SPI_RD_FAST_ADDR, 0x00); + } + + NVT_LOG("end\n"); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen clear FW status function. + * + * return: + * Executive outcomes. 0---succeed. -1---fail. + ****************************************************** + */ +int32_t nvt_spi_clear_fw_status(void) +{ + uint32_t addr; + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 20; + struct nvt_spi_data_t *ts = nvt_spi_data; + + for (i = 0; i < retry; i++) { + //---set xdata index to EVENT BUF ADDR--- + addr = ts->mmap->EVENT_BUF_ADDR; + nvt_spi_set_page(addr | NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE); + + //---clear fw status--- + buf[0] = NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0x00; + nvt_spi_write(buf, 2); + + //---read fw status--- + buf[0] = NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0xFF; + nvt_spi_read(buf, 2); + + if (buf[1] == 0x00) + break; + + usleep_range(10000, 11000); + } + + if (i >= retry) { + NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); + return -EIO; + } + + return 0; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen check FW status function. + + * return: + * Executive outcomes. 0---succeed. -1---failed. + ****************************************************** + */ +int32_t nvt_spi_check_fw_status(void) +{ + uint32_t addr; + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 50; + struct nvt_spi_data_t *ts = nvt_spi_data; + + usleep_range(20000, 21000); + + for (i = 0; i < retry; i++) { + //---set xdata index to EVENT BUF ADDR--- + addr = ts->mmap->EVENT_BUF_ADDR; + nvt_spi_set_page(addr | NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE); + + //---read fw status--- + buf[0] = NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0x00; + nvt_spi_read(buf, 2); + + if ((buf[1] & 0xF0) == 0xA0) + break; + + usleep_range(10000, 11000); + } + + if (i >= retry) { + NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); + return -EIO; + } + + return 0; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen check FW reset state function. + * + * return: + * Executive outcomes. 0---succeed. -1---failed. + ****************************************************** + */ +int32_t nvt_spi_check_fw_reset_state(enum NVT_SPI_RST_COMPLETE_STATE check_reset_state) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + int32_t retry = 0; + int32_t retry_max = (check_reset_state == NVT_SPI_RESET_STATE_INIT) ? 10 : 50; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR | NVT_SPI_EVENT_MAP_RESET_COMPLETE); + + while (1) { + //---read reset state--- + buf[0] = NVT_SPI_EVENT_MAP_RESET_COMPLETE; + buf[1] = 0x00; + nvt_spi_read(buf, 6); + + if ((buf[1] >= check_reset_state) && (buf[1] <= NVT_SPI_RESET_STATE_MAX)) { + ret = 0; + break; + } + + retry++; + if (unlikely(retry > retry_max)) { + NVT_ERR("error, retry=%d, buf[1]=0x%02X,0x%02X,0x%02X,0x%02X,0x%02X\n", + retry, buf[1], buf[2], buf[3], buf[4], buf[5]); + ret = -1; + break; + } + + usleep_range(10000, 11000); + } + + return ret; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen get firmware related information + * function. + * + * return: + * Executive outcomes. 0---success. -1---fail. + ****************************************************** + */ +int32_t nvt_spi_get_fw_info(void) +{ + uint8_t buf[64] = {0}; + uint32_t retry_count = 0; + int32_t ret = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + +info_retry: + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR | NVT_SPI_EVENT_MAP_FWINFO); + + //---read fw info--- + buf[0] = NVT_SPI_EVENT_MAP_FWINFO; + nvt_spi_read(buf, 39); + if ((buf[1] + buf[2]) != 0xFF) { + NVT_ERR("FW info is broken! fw_ver=0x%02X, ~fw_ver=0x%02X\n", buf[1], buf[2]); + if (retry_count < 3) { + retry_count++; + NVT_ERR("retry_count=%d\n", retry_count); + goto info_retry; + } else { + ts->fw_ver = 0; + ts->abs_x_max = NVT_SPI_TOUCH_DEFAULT_MAX_WIDTH; + ts->abs_y_max = NVT_SPI_TOUCH_DEFAULT_MAX_HEIGHT; + ts->max_button_num = NVT_SPI_TOUCH_KEY_NUM; + NVT_ERR("Set default fw_ver=%d, abs_x_max=%d, ", + ts->fw_ver, ts->abs_x_max); + NVT_ERR("abs_y_max=%d, max_button_num=%d!\n", + ts->abs_y_max, ts->max_button_num); + ret = -1; + goto out; + } + } + ts->fw_ver = buf[1]; + ts->x_num = buf[3]; + ts->y_num = buf[4]; + ts->abs_x_max = (uint16_t)((buf[5] << 8) | buf[6]); + ts->abs_y_max = (uint16_t)((buf[7] << 8) | buf[8]); + ts->max_button_num = buf[11]; + ts->nvt_pid = (uint16_t)((buf[36] << 8) | buf[35]); + if (ts->pen_support) { + ts->x_gang_num = buf[37]; + ts->y_gang_num = buf[38]; + } + + NVT_ERR("fw_ver=0x%02X, fw_type=0x%02X, PID=0x%04X\n", ts->fw_ver, buf[14], ts->nvt_pid); + + ret = 0; +out: + + return ret; +} + +/* + ****************************************************** + * Create Device Node (Proc Entry) + ****************************************************** + */ +#if NVT_SPI_TOUCH_PROC +static struct proc_dir_entry *nvt_spi_proc_entry; +#define DEVICE_NAME "NVTSPI" + +/* + ****************************************************** + * Description: + * Novatek touchscreen /proc/NVTSPI read function. + * + * return: + * Executive outcomes. 2---succeed. -5,-14---failed. + ****************************************************** + */ +static ssize_t nvt_spi_flash_read( + struct file *file, char __user *buff, size_t count, loff_t *offp) +{ + uint8_t *str = NULL; + int32_t ret = 0; + int32_t retries = 0; + int8_t spi_wr = 0; + uint8_t *buf; + + if ((count > NVT_SPI_TRANSFER_LEN + 3) || (count < 3)) { + NVT_ERR("invalid transfer len!\n"); + return -EFAULT; + } + + /* allocate buffer for spi transfer */ + str = kzalloc((count), GFP_KERNEL); + if (str == NULL) { + NVT_ERR("kzalloc for buf failed!\n"); + ret = -ENOMEM; + goto kzalloc_failed; + } + + buf = kzalloc((count), GFP_KERNEL | GFP_DMA); + if (buf == NULL) { + NVT_ERR("kzalloc for buf failed!\n"); + ret = -ENOMEM; + kfree(str); + str = NULL; + goto kzalloc_failed; + } + + if (copy_from_user(str, buff, count)) { + NVT_ERR("copy from user error\n"); + ret = -EFAULT; + goto out; + } + +#if NVT_SPI_TOUCH_ESD_PROTECT + /* + * stop esd check work to avoid case that 0x77 report righ after here to enable esd + * check again finally lead to trigger esd recovery bootloader reset + */ + cancel_delayed_work_sync(&nvt_spi_esd_check_work); + nvt_spi_esd_check_enable(false); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + spi_wr = str[0] >> 7; + memcpy(buf, str+2, ((str[0] & 0x7F) << 8) | str[1]); + + if (spi_wr == NVT_SPI_WRITE) { //SPI write + while (retries < 20) { + ret = nvt_spi_write(buf, ((str[0] & 0x7F) << 8) | str[1]); + if (!ret) + break; + NVT_ERR("error, retries=%d, ret=%d\n", retries, ret); + + retries++; + } + + if (unlikely(retries == 20)) { + NVT_ERR("error, ret = %d\n", ret); + ret = -EIO; + goto out; + } + } else if (spi_wr == NVT_SPI_READ) { //SPI read + while (retries < 20) { + ret = nvt_spi_read(buf, ((str[0] & 0x7F) << 8) | str[1]); + if (!ret) + break; + NVT_ERR("error, retries=%d, ret=%d\n", retries, ret); + + retries++; + } + + memcpy(str+2, buf, ((str[0] & 0x7F) << 8) | str[1]); + // copy buff to user if spi transfer + if (retries < 20) { + if (copy_to_user(buff, str, count)) { + ret = -EFAULT; + goto out; + } + } + + if (unlikely(retries == 20)) { + NVT_ERR("error, ret = %d\n", ret); + ret = -EIO; + goto out; + } + } else { + NVT_ERR("Call error, str[0]=%d\n", str[0]); + ret = -EFAULT; + goto out; + } + +out: + kfree(str); + kfree(buf); +kzalloc_failed: + return ret; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen /proc/NVTSPI open function. + * + * return: + * Executive outcomes. 0---succeed. -12---failed. + ****************************************************** + */ +static int32_t nvt_spi_flash_open(struct inode *inode, struct file *file) +{ + struct nvt_spi_flash_data *dev; + + dev = kmalloc(sizeof(struct nvt_spi_flash_data), GFP_KERNEL); + if (dev == NULL) { + NVT_ERR("Failed to allocate memory for nvt flash data\n"); + return -ENOMEM; + } + + rwlock_init(&dev->lock); + file->private_data = dev; + + return 0; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen /proc/NVTSPI close function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t nvt_spi_flash_close(struct inode *inode, struct file *file) +{ + struct nvt_spi_flash_data *dev = file->private_data; + + kfree(dev); + + return 0; +} + +static const struct proc_ops nvt_flash_fops_spi = { + .proc_open = nvt_spi_flash_open, + .proc_release = nvt_spi_flash_close, + .proc_read = nvt_spi_flash_read, +}; + +/* + ****************************************************** + * Description: + * Novatek touchscreen /proc/NVTSPI initial function. + * + * return: + * Executive outcomes. 0---succeed. -12---failed. + ****************************************************** + */ +static int32_t nvt_spi_flash_proc_init(void) +{ + nvt_spi_proc_entry = proc_create(DEVICE_NAME, 0444, NULL, &nvt_flash_fops_spi); + if (nvt_spi_proc_entry == NULL) { + NVT_ERR("Failed!\n"); + return -ENOMEM; + } + + NVT_LOG("Succeeded!\n"); + + NVT_LOG("============================================================\n"); + NVT_LOG("Create /proc/%s\n", DEVICE_NAME); + NVT_LOG("============================================================\n"); + + return 0; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen /proc/NVTSPI deinitial function. + * + * return: + * n.a. + ****************************************************** + */ +static void nvt_spi_flash_proc_deinit(void) +{ + if (nvt_spi_proc_entry != NULL) { + remove_proc_entry(DEVICE_NAME, NULL); + nvt_spi_proc_entry = NULL; + NVT_LOG("Removed /proc/%s\n", DEVICE_NAME); + } +} +#endif + +#if NVT_SPI_WAKEUP_GESTURE +#define GESTURE_WORD_C 12 +#define GESTURE_WORD_W 13 +#define GESTURE_WORD_V 14 +#define GESTURE_DOUBLE_CLICK 15 +#define GESTURE_WORD_Z 16 +#define GESTURE_WORD_M 17 +#define GESTURE_WORD_O 18 +#define GESTURE_WORD_e 19 +#define GESTURE_WORD_S 20 +#define GESTURE_SLIDE_UP 21 +#define GESTURE_SLIDE_DOWN 22 +#define GESTURE_SLIDE_LEFT 23 +#define GESTURE_SLIDE_RIGHT 24 +/* customized gesture id */ +#define DATA_PROTOCOL 30 + +/* function page definition */ +#define FUNCPAGE_GESTURE 1 + +/* + ******************************************************* + * Description: + * Novatek touchscreen wake up gesture key report function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_wakeup_gesture_report(uint8_t gesture_id, uint8_t *data) +{ + uint32_t keycode = 0; + uint8_t func_type = data[2]; + uint8_t func_id = data[3]; + struct nvt_spi_data_t *ts = nvt_spi_data; + + /* support fw specifal data protocol */ + if ((gesture_id == DATA_PROTOCOL) && (func_type == FUNCPAGE_GESTURE)) + gesture_id = func_id; + else if (gesture_id > DATA_PROTOCOL) { + NVT_ERR("gesture_id %d is invalid, func_type=%d, func_id=%d\n", + gesture_id, func_type, func_id); + return; + } + + NVT_LOG("gesture_id = %d\n", gesture_id); + + switch (gesture_id) { + case GESTURE_WORD_C: + NVT_LOG("Gesture : Word-C.\n"); + keycode = nvt_spi_gesture_key_array[0]; + break; + case GESTURE_WORD_W: + NVT_LOG("Gesture : Word-W.\n"); + keycode = nvt_spi_gesture_key_array[1]; + break; + case GESTURE_WORD_V: + NVT_LOG("Gesture : Word-V.\n"); + keycode = nvt_spi_gesture_key_array[2]; + break; + case GESTURE_DOUBLE_CLICK: + NVT_LOG("Gesture : Double Click.\n"); + keycode = nvt_spi_gesture_key_array[3]; + break; + case GESTURE_WORD_Z: + NVT_LOG("Gesture : Word-Z.\n"); + keycode = nvt_spi_gesture_key_array[4]; + break; + case GESTURE_WORD_M: + NVT_LOG("Gesture : Word-M.\n"); + keycode = nvt_spi_gesture_key_array[5]; + break; + case GESTURE_WORD_O: + NVT_LOG("Gesture : Word-O.\n"); + keycode = nvt_spi_gesture_key_array[6]; + break; + case GESTURE_WORD_e: + NVT_LOG("Gesture : Word-e.\n"); + keycode = nvt_spi_gesture_key_array[7]; + break; + case GESTURE_WORD_S: + NVT_LOG("Gesture : Word-S.\n"); + keycode = nvt_spi_gesture_key_array[8]; + break; + case GESTURE_SLIDE_UP: + NVT_LOG("Gesture : Slide UP.\n"); + keycode = nvt_spi_gesture_key_array[9]; + break; + case GESTURE_SLIDE_DOWN: + NVT_LOG("Gesture : Slide DOWN.\n"); + keycode = nvt_spi_gesture_key_array[10]; + break; + case GESTURE_SLIDE_LEFT: + NVT_LOG("Gesture : Slide LEFT.\n"); + keycode = nvt_spi_gesture_key_array[11]; + break; + case GESTURE_SLIDE_RIGHT: + NVT_LOG("Gesture : Slide RIGHT.\n"); + keycode = nvt_spi_gesture_key_array[12]; + break; + default: + break; + } + + if (keycode > 0) { + input_report_key(ts->input_dev, keycode, 1); + input_sync(ts->input_dev); + input_report_key(ts->input_dev, keycode, 0); + input_sync(ts->input_dev); + } +} +#endif + +/* + ****************************************************** + * Description: + * Novatek touchscreen parse device tree function. + * + * return: + * n.a. + ****************************************************** + */ +#ifdef CONFIG_OF +static int32_t nvt_spi_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + int32_t ret = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + ts->reset_gpio = of_get_named_gpio_flags(np, "novatek,reset-gpio", 0, &ts->reset_flags); + NVT_LOG("novatek,reset-gpio=%d\n", ts->reset_gpio); +#endif + ts->irq_gpio = of_get_named_gpio_flags(np, "novatek,irq-gpio", 0, &ts->irq_flags); + NVT_LOG("novatek,irq-gpio=%d\n", ts->irq_gpio); + + ts->pen_support = of_property_read_bool(np, "novatek,pen-support"); + NVT_LOG("novatek,pen-support=%d\n", ts->pen_support); + + ts->wgp_stylus = of_property_read_bool(np, "novatek,wgp-stylus"); + NVT_LOG("novatek,wgp-stylus=%d\n", ts->wgp_stylus); + + ret = of_property_read_u32(np, "novatek,swrst-n8-addr", &NVT_SPI_SWRST_N8_ADDR); + if (ret) { + NVT_ERR("error reading novatek,swrst-n8-addr. ret=%d\n", ret); + return ret; + } + NVT_LOG("SWRST_N8_ADDR=0x%06X\n", NVT_SPI_SWRST_N8_ADDR); + + ret = of_property_read_u32(np, "novatek,spi-rd-fast-addr", &NVT_SPI_RD_FAST_ADDR); + if (ret) { + NVT_LOG("not support novatek,spi-rd-fast-addr\n"); + NVT_SPI_RD_FAST_ADDR = 0; + ret = 0; + } else + NVT_LOG("SPI_RD_FAST_ADDR=0x%06X\n", NVT_SPI_RD_FAST_ADDR); + + return ret; +} +#else +static int32_t nvt_spi_parse_dt(struct device *dev) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + ts->reset_gpio = NVTTOUCH_RST_PIN; +#endif + ts->irq_gpio = NVTTOUCH_INT_PIN; + return 0; +} +#endif + +/* + ****************************************************** + * Description: + * Novatek touchscreen config and request gpio + * + * return: + * Executive outcomes. 0---succeed. not 0---failed. + ****************************************************** + */ +static int nvt_spi_gpio_config(struct nvt_spi_data_t *ts) +{ + int32_t ret = 0; + +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + /* request RST-pin (Output/High) */ + if (gpio_is_valid(ts->reset_gpio)) { + ret = gpio_request_one(ts->reset_gpio, GPIOF_OUT_INIT_LOW, "NVT-tp-rst"); + if (ret) { + NVT_ERR("Failed to request NVT-tp-rst GPIO\n"); + goto err_request_reset_gpio; + } + } +#endif + + /* request INT-pin (Input) */ + if (gpio_is_valid(ts->irq_gpio)) { + ret = gpio_request_one(ts->irq_gpio, GPIOF_IN, "NVT-int"); + if (ret) { + NVT_ERR("Failed to request NVT-int GPIO\n"); + goto err_request_irq_gpio; + } + } + + return ret; + +err_request_irq_gpio: +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + gpio_free(ts->reset_gpio); +err_request_reset_gpio: +#endif + return ret; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen deconfig gpio + * + * return: + * n.a. + ****************************************************** + */ +static void nvt_spi_gpio_deconfig(struct nvt_spi_data_t *ts) +{ + if (gpio_is_valid(ts->irq_gpio)) + gpio_free(ts->irq_gpio); +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + if (gpio_is_valid(ts->reset_gpio)) + gpio_free(ts->reset_gpio); +#endif +} + +static uint8_t nvt_spi_fw_recovery(uint8_t *point_data) +{ + uint8_t i = 0; + uint8_t detected = true; + + /* check pattern */ + for (i = 1; i < 7; i++) { + if (point_data[i] != 0x77) { + detected = false; + break; + } + } + + return detected; +} + +#if NVT_SPI_TOUCH_ESD_PROTECT +void nvt_spi_esd_check_enable(uint8_t enable) +{ + /* update interrupt timer */ + nvt_spi_irq_timer = jiffies; + /* clear esd_retry counter, if protect function is enabled */ + nvt_spi_esd_retry = enable ? 0 : nvt_spi_esd_retry; + /* enable/disable esd check flag */ + nvt_spi_esd_check = enable; +} + +static void nvt_spi_esd_check_func(struct work_struct *work) +{ + unsigned int timer = jiffies_to_msecs(jiffies - nvt_spi_irq_timer); + struct nvt_spi_data_t *ts = nvt_spi_data; + + //NVT_LOG("esd_check = %d (retry %d)\n", esd_check, esd_retry); + + if ((timer > NVT_SPI_TOUCH_ESD_CHECK_PERIOD) && esd_check) { + mutex_lock(&ts->lock); + NVT_ERR("do ESD recovery, timer = %d, retry = %d\n", timer, nvt_spi_esd_retry); + /* do esd recovery, reload fw */ + nvt_spi_update_firmware(NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME); + mutex_unlock(&ts->lock); + /* update interrupt timer */ + nvt_spi_irq_timer = jiffies; + /* update esd_retry counter */ + nvt_spi_esd_retry++; + } + + queue_delayed_work(nvt_spi_esd_check_wq, &nvt_spi_esd_check_work, + msecs_to_jiffies(NVT_SPI_TOUCH_ESD_CHECK_PERIOD)); +} +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + +#define NVT_SPI_PEN_DATA_LEN 14 +#if NVT_SPI_CHECK_PEN_DATA_CHECKSUM +static int32_t nvt_spi_pen_data_checksum(uint8_t *buf, uint8_t length) +{ + uint8_t checksum = 0; + int32_t i = 0; + + // Calculate checksum + for (i = 0; i < length - 1; i++) + checksum += buf[i]; + checksum = (~checksum + 1); + + // Compare ckecksum and dump fail data + if (checksum != buf[length - 1]) { + NVT_ERR("pen packet checksum not match. (buf[%d]=0x%02X, checksum=0x%02X)\n", + length - 1, buf[length - 1], checksum); + //--- dump pen buf --- + for (i = 0; i < length; i++) + NVT_ERR("%02X ", buf[i]); + + NVT_ERR("\n"); + + return -EIO; + } + + return 0; +} +#endif // #if NVT_SPI_CHECK_PEN_DATA_CHECKSUM + +#if NVT_SPI_TOUCH_WDT_RECOVERY +static uint8_t nvt_spi_recovery_cnt; +static uint8_t nvt_spi_wdt_fw_recovery(uint8_t *point_data) +{ + uint32_t recovery_cnt_max = 10; + uint8_t recovery_enable = false; + uint8_t i = 0; + + nvt_spi_recovery_cnt++; + + /* check pattern */ + for (i = 1 ; i < 7 ; i++) { + if ((point_data[i] != 0xFD) && (point_data[i] != 0xFE)) { + nvt_spi_recovery_cnt = 0; + break; + } + } + + if (nvt_spi_recovery_cnt > recovery_cnt_max) { + recovery_enable = true; + nvt_spi_recovery_cnt = 0; + } + + return recovery_enable; +} +#endif /* #if NVT_SPI_TOUCH_WDT_RECOVERY */ + +#if NVT_SPI_POINT_DATA_CHECKSUM +static int32_t nvt_spi_point_data_checksum(uint8_t *buf, uint8_t length) +{ + uint8_t checksum = 0; + int32_t i = 0; + + // Generate checksum + for (i = 0; i < length - 1; i++) + checksum += buf[i + 1]; + + checksum = (~checksum + 1); + + // Compare ckecksum and dump fail data + if (checksum != buf[length]) { + NVT_ERR("packet checksum not match. (point_data[%d]=0x%02X, checksum=0x%02X)\n", + length, buf[length], checksum); + + for (i = 0; i < 10; i++) + NVT_LOG("%02X %02X %02X %02X %02X %02X\n", + buf[1 + i * 6], buf[2 + i * 6], buf[3 + i * 6], + buf[4 + i * 6], buf[5 + i * 6], buf[6 + i * 6]); + + NVT_LOG("%02X %02X %02X %02X %02X\n", + buf[61], buf[62], buf[63], buf[64], buf[65]); + return -EIO; + } + + return 0; +} +#endif /* NVT_SPI_POINT_DATA_CHECKSUM */ + +/* + ****************************************************** + * Description: + * Novatek touchscreen work function. + * + * return: + * n.a. + ****************************************************** + */ +#define NVT_SPI_POINT_DATA_LEN 65 +#define NVT_SPI_POINT (NVT_SPI_POINT_DATA_LEN + NVT_SPI_PEN_DATA_LEN + 1 + NVT_SPI_DUMMY_BYTES) +static irqreturn_t nvt_spi_work_func(int irq, void *data) +{ + int32_t ret = -1; + uint8_t point_data[NVT_SPI_POINT] = {0}; + uint32_t position = 0; + uint32_t input_x = 0; + uint32_t input_y = 0; + uint32_t input_w = 0; + uint32_t input_p = 0; + uint8_t input_id = 0; +#if NVT_SPI_MT_PROTOCOL_B + uint8_t press_id[NVT_SPI_TOUCH_MAX_FINGER_NUM] = {0}; +#endif /* NVT_SPI_MT_PROTOCOL_B */ + int32_t i = 0; + int32_t finger_cnt = 0; + uint8_t pen_format_id = 0; + uint32_t pen_x = 0; + uint32_t pen_y = 0; + uint32_t pen_pressure = 0; + uint32_t pen_distance = 0; + int8_t pen_tilt_x = 0; + int8_t pen_tilt_y = 0; + uint32_t pen_btn1 = 0; + uint32_t pen_btn2 = 0; + uint32_t pen_battery = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + +#if NVT_SPI_WAKEUP_GESTURE + if (bTouchIsAwake == 0) + pm_wakeup_event(&ts->input_dev->dev, 5000); + +#endif + + mutex_lock(&ts->lock); + + if (ts->pen_support) + ret = nvt_spi_read(point_data, NVT_SPI_POINT_DATA_LEN + NVT_SPI_PEN_DATA_LEN + 1); + else + ret = nvt_spi_read(point_data, NVT_SPI_POINT_DATA_LEN + 1); + + if (ret < 0) { + NVT_ERR("nvt_spi_read failed.(%d)\n", ret); + goto XFER_ERROR; + } + + //--- dump SPI buf --- +/* + * for (i = 0; i < 10; i++) { + * printk("%02X %02X %02X %02X %02X %02X ", + * point_data[1+i*6], point_data[2+i*6], point_data[3+i*6], + * point_data[4+i*6], point_data[5+i*6], point_data[6+i*6]); + * } + * printk("\n"); + */ + +#if NVT_SPI_TOUCH_WDT_RECOVERY + /* ESD protect by WDT */ + if (nvt_spi_wdt_fw_recovery(point_data)) { + NVT_ERR("Recover for fw reset, %02X\n", point_data[1]); + nvt_spi_update_firmware(NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME); + goto XFER_ERROR; + } +#endif /* #if NVT_SPI_TOUCH_WDT_RECOVERY */ + + /* ESD protect by FW handshake */ + if (nvt_spi_fw_recovery(point_data)) { +#if NVT_SPI_TOUCH_ESD_PROTECT + nvt_spi_esd_check_enable(true); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + goto XFER_ERROR; + } + +#if NVT_SPI_POINT_DATA_CHECKSUM + if (NVT_SPI_POINT_DATA_LEN >= NVT_SPI_POINT_DATA_CHECKSUM_LEN) { + ret = nvt_spi_point_data_checksum(point_data, NVT_SPI_POINT_DATA_CHECKSUM_LEN); + if (ret) + goto XFER_ERROR; + } +#endif /* NVT_SPI_POINT_DATA_CHECKSUM */ + +#if NVT_SPI_WAKEUP_GESTURE + if (bTouchIsAwake == 0) { + input_id = (uint8_t)(point_data[1] >> 3); + nvt_spi_wakeup_gesture_report(input_id, point_data); + mutex_unlock(&ts->lock); + return IRQ_HANDLED; + } +#endif + + finger_cnt = 0; + + for (i = 0; i < ts->max_touch_num; i++) { + position = 1 + 6 * i; + input_id = (uint8_t)(point_data[position + 0] >> 3); + if ((input_id == 0) || (input_id > ts->max_touch_num)) + continue; + + if (((point_data[position] & 0x07) == 0x01) + || ((point_data[position] & 0x07) == 0x02)) { + //finger down (enter & moving) +#if NVT_SPI_TOUCH_ESD_PROTECT + /* update interrupt timer */ + nvt_spi_irq_timer = jiffies; +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + input_x = (uint32_t)(point_data[position + 1] << 4); + input_x += (uint32_t) (point_data[position + 3] >> 4); + + input_y = (uint32_t)(point_data[position + 2] << 4); + input_y += (uint32_t) (point_data[position + 3] & 0x0F); + if ((input_x < 0) || (input_y < 0)) + continue; + if ((input_x > ts->abs_x_max) || (input_y > ts->abs_y_max)) + continue; + input_w = (uint32_t)(point_data[position + 4]); + if (input_w == 0) + input_w = 1; + if (i < 2) { + input_p = (uint32_t)(point_data[position + 5]); + input_p += (uint32_t)(point_data[i + 63] << 8); + if (input_p > NVT_SPI_TOUCH_FORCE_NUM) + input_p = NVT_SPI_TOUCH_FORCE_NUM; + } else + input_p = (uint32_t)(point_data[position + 5]); + + if (input_p == 0) + input_p = 1; + +#if NVT_SPI_MT_PROTOCOL_B + press_id[input_id - 1] = 1; + input_mt_slot(ts->input_dev, input_id - 1); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true); +#else /* NVT_SPI_MT_PROTOCOL_B */ + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, input_id - 1); + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif /* NVT_SPI_MT_PROTOCOL_B */ + + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, input_x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, input_y); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, input_w); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, input_p); + +#if NVT_SPI_MT_PROTOCOL_B +#else /* NVT_SPI_MT_PROTOCOL_B */ + input_mt_sync(ts->input_dev); +#endif /* NVT_SPI_MT_PROTOCOL_B */ + + finger_cnt++; + } + } + +#if NVT_SPI_MT_PROTOCOL_B + for (i = 0; i < ts->max_touch_num; i++) { + if (press_id[i] != 1) { + input_mt_slot(ts->input_dev, i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); + } + } + + input_report_key(ts->input_dev, BTN_TOUCH, (finger_cnt > 0)); +#else /* NVT_SPI_MT_PROTOCOL_B */ + if (finger_cnt == 0) { + input_report_key(ts->input_dev, BTN_TOUCH, 0); + input_mt_sync(ts->input_dev); + } +#endif /* NVT_SPI_MT_PROTOCOL_B */ + +#if NVT_SPI_TOUCH_KEY_NUM > 0 + if (point_data[61] == 0xF8) { +#if NVT_SPI_TOUCH_ESD_PROTECT + /* update interrupt timer */ + nvt_spi_irq_timer = jiffies; +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + for (i = 0; i < ts->max_button_num; i++) + input_report_key(ts->input_dev, nvt_spi_touch_key_array[i], + ((point_data[62] >> i) & 0x01)); + + } else { + for (i = 0; i < ts->max_button_num; i++) + input_report_key(ts->input_dev, nvt_spi_touch_key_array[i], 0); + } +#endif + + input_sync(ts->input_dev); + + if (ts->pen_support) { + + //--- dump pen buf --- +/* + * printk("%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", + * point_data[66], point_data[67], point_data[68], point_data[69], + * point_data[70], point_data[71], point_data[72], point_data[73], + * point_data[74], point_data[75], point_data[76], point_data[77], + * point_data[78], point_data[79]); + */ +#if NVT_SPI_CHECK_PEN_DATA_CHECKSUM + if (nvt_spi_pen_data_checksum(&point_data[66], NVT_SPI_PEN_DATA_LEN)) { + // pen data packet checksum not match, skip it + goto XFER_ERROR; + } +#endif // #if NVT_SPI_CHECK_PEN_DATA_CHECKSUM + + // parse and handle pen report + pen_format_id = point_data[66]; + if (pen_format_id != 0xFF) { + if (pen_format_id == 0x01) { + // report pen data + pen_x = (uint32_t)(point_data[67] << 8); + pen_x += (uint32_t)(point_data[68]); + + pen_y = (uint32_t)(point_data[69] << 8); + pen_y += (uint32_t)(point_data[70]); + + pen_pressure = (uint32_t)(point_data[71] << 8); + pen_pressure += (uint32_t)(point_data[72]); + + pen_tilt_x = (int32_t)point_data[73]; + pen_tilt_y = (int32_t)point_data[74]; + + pen_distance = (uint32_t)(point_data[75] << 8); + pen_distance += (uint32_t)(point_data[76]); + + pen_btn1 = (uint32_t)(point_data[77] & 0x01); + pen_btn2 = (uint32_t)((point_data[77] >> 1) & 0x01); + pen_battery = (uint32_t)point_data[78]; +// printk("x=%d,y=%d,p=%d,tx=%d,ty=%d,d=%d,b1=%d,b2=%d,bat=%d\n", +// pen_x, pen_y, pen_pressure, pen_tilt_x, pen_tilt_y, +// pen_distance, pen_btn1, pen_btn2, pen_battery); + + input_report_abs(ts->pen_input_dev, ABS_X, pen_x); + input_report_abs(ts->pen_input_dev, ABS_Y, pen_y); + input_report_abs(ts->pen_input_dev, ABS_PRESSURE, pen_pressure); + input_report_key(ts->pen_input_dev, BTN_TOUCH, !!pen_pressure); + input_report_abs(ts->pen_input_dev, ABS_TILT_X, pen_tilt_x); + input_report_abs(ts->pen_input_dev, ABS_TILT_Y, pen_tilt_y); + input_report_abs(ts->pen_input_dev, ABS_DISTANCE, pen_distance); + input_report_key(ts->pen_input_dev, BTN_TOOL_PEN, + !!pen_distance || !!pen_pressure); + input_report_key(ts->pen_input_dev, BTN_STYLUS, pen_btn1); + input_report_key(ts->pen_input_dev, BTN_STYLUS2, pen_btn2); + // TBD: pen battery event report + // NVT_LOG("pen_battery=%d\n", pen_battery); + } else if (pen_format_id == 0xF0) { + // report Pen ID + } else { + NVT_ERR("Unknown pen format id!\n"); + goto XFER_ERROR; + } + } else { // pen_format_id = 0xFF, i.e. no pen present + input_report_abs(ts->pen_input_dev, ABS_X, 0); + input_report_abs(ts->pen_input_dev, ABS_Y, 0); + input_report_abs(ts->pen_input_dev, ABS_PRESSURE, 0); + input_report_abs(ts->pen_input_dev, ABS_TILT_X, 0); + input_report_abs(ts->pen_input_dev, ABS_TILT_Y, 0); + input_report_abs(ts->pen_input_dev, ABS_DISTANCE, 0); + input_report_key(ts->pen_input_dev, BTN_TOUCH, 0); + input_report_key(ts->pen_input_dev, BTN_TOOL_PEN, 0); + input_report_key(ts->pen_input_dev, BTN_STYLUS, 0); + input_report_key(ts->pen_input_dev, BTN_STYLUS2, 0); + } + + input_sync(ts->pen_input_dev); + } /* if (ts->pen_support) */ + +XFER_ERROR: + + mutex_unlock(&ts->lock); + + return IRQ_HANDLED; +} + + +/* + ******************************************************* + * Description: + * Novatek touchscreen check chip version trim function. + * + * return: + * Executive outcomes. 0---NVT IC. -1---not NVT IC. + ****************************************************** + */ +static int8_t nvt_spi_check_chip_ver_trim(uint32_t chip_ver_trim_addr) +{ + uint8_t buf[8] = {0}; + int32_t retry = 0; + int32_t list = 0; + int32_t i = 0; + int32_t found_nvt_chip = 0; + int32_t ret = -1; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---Check for 5 times--- + for (retry = 5; retry > 0; retry--) { + + nvt_spi_bootloader_reset(); + + nvt_spi_set_page(chip_ver_trim_addr); + + buf[0] = chip_ver_trim_addr & 0x7F; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = 0x00; + buf[6] = 0x00; + nvt_spi_read(buf, 7); + NVT_LOG("buf[1]=0x%02X,[2]=0x%02X,[3]=0x%02X,[4]=0x%02X,[5]=0x%02X,[6]=0x%02X\n", + buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); + + // compare read chip id on supported list + for (list = 0; list < ARRAY_SIZE(nvt_spi_trim_id_table); list++) { + found_nvt_chip = 0; + + // compare each byte + for (i = 0; i < NVT_SPI_ID_BYTE_MAX; i++) { + if (nvt_spi_trim_id_table[list].mask[i]) { + if (buf[i + 1] != nvt_spi_trim_id_table[list].id[i]) + break; + } + } + + if (i == NVT_SPI_ID_BYTE_MAX) + found_nvt_chip = 1; + + if (found_nvt_chip) { + NVT_LOG("This is NVT touch IC\n"); + ts->mmap = nvt_spi_trim_id_table[list].mmap; + ts->hw_crc = nvt_spi_trim_id_table[list].hwinfo->hw_crc; + ret = 0; + goto out; + } else { + ts->mmap = NULL; + ret = -1; + } + } + + msleep(20); + } + +out: + return ret; +} + +#if defined(CONFIG_DRM) +static int nvt_spi_check_dt(struct device_node *np) +{ + int i; + int count; + struct device_node *node; + struct drm_panel *panel; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) + return 0; + + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + panel = of_drm_find_panel(node); + of_node_put(node); + if (!IS_ERR(panel)) { + active_spi_panel = panel; + return 0; + } + } + + return PTR_ERR(panel); +} +#endif + +static int32_t nvt_spi_late_probe(struct spi_device *client) +{ + int32_t ret = 0; + + //---check chip version trim--- + ret = nvt_spi_check_chip_ver_trim(NVT_SPI_CHIP_VER_TRIM_ADDR); + if (ret) { + NVT_LOG("try to check from old chip ver trim address\n"); + ret = nvt_spi_check_chip_ver_trim(NVT_SPI_CHIP_VER_TRIM_OLD_ADDR); + if (ret) { + NVT_ERR("chip is not identified\n"); + ret = -EINVAL; + return ret; + } + } + + if (nvt_spi_update_firmware(NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME)) + NVT_ERR("download firmware failed, ignore check fw state\n"); + else + nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_REK); + + //---set device node--- +#if NVT_SPI_TOUCH_PROC + ret = nvt_spi_flash_proc_init(); + if (ret != 0) { + NVT_ERR("nvt flash proc init failed. ret=%d\n", ret); + goto err_flash_proc_init_failed; + } +#endif + +#if NVT_SPI_TOUCH_EXT_PROC + ret = nvt_spi_extra_proc_init(); + if (ret != 0) { + NVT_ERR("nvt extra proc init failed. ret=%d\n", ret); + goto err_extra_proc_init_failed; + } +#endif + +#if NVT_SPI_TOUCH_MP + ret = nvt_spi_mp_proc_init(); + if (ret != 0) { + NVT_ERR("nvt mp proc init failed. ret=%d\n", ret); + goto err_mp_proc_init_failed; + } +#endif + + bTouchIsAwake = 1; + NVT_LOG("end\n"); + + nvt_spi_irq_enable(true); + return 0; + +#if NVT_SPI_TOUCH_MP + nvt_spi_mp_proc_deinit(); +err_mp_proc_init_failed: +#endif +#if NVT_SPI_TOUCH_EXT_PROC + nvt_spi_extra_proc_deinit(); +err_extra_proc_init_failed: +#endif +#if NVT_SPI_TOUCH_PROC + nvt_spi_flash_proc_deinit(); +err_flash_proc_init_failed: +#endif + return 0; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen driver probe function. + * + * return: + * Executive outcomes. 0---succeed. negative---failed + ****************************************************** + */ +static int32_t nvt_spi_probe(struct spi_device *client) +{ + int32_t ret = 0; +#if defined(CONFIG_DRM) + struct device_node *dp = NULL; +#endif +#if ((NVT_SPI_TOUCH_KEY_NUM > 0) || NVT_SPI_WAKEUP_GESTURE) + int32_t retry = 0; +#endif + struct nvt_spi_data_t *ts; + +#if defined(CONFIG_DRM) + dp = client->dev.of_node; + + ret = nvt_spi_check_dt(dp); + if (ret == -EPROBE_DEFER) + return ret; + + if (ret) { + ret = -ENODEV; + return ret; + } +#endif + + NVT_LOG("start\n"); + + ts = kzalloc(sizeof(struct nvt_spi_data_t), GFP_KERNEL); + if (ts == NULL) { + NVT_ERR("failed to allocated memory for nvt ts data\n"); + return -ENOMEM; + } + nvt_spi_data = ts; + + ts->xbuf = kzalloc(NVT_SPI_XBUF_LEN, GFP_KERNEL); + if (ts->xbuf == NULL) { + NVT_ERR("kzalloc for xbuf failed!\n"); + ret = -ENOMEM; + goto err_malloc_xbuf; + } + + ts->rbuf = kzalloc(NVT_SPI_READ_LEN, GFP_KERNEL); + if (ts->rbuf == NULL) { + NVT_ERR("kzalloc for rbuf failed!\n"); + ret = -ENOMEM; + goto err_malloc_rbuf; + } + + ts->client = client; + spi_set_drvdata(client, ts); + + //---prepare for spi parameter--- + if (ts->client->master->flags & SPI_MASTER_HALF_DUPLEX) { + NVT_ERR("Full duplex not supported by master\n"); + ret = -EIO; + goto err_ckeck_full_duplex; + } + ts->client->bits_per_word = 8; + ts->client->mode = SPI_MODE_0; + + ret = spi_setup(ts->client); + if (ret < 0) { + NVT_ERR("Failed to perform SPI setup\n"); + goto err_spi_setup; + } + + NVT_LOG("mode=%d, max_speed_hz=%d\n", ts->client->mode, ts->client->max_speed_hz); + + //---parse dts--- + ret = nvt_spi_parse_dt(&client->dev); + if (ret) { + NVT_ERR("parse dt error\n"); + goto err_spi_setup; + } + + //---request and config GPIOs--- + ret = nvt_spi_gpio_config(ts); + if (ret) { + NVT_ERR("gpio config error!\n"); + goto err_gpio_config_failed; + } + + mutex_init(&ts->lock); + mutex_init(&ts->xbuf_lock); + + //---eng reset before TP_RESX high + nvt_spi_eng_reset(); + +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 1); +#endif + + // need 10ms delay after POR(power on reset) + msleep(20); + + ts->abs_x_max = NVT_SPI_TOUCH_DEFAULT_MAX_WIDTH; + ts->abs_y_max = NVT_SPI_TOUCH_DEFAULT_MAX_HEIGHT; + + //---allocate input device--- + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) { + NVT_ERR("allocate input device failed\n"); + ret = -ENOMEM; + goto err_input_dev_alloc_failed; + } + + ts->max_touch_num = NVT_SPI_TOUCH_MAX_FINGER_NUM; + +#if NVT_SPI_TOUCH_KEY_NUM > 0 + ts->max_button_num = NVT_SPI_TOUCH_KEY_NUM; +#endif + + ts->int_trigger_type = NVT_SPI_INT_TRIGGER_TYPE; + + + //---set input device info.--- + ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + ts->input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + ts->input_dev->propbit[0] = BIT(INPUT_PROP_DIRECT); + +#if NVT_SPI_MT_PROTOCOL_B + input_mt_init_slots(ts->input_dev, ts->max_touch_num, 0); +#endif + + // pressure = NVT_SPI_TOUCH_FORCE_NUM + input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0, NVT_SPI_TOUCH_FORCE_NUM, 0, 0); + +#if NVT_SPI_TOUCH_MAX_FINGER_NUM > 1 + // area = 255 + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0, ts->abs_x_max, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0, ts->abs_y_max, 0, 0); +#if NVT_SPI_MT_PROTOCOL_B + // no need to set ABS_MT_TRACKING_ID, input_mt_init_slots() already set it +#else + input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, ts->max_touch_num, 0, 0); +#endif //NVT_SPI_MT_PROTOCOL_B +#endif //NVT_SPI_TOUCH_MAX_FINGER_NUM > 1 + +#if NVT_SPI_TOUCH_KEY_NUM > 0 + for (retry = 0; retry < ts->max_button_num; retry++) + input_set_capability(ts->input_dev, EV_KEY, nvt_spi_touch_key_array[retry]); +#endif + +#if NVT_SPI_WAKEUP_GESTURE + for (retry = 0; retry < ARRAY_SIZE(nvt_spi_gesture_key_array); retry++) + input_set_capability(ts->input_dev, EV_KEY, nvt_spi_gesture_key_array[retry]); +#endif + + snprintf(ts->phys, sizeof(ts->phys), "input/ts"); + ts->input_dev->name = NVT_SPI_TS_NAME; + ts->input_dev->phys = ts->phys; + ts->input_dev->id.bustype = BUS_SPI; + + //---register input device--- + ret = input_register_device(ts->input_dev); + if (ret) { + NVT_ERR("register input device (%s) failed. ret=%d\n", ts->input_dev->name, ret); + goto err_input_register_device_failed; + } + + if (ts->pen_support) { + //---allocate pen input device--- + ts->pen_input_dev = input_allocate_device(); + if (ts->pen_input_dev == NULL) { + NVT_ERR("allocate pen input device failed\n"); + ret = -ENOMEM; + goto err_pen_input_dev_alloc_failed; + } + + //---set pen input device info.--- + ts->pen_input_dev->evbit[0] = + BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + ts->pen_input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + ts->pen_input_dev->keybit[BIT_WORD(BTN_TOOL_PEN)] |= BIT_MASK(BTN_TOOL_PEN); + //ts->pen_input_dev->keybit[BIT_WORD(BTN_TOOL_RUBBER)] + // |= BIT_MASK(BTN_TOOL_RUBBER); + ts->pen_input_dev->keybit[BIT_WORD(BTN_STYLUS)] |= BIT_MASK(BTN_STYLUS); + ts->pen_input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2); + ts->pen_input_dev->propbit[0] = BIT(INPUT_PROP_DIRECT); + + if (ts->wgp_stylus) { + input_set_abs_params(ts->pen_input_dev, ABS_X, 0, + ts->abs_x_max * 2, 0, 0); + input_set_abs_params(ts->pen_input_dev, ABS_Y, 0, + ts->abs_y_max * 2, 0, 0); + } else { + input_set_abs_params(ts->pen_input_dev, ABS_X, 0, + ts->abs_x_max, 0, 0); + + input_set_abs_params(ts->pen_input_dev, ABS_Y, 0, + ts->abs_y_max, 0, 0); + } + input_set_abs_params(ts->pen_input_dev, ABS_PRESSURE, 0, + NVT_SPI_PEN_PRESSURE_MAX, 0, 0); + input_set_abs_params(ts->pen_input_dev, ABS_DISTANCE, 0, + NVT_SPI_PEN_DISTANCE_MAX, 0, 0); + input_set_abs_params(ts->pen_input_dev, ABS_TILT_X, + NVT_SPI_PEN_TILT_MIN, NVT_SPI_PEN_TILT_MAX, 0, 0); + input_set_abs_params(ts->pen_input_dev, ABS_TILT_Y, + NVT_SPI_PEN_TILT_MIN, NVT_SPI_PEN_TILT_MAX, 0, 0); + + snprintf(ts->pen_phys, sizeof(ts->pen_phys), "input/pen"); + ts->pen_input_dev->name = NVT_SPI_PEN_NAME; + ts->pen_input_dev->phys = ts->pen_phys; + ts->pen_input_dev->id.bustype = BUS_SPI; + + //---register pen input device--- + ret = input_register_device(ts->pen_input_dev); + if (ret) { + NVT_ERR("register pen input device (%s) failed. ret=%d\n", + ts->pen_input_dev->name, ret); + goto err_pen_input_register_device_failed; + } + } /* if (ts->pen_support) */ + + //---set int-pin & request irq--- + client->irq = gpio_to_irq(ts->irq_gpio); + if (client->irq) { + NVT_LOG("int_trigger_type=%d\n", ts->int_trigger_type); + ts->irq_enabled = true; + ret = request_threaded_irq(client->irq, NULL, nvt_spi_work_func, + ts->int_trigger_type | IRQF_ONESHOT, NVT_SPI_NAME, ts); + if (ret != 0) { + NVT_ERR("request irq failed. ret=%d\n", ret); + goto err_int_request_failed; + } else { + nvt_spi_irq_enable(false); + NVT_LOG("request irq %d succeed\n", client->irq); + } + } + +#if NVT_SPI_WAKEUP_GESTURE + device_init_wakeup(&ts->input_dev->dev, 1); +#endif + +#if NVT_SPI_BOOT_UPDATE_FIRMWARE + nvt_spi_fwu_wq = alloc_workqueue("nvt_spi_fwu_wq", WQ_UNBOUND | WQ_MEM_RECLAIM, 1); + if (!nvt_spi_fwu_wq) { + NVT_ERR("nvt_spi_fwu_wq create workqueue failed\n"); + ret = -ENOMEM; + goto err_create_nvt_spi_fwu_wq_failed; + } + INIT_DELAYED_WORK(&ts->nvt_fwu_work, nvt_spi_update_firmware_work); + + // please make sure boot update start after display reset(RESX) sequence + //queue_delayed_work(nvt_spi_fwu_wq, &ts->nvt_fwu_work, msecs_to_jiffies(14000)); +#endif + + NVT_LOG("NVT_SPI_TOUCH_ESD_PROTECT is %d\n", NVT_SPI_TOUCH_ESD_PROTECT); +#if NVT_SPI_TOUCH_ESD_PROTECT + INIT_DELAYED_WORK(&nvt_spi_esd_check_work, nvt_spi_esd_check_func); + nvt_spi_esd_check_wq = alloc_workqueue("nvt_spi_esd_check_wq", WQ_MEM_RECLAIM, 1); + if (!nvt_spi_esd_check_wq) { + NVT_ERR("nvt_spi_esd_check_wq create workqueue failed\n"); + ret = -ENOMEM; + goto err_create_nvt_spi_esd_check_wq_failed; + } + queue_delayed_work(nvt_spi_esd_check_wq, &nvt_spi_esd_check_work, + msecs_to_jiffies(NVT_SPI_TOUCH_ESD_CHECK_PERIOD)); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + +#if defined(CONFIG_DRM) + //if (!strcmp(ts->touch_environment, "pvm")) + nvt_spi_register_for_panel_events(client->dev.of_node, ts); +#elif defined(_MSM_DRM_NOTIFY_H_) + ts->drm_notif.notifier_call = nvt_drm_notifier_callback; + ret = msm_drm_register_client(&ts->drm_notif); + if (ret) { + NVT_ERR("register drm_notifier failed. ret=%d\n", ret); + goto err_register_drm_notif_failed; + } +#else + ts->fb_notif.notifier_call = nvt_fb_notifier_callback; + ret = fb_register_client(&ts->fb_notif); + if (ret) { + NVT_ERR("register fb_notifier failed. ret=%d\n", ret); + goto err_register_fb_notif_failed; + } +#endif + + NVT_LOG("end\n"); + return 0; + +#if defined(CONFIG_DRM) + +#elif defined(_MSM_DRM_NOTIFY_H_) +err_register_drm_notif_failed: +#else +err_register_fb_notif_failed: +#endif + +#if NVT_SPI_TOUCH_MP + nvt_spi_mp_proc_deinit(); +#endif +#if NVT_SPI_TOUCH_EXT_PROC + nvt_spi_extra_proc_deinit(); + +#endif +#if NVT_SPI_TOUCH_PROC + nvt_spi_flash_proc_deinit(); + +#endif +#if NVT_SPI_TOUCH_ESD_PROTECT + if (nvt_spi_esd_check_wq) { + cancel_delayed_work_sync(&nvt_spi_esd_check_work); + destroy_workqueue(nvt_spi_esd_check_wq); + nvt_spi_esd_check_wq = NULL; + } +err_create_nvt_spi_esd_check_wq_failed: +#endif +#if NVT_SPI_BOOT_UPDATE_FIRMWARE + if (nvt_spi_fwu_wq) { + cancel_delayed_work_sync(&ts->nvt_fwu_work); + destroy_workqueue(nvt_spi_fwu_wq); + nvt_spi_fwu_wq = NULL; + } +err_create_nvt_spi_fwu_wq_failed: +#endif +#if NVT_SPI_WAKEUP_GESTURE + device_init_wakeup(&ts->input_dev->dev, 0); +#endif + free_irq(client->irq, ts); +err_int_request_failed: + if (ts->pen_support) { + input_unregister_device(ts->pen_input_dev); + ts->pen_input_dev = NULL; + } +err_pen_input_register_device_failed: + if (ts->pen_support) { + if (ts->pen_input_dev) { + input_free_device(ts->pen_input_dev); + ts->pen_input_dev = NULL; + } + } +err_pen_input_dev_alloc_failed: + input_unregister_device(ts->input_dev); + ts->input_dev = NULL; +err_input_register_device_failed: + if (ts->input_dev) { + input_free_device(ts->input_dev); + ts->input_dev = NULL; + } +err_input_dev_alloc_failed: + mutex_destroy(&ts->xbuf_lock); + mutex_destroy(&ts->lock); + nvt_spi_gpio_deconfig(ts); +err_gpio_config_failed: +err_spi_setup: +err_ckeck_full_duplex: + spi_set_drvdata(client, NULL); + kfree(ts->rbuf); + ts->rbuf = NULL; +err_malloc_rbuf: + kfree(ts->xbuf); + ts->xbuf = NULL; +err_malloc_xbuf: + kfree(ts); + ts = NULL; + + return ret; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen driver release function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t nvt_spi_remove(struct spi_device *client) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + NVT_LOG("Removing driver...\n"); + +#if defined(CONFIG_DRM) + if (active_spi_panel && ts->notifier_cookie) + panel_event_notifier_unregister(ts->notifier_cookie); +#elif defined(_MSM_DRM_NOTIFY_H_) + if (msm_drm_unregister_client(&ts->drm_notif)) + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); +#else + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier.\n"); +#endif + +#if NVT_SPI_TOUCH_MP + nvt_spi_mp_proc_deinit(); +#endif +#if NVT_SPI_TOUCH_EXT_PROC + nvt_spi_extra_proc_deinit(); +#endif +#if NVT_SPI_TOUCH_PROC + nvt_spi_flash_proc_deinit(); +#endif + +#if NVT_SPI_TOUCH_ESD_PROTECT + if (nvt_spi_esd_check_wq) { + cancel_delayed_work_sync(&nvt_spi_esd_check_work); + nvt_spi_esd_check_enable(false); + destroy_workqueue(nvt_spi_esd_check_wq); + nvt_spi_esd_check_wq = NULL; + } +#endif + +#if NVT_SPI_BOOT_UPDATE_FIRMWARE + if (nvt_spi_fwu_wq) { + cancel_delayed_work_sync(&ts->nvt_fwu_work); + destroy_workqueue(nvt_spi_fwu_wq); + nvt_spi_fwu_wq = NULL; + } +#endif + +#if NVT_SPI_WAKEUP_GESTURE + device_init_wakeup(&ts->input_dev->dev, 0); +#endif + + nvt_spi_irq_enable(false); + free_irq(client->irq, ts); + + mutex_destroy(&ts->xbuf_lock); + mutex_destroy(&ts->lock); + + nvt_spi_gpio_deconfig(ts); + + if (ts->pen_support) { + if (ts->pen_input_dev) { + input_unregister_device(ts->pen_input_dev); + ts->pen_input_dev = NULL; + } + } + + if (ts->input_dev) { + input_unregister_device(ts->input_dev); + ts->input_dev = NULL; + } + + spi_set_drvdata(client, NULL); + + kfree(ts->xbuf); + ts->xbuf = NULL; + + kfree(ts); + ts = NULL; + + return 0; +} + +static void nvt_spi_shutdown(struct spi_device *client) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + NVT_LOG("Shutdown driver...\n"); + + nvt_spi_irq_enable(false); + +#if defined(CONFIG_DRM) + if (active_spi_panel && ts->notifier_cookie) + panel_event_notifier_unregister(ts->notifier_cookie); +#elif defined(_MSM_DRM_NOTIFY_H_) + if (msm_drm_unregister_client(&ts->drm_notif)) + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); +#else + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier.\n"); +#endif + +#if NVT_SPI_TOUCH_MP + nvt_spi_mp_proc_deinit(); +#endif +#if NVT_SPI_TOUCH_EXT_PROC + nvt_spi_extra_proc_deinit(); +#endif +#if NVT_SPI_TOUCH_PROC + nvt_spi_flash_proc_deinit(); +#endif + +#if NVT_SPI_TOUCH_ESD_PROTECT + if (nvt_spi_esd_check_wq) { + cancel_delayed_work_sync(&nvt_spi_esd_check_work); + nvt_spi_esd_check_enable(false); + destroy_workqueue(nvt_spi_esd_check_wq); + nvt_spi_esd_check_wq = NULL; + } +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + +#if NVT_SPI_BOOT_UPDATE_FIRMWARE + if (nvt_spi_fwu_wq) { + cancel_delayed_work_sync(&ts->nvt_fwu_work); + destroy_workqueue(nvt_spi_fwu_wq); + nvt_spi_fwu_wq = NULL; + } +#endif + +#if NVT_SPI_WAKEUP_GESTURE + device_init_wakeup(&ts->input_dev->dev, 0); +#endif +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen driver suspend function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t nvt_spi_suspend(struct device *dev) +{ + uint8_t buf[4] = {0}; +#if NVT_SPI_MT_PROTOCOL_B + uint32_t i = 0; +#endif + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (!bTouchIsAwake) { + NVT_LOG("Touch is already suspend\n"); + return 0; + } + +#if !NVT_SPI_WAKEUP_GESTURE + nvt_spi_irq_enable(false); +#endif + +#if NVT_SPI_TOUCH_ESD_PROTECT + NVT_LOG("cancel delayed work sync\n"); + cancel_delayed_work_sync(&nvt_spi_esd_check_work); + nvt_spi_esd_check_enable(false); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + mutex_lock(&ts->lock); + + NVT_LOG("start\n"); + + bTouchIsAwake = 0; + +#if NVT_SPI_WAKEUP_GESTURE + //---write command to enter "wakeup gesture mode"--- + buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD; + buf[1] = 0x13; + nvt_spi_write(buf, 2); + + enable_irq_wake(ts->client->irq); + + NVT_LOG("Enabled touch wakeup gesture\n"); + +#else // NVT_SPI_WAKEUP_GESTURE + //---write command to enter "deep sleep mode"--- + buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD; + buf[1] = 0x11; + nvt_spi_write(buf, 2); +#endif // NVT_SPI_WAKEUP_GESTURE + + mutex_unlock(&ts->lock); + + /* release all touches */ +#if NVT_SPI_MT_PROTOCOL_B + for (i = 0; i < ts->max_touch_num; i++) { + input_mt_slot(ts->input_dev, i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); + } +#endif + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#if !NVT_SPI_MT_PROTOCOL_B + input_mt_sync(ts->input_dev); +#endif + input_sync(ts->input_dev); + + /* release pen event */ + if (ts->pen_support) { + input_report_abs(ts->pen_input_dev, ABS_X, 0); + input_report_abs(ts->pen_input_dev, ABS_Y, 0); + input_report_abs(ts->pen_input_dev, ABS_PRESSURE, 0); + input_report_abs(ts->pen_input_dev, ABS_TILT_X, 0); + input_report_abs(ts->pen_input_dev, ABS_TILT_Y, 0); + input_report_abs(ts->pen_input_dev, ABS_DISTANCE, 0); + input_report_key(ts->pen_input_dev, BTN_TOUCH, 0); + input_report_key(ts->pen_input_dev, BTN_TOOL_PEN, 0); + input_report_key(ts->pen_input_dev, BTN_STYLUS, 0); + input_report_key(ts->pen_input_dev, BTN_STYLUS2, 0); + input_sync(ts->pen_input_dev); + } + + msleep(50); + + NVT_LOG("end\n"); + + return 0; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen driver resume function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t nvt_spi_resume(struct device *dev) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (bTouchIsAwake || ts->fw_ver == 0) { + nvt_spi_late_probe(ts->client); + NVT_LOG("nvt_spi_late_probe\n"); + return 0; + } + + mutex_lock(&ts->lock); + + NVT_LOG("start\n"); + + // please make sure display reset(RESX) sequence and mipi dsi cmds sent before this +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 1); +#endif + + if (nvt_spi_update_firmware(NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME)) + NVT_ERR("download firmware failed, ignore check fw state\n"); + else + nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_REK); + +#if !NVT_SPI_WAKEUP_GESTURE + nvt_spi_irq_enable(true); +#endif + +#if NVT_SPI_TOUCH_ESD_PROTECT + nvt_spi_esd_check_enable(false); + queue_delayed_work(nvt_spi_esd_check_wq, &nvt_spi_esd_check_work, + msecs_to_jiffies(NVT_SPI_TOUCH_ESD_CHECK_PERIOD)); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + bTouchIsAwake = 1; + + mutex_unlock(&ts->lock); + + NVT_LOG("end\n"); + + return 0; +} + +#if defined(CONFIG_DRM) + +static void nvt_spi_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data) +{ + struct nvt_spi_data_t *ts = client_data; + + if (!notification) { + pr_err("Invalid notification\n"); + return; + } + + NVT_LOG("Notification type:%d, early_trigger:%d", + notification->notif_type, + notification->notif_data.early_trigger); + + switch (notification->notif_type) { + case DRM_PANEL_EVENT_UNBLANK: + if (notification->notif_data.early_trigger) + NVT_LOG("resume notification pre commit\n"); + else + nvt_spi_resume(&ts->client->dev); + break; + + case DRM_PANEL_EVENT_BLANK: + if (notification->notif_data.early_trigger) + nvt_spi_suspend(&ts->client->dev); + else + NVT_LOG("suspend notification post commit\n"); + break; + + case DRM_PANEL_EVENT_BLANK_LP: + NVT_LOG("received lp event\n"); + break; + + case DRM_PANEL_EVENT_FPS_CHANGE: + NVT_LOG("shashank:Received fps change old fps:%d new fps:%d\n", + notification->notif_data.old_fps, + notification->notif_data.new_fps); + break; + default: + NVT_LOG("notification serviced :%d\n", + notification->notif_type); + break; + } +} + +#elif defined(_MSM_DRM_NOTIFY_H_) +static int nvt_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank; + struct nvt_spi_data_t *ts = + container_of(self, struct nvt_spi_data_t, drm_notif); + + if (!evdata || (evdata->id != 0)) + return 0; + + if (evdata->data && ts) { + blank = evdata->data; + if (event == MSM_DRM_EARLY_EVENT_BLANK) { + if (*blank == MSM_DRM_BLANK_POWERDOWN) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_spi_suspend(&ts->client->dev); + } + } else if (event == MSM_DRM_EVENT_BLANK) { + if (*blank == MSM_DRM_BLANK_UNBLANK) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_ts_resume(&ts->client->dev); + } + } + } + + return 0; +} +#else +static int nvt_fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct nvt_spi_data_t *ts = + container_of(self, struct nvt_spi_data_t, fb_notif); + + if (evdata && evdata->data && event == FB_EARLY_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_POWERDOWN) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_spi_suspend(&ts->client->dev); + } + } else if (evdata && evdata->data && event == FB_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + nvt_ts_resume(&ts->client->dev); + } + } + + return 0; +} +#endif + +static const struct spi_device_id nvt_spi_id[] = { + { NVT_SPI_NAME, 0 }, + { } +}; + +#ifdef CONFIG_OF +static const struct of_device_id nvt_spi_match_table[] = { + { .compatible = "novatek,NVT-ts",}, + { }, +}; +#endif + +static struct spi_driver nvt_spi_driver = { + .probe = nvt_spi_probe, + .remove = nvt_spi_remove, + .shutdown = nvt_spi_shutdown, + .id_table = nvt_spi_id, + .driver = { + .name = NVT_SPI_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = nvt_spi_match_table, +#endif + }, +}; + +/* + ****************************************************** + * Description: + * Driver Install function. + * + * return: + * Executive Outcomes. 0---succeed. not 0---failed. + ******************************************************* + */ +static int32_t __init nvt_spi_driver_init(void) +{ + int32_t ret = 0; + + NVT_LOG("start\n"); + + //---add spi driver--- + ret = spi_register_driver(&nvt_spi_driver); + if (ret) { + NVT_ERR("failed to add spi driver"); + goto err_driver; + } + + NVT_LOG("finished\n"); + +err_driver: + return ret; +} + +/* + ****************************************************** + * Description: + * Driver uninstall function. + * + * return: + * n.a. + ******************************************************* + */ +static void __exit nvt_spi_driver_exit(void) +{ + spi_unregister_driver(&nvt_spi_driver); +} + +module_init(nvt_spi_driver_init); +module_exit(nvt_spi_driver_exit); + +MODULE_DESCRIPTION("Novatek Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); + +#endif diff --git a/nt36xxx/nt36xxx.h b/nt36xxx/nt36xxx.h new file mode 100644 index 0000000000..1a298efb6a --- /dev/null +++ b/nt36xxx/nt36xxx.h @@ -0,0 +1,386 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 47247 $ + * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $ + * + * 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. + * + */ +#ifndef _LINUX_NVT_TOUCH_H +#define _LINUX_NVT_TOUCH_H + +#if !defined(NVT_NT36XXX_SPI) /* NT36XXX I2C */ + +#include +#include +#include +#include + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +#include "nt36xxx_mem_map.h" + +#define NVT_DEBUG 1 + +//---GPIO number--- +#define NVTTOUCH_RST_PIN 980 +#define NVTTOUCH_INT_PIN 943 + + +//---INT trigger mode--- +//#define IRQ_TYPE_EDGE_RISING 1 +//#define IRQ_TYPE_EDGE_FALLING 2 +#define INT_TRIGGER_TYPE IRQ_TYPE_EDGE_RISING + + +//---I2C driver info.--- +#define NVT_I2C_NAME "NVT-ts" +#define I2C_BLDR_Address 0x01 +#define I2C_FW_Address 0x01 +#define I2C_HW_Address 0x62 + +#if NVT_DEBUG +#define NVT_LOG(fmt, args...) pr_err("[%s] %s %d: " fmt, NVT_I2C_NAME, __func__, __LINE__, ##args) +#else +#define NVT_LOG(fmt, args...) pr_info("[%s] %s %d: " fmt, NVT_I2C_NAME, __func__, __LINE__, ##args) +#endif +#define NVT_ERR(fmt, args...) pr_err("[%s] %s %d: " fmt, NVT_I2C_NAME, __func__, __LINE__, ##args) + +//---Input device info.--- +#define NVT_TS_NAME "NVTCapacitiveTouchScreen" + + +//---Touch info.--- +#define TOUCH_DEFAULT_MAX_WIDTH 1080 +#define TOUCH_DEFAULT_MAX_HEIGHT 2408 +#define TOUCH_MAX_FINGER_NUM 10 +#define TOUCH_KEY_NUM 0 +#if TOUCH_KEY_NUM > 0 +extern const uint16_t touch_key_array[TOUCH_KEY_NUM]; +#endif +#define TOUCH_FORCE_NUM 1000 + +/* Enable only when module have tp reset pin and connected to host */ +#define NVT_TOUCH_SUPPORT_HW_RST 1 + +//---Customerized func.--- +#define NVT_TOUCH_PROC 1 +#define NVT_TOUCH_EXT_PROC 1 +#define NVT_TOUCH_MP 1 +#define MT_PROTOCOL_B 1 +#define WAKEUP_GESTURE 0 +#if WAKEUP_GESTURE +extern const uint16_t gesture_key_array[]; +#endif +#define BOOT_UPDATE_FIRMWARE 1 +#define BOOT_UPDATE_FIRMWARE_NAME "novatek_ts_fw.bin" + +//---ESD Protect.--- +#define NVT_TOUCH_ESD_PROTECT 0 +#define NVT_TOUCH_ESD_CHECK_PERIOD 1500 /* ms */ + +struct nvt_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct delayed_work nvt_fwu_work; + uint16_t addr; + int8_t phys[32]; + const struct i2c_device_id *id; +#if defined(CONFIG_DRM) + struct notifier_block drm_panel_notif; +#elif defined(_MSM_DRM_NOTIFY_H_) + struct notifier_block drm_notif; +#else + struct notifier_block fb_notif; + +#endif + uint8_t fw_ver; + uint8_t x_num; + uint8_t y_num; + uint16_t abs_x_max; + uint16_t abs_y_max; + uint8_t max_touch_num; + uint8_t max_button_num; + uint32_t int_trigger_type; + int32_t irq_gpio; + uint32_t irq_flags; + int32_t reset_gpio; + uint32_t reset_flags; + struct mutex lock; + const struct nvt_ts_mem_map *mmap; + uint8_t carrier_system; + uint16_t nvt_pid; + uint8_t xbuf[1025]; + struct mutex xbuf_lock; + bool irq_enabled; + void *notifier_cookie; +}; + +#if NVT_TOUCH_PROC +struct nvt_flash_data{ + rwlock_t lock; + struct i2c_client *client; +}; +#endif + +typedef enum { + RESET_STATE_INIT = 0xA0,// IC reset + RESET_STATE_REK, // ReK baseline + RESET_STATE_REK_FINISH, // baseline is ready + RESET_STATE_NORMAL_RUN, // normal run + RESET_STATE_MAX = 0xAF +} RST_COMPLETE_STATE; + +typedef enum { + EVENT_MAP_HOST_CMD = 0x50, + EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE = 0x51, + EVENT_MAP_RESET_COMPLETE = 0x60, + EVENT_MAP_FWINFO = 0x78, + EVENT_MAP_PROJECTID = 0x9A, +} I2C_EVENT_MAP; + +//---extern structures--- +extern struct nvt_ts_data *ts; + +//---extern functions--- +extern int32_t CTP_I2C_READ(struct i2c_client *client, uint16_t address, uint8_t *buf, uint16_t len); +extern int32_t CTP_I2C_WRITE(struct i2c_client *client, uint16_t address, uint8_t *buf, uint16_t len); +extern void nvt_bootloader_reset(void); +extern void nvt_sw_reset_idle(void); +extern int32_t nvt_check_fw_reset_state(RST_COMPLETE_STATE check_reset_state); +extern int32_t nvt_get_fw_info(void); +extern int32_t nvt_clear_fw_status(void); +extern int32_t nvt_check_fw_status(void); +extern int32_t nvt_set_page(uint16_t i2c_addr, uint32_t addr); +#if NVT_TOUCH_ESD_PROTECT +extern void nvt_esd_check_enable(uint8_t enable); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ +extern void nvt_stop_crc_reboot(void); + +#else /* NT36XXX_SPI */ + +#include +#include +#include +#include +#include + +#include "nt36xxx_mem_map.h" + +#define NVT_SPI_DEBUG 0 + +//---GPIO number--- +#define NVTTOUCH_RST_PIN 980 +#define NVTTOUCH_INT_PIN 943 + +//---INT trigger mode--- +//#define NVT_SPI_IRQ_TYPE_EDGE_RISING 1 +//#define NVT_SPI_IRQ_TYPE_EDGE_FALLING 2 +#define NVT_SPI_INT_TRIGGER_TYPE IRQ_TYPE_EDGE_RISING + +//---SPI driver info.--- +#define NVT_SPI_NAME "NVT-SPI" + +#if NVT_SPI_DEBUG +#define NVT_LOG(fmt, args...) pr_err("[%s] %s %d: " fmt, "NVT-SPI", __func__, __LINE__, ##args) +#else +#define NVT_LOG(fmt, args...) pr_debug("[%s] %s %d: " fmt, "NVT-SPI", __func__, __LINE__, ##args) +#endif +#define NVT_ERR(fmt, args...) pr_err("[%s] %s %d: " fmt, "NVT-SPI", __func__, __LINE__, ##args) + +//---Input device info.--- +#define NVT_SPI_TS_NAME "NVTCapacitiveTouchScreen" +#define NVT_SPI_PEN_NAME "NVTCapacitivePen" + +//---Touch info.--- +#define NVT_SPI_TOUCH_DEFAULT_MAX_WIDTH 1080 +#define NVT_SPI_TOUCH_DEFAULT_MAX_HEIGHT 2400 +#define NVT_SPI_TOUCH_MAX_FINGER_NUM 10 +#define NVT_SPI_TOUCH_KEY_NUM 0 +#if NVT_SPI_TOUCH_KEY_NUM > 0 +extern const uint16_t nvt_spi_touch_key_array[NVT_SPI_TOUCH_KEY_NUM]; +#endif +#define NVT_SPI_TOUCH_FORCE_NUM 1000 +//---for Pen--- +#define NVT_SPI_PEN_PRESSURE_MAX (4095) +#define NVT_SPI_PEN_DISTANCE_MAX (1) +#define NVT_SPI_PEN_TILT_MIN (-60) +#define NVT_SPI_PEN_TILT_MAX (60) + +/* Enable only when module have tp reset pin and connected to host */ +#define NVT_SPI_TOUCH_SUPPORT_HW_RST 0 + +//---Customerized func.--- +#define NVT_SPI_TOUCH_PROC 1 +#define NVT_SPI_TOUCH_EXT_PROC 1 +#define NVT_SPI_TOUCH_MP 0 +#define NVT_SPI_MT_PROTOCOL_B 1 +#define NVT_SPI_WAKEUP_GESTURE 0 + +#if NVT_SPI_WAKEUP_GESTURE +extern const uint16_t nvt_spi_gesture_key_array[]; +#endif +#define NVT_SPI_BOOT_UPDATE_FIRMWARE 1 +#define NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME "novatek_spi_fw.bin" +#define NVT_SPI_MP_UPDATE_FIRMWARE_NAME "novatek_ts_mp.bin" +#define NVT_SPI_POINT_DATA_CHECKSUM 1 +#define NVT_SPI_POINT_DATA_CHECKSUM_LEN 65 + +//---ESD Protect.--- +#define NVT_SPI_TOUCH_ESD_PROTECT 0 +#define NVT_SPI_TOUCH_ESD_CHECK_PERIOD 1500 /* ms */ +#define NVT_SPI_TOUCH_WDT_RECOVERY 1 + +#define NVT_SPI_CHECK_PEN_DATA_CHECKSUM 0 + +struct nvt_spi_data_t { + struct spi_device *client; + struct input_dev *input_dev; + struct delayed_work nvt_fwu_work; + uint16_t addr; + int8_t phys[32]; + +#if defined(CONFIG_DRM) + struct notifier_block drm_panel_notif; +#elif defined(_MSM_DRM_NOTIFY_H_) + struct notifier_block drm_notif; +#else + struct notifier_block fb_notif; +#endif + + uint8_t fw_ver; + uint8_t x_num; + uint8_t y_num; + uint16_t abs_x_max; + uint16_t abs_y_max; + uint8_t max_touch_num; + uint8_t max_button_num; + uint32_t int_trigger_type; + int32_t irq_gpio; + uint32_t irq_flags; + int32_t reset_gpio; + uint32_t reset_flags; + struct mutex lock; + const struct nvt_spi_mem_map *mmap; + uint8_t hw_crc; + uint16_t nvt_pid; + uint8_t *rbuf; + uint8_t *xbuf; + struct mutex xbuf_lock; + bool irq_enabled; + bool pen_support; + bool wgp_stylus; + uint8_t x_gang_num; + uint8_t y_gang_num; + struct input_dev *pen_input_dev; + int8_t pen_phys[32]; + + void *notifier_cookie; + const char *touch_environment; + +#ifdef CONFIG_NOVATEK_SPI_TRUSTED_TOUCH + struct trusted_touch_vm_info *vm_info; + struct mutex fts_clk_io_ctrl_mutex; + struct completion trusted_touch_powerdown; + struct clk *core_clk; + struct clk *iface_clk; + atomic_t trusted_touch_initialized; + atomic_t trusted_touch_enabled; + atomic_t trusted_touch_underway; + atomic_t trusted_touch_event; + atomic_t trusted_touch_abort_status; + atomic_t delayed_vm_probe_pending; + atomic_t trusted_touch_mode; +#endif +}; + +#if NVT_SPI_TOUCH_PROC +struct nvt_spi_flash_data { + rwlock_t lock; +}; +#endif + +enum NVT_SPI_RST_COMPLETE_STATE { + NVT_SPI_RESET_STATE_INIT = 0xA0,// IC reset + NVT_SPI_RESET_STATE_REK, // ReK baseline + NVT_SPI_RESET_STATE_REK_FINISH, // baseline is ready + NVT_SPI_RESET_STATE_NORMAL_RUN, // normal run + NVT_SPI_RESET_STATE_MAX = 0xAF +}; + +enum NVT_SPI_EVENT_MAP { + NVT_SPI_EVENT_MAP_HOST_CMD = 0x50, + NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE = 0x51, + NVT_SPI_EVENT_MAP_RESET_COMPLETE = 0x60, + NVT_SPI_EVENT_MAP_FWINFO = 0x78, + NVT_SPI_EVENT_MAP_PROJECTID = 0x9A, +}; + +//---SPI READ/WRITE--- +#define NVT_SPI_WRITE_MASK(a) (a | 0x80) +#define NVT_SPI_READ_MASK(a) (a & 0x7F) + +#define NVT_SPI_DUMMY_BYTES (1) +#define NVT_SPI_TRANSFER_LEN (63*1024) +#define NVT_SPI_READ_LEN (2*1024) +#define NVT_SPI_XBUF_LEN (NVT_SPI_TRANSFER_LEN+1+NVT_SPI_DUMMY_BYTES) + +enum NVT_SPI_RW { + NVT_SPI_WRITE = 0, + NVT_SPI_READ = 1 +}; + +//---extern structures--- +extern struct nvt_spi_data_t *nvt_spi_data; + +//---extern functions--- +int32_t nvt_spi_read(uint8_t *buf, uint16_t len); +int32_t nvt_spi_write(uint8_t *buf, uint16_t len); +void nvt_spi_bootloader_reset(void); +void nvt_spi_eng_reset(void); +void nvt_spi_sw_reset(void); +void nvt_spi_sw_reset_idle(void); +void nvt_spi_boot_ready(void); +void nvt_spi_bld_crc_enable(void); +void nvt_spi_fw_crc_enable(void); +void nvt_spi_tx_auto_copy_mode(void); +int32_t nvt_spi_update_firmware(char *firmware_name); +void nvt_spi_update_firmware_work(struct work_struct *work); +int32_t nvt_spi_check_fw_reset_state(enum NVT_SPI_RST_COMPLETE_STATE reset_state); +int32_t nvt_spi_get_fw_info(void); +int32_t nvt_spi_clear_fw_status(void); +int32_t nvt_spi_check_fw_status(void); +int32_t nvt_spi_check_spi_dma_tx_info(void); +int32_t nvt_spi_set_page(uint32_t addr); +int32_t nvt_spi_write_addr(uint32_t addr, uint8_t data); + +#if NVT_SPI_TOUCH_EXT_PROC +int32_t nvt_spi_extra_proc_init(void); +void nvt_spi_extra_proc_deinit(void); +#endif + +#if NVT_SPI_TOUCH_MP +extern int32_t nvt_spi_mp_proc_init(void); +extern void nvt_spi_mp_proc_deinit(void); +#endif + +#if NVT_SPI_TOUCH_ESD_PROTECT +extern void nvt_spi_esd_check_enable(uint8_t enable); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + +#endif + +#endif /* _LINUX_NVT_TOUCH_H */ diff --git a/nt36xxx/nt36xxx_ext_proc.c b/nt36xxx/nt36xxx_ext_proc.c new file mode 100644 index 0000000000..d08d8b3162 --- /dev/null +++ b/nt36xxx/nt36xxx_ext_proc.c @@ -0,0 +1,1537 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 47247 $ + * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $ + * + * 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. + * + */ + +#if !defined(NVT_NT36XXX_SPI) /* TOUCHSCREEN_NT36XXX I2C */ + +#include +#include + +#include "nt36xxx.h" + +#if NVT_TOUCH_EXT_PROC +#define NVT_FW_VERSION "nvt_fw_version" +#define NVT_BASELINE "nvt_baseline" +#define NVT_RAW "nvt_raw" +#define NVT_DIFF "nvt_diff" + +#define BUS_TRANSFER_LENGTH 64 + +#define NORMAL_MODE 0x00 +#define TEST_MODE_1 0x21 +#define TEST_MODE_2 0x22 +#define HANDSHAKING_HOST_READY 0xBB + +#define XDATA_SECTOR_SIZE 256 + +static uint8_t xdata_tmp[2048] = {0}; +static int32_t xdata[2048] = {0}; + +static struct proc_dir_entry *NVT_proc_fw_version_entry; +static struct proc_dir_entry *NVT_proc_baseline_entry; +static struct proc_dir_entry *NVT_proc_raw_entry; +static struct proc_dir_entry *NVT_proc_diff_entry; + +/******************************************************* +Description: + Novatek touchscreen change mode function. + +return: + n.a. +*******************************************************/ +void nvt_change_mode(uint8_t mode) +{ + uint8_t buf[8] = {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + + //---set mode--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = mode; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); + + if (mode == NORMAL_MODE) { + buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = HANDSHAKING_HOST_READY; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); + msleep(20); + } +} + +/******************************************************* +Description: + Novatek touchscreen get firmware pipe function. + +return: + Executive outcomes. 0---pipe 0. 1---pipe 1. +*******************************************************/ +uint8_t nvt_get_fw_pipe(void) +{ + uint8_t buf[8]= {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE); + + //---read fw status--- + buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0x00; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2); + + //NVT_LOG("FW pipe=%d, buf[1]=0x%02X\n", (buf[1]&0x01), buf[1]); + + return (buf[1] & 0x01); +} + +/******************************************************* +Description: + Novatek touchscreen read meta data function. + +return: + n.a. +*******************************************************/ +void nvt_read_mdata(uint32_t xdata_addr, uint32_t xdata_btn_addr) +{ + int32_t i = 0; + int32_t j = 0; + int32_t k = 0; + uint8_t buf[BUS_TRANSFER_LENGTH + 1] = {0}; + uint32_t head_addr = 0; + int32_t dummy_len = 0; + int32_t data_len = 0; + int32_t residual_len = 0; + + //---set xdata sector address & length--- + head_addr = xdata_addr - (xdata_addr % XDATA_SECTOR_SIZE); + dummy_len = xdata_addr - head_addr; + data_len = ts->x_num * ts->y_num * 2; + residual_len = (head_addr + dummy_len + data_len) % XDATA_SECTOR_SIZE; + + //printk("head_addr=0x%05X, dummy_len=0x%05X, data_len=0x%05X, residual_len=0x%05X\n", head_addr, dummy_len, data_len, residual_len); + + //read xdata : step 1 + for (i = 0; i < ((dummy_len + data_len) / XDATA_SECTOR_SIZE); i++) { + //---change xdata index--- + nvt_set_page(I2C_FW_Address, head_addr + XDATA_SECTOR_SIZE * i); + + //---read xdata by BUS_TRANSFER_LENGTH + for (j = 0; j < (XDATA_SECTOR_SIZE / BUS_TRANSFER_LENGTH); j++) { + //---read data--- + buf[0] = BUS_TRANSFER_LENGTH * j; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, BUS_TRANSFER_LENGTH + 1); + + //---copy buf to xdata_tmp--- + for (k = 0; k < BUS_TRANSFER_LENGTH; k++) { + xdata_tmp[XDATA_SECTOR_SIZE * i + BUS_TRANSFER_LENGTH * j + k] = buf[k + 1]; + //printk("0x%02X, 0x%04X\n", buf[k+1], (XDATA_SECTOR_SIZE*i + BUS_TRANSFER_LENGTH*j + k)); + } + } + //printk("addr=0x%05X\n", (head_addr+XDATA_SECTOR_SIZE*i)); + } + + //read xdata : step2 + if (residual_len != 0) { + //---change xdata index--- + nvt_set_page(I2C_FW_Address, xdata_addr + data_len - residual_len); + + //---read xdata by BUS_TRANSFER_LENGTH + for (j = 0; j < (residual_len / BUS_TRANSFER_LENGTH + 1); j++) { + //---read data--- + buf[0] = BUS_TRANSFER_LENGTH * j; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, BUS_TRANSFER_LENGTH + 1); + + //---copy buf to xdata_tmp--- + for (k = 0; k < BUS_TRANSFER_LENGTH; k++) { + xdata_tmp[(dummy_len + data_len - residual_len) + BUS_TRANSFER_LENGTH * j + k] = buf[k + 1]; + //printk("0x%02X, 0x%04x\n", buf[k+1], ((dummy_len+data_len-residual_len) + BUS_TRANSFER_LENGTH*j + k)); + } + } + //printk("addr=0x%05X\n", (xdata_addr+data_len-residual_len)); + } + + //---remove dummy data and 2bytes-to-1data--- + for (i = 0; i < (data_len / 2); i++) { + xdata[i] = (int16_t)(xdata_tmp[dummy_len + i * 2] + 256 * xdata_tmp[dummy_len + i * 2 + 1]); + } + +#if TOUCH_KEY_NUM > 0 + //read button xdata : step3 + //---change xdata index--- + nvt_set_page(I2C_FW_Address, xdata_btn_addr); + + //---read data--- + buf[0] = (xdata_btn_addr & 0xFF); + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, (TOUCH_KEY_NUM * 2 + 1)); + + //---2bytes-to-1data--- + for (i = 0; i < TOUCH_KEY_NUM; i++) { + xdata[ts->x_num * ts->y_num + i] = (int16_t)(buf[1 + i * 2] + 256 * buf[1 + i * 2 + 1]); + } +#endif + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR); +} + +/******************************************************* +Description: + Novatek touchscreen get meta data function. + +return: + n.a. +*******************************************************/ +void nvt_get_mdata(int32_t *buf, uint8_t *m_x_num, uint8_t *m_y_num) +{ + *m_x_num = ts->x_num; + *m_y_num = ts->y_num; + memcpy(buf, xdata, ((ts->x_num * ts->y_num + TOUCH_KEY_NUM) * sizeof(int32_t))); +} + +/******************************************************* +Description: + Novatek touchscreen firmware version show function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t c_fw_version_show(struct seq_file *m, void *v) +{ + seq_printf(m, "fw_ver=%d, x_num=%d, y_num=%d, button_num=%d\n", ts->fw_ver, ts->x_num, ts->y_num, ts->max_button_num); + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen xdata sequence print show + function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t c_show(struct seq_file *m, void *v) +{ + int32_t i = 0; + int32_t j = 0; + + for (i = 0; i < ts->y_num; i++) { + for (j = 0; j < ts->x_num; j++) { + seq_printf(m, "%5d, ", xdata[i * ts->x_num + j]); + } + seq_puts(m, "\n"); + } + +#if TOUCH_KEY_NUM > 0 + for (i = 0; i < TOUCH_KEY_NUM; i++) { + seq_printf(m, "%5d, ", xdata[ts->x_num * ts->y_num + i]); + } + seq_puts(m, "\n"); +#endif + + seq_printf(m, "\n\n"); + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen xdata sequence print start + function. + +return: + Executive outcomes. 1---call next function. + NULL---not call next function and sequence loop + stop. +*******************************************************/ +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < 1 ? (void *)1 : NULL; +} + +/******************************************************* +Description: + Novatek touchscreen xdata sequence print next + function. + +return: + Executive outcomes. NULL---no next and call sequence + stop function. +*******************************************************/ +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return NULL; +} + +/******************************************************* +Description: + Novatek touchscreen xdata sequence print stop + function. + +return: + n.a. +*******************************************************/ +static void c_stop(struct seq_file *m, void *v) +{ + return; +} + +const struct seq_operations nvt_fw_version_seq_ops = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = c_fw_version_show +}; + +const struct seq_operations nvt_seq_ops = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = c_show +}; + +/******************************************************* +Description: + Novatek touchscreen /proc/nvt_fw_version open + function. + +return: + n.a. +*******************************************************/ +static int32_t nvt_fw_version_open(struct inode *inode, struct file *file) +{ + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + + NVT_LOG("++\n"); + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + if (nvt_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_fw_version_seq_ops); +} + +static const struct proc_ops nvt_fw_version_fops = { + .proc_open = nvt_fw_version_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/******************************************************* +Description: + Novatek touchscreen /proc/nvt_baseline open function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t nvt_baseline_open(struct inode *inode, struct file *file) +{ + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + + NVT_LOG("++\n"); + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + if (nvt_clear_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_change_mode(TEST_MODE_2); + + if (nvt_check_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_read_mdata(ts->mmap->BASELINE_ADDR, ts->mmap->BASELINE_BTN_ADDR); + + nvt_change_mode(NORMAL_MODE); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_seq_ops); +} + +static const struct proc_ops nvt_baseline_fops = { + .proc_open = nvt_baseline_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/******************************************************* +Description: + Novatek touchscreen /proc/nvt_raw open function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t nvt_raw_open(struct inode *inode, struct file *file) +{ + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + + NVT_LOG("++\n"); + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + if (nvt_clear_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_change_mode(TEST_MODE_2); + + if (nvt_check_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_get_fw_pipe() == 0) + nvt_read_mdata(ts->mmap->RAW_PIPE0_ADDR, ts->mmap->RAW_BTN_PIPE0_ADDR); + else + nvt_read_mdata(ts->mmap->RAW_PIPE1_ADDR, ts->mmap->RAW_BTN_PIPE1_ADDR); + + nvt_change_mode(NORMAL_MODE); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_seq_ops); +} + +static const struct proc_ops nvt_raw_fops = { + .proc_open = nvt_raw_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/******************************************************* +Description: + Novatek touchscreen /proc/nvt_diff open function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +static int32_t nvt_diff_open(struct inode *inode, struct file *file) +{ + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + + NVT_LOG("++\n"); + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + if (nvt_clear_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_change_mode(TEST_MODE_2); + + if (nvt_check_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_get_fw_pipe() == 0) + nvt_read_mdata(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_ADDR); + else + nvt_read_mdata(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_ADDR); + + nvt_change_mode(NORMAL_MODE); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_seq_ops); +} + +static const struct proc_ops nvt_diff_fops = { + .proc_open = nvt_diff_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/******************************************************* +Description: + Novatek touchscreen extra function proc. file node + initial function. + +return: + Executive outcomes. 0---succeed. -12---failed. +*******************************************************/ +int32_t nvt_extra_proc_init(void) +{ + NVT_proc_fw_version_entry = proc_create(NVT_FW_VERSION, 0444, NULL,&nvt_fw_version_fops); + if (NVT_proc_fw_version_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_FW_VERSION); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_FW_VERSION); + } + + NVT_proc_baseline_entry = proc_create(NVT_BASELINE, 0444, NULL,&nvt_baseline_fops); + if (NVT_proc_baseline_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_BASELINE); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_BASELINE); + } + + NVT_proc_raw_entry = proc_create(NVT_RAW, 0444, NULL,&nvt_raw_fops); + if (NVT_proc_raw_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_RAW); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_RAW); + } + + NVT_proc_diff_entry = proc_create(NVT_DIFF, 0444, NULL,&nvt_diff_fops); + if (NVT_proc_diff_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_DIFF); + return -ENOMEM; + } else { + NVT_LOG("create proc/%s Succeeded!\n", NVT_DIFF); + } + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen extra function proc. file node + deinitial function. + +return: + n.a. +*******************************************************/ +void nvt_extra_proc_deinit(void) +{ + if (NVT_proc_fw_version_entry != NULL) { + remove_proc_entry(NVT_FW_VERSION, NULL); + NVT_proc_fw_version_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_FW_VERSION); + } + + if (NVT_proc_baseline_entry != NULL) { + remove_proc_entry(NVT_BASELINE, NULL); + NVT_proc_baseline_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_BASELINE); + } + + if (NVT_proc_raw_entry != NULL) { + remove_proc_entry(NVT_RAW, NULL); + NVT_proc_raw_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_RAW); + } + + if (NVT_proc_diff_entry != NULL) { + remove_proc_entry(NVT_DIFF, NULL); + NVT_proc_diff_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_DIFF); + } +} +#endif + +#else /* NT36XXX_SPI */ + +#include +#include + +#include "nt36xxx.h" + +#if NVT_SPI_TOUCH_EXT_PROC +#define NVT_SPI_FW_VERSION "nvt_fw_version" +#define NVT_SPI_BASELINE "nvt_baseline" +#define NVT_SPI_RAW "nvt_raw" +#define NVT_SPI_DIFF "nvt_diff" +#define NVT_SPI_PEN_DIFF "nvt_pen_diff" + +#define NVT_SPI_BUS_TRANSFER_LENGTH 256 + +#define NVT_SPI_NORMAL_MODE 0x00 +#define NVT_SPI_TEST_MODE_2 0x22 +#define NVT_SPI_HANDSHAKING_HOST_READY 0xBB + +#define NVT_SPI_XDATA_SECTOR_SIZE 256 + +static uint8_t nvt_spi_xdata_tmp[5000] = {0}; +static int32_t nvt_spi_xdata[2500] = {0}; +static int32_t nvt_spi_xdata_pen_tip_x[256] = {0}; +static int32_t nvt_spi_xdata_pen_tip_y[256] = {0}; +static int32_t nvt_spi_xdata_pen_ring_x[256] = {0}; +static int32_t nvt_spi_xdata_pen_ring_y[256] = {0}; + +static struct proc_dir_entry *nvt_spi_proc_fw_version_entry; +static struct proc_dir_entry *nvt_spi_proc_baseline_entry; +static struct proc_dir_entry *nvt_spi_proc_raw_entry; +static struct proc_dir_entry *nvt_spi_proc_diff_entry; +static struct proc_dir_entry *nvt_spi_proc_pen_diff_entry; + +/* + ******************************************************* + * Description: + * Novatek touchscreen change mode function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_change_mode(uint8_t mode) +{ + uint8_t buf[8] = {0}; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR | NVT_SPI_EVENT_MAP_HOST_CMD); + + //---set mode--- + buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD; + buf[1] = mode; + nvt_spi_write(buf, 2); + + if (mode == NVT_SPI_NORMAL_MODE) { + usleep_range(20000, 21000); + buf[0] = NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = NVT_SPI_HANDSHAKING_HOST_READY; + nvt_spi_write(buf, 2); + usleep_range(20000, 21000); + } +} + +static int32_t nvt_set_pen_inband_mode_1_spi(uint8_t freq_idx, uint8_t x_term) +{ + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 5; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR | NVT_SPI_EVENT_MAP_HOST_CMD); + + //---set mode--- + buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD; + buf[1] = 0xC1; + buf[2] = 0x02; + buf[3] = freq_idx; + buf[4] = x_term; + nvt_spi_write(buf, 5); + + for (i = 0; i < retry; i++) { + buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD; + buf[1] = 0xFF; + nvt_spi_read(buf, 2); + + if (buf[1] == 0x00) + break; + + usleep_range(10000, 11000); + } + + if (i >= retry) { + NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); + return -EIO; + } + + return 0; +} + +static int32_t nvt_spi_set_pen_normal_mode(void) +{ + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 5; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR | NVT_SPI_EVENT_MAP_HOST_CMD); + + //---set mode--- + buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD; + buf[1] = 0xC1; + buf[2] = 0x04; + nvt_spi_write(buf, 3); + + for (i = 0; i < retry; i++) { + buf[0] = NVT_SPI_EVENT_MAP_HOST_CMD; + buf[1] = 0xFF; + nvt_spi_read(buf, 2); + + if (buf[1] == 0x00) + break; + + usleep_range(10000, 11000); + } + + if (i >= retry) { + NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); + return -EIO; + } + + return 0; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen get firmware pipe function. + * + * return: + * Executive outcomes. 0---pipe 0. 1---pipe 1. + ****************************************************** + */ +uint8_t nvt_spi_get_fw_pipe(void) +{ + uint32_t addr; + uint8_t buf[8] = {0}; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata index to EVENT BUF ADDR--- + addr = ts->mmap->EVENT_BUF_ADDR; + nvt_spi_set_page(addr | NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE); + + //---read fw status--- + buf[0] = NVT_SPI_EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0x00; + nvt_spi_read(buf, 2); + + //NVT_LOG("FW pipe=%d, buf[1]=0x%02X\n", (buf[1]&0x01), buf[1]); + + return (buf[1] & 0x01); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen read meta data function. + * + * return: + * n.a. + ****************************************************** + */ +static void nvt_spi_read_mdata(uint32_t xdata_addr, uint32_t xdata_btn_addr) +{ + int32_t i = 0; + int32_t j = 0; + int32_t k = 0; + uint8_t buf[NVT_SPI_BUS_TRANSFER_LENGTH + 2] = {0}; + uint32_t head_addr = 0; + int32_t dummy_len = 0; + int32_t data_len = 0; + int32_t residual_len = 0; + int32_t index, data; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata sector address & length--- + head_addr = xdata_addr - (xdata_addr % NVT_SPI_XDATA_SECTOR_SIZE); + dummy_len = xdata_addr - head_addr; + data_len = ts->x_num * ts->y_num * 2; + residual_len = (head_addr + dummy_len + data_len) % NVT_SPI_XDATA_SECTOR_SIZE; + + //printk("head_addr=0x%05X, dummy_len=0x%05X, data_len=0x%05X, residual_len=0x%05X\n", + // head_addr, dummy_len, data_len, residual_len); + + //read xdata : step 1 + for (i = 0; i < ((dummy_len + data_len) / NVT_SPI_XDATA_SECTOR_SIZE); i++) { + //---change xdata index--- + nvt_spi_set_page(head_addr + NVT_SPI_XDATA_SECTOR_SIZE * i); + + //---read xdata by NVT_SPI_BUS_TRANSFER_LENGTH + for (j = 0; j < (NVT_SPI_XDATA_SECTOR_SIZE / NVT_SPI_BUS_TRANSFER_LENGTH); j++) { + //---read data--- + buf[0] = NVT_SPI_BUS_TRANSFER_LENGTH * j; + nvt_spi_read(buf, NVT_SPI_BUS_TRANSFER_LENGTH + 1); + + //---copy buf to nvt_spi_xdata_tmp--- + for (k = 0; k < NVT_SPI_BUS_TRANSFER_LENGTH; k++) { + index = NVT_SPI_XDATA_SECTOR_SIZE * i; + index += NVT_SPI_BUS_TRANSFER_LENGTH * j + k; + nvt_spi_xdata_tmp[index] = buf[k + 1]; + //printk("0x%02X, 0x%04X\n", buf[k+1], index); + } + } + //printk("addr=0x%05X\n", (head_addr+NVT_SPI_XDATA_SECTOR_SIZE*i)); + } + + //read xdata : step2 + if (residual_len != 0) { + //---change xdata index--- + nvt_spi_set_page(xdata_addr + data_len - residual_len); + + //---read xdata by NVT_SPI_BUS_TRANSFER_LENGTH + for (j = 0; j < (residual_len / NVT_SPI_BUS_TRANSFER_LENGTH + 1); j++) { + //---read data--- + buf[0] = NVT_SPI_BUS_TRANSFER_LENGTH * j; + nvt_spi_read(buf, NVT_SPI_BUS_TRANSFER_LENGTH + 1); + + //---copy buf to nvt_spi_xdata_tmp--- + for (k = 0; k < NVT_SPI_BUS_TRANSFER_LENGTH; k++) { + index = (dummy_len + data_len - residual_len); + index += NVT_SPI_BUS_TRANSFER_LENGTH * j + k; + nvt_spi_xdata_tmp[index] = buf[k + 1]; + //printk("0x%02X, 0x%04x\n", buf[k+1], index); + + } + } + //printk("addr=0x%05X\n", (xdata_addr+data_len-residual_len)); + } + + //---remove dummy data and 2bytes-to-1data--- + for (i = 0; i < (data_len / 2); i++) { + data = nvt_spi_xdata_tmp[dummy_len + i * 2]; + data += 256 * nvt_spi_xdata_tmp[dummy_len + i * 2 + 1]; + nvt_spi_xdata[i] = (int16_t)data; + } + +#if NVT_SPI_TOUCH_KEY_NUM > 0 + //read button xdata : step3 + //---change xdata index--- + nvt_spi_set_page(xdata_btn_addr); + + //---read data--- + buf[0] = (xdata_btn_addr & 0xFF); + nvt_spi_read(buf, (NVT_SPI_TOUCH_KEY_NUM * 2 + 1)); + + //---2bytes-to-1data--- + for (i = 0; i < NVT_SPI_TOUCH_KEY_NUM; i++) + nvt_spi_xdata[ts->x_num * ts->y_num + i] = + (int16_t)(buf[1 + i * 2] + 256 * buf[1 + i * 2 + 1]); +#endif + + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen get meta data function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_get_mdata(int32_t *buf, uint8_t *m_x_num, uint8_t *m_y_num) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + *m_x_num = ts->x_num; + *m_y_num = ts->y_num; + memcpy(buf, nvt_spi_xdata, + ((ts->x_num * ts->y_num + NVT_SPI_TOUCH_KEY_NUM) * sizeof(int32_t))); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen read and get number of meta data function. + * + * return: + * n.a. + ******************************************************* + */ +void nvt_spi_read_get_num_mdata(uint32_t xdata_addr, int32_t *buffer, uint32_t num) +{ + int32_t i = 0; + int32_t j = 0; + int32_t k = 0; + uint8_t buf[NVT_SPI_BUS_TRANSFER_LENGTH + 2] = {0}; + uint32_t head_addr = 0; + int32_t dummy_len = 0; + int32_t data_len = 0; + int32_t residual_len = 0; + int32_t index, data; + struct nvt_spi_data_t *ts = nvt_spi_data; + + //---set xdata sector address & length--- + head_addr = xdata_addr - (xdata_addr % NVT_SPI_XDATA_SECTOR_SIZE); + dummy_len = xdata_addr - head_addr; + data_len = num * 2; + residual_len = (head_addr + dummy_len + data_len) % NVT_SPI_XDATA_SECTOR_SIZE; + + //printk("head_addr=0x%05X, dummy_len=0x%05X, data_len=0x%05X, residual_len=0x%05X\n", + // head_addr, dummy_len, data_len, residual_len); + + //read xdata : step 1 + for (i = 0; i < ((dummy_len + data_len) / NVT_SPI_XDATA_SECTOR_SIZE); i++) { + //---change xdata index--- + nvt_spi_set_page(head_addr + NVT_SPI_XDATA_SECTOR_SIZE * i); + + //---read xdata by NVT_SPI_BUS_TRANSFER_LENGTH + for (j = 0; j < (NVT_SPI_XDATA_SECTOR_SIZE / NVT_SPI_BUS_TRANSFER_LENGTH); j++) { + //---read data--- + buf[0] = NVT_SPI_BUS_TRANSFER_LENGTH * j; + nvt_spi_read(buf, NVT_SPI_BUS_TRANSFER_LENGTH + 1); + + //---copy buf to nvt_spi_xdata_tmp--- + for (k = 0; k < NVT_SPI_BUS_TRANSFER_LENGTH; k++) { + index = NVT_SPI_XDATA_SECTOR_SIZE * i; + index += NVT_SPI_BUS_TRANSFER_LENGTH * j + k; + nvt_spi_xdata_tmp[index] = buf[k + 1]; + } + } + //printk("addr=0x%05X\n", (head_addr+NVT_SPI_XDATA_SECTOR_SIZE*i)); + } + + //read xdata : step2 + if (residual_len != 0) { + //---change xdata index--- + nvt_spi_set_page(xdata_addr + data_len - residual_len); + + //---read xdata by NVT_SPI_BUS_TRANSFER_LENGTH + for (j = 0; j < (residual_len / NVT_SPI_BUS_TRANSFER_LENGTH + 1); j++) { + //---read data--- + buf[0] = NVT_SPI_BUS_TRANSFER_LENGTH * j; + nvt_spi_read(buf, NVT_SPI_BUS_TRANSFER_LENGTH + 1); + + //---copy buf to nvt_spi_xdata_tmp--- + for (k = 0; k < NVT_SPI_BUS_TRANSFER_LENGTH; k++) { + index = (dummy_len + data_len - residual_len); + index += NVT_SPI_BUS_TRANSFER_LENGTH * j + k; + nvt_spi_xdata_tmp[index] = buf[k + 1]; + //printk("0x%02X, 0x%04x\n", buf[k+1], index)); + + } + } + //printk("addr=0x%05X\n", (xdata_addr+data_len-residual_len)); + } + + //---remove dummy data and 2bytes-to-1data--- + for (i = 0; i < (data_len / 2); i++) { + data = nvt_spi_xdata_tmp[dummy_len + i * 2]; + data += 256 * nvt_spi_xdata_tmp[dummy_len + i * 2 + 1]; + buffer[i] = (int16_t)data; + } + + //---set xdata index to EVENT BUF ADDR--- + nvt_spi_set_page(ts->mmap->EVENT_BUF_ADDR); +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen firmware version show function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t c_fw_version_show(struct seq_file *m, void *v) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + seq_printf(m, "fw_ver=%d, x_num=%d, y_num=%d, button_num=%d\n", + ts->fw_ver, ts->x_num, ts->y_num, ts->max_button_num); + return 0; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen xdata sequence print show + * function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t c_show(struct seq_file *m, void *v) +{ + int32_t i = 0; + int32_t j = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + + for (i = 0; i < ts->y_num; i++) { + for (j = 0; j < ts->x_num; j++) + seq_printf(m, "%5d, ", nvt_spi_xdata[i * ts->x_num + j]); + + seq_puts(m, "\n"); + } + +#if NVT_SPI_TOUCH_KEY_NUM > 0 + for (i = 0; i < NVT_SPI_TOUCH_KEY_NUM; i++) + seq_printf(m, "%5d, ", nvt_spi_xdata[ts->x_num * ts->y_num + i]); + + seq_puts(m, "\n"); +#endif + + seq_puts(m, "\n\n"); + return 0; +} + +/* + ******************************************************* + * Description: + * Novatek pen 1D diff xdata sequence print show + * function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t c_pen_1d_diff_show(struct seq_file *m, void *v) +{ + int32_t i = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + + seq_puts(m, "Tip X:\n"); + for (i = 0; i < ts->x_num; i++) + seq_printf(m, "%5d, ", nvt_spi_xdata_pen_tip_x[i]); + + seq_puts(m, "\n"); + seq_puts(m, "Tip Y:\n"); + for (i = 0; i < ts->y_num; i++) + seq_printf(m, "%5d, ", nvt_spi_xdata_pen_tip_y[i]); + + seq_puts(m, "\n"); + seq_puts(m, "Ring X:\n"); + for (i = 0; i < ts->x_num; i++) + seq_printf(m, "%5d, ", nvt_spi_xdata_pen_ring_x[i]); + + seq_puts(m, "\n"); + seq_puts(m, "Ring Y:\n"); + for (i = 0; i < ts->y_num; i++) + seq_printf(m, "%5d, ", nvt_spi_xdata_pen_ring_y[i]); + + seq_puts(m, "\n"); + + seq_puts(m, "\n\n"); + return 0; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen xdata sequence print start + * function. + * + * return: + * Executive outcomes. 1---call next function. + * NULL---not call next function and sequence loop + * stop. + ******************************************************* + */ +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < 1 ? (void *)1 : NULL; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen xdata sequence print next + * function. + * + * return: + * Executive outcomes. NULL---no next and call sequence + * stop function. + ****************************************************** + */ +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return NULL; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen xdata sequence print stop + * function. + * + * return: + * n.a. + ****************************************************** + */ +static void c_stop(struct seq_file *m, void *v) +{ +} + +const struct seq_operations nvt_spi_fw_version_seq_ops = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = c_fw_version_show +}; + +const struct seq_operations nvt_spi_seq_ops = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = c_show +}; + +const struct seq_operations nvt_spi_pen_diff_seq_ops = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = c_pen_1d_diff_show +}; + +/* + ******************************************************* + * Description: + * Novatek touchscreen /proc/nvt_fw_version open + * function. + * + * return: + * n.a. + ****************************************************** + */ +static int32_t nvt_spi_fw_version_open(struct inode *inode, struct file *file) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (mutex_lock_interruptible(&ts->lock)) + return -ERESTARTSYS; + + NVT_LOG("++\n"); + +#if NVT_SPI_TOUCH_ESD_PROTECT + nvt_spi_esd_check_enable(false); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + if (nvt_spi_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_spi_fw_version_seq_ops); +} + +static const struct proc_ops nvt_spi_fw_version_fops = { + .proc_open = nvt_spi_fw_version_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/* + ******************************************************* + * Description: + * Novatek touchscreen /proc/nvt_baseline open function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t nvt_spi_baseline_open(struct inode *inode, struct file *file) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (mutex_lock_interruptible(&ts->lock)) + return -ERESTARTSYS; + + NVT_LOG("++\n"); + +#if NVT_SPI_TOUCH_ESD_PROTECT + nvt_spi_esd_check_enable(false); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + if (nvt_spi_clear_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_spi_change_mode(NVT_SPI_TEST_MODE_2); + + if (nvt_spi_check_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_spi_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_spi_read_mdata(ts->mmap->BASELINE_ADDR, ts->mmap->BASELINE_BTN_ADDR); + + nvt_spi_change_mode(NVT_SPI_NORMAL_MODE); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_spi_seq_ops); +} + +static const struct proc_ops nvt_spi_baseline_fops = { + .proc_open = nvt_spi_baseline_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/* + ******************************************************* + * Description: + * Novatek touchscreen /proc/nvt_raw open function. + * + * return: + * Executive outcomes. 0---succeed. + ****************************************************** + */ +static int32_t nvt_spi_raw_open(struct inode *inode, struct file *file) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (mutex_lock_interruptible(&ts->lock)) + return -ERESTARTSYS; + + NVT_LOG("++\n"); + +#if NVT_SPI_TOUCH_ESD_PROTECT + nvt_spi_esd_check_enable(false); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + if (nvt_spi_clear_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_spi_change_mode(NVT_SPI_TEST_MODE_2); + + if (nvt_spi_check_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_spi_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_spi_get_fw_pipe() == 0) + nvt_spi_read_mdata(ts->mmap->RAW_PIPE0_ADDR, ts->mmap->RAW_BTN_PIPE0_ADDR); + else + nvt_spi_read_mdata(ts->mmap->RAW_PIPE1_ADDR, ts->mmap->RAW_BTN_PIPE1_ADDR); + + nvt_spi_change_mode(NVT_SPI_NORMAL_MODE); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_spi_seq_ops); +} + +static const struct proc_ops nvt_spi_raw_fops = { + .proc_open = nvt_spi_raw_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/* + ****************************************************** + * Description: + * Novatek touchscreen /proc/nvt_diff open function. + * + * return: + * Executive outcomes. 0---succeed. negative---failed. + ****************************************************** + */ +static int32_t nvt_spi_diff_open(struct inode *inode, struct file *file) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (mutex_lock_interruptible(&ts->lock)) + return -ERESTARTSYS; + + NVT_LOG("++\n"); + +#if NVT_SPI_TOUCH_ESD_PROTECT + nvt_spi_esd_check_enable(false); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + if (nvt_spi_clear_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_spi_change_mode(NVT_SPI_TEST_MODE_2); + + if (nvt_spi_check_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_spi_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_spi_get_fw_pipe() == 0) + nvt_spi_read_mdata(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_ADDR); + else + nvt_spi_read_mdata(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_ADDR); + + nvt_spi_change_mode(NVT_SPI_NORMAL_MODE); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_spi_seq_ops); +} + +static const struct proc_ops nvt_spi_diff_fops = { + .proc_open = nvt_spi_diff_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/* + ******************************************************* + * Description: + * Novatek touchscreen /proc/nvt_pen_diff open function. + * + * return: + * Executive outcomes. 0---succeed. negative---failed. + ****************************************************** + */ +static int32_t nvt_spi_pen_diff_open(struct inode *inode, struct file *file) +{ + uint32_t addr; + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (mutex_lock_interruptible(&ts->lock)) + return -ERESTARTSYS; + + NVT_LOG("++\n"); + +#if NVT_SPI_TOUCH_ESD_PROTECT + nvt_spi_esd_check_enable(false); +#endif /* #if NVT_SPI_TOUCH_ESD_PROTECT */ + + if (nvt_set_pen_inband_mode_1_spi(0xFF, 0x00)) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_NORMAL_RUN)) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_spi_clear_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + nvt_spi_change_mode(NVT_SPI_TEST_MODE_2); + + if (nvt_spi_check_fw_status()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + if (nvt_spi_get_fw_info()) { + mutex_unlock(&ts->lock); + return -EAGAIN; + } + + addr = ts->mmap->PEN_1D_DIFF_TIP_X_ADDR; + nvt_spi_read_get_num_mdata(addr, nvt_spi_xdata_pen_tip_x, ts->x_num); + + addr = ts->mmap->PEN_1D_DIFF_TIP_Y_ADDR; + nvt_spi_read_get_num_mdata(addr, nvt_spi_xdata_pen_tip_y, ts->y_num); + + addr = ts->mmap->PEN_1D_DIFF_RING_X_ADDR; + nvt_spi_read_get_num_mdata(addr, nvt_spi_xdata_pen_ring_x, ts->x_num); + + addr = ts->mmap->PEN_1D_DIFF_RING_Y_ADDR; + nvt_spi_read_get_num_mdata(addr, nvt_spi_xdata_pen_ring_y, ts->y_num); + + nvt_spi_change_mode(NVT_SPI_NORMAL_MODE); + + nvt_spi_set_pen_normal_mode(); + + nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_NORMAL_RUN); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + return seq_open(file, &nvt_spi_pen_diff_seq_ops); +} + +static const struct proc_ops nvt_spi_pen_diff_fops = { + .proc_open = nvt_spi_pen_diff_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +/* + ******************************************************* + * Description: + * Novatek touchscreen extra function proc. file node + * initial function. + * + * return: + * Executive outcomes. 0---succeed. -12---failed. + ****************************************************** + */ +int32_t nvt_spi_extra_proc_init(void) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + nvt_spi_proc_fw_version_entry = proc_create(NVT_SPI_FW_VERSION, 0444, NULL, + &nvt_spi_fw_version_fops); + if (nvt_spi_proc_fw_version_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_SPI_FW_VERSION); + return -ENOMEM; + } + NVT_LOG("create proc/%s Succeeded!\n", NVT_SPI_FW_VERSION); + + nvt_spi_proc_baseline_entry = proc_create(NVT_SPI_BASELINE, 0444, NULL, + &nvt_spi_baseline_fops); + if (nvt_spi_proc_baseline_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_SPI_BASELINE); + return -ENOMEM; + } + NVT_LOG("create proc/%s Succeeded!\n", NVT_SPI_BASELINE); + + nvt_spi_proc_raw_entry = proc_create(NVT_SPI_RAW, 0444, NULL, &nvt_spi_raw_fops); + if (nvt_spi_proc_raw_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_SPI_RAW); + return -ENOMEM; + } + NVT_LOG("create proc/%s Succeeded!\n", NVT_SPI_RAW); + + nvt_spi_proc_diff_entry = proc_create(NVT_SPI_DIFF, 0444, NULL, &nvt_spi_diff_fops); + if (nvt_spi_proc_diff_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_SPI_DIFF); + return -ENOMEM; + } + NVT_LOG("create proc/%s Succeeded!\n", NVT_SPI_DIFF); + + if (ts->pen_support) { + nvt_spi_proc_pen_diff_entry = proc_create(NVT_SPI_PEN_DIFF, 0444, NULL, + &nvt_spi_pen_diff_fops); + if (nvt_spi_proc_pen_diff_entry == NULL) { + NVT_ERR("create proc/%s Failed!\n", NVT_SPI_PEN_DIFF); + return -ENOMEM; + } + NVT_LOG("create proc/%s Succeeded!\n", NVT_SPI_PEN_DIFF); + } + + return 0; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen extra function proc. file node + * deinitial function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_extra_proc_deinit(void) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + if (nvt_spi_proc_fw_version_entry != NULL) { + remove_proc_entry(NVT_SPI_FW_VERSION, NULL); + nvt_spi_proc_fw_version_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_SPI_FW_VERSION); + } + + if (nvt_spi_proc_baseline_entry != NULL) { + remove_proc_entry(NVT_SPI_BASELINE, NULL); + nvt_spi_proc_baseline_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_SPI_BASELINE); + } + + if (nvt_spi_proc_raw_entry != NULL) { + remove_proc_entry(NVT_SPI_RAW, NULL); + nvt_spi_proc_raw_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_SPI_RAW); + } + + if (nvt_spi_proc_diff_entry != NULL) { + remove_proc_entry(NVT_SPI_DIFF, NULL); + nvt_spi_proc_diff_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_SPI_DIFF); + } + + if (ts->pen_support) { + if (nvt_spi_proc_pen_diff_entry != NULL) { + remove_proc_entry(NVT_SPI_PEN_DIFF, NULL); + nvt_spi_proc_pen_diff_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_SPI_PEN_DIFF); + } + } +} +#endif + + + +#endif diff --git a/nt36xxx/nt36xxx_fw_update.c b/nt36xxx/nt36xxx_fw_update.c new file mode 100644 index 0000000000..cbce144794 --- /dev/null +++ b/nt36xxx/nt36xxx_fw_update.c @@ -0,0 +1,2002 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 47247 $ + * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $ + * + * 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. + * + */ + +#if !defined(NVT_NT36XXX_SPI) /* NT36XXX I2C */ + +#include + +#include "nt36xxx.h" + +#if BOOT_UPDATE_FIRMWARE + +#define SIZE_4KB 4096 +#define FLASH_SECTOR_SIZE SIZE_4KB +#define SIZE_64KB 65536 +#define BLOCK_64KB_NUM 4 +#define FW_BIN_VER_OFFSET (fw_need_write_size - SIZE_4KB) +#define FW_BIN_VER_BAR_OFFSET (FW_BIN_VER_OFFSET + 1) + +#define NVT_FLASH_END_FLAG_LEN 3 +#define NVT_FLASH_END_FLAG_ADDR (fw_need_write_size - NVT_FLASH_END_FLAG_LEN) + +const struct firmware *fw_entry = NULL; +static size_t fw_need_write_size = 0; + +static int32_t nvt_get_fw_need_write_size(const struct firmware *fw_entry) +{ + int32_t i = 0; + int32_t total_sectors_to_check = 0; + + total_sectors_to_check = fw_entry->size / FLASH_SECTOR_SIZE; + /* printk("total_sectors_to_check = %d\n", total_sectors_to_check); */ + + for (i = total_sectors_to_check; i > 0; i--) { + /* printk("current end flag address checked = 0x%X\n", i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN); */ + /* check if there is end flag "NVT" at the end of this sector */ + if ((memcmp((const char *)&fw_entry->data[i * FLASH_SECTOR_SIZE - + NVT_FLASH_END_FLAG_LEN], "NVT", NVT_FLASH_END_FLAG_LEN) == 0) || + (memcmp((const char *)&fw_entry->data[i * FLASH_SECTOR_SIZE - + NVT_FLASH_END_FLAG_LEN], "MOD", NVT_FLASH_END_FLAG_LEN) == 0)) { + fw_need_write_size = i * FLASH_SECTOR_SIZE; + NVT_LOG("fw_need_write_size = %zu(0x%zx)\n", fw_need_write_size, fw_need_write_size); + return 0; + } + } + + NVT_ERR("end flag \"NVT\" not found!\n"); + return -1; +} + +/******************************************************* +Description: + Novatek touchscreen request update firmware function. + +return: + Executive outcomes. 0---succeed. -1,-22---failed. +*******************************************************/ +int32_t update_firmware_request(char *filename) +{ + int32_t ret = 0; + + if (NULL == filename) { + return -1; + } + + NVT_LOG("filename is %s\n", filename); + + ret = request_firmware(&fw_entry, filename, &ts->client->dev); + if (ret) { + NVT_ERR("firmware load failed, ret=%d\n", ret); + return ret; + } + + // check FW need to write size + if (nvt_get_fw_need_write_size(fw_entry)) { + NVT_ERR("get fw need to write size fail!\n"); + return -EINVAL; + } + + // check if FW version add FW version bar equals 0xFF + if (*(fw_entry->data + FW_BIN_VER_OFFSET) + *(fw_entry->data + FW_BIN_VER_BAR_OFFSET) != 0xFF) { + NVT_ERR("bin file FW_VER + FW_VER_BAR should be 0xFF!\n"); + NVT_ERR("FW_VER=0x%02X, FW_VER_BAR=0x%02X\n", *(fw_entry->data+FW_BIN_VER_OFFSET), *(fw_entry->data+FW_BIN_VER_BAR_OFFSET)); + return -EINVAL; + } + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen release update firmware function. + +return: + n.a. +*******************************************************/ +void update_firmware_release(void) +{ + if (fw_entry) { + release_firmware(fw_entry); + } + fw_entry=NULL; +} + +/******************************************************* +Description: + Novatek touchscreen check firmware version function. + +return: + Executive outcomes. 0---need update. 1---need not + update. +*******************************************************/ +int32_t Check_FW_Ver(void) +{ + uint8_t buf[16] = {0}; + int32_t ret = 0; + + //write i2c index to EVENT BUF ADDR + ret = nvt_set_page(I2C_BLDR_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO); + if (ret < 0) { + NVT_ERR("i2c write error!(%d)\n", ret); + return ret; + } + + //read Firmware Version + buf[0] = EVENT_MAP_FWINFO; + buf[1] = 0x00; + buf[2] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 3); + if (ret < 0) { + NVT_ERR("i2c read error!(%d)\n", ret); + return ret; + } + + NVT_LOG("IC FW Ver = 0x%02X, FW Ver Bar = 0x%02X\n", buf[1], buf[2]); + NVT_LOG("Bin FW Ver = 0x%02X, FW ver Bar = 0x%02X\n", + fw_entry->data[FW_BIN_VER_OFFSET], fw_entry->data[FW_BIN_VER_BAR_OFFSET]); + + // check IC FW_VER + FW_VER_BAR equals 0xFF or not, need to update if not + if ((buf[1] + buf[2]) != 0xFF) { + NVT_ERR("IC FW_VER + FW_VER_BAR not equals to 0xFF!\n"); + return 0; + } + + // compare IC and binary FW version + if (buf[1] > fw_entry->data[FW_BIN_VER_OFFSET]) + return 1; + else + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen resume from deep power down function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +int32_t Resume_PD(void) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + int32_t retry = 0; + + // Resume Command + buf[0] = 0x00; + buf[1] = 0xAB; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Write Enable error!!(%d)\n", ret); + return ret; + } + + // Check 0xAA (Resume Command) + retry = 0; + while(1) { + msleep(1); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Resume Command) error!!(%d)\n", ret); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 20)) { + NVT_ERR("Check 0xAA (Resume Command) error!! status=0x%02X\n", buf[1]); + return -1; + } + } + msleep(10); + + NVT_LOG("Resume PD OK\n"); + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen check firmware checksum function. + +return: + Executive outcomes. 0---checksum not match. + 1---checksum match. -1--- checksum read failed. +*******************************************************/ +int32_t Check_CheckSum(void) +{ + uint8_t buf[64] = {0}; + uint32_t XDATA_Addr = ts->mmap->READ_FLASH_CHECKSUM_ADDR; + int32_t ret = 0; + int32_t i = 0; + int32_t k = 0; + uint16_t WR_Filechksum[BLOCK_64KB_NUM] = {0}; + uint16_t RD_Filechksum[BLOCK_64KB_NUM] = {0}; + size_t len_in_blk = 0; + int32_t retry = 0; + + if (Resume_PD()) { + NVT_ERR("Resume PD error!!\n"); + return -1; + } + + for (i = 0; i < BLOCK_64KB_NUM; i++) { + if (fw_need_write_size > (i * SIZE_64KB)) { + // Calculate WR_Filechksum of each 64KB block + len_in_blk = min(fw_need_write_size - i * SIZE_64KB, (size_t)SIZE_64KB); + WR_Filechksum[i] = i + 0x00 + 0x00 + (((len_in_blk - 1) >> 8) & 0xFF) + ((len_in_blk - 1) & 0xFF); + for (k = 0; k < len_in_blk; k++) { + WR_Filechksum[i] += fw_entry->data[k + i * SIZE_64KB]; + } + WR_Filechksum[i] = 65535 - WR_Filechksum[i] + 1; + + // Fast Read Command + buf[0] = 0x00; + buf[1] = 0x07; + buf[2] = i; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = ((len_in_blk - 1) >> 8) & 0xFF; + buf[6] = (len_in_blk - 1) & 0xFF; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 7); + if (ret < 0) { + NVT_ERR("Fast Read Command error!!(%d)\n", ret); + return ret; + } + // Check 0xAA (Fast Read Command) + retry = 0; + while (1) { + msleep(80); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Fast Read Command) error!!(%d)\n", ret); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 5)) { + NVT_ERR("Check 0xAA (Fast Read Command) failed, buf[1]=0x%02X, retry=%d\n", buf[1], retry); + return -1; + } + } + // Read Checksum (write addr high byte & middle byte) + ret = nvt_set_page(I2C_BLDR_Address, XDATA_Addr); + if (ret < 0) { + NVT_ERR("Read Checksum (write addr high byte & middle byte) error!!(%d)\n", ret); + return ret; + } + // Read Checksum + buf[0] = (XDATA_Addr) & 0xFF; + buf[1] = 0x00; + buf[2] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 3); + if (ret < 0) { + NVT_ERR("Read Checksum error!!(%d)\n", ret); + return ret; + } + + RD_Filechksum[i] = (uint16_t)((buf[2] << 8) | buf[1]); + if (WR_Filechksum[i] != RD_Filechksum[i]) { + NVT_ERR("RD_Filechksum[%d]=0x%04X, WR_Filechksum[%d]=0x%04X\n", i, RD_Filechksum[i], i, WR_Filechksum[i]); + NVT_ERR("firmware checksum not match!!\n"); + return 0; + } + } + } + + NVT_LOG("firmware checksum match\n"); + return 1; +} + +/******************************************************* +Description: + Novatek touchscreen initial bootloader and flash + block function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +int32_t Init_BootLoader(void) +{ + uint8_t buf[64] = {0}; + int32_t ret = 0; + int32_t retry = 0; + + // SW Reset & Idle + nvt_sw_reset_idle(); + + // Initiate Flash Block + buf[0] = 0x00; + buf[1] = 0x00; + buf[2] = I2C_FW_Address; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 3); + if (ret < 0) { + NVT_ERR("Inittial Flash Block error!!(%d)\n", ret); + return ret; + } + + // Check 0xAA (Initiate Flash Block) + retry = 0; + while(1) { + msleep(1); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Inittial Flash Block) error!!(%d)\n", ret); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 20)) { + NVT_ERR("Check 0xAA (Inittial Flash Block) error!! status=0x%02X\n", buf[1]); + return -1; + } + } + + NVT_LOG("Init OK \n"); + msleep(20); + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen erase flash sectors function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +int32_t Erase_Flash(void) +{ + uint8_t buf[64] = {0}; + int32_t ret = 0; + int32_t count = 0; + int32_t i = 0; + int32_t Flash_Address = 0; + int32_t retry = 0; + + // Write Enable + buf[0] = 0x00; + buf[1] = 0x06; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Write Enable (for Write Status Register) error!!(%d)\n", ret); + return ret; + } + // Check 0xAA (Write Enable) + retry = 0; + while (1) { + msleep(1); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Write Enable for Write Status Register) error!!(%d)\n", ret); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 20)) { + NVT_ERR("Check 0xAA (Write Enable for Write Status Register) error!! status=0x%02X\n", buf[1]); + return -1; + } + } + + // Write Status Register + buf[0] = 0x00; + buf[1] = 0x01; + buf[2] = 0x00; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 3); + if (ret < 0) { + NVT_ERR("Write Status Register error!!(%d)\n", ret); + return ret; + } + // Check 0xAA (Write Status Register) + retry = 0; + while (1) { + msleep(1); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Write Status Register) error!!(%d)\n", ret); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 20)) { + NVT_ERR("Check 0xAA (Write Status Register) error!! status=0x%02X\n", buf[1]); + return -1; + } + } + + // Read Status + retry = 0; + while (1) { + msleep(5); + buf[0] = 0x00; + buf[1] = 0x05; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Read Status (for Write Status Register) error!!(%d)\n", ret); + return ret; + } + + // Check 0xAA (Read Status) + buf[0] = 0x00; + buf[1] = 0x00; + buf[2] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 3); + if (ret < 0) { + NVT_ERR("Check 0xAA (Read Status for Write Status Register) error!!(%d)\n", ret); + return ret; + } + if ((buf[1] == 0xAA) && (buf[2] == 0x00)) { + break; + } + retry++; + if (unlikely(retry > 100)) { + NVT_ERR("Check 0xAA (Read Status for Write Status Register) failed, buf[1]=0x%02X, buf[2]=0x%02X, retry=%d\n", buf[1], buf[2], retry); + return -1; + } + } + + if (fw_need_write_size % FLASH_SECTOR_SIZE) + count = fw_need_write_size / FLASH_SECTOR_SIZE + 1; + else + count = fw_need_write_size / FLASH_SECTOR_SIZE; + + for(i = 0; i < count; i++) { + // Write Enable + buf[0] = 0x00; + buf[1] = 0x06; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Write Enable error!!(%d,%d)\n", ret, i); + return ret; + } + // Check 0xAA (Write Enable) + retry = 0; + while (1) { + msleep(1); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Write Enable) error!!(%d,%d)\n", ret, i); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 20)) { + NVT_ERR("Check 0xAA (Write Enable) error!! status=0x%02X\n", buf[1]); + return -1; + } + } + + Flash_Address = i * FLASH_SECTOR_SIZE; + + // Sector Erase + buf[0] = 0x00; + buf[1] = 0x20; // Command : Sector Erase + buf[2] = ((Flash_Address >> 16) & 0xFF); + buf[3] = ((Flash_Address >> 8) & 0xFF); + buf[4] = (Flash_Address & 0xFF); + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 5); + if (ret < 0) { + NVT_ERR("Sector Erase error!!(%d,%d)\n", ret, i); + return ret; + } + // Check 0xAA (Sector Erase) + retry = 0; + while (1) { + msleep(1); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Sector Erase) error!!(%d,%d)\n", ret, i); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 20)) { + NVT_ERR("Check 0xAA (Sector Erase) failed, buf[1]=0x%02X, retry=%d\n", buf[1], retry); + return -1; + } + } + + // Read Status + retry = 0; + while (1) { + msleep(5); + buf[0] = 0x00; + buf[1] = 0x05; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Read Status error!!(%d,%d)\n", ret, i); + return ret; + } + + // Check 0xAA (Read Status) + buf[0] = 0x00; + buf[1] = 0x00; + buf[2] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 3); + if (ret < 0) { + NVT_ERR("Check 0xAA (Read Status) error!!(%d,%d)\n", ret, i); + return ret; + } + if ((buf[1] == 0xAA) && (buf[2] == 0x00)) { + break; + } + retry++; + if (unlikely(retry > 100)) { + NVT_ERR("Check 0xAA (Read Status) failed, buf[1]=0x%02X, buf[2]=0x%02X, retry=%d\n", buf[1], buf[2], retry); + return -1; + } + } + } + + NVT_LOG("Erase OK \n"); + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen write flash sectors function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +int32_t Write_Flash(void) +{ + uint8_t buf[64] = {0}; + uint32_t XDATA_Addr = ts->mmap->RW_FLASH_DATA_ADDR; + uint32_t Flash_Address = 0; + int32_t i = 0, j = 0, k = 0; + uint8_t tmpvalue = 0; + int32_t count = 0; + int32_t ret = 0; + int32_t retry = 0; + int32_t percent = 0; + int32_t previous_percent = -1; + + // change I2C buffer index + ret = nvt_set_page(I2C_BLDR_Address, XDATA_Addr); + if (ret < 0) { + NVT_ERR("change I2C buffer index error!!(%d)\n", ret); + return ret; + } + + if (fw_need_write_size % 256) + count = fw_need_write_size / 256 + 1; + else + count = fw_need_write_size / 256; + + for (i = 0; i < count; i++) { + Flash_Address = i * 256; + + // Write Enable + buf[0] = 0x00; + buf[1] = 0x06; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Write Enable error!!(%d)\n", ret); + return ret; + } + // Check 0xAA (Write Enable) + retry = 0; + while (1) { + udelay(100); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Write Enable) error!!(%d,%d)\n", ret, i); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 20)) { + NVT_ERR("Check 0xAA (Write Enable) error!! status=0x%02X\n", buf[1]); + return -1; + } + } + + // Write Page : 256 bytes + for (j = 0; j < min(fw_need_write_size - i * 256, (size_t)256); j += 32) { + buf[0] = (XDATA_Addr + j) & 0xFF; + for (k = 0; k < 32; k++) { + buf[1 + k] = fw_entry->data[Flash_Address + j + k]; + } + ret = CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 33); + if (ret < 0) { + NVT_ERR("Write Page error!!(%d), j=%d\n", ret, j); + return ret; + } + } + if (fw_need_write_size - Flash_Address >= 256) + tmpvalue=(Flash_Address >> 16) + ((Flash_Address >> 8) & 0xFF) + (Flash_Address & 0xFF) + 0x00 + (255); + else + tmpvalue=(Flash_Address >> 16) + ((Flash_Address >> 8) & 0xFF) + (Flash_Address & 0xFF) + 0x00 + (fw_need_write_size - Flash_Address - 1); + + for (k = 0; k < min(fw_need_write_size - Flash_Address, (size_t)256); k++) + tmpvalue += fw_entry->data[Flash_Address + k]; + + tmpvalue = 255 - tmpvalue + 1; + + // Page Program + buf[0] = 0x00; + buf[1] = 0x02; + buf[2] = ((Flash_Address >> 16) & 0xFF); + buf[3] = ((Flash_Address >> 8) & 0xFF); + buf[4] = (Flash_Address & 0xFF); + buf[5] = 0x00; + buf[6] = min(fw_need_write_size - Flash_Address, (size_t)256) - 1; + buf[7] = tmpvalue; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 8); + if (ret < 0) { + NVT_ERR("Page Program error!!(%d), i=%d\n", ret, i); + return ret; + } + // Check 0xAA (Page Program) + retry = 0; + while (1) { + msleep(1); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Page Program error!!(%d)\n", ret); + return ret; + } + if (buf[1] == 0xAA || buf[1] == 0xEA) { + break; + } + retry++; + if (unlikely(retry > 20)) { + NVT_ERR("Check 0xAA (Page Program) failed, buf[1]=0x%02X, retry=%d\n", buf[1], retry); + return -1; + } + } + if (buf[1] == 0xEA) { + NVT_ERR("Page Program error!! i=%d\n", i); + return -3; + } + + // Read Status + retry = 0; + while (1) { + msleep(5); + buf[0] = 0x00; + buf[1] = 0x05; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Read Status error!!(%d)\n", ret); + return ret; + } + + // Check 0xAA (Read Status) + buf[0] = 0x00; + buf[1] = 0x00; + buf[2] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 3); + if (ret < 0) { + NVT_ERR("Check 0xAA (Read Status) error!!(%d)\n", ret); + return ret; + } + if (((buf[1] == 0xAA) && (buf[2] == 0x00)) || (buf[1] == 0xEA)) { + break; + } + retry++; + if (unlikely(retry > 100)) { + NVT_ERR("Check 0xAA (Read Status) failed, buf[1]=0x%02X, buf[2]=0x%02X, retry=%d\n", buf[1], buf[2], retry); + return -1; + } + } + if (buf[1] == 0xEA) { + NVT_ERR("Page Program error!! i=%d\n", i); + return -4; + } + + percent = ((i + 1) * 100) / count; + if (((percent % 10) == 0) && (percent != previous_percent)) { + NVT_LOG("Programming...%2d%%\n", percent); + previous_percent = percent; + } + } + + NVT_LOG("Program OK \n"); + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen verify checksum of written + flash function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +int32_t Verify_Flash(void) +{ + uint8_t buf[64] = {0}; + uint32_t XDATA_Addr = ts->mmap->READ_FLASH_CHECKSUM_ADDR; + int32_t ret = 0; + int32_t i = 0; + int32_t k = 0; + uint16_t WR_Filechksum[BLOCK_64KB_NUM] = {0}; + uint16_t RD_Filechksum[BLOCK_64KB_NUM] = {0}; + size_t len_in_blk = 0; + int32_t retry = 0; + + for (i = 0; i < BLOCK_64KB_NUM; i++) { + if (fw_need_write_size > (i * SIZE_64KB)) { + // Calculate WR_Filechksum of each 64KB block + len_in_blk = min(fw_need_write_size - i * SIZE_64KB, (size_t)SIZE_64KB); + WR_Filechksum[i] = i + 0x00 + 0x00 + (((len_in_blk - 1) >> 8) & 0xFF) + ((len_in_blk - 1) & 0xFF); + for (k = 0; k < len_in_blk; k++) { + WR_Filechksum[i] += fw_entry->data[k + i * SIZE_64KB]; + } + WR_Filechksum[i] = 65535 - WR_Filechksum[i] + 1; + + // Fast Read Command + buf[0] = 0x00; + buf[1] = 0x07; + buf[2] = i; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = ((len_in_blk - 1) >> 8) & 0xFF; + buf[6] = (len_in_blk - 1) & 0xFF; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 7); + if (ret < 0) { + NVT_ERR("Fast Read Command error!!(%d)\n", ret); + return ret; + } + // Check 0xAA (Fast Read Command) + retry = 0; + while (1) { + msleep(80); + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Fast Read Command) error!!(%d)\n", ret); + return ret; + } + if (buf[1] == 0xAA) { + break; + } + retry++; + if (unlikely(retry > 5)) { + NVT_ERR("Check 0xAA (Fast Read Command) failed, buf[1]=0x%02X, retry=%d\n", buf[1], retry); + return -1; + } + } + // Read Checksum (write addr high byte & middle byte) + ret = nvt_set_page(I2C_BLDR_Address, XDATA_Addr); + if (ret < 0) { + NVT_ERR("Read Checksum (write addr high byte & middle byte) error!!(%d)\n", ret); + return ret; + } + // Read Checksum + buf[0] = (XDATA_Addr) & 0xFF; + buf[1] = 0x00; + buf[2] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 3); + if (ret < 0) { + NVT_ERR("Read Checksum error!!(%d)\n", ret); + return ret; + } + + RD_Filechksum[i] = (uint16_t)((buf[2] << 8) | buf[1]); + if (WR_Filechksum[i] != RD_Filechksum[i]) { + NVT_ERR("Verify Fail%d!!\n", i); + NVT_ERR("RD_Filechksum[%d]=0x%04X, WR_Filechksum[%d]=0x%04X\n", i, RD_Filechksum[i], i, WR_Filechksum[i]); + return -1; + } + } + } + + NVT_LOG("Verify OK \n"); + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen update firmware function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +int32_t Update_Firmware(void) +{ + int32_t ret = 0; + + //---Stop CRC check to prevent IC auto reboot--- + nvt_stop_crc_reboot(); + + // Step 1 : initial bootloader + ret = Init_BootLoader(); + if (ret) { + return ret; + } + + // Step 2 : Resume PD + ret = Resume_PD(); + if (ret) { + return ret; + } + + // Step 3 : Erase + ret = Erase_Flash(); + if (ret) { + return ret; + } + + // Step 4 : Program + ret = Write_Flash(); + if (ret) { + return ret; + } + + // Step 5 : Verify + ret = Verify_Flash(); + if (ret) { + return ret; + } + + //Step 6 : Bootloader Reset + nvt_bootloader_reset(); + nvt_check_fw_reset_state(RESET_STATE_INIT); + nvt_get_fw_info(); + + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen check flash end flag function. + +return: + Executive outcomes. 0---succeed. 1,negative---failed. +*******************************************************/ +int32_t nvt_check_flash_end_flag(void) +{ + uint8_t buf[8] = {0}; + uint8_t nvt_end_flag[NVT_FLASH_END_FLAG_LEN + 1] = {0}; + int32_t ret = 0; + + // Step 1 : initial bootloader + ret = Init_BootLoader(); + if (ret) { + return ret; + } + + // Step 2 : Resume PD + ret = Resume_PD(); + if (ret) { + return ret; + } + + // Step 3 : unlock + buf[0] = 0x00; + buf[1] = 0x35; + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("write unlock error!!(%d)\n", ret); + return ret; + } + msleep(10); + + //Step 4 : Flash Read Command + buf[0] = 0x00; + buf[1] = 0x03; + buf[2] = (NVT_FLASH_END_FLAG_ADDR >> 16) & 0xFF; //Addr_H + buf[3] = (NVT_FLASH_END_FLAG_ADDR >> 8) & 0xFF; //Addr_M + buf[4] = NVT_FLASH_END_FLAG_ADDR & 0xFF; //Addr_L + buf[5] = (NVT_FLASH_END_FLAG_LEN >> 8) & 0xFF; //Len_H + buf[6] = NVT_FLASH_END_FLAG_LEN & 0xFF; //Len_L + ret = CTP_I2C_WRITE(ts->client, I2C_HW_Address, buf, 7); + if (ret < 0) { + NVT_ERR("write Read Command error!!(%d)\n", ret); + return ret; + } + msleep(10); + + // Check 0xAA (Read Command) + buf[0] = 0x00; + buf[1] = 0x00; + ret = CTP_I2C_READ(ts->client, I2C_HW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Check 0xAA (Read Command) error!!(%d)\n", ret); + return ret; + } + if (buf[1] != 0xAA) { + NVT_ERR("Check 0xAA (Read Command) error!! status=0x%02X\n", buf[1]); + return -1; + } + + msleep(10); + + //Step 5 : Read Flash Data + ret = nvt_set_page(I2C_BLDR_Address, ts->mmap->READ_FLASH_CHECKSUM_ADDR); + if (ret < 0) { + NVT_ERR("change index error!! (%d)\n", ret); + return ret; + } + msleep(10); + + // Read Back + buf[0] = ts->mmap->READ_FLASH_CHECKSUM_ADDR & 0xFF; + ret = CTP_I2C_READ(ts->client, I2C_BLDR_Address, buf, 6); + if (ret < 0) { + NVT_ERR("Read Back error!! (%d)\n", ret); + return ret; + } + + //buf[3:5] => NVT End Flag + strlcpy(nvt_end_flag, &buf[3], sizeof(nvt_end_flag)); + NVT_LOG("nvt_end_flag=%s (%02X %02X %02X)\n", nvt_end_flag, buf[3], buf[4], buf[5]); + + if ((memcmp(nvt_end_flag, "NVT", NVT_FLASH_END_FLAG_LEN) == 0) || + (memcmp(nvt_end_flag, "MOD", NVT_FLASH_END_FLAG_LEN) == 0)) { + return 0; + } else { + NVT_ERR("\"NVT\" end flag not found!\n"); + return 1; + } +} + +/******************************************************* +Description: + Novatek touchscreen update firmware when booting + function. + +return: + n.a. +*******************************************************/ +void Boot_Update_Firmware(struct work_struct *work) +{ + int32_t ret = 0; + + char firmware_name[256] = ""; + + snprintf(firmware_name, sizeof(firmware_name), + BOOT_UPDATE_FIRMWARE_NAME); + + // request bin file in "/etc/firmware" + ret = update_firmware_request(firmware_name); + if (ret) { + NVT_ERR("update_firmware_request failed. (%d)\n", ret); + return; + } + + mutex_lock(&ts->lock); + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + nvt_sw_reset_idle(); + + ret = Check_CheckSum(); + + if (ret < 0) { // read firmware checksum failed + NVT_ERR("read firmware checksum failed\n"); + Update_Firmware(); + } else if ((ret == 0) && (Check_FW_Ver() == 0)) { // (fw checksum not match) && (bin fw version >= ic fw version) + NVT_LOG("firmware version not match\n"); + Update_Firmware(); + } else if (nvt_check_flash_end_flag()) { + NVT_LOG("check flash end flag failed\n"); + Update_Firmware(); + } else { + // Bootloader Reset + nvt_bootloader_reset(); + ret = nvt_check_fw_reset_state(RESET_STATE_INIT); + if (ret) { + NVT_LOG("check fw reset state failed\n"); + Update_Firmware(); + } + } + + mutex_unlock(&ts->lock); + + update_firmware_release(); +} +#endif /* BOOT_UPDATE_FIRMWARE */ + +#else /* NT36XXX_SPI */ + +#include +#include + +#include "nt36xxx.h" + +#if NVT_SPI_BOOT_UPDATE_FIRMWARE + +#define SIZE_4KB 4096 +#define FLASH_SECTOR_SIZE SIZE_4KB +#define FW_BIN_VER_OFFSET (nvt_spi_fw_need_write_size - SIZE_4KB) +#define FW_BIN_VER_BAR_OFFSET (FW_BIN_VER_OFFSET + 1) +#define NVT_FLASH_END_FLAG_LEN 3 +#define NVT_FLASH_END_FLAG_ADDR (nvt_spi_fw_need_write_size - NVT_FLASH_END_FLAG_LEN) + +#define NVT_DUMP_PARTITION (0) +#define NVT_DUMP_PARTITION_LEN (1024) +#define NVT_DUMP_PARTITION_PATH "/data/local/tmp" + +static ktime_t nvt_spi_start, nvt_spi_end; +static const struct firmware *nvt_spi_fw_entry; +static size_t nvt_spi_fw_need_write_size; +static uint8_t *nvt_spi_fwbuf; + +struct nvt_spi_bin_map_t { + char name[12]; + uint32_t BIN_addr; + uint32_t SRAM_addr; + uint32_t size; + uint32_t crc; +}; + +static struct nvt_spi_bin_map_t *nvt_spi_bin_map; + +static int32_t nvt_spi_get_fw_need_write_size(const struct firmware *fw_entry) +{ + int32_t i = 0; + int32_t total_sectors_to_check = 0; + + total_sectors_to_check = fw_entry->size / FLASH_SECTOR_SIZE; + /* printk("total_sectors_to_check = %d\n", total_sectors_to_check); */ + + for (i = total_sectors_to_check; i > 0; i--) { + // printk("current end flag address checked = 0x%X\n", + // i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN); + /* check if there is end flag "NVT" at the end of this sector */ + if (memcmp(&fw_entry->data[i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN], + "NVT", NVT_FLASH_END_FLAG_LEN) == 0) { + nvt_spi_fw_need_write_size = i * FLASH_SECTOR_SIZE; + NVT_LOG("fw_need_write_size = %zu(0x%zx), NVT end flag\n", + nvt_spi_fw_need_write_size, nvt_spi_fw_need_write_size); + return 0; + } + + /* check if there is end flag "MOD" at the end of this sector */ + if (memcmp(&fw_entry->data[i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN], + "MOD", NVT_FLASH_END_FLAG_LEN) == 0) { + nvt_spi_fw_need_write_size = i * FLASH_SECTOR_SIZE; + NVT_LOG("fw_need_write_size = %zu(0x%zx), MOD end flag\n", + nvt_spi_fw_need_write_size, nvt_spi_fw_need_write_size); + return 0; + } + } + + NVT_ERR("end flag \"NVT\" \"MOD\" not found!\n"); + return -EINVAL; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen init variable and allocate buffer + * for download firmware function. + * + * return: + * n.a. + ****************************************************** + */ +static int32_t nvt_spi_download_init(void) +{ + uint8_t *buf; + /* allocate buffer for transfer firmware */ + //NVT_LOG("NVT_TRANSFER_LEN = 0x%06X\n", NVT_SPI_TRANSFER_LEN); + + if (nvt_spi_fwbuf == NULL) { + buf = kzalloc((NVT_SPI_TRANSFER_LEN + 1 + NVT_SPI_DUMMY_BYTES), GFP_KERNEL); + if (buf == NULL) { + NVT_ERR("kzalloc for fwbuf failed!\n"); + return -ENOMEM; + } + nvt_spi_fwbuf = buf; + } + + return 0; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen checksum function. Calculate bin + * file checksum for comparison. + * + * return: + * n.a. + ****************************************************** + */ +static uint32_t CheckSum(const u8 *data, size_t len) +{ + uint32_t i = 0; + uint32_t checksum = 0; + + for (i = 0 ; i < len + 1; i++) + checksum += data[i]; + + checksum += len; + checksum = ~checksum + 1; + + return checksum; +} + +static uint32_t byte_to_word(const uint8_t *data) +{ + return data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24); +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen parsing bin header function. + * + * return: + * n.a. + ****************************************************** + */ +static uint32_t nvt_spi_partition; +static uint8_t nvt_spi_ilm_dlm_num = 2; +static uint8_t nvt_spi_cascade_2nd_header_info; +static int32_t nvt_spi_bin_header_parser(const u8 *fwdata, size_t fwsize) +{ + uint32_t list = 0; + uint32_t pos = 0x00; + uint32_t end = 0x00; + uint8_t info_sec_num = 0; + uint8_t ovly_sec_num = 0; + uint8_t ovly_info = 0; + uint8_t find_bin_header = 0; + struct nvt_spi_bin_map_t *bin_map = NULL; + struct nvt_spi_data_t *ts = nvt_spi_data; + + /* Find the header size */ + end = fwdata[0] + (fwdata[1] << 8) + (fwdata[2] << 16) + (fwdata[3] << 24); + + /* check cascade next header */ + nvt_spi_cascade_2nd_header_info = (fwdata[0x20] & 0x02) >> 1; + NVT_LOG("cascade_2nd_header_info = %d\n", nvt_spi_cascade_2nd_header_info); + + if (nvt_spi_cascade_2nd_header_info) { + pos = 0x30; // info section start at 0x30 offset + while (pos < (end / 2)) { + info_sec_num++; + pos += 0x10; /* each header info is 16 bytes */ + } + + info_sec_num = info_sec_num + 1; //next header section + } else { + pos = 0x30; // info section start at 0x30 offset + while (pos < end) { + info_sec_num++; + pos += 0x10; /* each header info is 16 bytes */ + } + } + + /* + * Find the DLM OVLY section + * [0:3] Overlay Section Number + * [4] Overlay Info + */ + ovly_info = (fwdata[0x28] & 0x10) >> 4; + ovly_sec_num = (ovly_info) ? (fwdata[0x28] & 0x0F) : 0; + + /* + * calculate all partition number + * ilm_dlm_num (ILM & DLM) + ovly_sec_num + info_sec_num + */ + nvt_spi_partition = nvt_spi_ilm_dlm_num + ovly_sec_num + info_sec_num; + NVT_LOG("ovly_info=%d, ilm_dlm_num=%d, ovly_sec_num=%d, info_sec_num=%d, partition=%d\n", + ovly_info, nvt_spi_ilm_dlm_num, ovly_sec_num, info_sec_num, nvt_spi_partition); + + + /* allocated memory for header info */ + bin_map = kzalloc((nvt_spi_partition+1) * sizeof(struct nvt_spi_bin_map_t), GFP_KERNEL); + if (bin_map == NULL) { + NVT_ERR("kzalloc for bin_map failed!\n"); + return -ENOMEM; + } + nvt_spi_bin_map = bin_map; + + for (list = 0; list < nvt_spi_partition; list++) { + /* + * [1] parsing ILM & DLM header info + * BIN_addr : SRAM_addr : size (12-bytes) + * crc located at 0x18 & 0x1C + */ + if (list < nvt_spi_ilm_dlm_num) { + bin_map[list].BIN_addr = byte_to_word(&fwdata[0 + list * 12]); + bin_map[list].SRAM_addr = byte_to_word(&fwdata[4 + list * 12]); + bin_map[list].size = byte_to_word(&fwdata[8 + list * 12]); + if (ts->hw_crc) + bin_map[list].crc = byte_to_word(&fwdata[0x18 + list * 4]); + else { //ts->hw_crc + if ((bin_map[list].BIN_addr + bin_map[list].size) < fwsize) + bin_map[list].crc = CheckSum( + &fwdata[bin_map[list].BIN_addr], + bin_map[list].size); + else { + NVT_ERR("access range (0x%08X to 0x%08X) is too large!\n", + bin_map[list].BIN_addr, + bin_map[list].BIN_addr + bin_map[list].size); + return -EINVAL; + } + } //ts->hw_crc + if (list == 0) + snprintf(bin_map[list].name, sizeof(bin_map[list].name), "ILM"); + else if (list == 1) + snprintf(bin_map[list].name, sizeof(bin_map[list].name), "DLM"); + } + + /* + * [2] parsing others header info + * SRAM_addr : size : BIN_addr : crc (16-bytes) + */ + if ((list >= nvt_spi_ilm_dlm_num) + && (list < (nvt_spi_ilm_dlm_num + info_sec_num))) { + + if (find_bin_header == 0) { + /* others partition located at 0x30 offset */ + pos = 0x30 + (0x10 * (list - nvt_spi_ilm_dlm_num)); + } else if (find_bin_header && nvt_spi_cascade_2nd_header_info) { + /* cascade 2nd header info */ + pos = end - 0x10; + } + + bin_map[list].SRAM_addr = byte_to_word(&fwdata[pos]); + bin_map[list].size = byte_to_word(&fwdata[pos + 4]); + bin_map[list].BIN_addr = byte_to_word(&fwdata[pos + 8]); + if (ts->hw_crc) + bin_map[list].crc = byte_to_word(&fwdata[pos + 12]); + else { //ts->hw_crc + if ((bin_map[list].BIN_addr + bin_map[list].size) < fwsize) + bin_map[list].crc = CheckSum( + &fwdata[bin_map[list].BIN_addr], + bin_map[list].size); + else { + NVT_ERR("access range (0x%08X to 0x%08X) is too large!\n", + bin_map[list].BIN_addr, + bin_map[list].BIN_addr + bin_map[list].size); + return -EINVAL; + } + } //ts->hw_crc + /* detect header end to protect parser function */ + if ((bin_map[list].BIN_addr < end) && (bin_map[list].size != 0)) { + snprintf(bin_map[list].name, sizeof(bin_map[list].name), + "Header"); + find_bin_header = 1; + } else + snprintf(bin_map[list].name, sizeof(bin_map[list].name), + "Info-%d", (list - nvt_spi_ilm_dlm_num)); + } + + /* + * [3] parsing overlay section header info + * SRAM_addr : size : BIN_addr : crc (16-bytes) + */ + if (list >= (nvt_spi_ilm_dlm_num + info_sec_num)) { + /* overlay info located at DLM (list = 1) start addr */ + pos = bin_map[1].BIN_addr; + pos += (0x10 * (list - nvt_spi_ilm_dlm_num - info_sec_num)); + + bin_map[list].SRAM_addr = byte_to_word(&fwdata[pos]); + bin_map[list].size = byte_to_word(&fwdata[pos + 4]); + bin_map[list].BIN_addr = byte_to_word(&fwdata[pos + 8]); + if (ts->hw_crc) + bin_map[list].crc = byte_to_word(&fwdata[pos + 12]); + else { //ts->hw_crc + if ((bin_map[list].BIN_addr + bin_map[list].size) < fwsize) + bin_map[list].crc = CheckSum( + &fwdata[bin_map[list].BIN_addr], + bin_map[list].size); + else { + NVT_ERR("access range (0x%08X to 0x%08X) is too large!\n", + bin_map[list].BIN_addr, + bin_map[list].BIN_addr + bin_map[list].size); + return -EINVAL; + } + } //ts->hw_crc + snprintf(bin_map[list].name, sizeof(bin_map[list].name), + "Overlay-%d", + (list - nvt_spi_ilm_dlm_num - info_sec_num)); + } + + /* BIN size error detect */ + if ((bin_map[list].BIN_addr + bin_map[list].size) > fwsize) { + NVT_ERR("access range (0x%08X to 0x%08X) is larger than bin size!\n", + bin_map[list].BIN_addr, + bin_map[list].BIN_addr + bin_map[list].size); + return -EINVAL; + } + +// NVT_LOG("[%d][%s] SRAM (0x%08X), SIZE (0x%08X), BIN (0x%08X), CRC (0x%08X)\n", +// list, bin_map[list].name, +// bin_map[list].SRAM_addr, bin_map[list].size, +// bin_map[list].BIN_addr, bin_map[list].crc); + } + + return 0; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen release update firmware function. + * + * return: + * n.a. + ****************************************************** + */ +static void nvt_spi_update_firmware_release(void) +{ + if (nvt_spi_fw_entry) + release_firmware(nvt_spi_fw_entry); + + nvt_spi_fw_entry = NULL; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen request update firmware function. + * + * return: + * Executive outcomes. 0---succeed. -1,-22---failed. + ****************************************************** + */ +static int32_t nvt_spi_update_firmware_request(char *filename) +{ + uint8_t retry = 0; + int32_t ret = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + uint8_t ver; + + if (filename == NULL) + return -ENOENT; + + while (1) { + NVT_LOG("filename is %s\n", filename); + + ret = request_firmware(&nvt_spi_fw_entry, filename, &ts->client->dev); + if (ret) { + NVT_ERR("firmware load failed, ret=%d\n", ret); + goto request_fail; + } + + // check FW need to write size + if (nvt_spi_get_fw_need_write_size(nvt_spi_fw_entry)) { + NVT_ERR("get fw need to write size fail!\n"); + ret = -EINVAL; + goto invalid; + } + + // check if FW version add FW version bar equals 0xFF + ver = *(nvt_spi_fw_entry->data + FW_BIN_VER_OFFSET); + if (ver + *(nvt_spi_fw_entry->data + FW_BIN_VER_BAR_OFFSET) != 0xFF) { + NVT_ERR("bin file FW_VER + FW_VER_BAR should be 0xFF!\n"); + NVT_ERR("FW_VER=0x%02X, FW_VER_BAR=0x%02X\n", + *(nvt_spi_fw_entry->data+FW_BIN_VER_OFFSET), + *(nvt_spi_fw_entry->data+FW_BIN_VER_BAR_OFFSET)); + ret = -ENOEXEC; + goto invalid; + } + + /* BIN Header Parser */ + ret = nvt_spi_bin_header_parser(nvt_spi_fw_entry->data, nvt_spi_fw_entry->size); + if (ret) { + NVT_ERR("bin header parser failed\n"); + goto invalid; + } else + break; + +invalid: + nvt_spi_update_firmware_release(); + if (!IS_ERR_OR_NULL(nvt_spi_bin_map)) { + kfree(nvt_spi_bin_map); + nvt_spi_bin_map = NULL; + } + +request_fail: + retry++; + if (unlikely(retry > 2)) { + NVT_ERR("error, retry=%d\n", retry); + break; + } + } + + return ret; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen write data to sram function. + * + * - fwdata : The buffer is written + * - SRAM_addr: The sram destination address + * - size : Number of data bytes in @fwdata being written + * - BIN_addr : The transferred data offset of @fwdata + * + * return: + * Executive outcomes. 0---succeed. else---fail. + ******************************************************* + */ +static int32_t nvt_spi_write_sram(const u8 *fwdata, + uint32_t SRAM_addr, uint32_t size, uint32_t BIN_addr) +{ + int32_t ret = 0; + uint32_t i = 0; + uint16_t len = 0; + int32_t count = 0; + + if (size % NVT_SPI_TRANSFER_LEN) + count = (size / NVT_SPI_TRANSFER_LEN) + 1; + else + count = (size / NVT_SPI_TRANSFER_LEN); + + for (i = 0 ; i < count ; i++) { + len = (size < NVT_SPI_TRANSFER_LEN) ? size : NVT_SPI_TRANSFER_LEN; + + //---set xdata index to start address of SRAM--- + ret = nvt_spi_set_page(SRAM_addr); + if (ret) { + NVT_ERR("set page failed, ret = %d\n", ret); + return ret; + } + + //---write data into SRAM--- + nvt_spi_fwbuf[0] = SRAM_addr & 0x7F; //offset + memcpy(nvt_spi_fwbuf+1, &fwdata[BIN_addr], len); //payload + ret = nvt_spi_write(nvt_spi_fwbuf, len+1); + if (ret) { + NVT_ERR("write to sram failed, ret = %d\n", ret); + return ret; + } + + SRAM_addr += NVT_SPI_TRANSFER_LEN; + BIN_addr += NVT_SPI_TRANSFER_LEN; + size -= NVT_SPI_TRANSFER_LEN; + } + + return ret; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen nvt_spi_write_firmware function to write + * firmware into each partition. + * + * return: + * n.a. + ****************************************************** + */ +static int32_t nvt_spi_write_firmware(const u8 *fwdata, size_t fwsize) +{ + uint32_t list = 0; + char *name; + uint32_t BIN_addr, SRAM_addr, size; + int32_t ret = 0; + + memset(nvt_spi_fwbuf, 0, (NVT_SPI_TRANSFER_LEN+1)); + + for (list = 0; list < nvt_spi_partition; list++) { + /* initialize variable */ + SRAM_addr = nvt_spi_bin_map[list].SRAM_addr; + size = nvt_spi_bin_map[list].size; + BIN_addr = nvt_spi_bin_map[list].BIN_addr; + name = nvt_spi_bin_map[list].name; + +// NVT_LOG("[%d][%s] SRAM (0x%08X), SIZE (0x%08X), BIN (0x%08X)\n", +// list, name, SRAM_addr, size, BIN_addr); + + /* Check data size */ + if ((BIN_addr + size) > fwsize) { + NVT_ERR("access range (0x%08X to 0x%08X) is larger than bin size!\n", + BIN_addr, BIN_addr + size); + ret = -EINVAL; + goto out; + } + + /* ignore reserved partition (Reserved Partition size is zero) */ + if (!size) + continue; + else + size = size + 1; + + /* write data to SRAM */ + ret = nvt_spi_write_sram(fwdata, SRAM_addr, size, BIN_addr); + if (ret) { + NVT_ERR("sram program failed, ret = %d\n", ret); + goto out; + } + } + +out: + return ret; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen check checksum function. + * This function will compare file checksum and fw checksum. + * + * return: + * n.a. + ******************************************************* + */ +static int32_t nvt_spi_check_fw_checksum(void) +{ + uint32_t fw_checksum = 0; + uint32_t len = nvt_spi_partition * 4; + uint32_t list = 0; + int32_t ret = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + + memset(nvt_spi_fwbuf, 0, (len+1)); + + //---set xdata index to checksum--- + nvt_spi_set_page(ts->mmap->R_ILM_CHECKSUM_ADDR); + + /* read checksum */ + nvt_spi_fwbuf[0] = (ts->mmap->R_ILM_CHECKSUM_ADDR) & 0x7F; + ret = nvt_spi_read(nvt_spi_fwbuf, len+1); + if (ret) { + NVT_ERR("Read fw checksum failed\n"); + return ret; + } + + /* + * Compare each checksum from fw + * ILM + DLM + Overlay + Info + * nvt_spi_ilm_dlm_num (ILM & DLM) + ovly_sec_num + info_sec_num + */ + for (list = 0; list < nvt_spi_partition; list++) { + fw_checksum = byte_to_word(&nvt_spi_fwbuf[1+list*4]); + + /* ignore reserved partition (Reserved Partition size is zero) */ + if (!nvt_spi_bin_map[list].size) + continue; + + if (nvt_spi_bin_map[list].crc != fw_checksum) { + NVT_ERR("[%d] BIN_checksum=0x%08X, FW_checksum=0x%08X\n", + list, nvt_spi_bin_map[list].crc, fw_checksum); + ret = -EIO; + } + } + + return ret; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen set bootload crc reg bank function. + * This function will set hw crc reg before enable crc function. + * + * return: + * n.a. + ****************************************************** + */ +static void nvt_spi_set_bld_crc_bank(uint32_t DES_ADDR, uint32_t SRAM_ADDR, + uint32_t LENGTH_ADDR, uint32_t size, + uint32_t G_CHECKSUM_ADDR, uint32_t crc) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + /* write destination address */ + nvt_spi_set_page(DES_ADDR); + nvt_spi_fwbuf[0] = DES_ADDR & 0x7F; + nvt_spi_fwbuf[1] = (SRAM_ADDR) & 0xFF; + nvt_spi_fwbuf[2] = (SRAM_ADDR >> 8) & 0xFF; + nvt_spi_fwbuf[3] = (SRAM_ADDR >> 16) & 0xFF; + nvt_spi_write(nvt_spi_fwbuf, 4); + + /* write length */ + //nvt_spi_set_page(LENGTH_ADDR); + nvt_spi_fwbuf[0] = LENGTH_ADDR & 0x7F; + nvt_spi_fwbuf[1] = (size) & 0xFF; + nvt_spi_fwbuf[2] = (size >> 8) & 0xFF; + nvt_spi_fwbuf[3] = (size >> 16) & 0x01; + if (ts->hw_crc == 1) + nvt_spi_write(nvt_spi_fwbuf, 3); + else if (ts->hw_crc > 1) + nvt_spi_write(nvt_spi_fwbuf, 4); + + /* write golden dlm checksum */ + //nvt_spi_set_page(G_CHECKSUM_ADDR); + nvt_spi_fwbuf[0] = G_CHECKSUM_ADDR & 0x7F; + nvt_spi_fwbuf[1] = (crc) & 0xFF; + nvt_spi_fwbuf[2] = (crc >> 8) & 0xFF; + nvt_spi_fwbuf[3] = (crc >> 16) & 0xFF; + nvt_spi_fwbuf[4] = (crc >> 24) & 0xFF; + nvt_spi_write(nvt_spi_fwbuf, 5); +} + +/* + ********************************************************* + * Description: + * Novatek touchscreen set BLD hw crc function. + * This function will set ILM and DLM crc information to register. + * + * return: + * n.a. + ********************************************************* + */ +static void nvt_spi_set_bld_hw_crc(void) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + /* [0] ILM */ + /* write register bank */ + nvt_spi_set_bld_crc_bank(ts->mmap->ILM_DES_ADDR, nvt_spi_bin_map[0].SRAM_addr, + ts->mmap->ILM_LENGTH_ADDR, nvt_spi_bin_map[0].size, + ts->mmap->G_ILM_CHECKSUM_ADDR, nvt_spi_bin_map[0].crc); + + /* [1] DLM */ + /* write register bank */ + nvt_spi_set_bld_crc_bank(ts->mmap->DLM_DES_ADDR, nvt_spi_bin_map[1].SRAM_addr, + ts->mmap->DLM_LENGTH_ADDR, nvt_spi_bin_map[1].size, + ts->mmap->G_DLM_CHECKSUM_ADDR, nvt_spi_bin_map[1].crc); +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen read BLD hw crc info function. + * This function will check crc results from register. + * + * return: + * n.a. + ****************************************************** + */ +static void nvt_spi_read_bld_hw_crc(void) +{ + uint8_t buf[8] = {0}; + uint32_t g_crc = 0, r_crc = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + + /* CRC Flag */ + nvt_spi_set_page(ts->mmap->BLD_ILM_DLM_CRC_ADDR); + buf[0] = ts->mmap->BLD_ILM_DLM_CRC_ADDR & 0x7F; + buf[1] = 0x00; + nvt_spi_read(buf, 2); + NVT_ERR("crc_done = %d, ilm_crc_flag = %d, dlm_crc_flag = %d\n", + (buf[1] >> 2) & 0x01, (buf[1] >> 0) & 0x01, (buf[1] >> 1) & 0x01); + + /* ILM CRC */ + nvt_spi_set_page(ts->mmap->G_ILM_CHECKSUM_ADDR); + buf[0] = ts->mmap->G_ILM_CHECKSUM_ADDR & 0x7F; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + nvt_spi_read(buf, 5); + g_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24); + + nvt_spi_set_page(ts->mmap->R_ILM_CHECKSUM_ADDR); + buf[0] = ts->mmap->R_ILM_CHECKSUM_ADDR & 0x7F; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + nvt_spi_read(buf, 5); + r_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24); + + NVT_ERR("ilm: bin crc = 0x%08X, golden = 0x%08X, result = 0x%08X\n", + nvt_spi_bin_map[0].crc, g_crc, r_crc); + + /* DLM CRC */ + nvt_spi_set_page(ts->mmap->G_DLM_CHECKSUM_ADDR); + buf[0] = ts->mmap->G_DLM_CHECKSUM_ADDR & 0x7F; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + nvt_spi_read(buf, 5); + g_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24); + + nvt_spi_set_page(ts->mmap->R_DLM_CHECKSUM_ADDR); + buf[0] = ts->mmap->R_DLM_CHECKSUM_ADDR & 0x7F; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + nvt_spi_read(buf, 5); + r_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24); + + NVT_ERR("dlm: bin crc = 0x%08X, golden = 0x%08X, result = 0x%08X\n", + nvt_spi_bin_map[1].crc, g_crc, r_crc); + +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen Download_Firmware with HW CRC + * function. It's complete download firmware flow. + * + * return: + * Executive outcomes. 0---succeed. else---fail. + ****************************************************** + */ +static int32_t nvt_spi_download_firmware_hw_crc(void) +{ + uint8_t retry = 0; + int32_t ret = 0; + const struct firmware *fw = nvt_spi_fw_entry; + + nvt_spi_start = ktime_get(); + + while (1) { + /* bootloader reset to reset MCU */ + nvt_spi_bootloader_reset(); + + /* set ilm & dlm reg bank */ + nvt_spi_set_bld_hw_crc(); + + /* Start to write firmware process */ + if (nvt_spi_cascade_2nd_header_info) { + /* for cascade */ + nvt_spi_tx_auto_copy_mode(); + + ret = nvt_spi_write_firmware(fw->data, fw->size); + if (ret) { + NVT_ERR("Write_Firmware failed. (%d)\n", ret); + goto fail; + } + + ret = nvt_spi_check_spi_dma_tx_info(); + if (ret) { + NVT_ERR("spi dma tx info failed. (%d)\n", ret); + goto fail; + } + } else { + ret = nvt_spi_write_firmware(fw->data, fw->size); + if (ret) { + NVT_ERR("Write_Firmware failed. (%d)\n", ret); + goto fail; + } + } + +#if NVT_DUMP_PARTITION + ret = nvt_dump_partition(); + if (ret) + NVT_ERR("nvt_dump_partition failed, ret = %d\n", ret); +#endif + + /* enable hw bld crc function */ + nvt_spi_bld_crc_enable(); + + /* clear fw reset status & enable fw crc check */ + nvt_spi_fw_crc_enable(); + + /* Set Boot Ready Bit */ + nvt_spi_boot_ready(); + + ret = nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_INIT); + if (ret) { + NVT_ERR("nvt_check_fw_reset_state failed. (%d)\n", ret); + goto fail; + } else { + break; + } + +fail: + retry++; + if (unlikely(retry > 2)) { + NVT_ERR("error, retry=%d\n", retry); + nvt_spi_read_bld_hw_crc(); + break; + } + } + + nvt_spi_end = ktime_get(); + + return ret; +} + +/* + ******************************************************** + * Description: + * Novatek touchscreen Download_Firmware function. It's + * complete download firmware flow. + * + * return: + * n.a. + ****************************************************** + */ +static int32_t nvt_spi_download_firmware(void) +{ + uint32_t addr; + uint8_t retry = 0; + int32_t ret = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + + nvt_spi_start = ktime_get(); + + while (1) { + /* + * Send eng reset cmd before download FW + * Keep TP_RESX low when send eng reset cmd + */ +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 0); + mdelay(1); //wait 1ms +#endif + nvt_spi_eng_reset(); +#if NVT_SPI_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 1); + mdelay(10); //wait tRT2BRST after TP_RST +#endif + nvt_spi_bootloader_reset(); + + addr = ts->mmap->EVENT_BUF_ADDR; + /* clear fw reset status */ + nvt_spi_write_addr(addr | NVT_SPI_EVENT_MAP_RESET_COMPLETE, 0x00); + + /* Start to write firmware process */ + ret = nvt_spi_write_firmware(nvt_spi_fw_entry->data, nvt_spi_fw_entry->size); + if (ret) { + NVT_ERR("Write_Firmware failed. (%d)\n", ret); + goto fail; + } + +#if NVT_DUMP_PARTITION + ret = nvt_dump_partition(); + if (ret) + NVT_ERR("nvt_dump_partition failed, ret = %d\n", ret); +#endif + + /* Set Boot Ready Bit */ + nvt_spi_boot_ready(); + + ret = nvt_spi_check_fw_reset_state(NVT_SPI_RESET_STATE_INIT); + if (ret) { + NVT_ERR("nvt_check_fw_reset_state failed. (%d)\n", ret); + goto fail; + } + + /* check fw checksum result */ + ret = nvt_spi_check_fw_checksum(); + if (ret) { + NVT_ERR("firmware checksum not match, retry=%d\n", retry); + goto fail; + } else + break; + +fail: + retry++; + if (unlikely(retry > 2)) { + NVT_ERR("error, retry=%d\n", retry); + break; + } + } + + nvt_spi_end = ktime_get(); + + return ret; +} + +/* + ****************************************************** + * Description: + * Novatek touchscreen update firmware main function. + * + * return: + * n.a. + ****************************************************** + */ +int32_t nvt_spi_update_firmware(char *firmware_name) +{ + int32_t ret = 0; + struct nvt_spi_data_t *ts = nvt_spi_data; + + // request bin file in "/etc/firmware" + ret = nvt_spi_update_firmware_request(firmware_name); + if (ret) { + NVT_ERR("update_firmware_request failed. (%d)\n", ret); + goto request_firmware_fail; + } + + /* initial buffer and variable */ + ret = nvt_spi_download_init(); + if (ret) { + NVT_ERR("Download Init failed. (%d)\n", ret); + goto download_fail; + } + + /* download firmware process */ + if (ts->hw_crc) + ret = nvt_spi_download_firmware_hw_crc(); + else + ret = nvt_spi_download_firmware(); + if (ret) { + NVT_ERR("Download Firmware failed. (%d)\n", ret); + goto download_fail; + } + + NVT_LOG("Update firmware success! <%ld us>\n", + (long) ktime_us_delta(nvt_spi_end, nvt_spi_start)); + + /* Get FW Info */ + ret = nvt_spi_get_fw_info(); + if (ret) + NVT_ERR("nvt_get_fw_info failed. (%d)\n", ret); + +download_fail: + if (!IS_ERR_OR_NULL(nvt_spi_bin_map)) { + kfree(nvt_spi_bin_map); + nvt_spi_bin_map = NULL; + } + + nvt_spi_update_firmware_release(); +request_firmware_fail: + + return ret; +} + +/* + ******************************************************* + * Description: + * Novatek touchscreen update firmware when booting + * function. + * + * return: + * n.a. + ****************************************************** + */ +void nvt_spi_update_firmware_work(struct work_struct *work) +{ + struct nvt_spi_data_t *ts = nvt_spi_data; + + mutex_lock(&ts->lock); + nvt_spi_update_firmware(NVT_SPI_BOOT_UPDATE_FIRMWARE_NAME); + mutex_unlock(&ts->lock); +} +#endif + +#endif diff --git a/nt36xxx/nt36xxx_mem_map.h b/nt36xxx/nt36xxx_mem_map.h new file mode 100644 index 0000000000..c75d2d4cdb --- /dev/null +++ b/nt36xxx/nt36xxx_mem_map.h @@ -0,0 +1,607 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 48764 $ + * $Date: 2019-08-08 14:52:12 +0800 (Thu, 08 Aug 2019) $ + * + * 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. + * + */ +#define CHIP_VER_TRIM_ADDR 0x3F004 +#define CHIP_VER_TRIM_OLD_ADDR 0x1F64E + +#if !defined(NVT_NT36XXX_SPI) /* NT36XXX I2C */ + +struct nvt_ts_mem_map { + uint32_t EVENT_BUF_ADDR; + uint32_t RAW_PIPE0_ADDR; + uint32_t RAW_PIPE1_ADDR; + uint32_t BASELINE_ADDR; + uint32_t BASELINE_BTN_ADDR; + uint32_t DIFF_PIPE0_ADDR; + uint32_t DIFF_PIPE1_ADDR; + uint32_t RAW_BTN_PIPE0_ADDR; + uint32_t RAW_BTN_PIPE1_ADDR; + uint32_t DIFF_BTN_PIPE0_ADDR; + uint32_t DIFF_BTN_PIPE1_ADDR; + uint32_t READ_FLASH_CHECKSUM_ADDR; + uint32_t RW_FLASH_DATA_ADDR; +}; + +struct nvt_ts_hw_info { + uint8_t carrier_system; + uint8_t hw_crc; +}; + +static const struct nvt_ts_mem_map NT36526_memory_map = { + .EVENT_BUF_ADDR = 0x22D00, + .RAW_PIPE0_ADDR = 0x24000, + .RAW_PIPE1_ADDR = 0x24000, + .BASELINE_ADDR = 0x21758, + .BASELINE_BTN_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x20AB0, + .DIFF_PIPE1_ADDR = 0x24AB0, + .RAW_BTN_PIPE0_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0, + .READ_FLASH_CHECKSUM_ADDR = 0x24000, + .RW_FLASH_DATA_ADDR = 0x24002, +}; + +static const struct nvt_ts_mem_map NT36675_memory_map = { + .EVENT_BUF_ADDR = 0x22D00, + .RAW_PIPE0_ADDR = 0x24000, + .RAW_PIPE1_ADDR = 0x24000, + .BASELINE_ADDR = 0x21B90, + .BASELINE_BTN_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x20C60, + .DIFF_PIPE1_ADDR = 0x24C60, + .RAW_BTN_PIPE0_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0, + .READ_FLASH_CHECKSUM_ADDR = 0x24000, + .RW_FLASH_DATA_ADDR = 0x24002, +}; + +static const struct nvt_ts_mem_map NT36672A_memory_map = { + .EVENT_BUF_ADDR = 0x21C00, + .RAW_PIPE0_ADDR = 0x20000, + .RAW_PIPE1_ADDR = 0x23000, + .BASELINE_ADDR = 0x20BFC, + .BASELINE_BTN_ADDR = 0x23BFC, + .DIFF_PIPE0_ADDR = 0x206DC, + .DIFF_PIPE1_ADDR = 0x236DC, + .RAW_BTN_PIPE0_ADDR = 0x20510, + .RAW_BTN_PIPE1_ADDR = 0x23510, + .DIFF_BTN_PIPE0_ADDR = 0x20BF0, + .DIFF_BTN_PIPE1_ADDR = 0x23BF0, + .READ_FLASH_CHECKSUM_ADDR = 0x24000, + .RW_FLASH_DATA_ADDR = 0x24002, +}; + +static const struct nvt_ts_mem_map NT36772_memory_map = { + .EVENT_BUF_ADDR = 0x11E00, + .RAW_PIPE0_ADDR = 0x10000, + .RAW_PIPE1_ADDR = 0x12000, + .BASELINE_ADDR = 0x10E70, + .BASELINE_BTN_ADDR = 0x12E70, + .DIFF_PIPE0_ADDR = 0x10830, + .DIFF_PIPE1_ADDR = 0x12830, + .RAW_BTN_PIPE0_ADDR = 0x10E60, + .RAW_BTN_PIPE1_ADDR = 0x12E60, + .DIFF_BTN_PIPE0_ADDR = 0x10E68, + .DIFF_BTN_PIPE1_ADDR = 0x12E68, + .READ_FLASH_CHECKSUM_ADDR = 0x14000, + .RW_FLASH_DATA_ADDR = 0x14002, +}; + +static const struct nvt_ts_mem_map NT36525_memory_map = { + .EVENT_BUF_ADDR = 0x11A00, + .RAW_PIPE0_ADDR = 0x10000, + .RAW_PIPE1_ADDR = 0x12000, + .BASELINE_ADDR = 0x10B08, + .BASELINE_BTN_ADDR = 0x12B08, + .DIFF_PIPE0_ADDR = 0x1064C, + .DIFF_PIPE1_ADDR = 0x1264C, + .RAW_BTN_PIPE0_ADDR = 0x10634, + .RAW_BTN_PIPE1_ADDR = 0x12634, + .DIFF_BTN_PIPE0_ADDR = 0x10AFC, + .DIFF_BTN_PIPE1_ADDR = 0x12AFC, + .READ_FLASH_CHECKSUM_ADDR = 0x14000, + .RW_FLASH_DATA_ADDR = 0x14002, +}; + +static const struct nvt_ts_mem_map NT36676F_memory_map = { + .EVENT_BUF_ADDR = 0x11A00, + .RAW_PIPE0_ADDR = 0x10000, + .RAW_PIPE1_ADDR = 0x12000, + .BASELINE_ADDR = 0x10B08, + .BASELINE_BTN_ADDR = 0x12B08, + .DIFF_PIPE0_ADDR = 0x1064C, + .DIFF_PIPE1_ADDR = 0x1264C, + .RAW_BTN_PIPE0_ADDR = 0x10634, + .RAW_BTN_PIPE1_ADDR = 0x12634, + .DIFF_BTN_PIPE0_ADDR = 0x10AFC, + .DIFF_BTN_PIPE1_ADDR = 0x12AFC, + .READ_FLASH_CHECKSUM_ADDR = 0x14000, + .RW_FLASH_DATA_ADDR = 0x14002, +}; + +static struct nvt_ts_hw_info NT36526_hw_info = { + .carrier_system = 2, + .hw_crc = 2, +}; + +static struct nvt_ts_hw_info NT36675_hw_info = { + .carrier_system = 2, + .hw_crc = 2, +}; + +static struct nvt_ts_hw_info NT36672A_hw_info = { + .carrier_system = 0, + .hw_crc = 1, +}; + +static struct nvt_ts_hw_info NT36772_hw_info = { + .carrier_system = 0, + .hw_crc = 0, +}; + +static struct nvt_ts_hw_info NT36525_hw_info = { + .carrier_system = 0, + .hw_crc = 0, +}; + +static struct nvt_ts_hw_info NT36676F_hw_info = { + .carrier_system = 0, + .hw_crc = 0, +}; + +#define NVT_ID_BYTE_MAX 6 +struct nvt_ts_trim_id_table { + uint8_t id[NVT_ID_BYTE_MAX]; + uint8_t mask[NVT_ID_BYTE_MAX]; + const struct nvt_ts_mem_map *mmap; + const struct nvt_ts_hw_info *hwinfo; +}; + +static const struct nvt_ts_trim_id_table trim_id_table[] = { + {.id = {0x20, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x00, 0xFF, 0xFF, 0x80, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x0C, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0E, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x0C, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x26, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36526_memory_map, .hwinfo = &NT36526_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x75, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0x55, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xAA, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xAA, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36525_memory_map, .hwinfo = &NT36525_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x76, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36676F_memory_map, .hwinfo = &NT36676F_hw_info} +}; + +#else /* NT36XXX_SPI */ + +#define NVT_SPI_CHIP_VER_TRIM_ADDR 0x3F004 +#define NVT_SPI_CHIP_VER_TRIM_OLD_ADDR 0x1F64E + +struct nvt_spi_mem_map { + uint32_t EVENT_BUF_ADDR; + uint32_t RAW_PIPE0_ADDR; + uint32_t RAW_PIPE1_ADDR; + uint32_t BASELINE_ADDR; + uint32_t BASELINE_BTN_ADDR; + uint32_t DIFF_PIPE0_ADDR; + uint32_t DIFF_PIPE1_ADDR; + uint32_t RAW_BTN_PIPE0_ADDR; + uint32_t RAW_BTN_PIPE1_ADDR; + uint32_t DIFF_BTN_PIPE0_ADDR; + uint32_t DIFF_BTN_PIPE1_ADDR; + uint32_t PEN_2D_BL_TIP_X_ADDR; + uint32_t PEN_2D_BL_TIP_Y_ADDR; + uint32_t PEN_2D_BL_RING_X_ADDR; + uint32_t PEN_2D_BL_RING_Y_ADDR; + uint32_t PEN_2D_DIFF_TIP_X_ADDR; + uint32_t PEN_2D_DIFF_TIP_Y_ADDR; + uint32_t PEN_2D_DIFF_RING_X_ADDR; + uint32_t PEN_2D_DIFF_RING_Y_ADDR; + uint32_t PEN_2D_RAW_TIP_X_ADDR; + uint32_t PEN_2D_RAW_TIP_Y_ADDR; + uint32_t PEN_2D_RAW_RING_X_ADDR; + uint32_t PEN_2D_RAW_RING_Y_ADDR; + uint32_t PEN_1D_DIFF_TIP_X_ADDR; + uint32_t PEN_1D_DIFF_TIP_Y_ADDR; + uint32_t PEN_1D_DIFF_RING_X_ADDR; + uint32_t PEN_1D_DIFF_RING_Y_ADDR; + uint32_t READ_FLASH_CHECKSUM_ADDR; + uint32_t RW_FLASH_DATA_ADDR; + /* Phase 2 Host Download */ + uint32_t BOOT_RDY_ADDR; + uint32_t POR_CD_ADDR; + uint32_t TX_AUTO_COPY_EN; + uint32_t SPI_DMA_TX_INFO; + /* BLD CRC */ + uint32_t BLD_LENGTH_ADDR; + uint32_t ILM_LENGTH_ADDR; + uint32_t DLM_LENGTH_ADDR; + uint32_t BLD_DES_ADDR; + uint32_t ILM_DES_ADDR; + uint32_t DLM_DES_ADDR; + uint32_t G_ILM_CHECKSUM_ADDR; + uint32_t G_DLM_CHECKSUM_ADDR; + uint32_t R_ILM_CHECKSUM_ADDR; + uint32_t R_DLM_CHECKSUM_ADDR; + uint32_t BLD_CRC_EN_ADDR; + uint32_t DMA_CRC_EN_ADDR; + uint32_t BLD_ILM_DLM_CRC_ADDR; + uint32_t DMA_CRC_FLAG_ADDR; +}; + +struct nvt_spi_hw_info { + uint8_t hw_crc; +}; + +static const struct nvt_spi_mem_map NT36523_memory_map = { + .EVENT_BUF_ADDR = 0x2FE00, + .RAW_PIPE0_ADDR = 0x30FA0, + .RAW_PIPE1_ADDR = 0x30FA0, + .BASELINE_ADDR = 0x36510, + .BASELINE_BTN_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x373E8, + .DIFF_PIPE1_ADDR = 0x38068, + .RAW_BTN_PIPE0_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0, + .PEN_2D_BL_TIP_X_ADDR = 0x2988A, + .PEN_2D_BL_TIP_Y_ADDR = 0x29A1A, + .PEN_2D_BL_RING_X_ADDR = 0x29BAA, + .PEN_2D_BL_RING_Y_ADDR = 0x29D3A, + .PEN_2D_DIFF_TIP_X_ADDR = 0x29ECA, + .PEN_2D_DIFF_TIP_Y_ADDR = 0x2A05A, + .PEN_2D_DIFF_RING_X_ADDR = 0x2A1EA, + .PEN_2D_DIFF_RING_Y_ADDR = 0x2A37A, + .PEN_2D_RAW_TIP_X_ADDR = 0x2A50A, + .PEN_2D_RAW_TIP_Y_ADDR = 0x2A69A, + .PEN_2D_RAW_RING_X_ADDR = 0x2A82A, + .PEN_2D_RAW_RING_Y_ADDR = 0x2A9BA, + .PEN_1D_DIFF_TIP_X_ADDR = 0x2AB4A, + .PEN_1D_DIFF_TIP_Y_ADDR = 0x2ABAE, + .PEN_1D_DIFF_RING_X_ADDR = 0x2AC12, + .PEN_1D_DIFF_RING_Y_ADDR = 0x2AC76, + .READ_FLASH_CHECKSUM_ADDR = 0x24000, + .RW_FLASH_DATA_ADDR = 0x24002, + /* Phase 2 Host Download */ + .BOOT_RDY_ADDR = 0x3F10D, + .TX_AUTO_COPY_EN = 0x3F7E8, + .SPI_DMA_TX_INFO = 0x3F7F1, + /* BLD CRC */ + .BLD_LENGTH_ADDR = 0x3F138, //0x3F138 ~ 0x3F13A (3 bytes) + .ILM_LENGTH_ADDR = 0x3F118, //0x3F118 ~ 0x3F11A (3 bytes) + .DLM_LENGTH_ADDR = 0x3F130, //0x3F130 ~ 0x3F132 (3 bytes) + .BLD_DES_ADDR = 0x3F114, //0x3F114 ~ 0x3F116 (3 bytes) + .ILM_DES_ADDR = 0x3F128, //0x3F128 ~ 0x3F12A (3 bytes) + .DLM_DES_ADDR = 0x3F12C, //0x3F12C ~ 0x3F12E (3 bytes) + .G_ILM_CHECKSUM_ADDR = 0x3F100, //0x3F100 ~ 0x3F103 (4 bytes) + .G_DLM_CHECKSUM_ADDR = 0x3F104, //0x3F104 ~ 0x3F107 (4 bytes) + .R_ILM_CHECKSUM_ADDR = 0x3F120, //0x3F120 ~ 0x3F123 (4 bytes) + .R_DLM_CHECKSUM_ADDR = 0x3F124, //0x3F124 ~ 0x3F127 (4 bytes) + .BLD_CRC_EN_ADDR = 0x3F30E, + .DMA_CRC_EN_ADDR = 0x3F136, + .BLD_ILM_DLM_CRC_ADDR = 0x3F133, + .DMA_CRC_FLAG_ADDR = 0x3F134, +}; + +static const struct nvt_spi_mem_map NT36526_memory_map = { + .EVENT_BUF_ADDR = 0x22D00, + .RAW_PIPE0_ADDR = 0x24000, + .RAW_PIPE1_ADDR = 0x24000, + .BASELINE_ADDR = 0x21758, + .BASELINE_BTN_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x20AB0, + .DIFF_PIPE1_ADDR = 0x24AB0, + .RAW_BTN_PIPE0_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0, + .READ_FLASH_CHECKSUM_ADDR = 0x24000, + .RW_FLASH_DATA_ADDR = 0x24002, + /* Phase 2 Host Download */ + .BOOT_RDY_ADDR = 0x3F10D, + /* BLD CRC */ + .BLD_LENGTH_ADDR = 0x3F138, //0x3F138 ~ 0x3F13A (3 bytes) + .ILM_LENGTH_ADDR = 0x3F118, //0x3F118 ~ 0x3F11A (3 bytes) + .DLM_LENGTH_ADDR = 0x3F130, //0x3F130 ~ 0x3F132 (3 bytes) + .BLD_DES_ADDR = 0x3F114, //0x3F114 ~ 0x3F116 (3 bytes) + .ILM_DES_ADDR = 0x3F128, //0x3F128 ~ 0x3F12A (3 bytes) + .DLM_DES_ADDR = 0x3F12C, //0x3F12C ~ 0x3F12E (3 bytes) + .G_ILM_CHECKSUM_ADDR = 0x3F100, //0x3F100 ~ 0x3F103 (4 bytes) + .G_DLM_CHECKSUM_ADDR = 0x3F104, //0x3F104 ~ 0x3F107 (4 bytes) + .R_ILM_CHECKSUM_ADDR = 0x3F120, //0x3F120 ~ 0x3F123 (4 bytes) + .R_DLM_CHECKSUM_ADDR = 0x3F124, //0x3F124 ~ 0x3F127 (4 bytes) + .BLD_CRC_EN_ADDR = 0x3F30E, + .DMA_CRC_EN_ADDR = 0x3F136, + .BLD_ILM_DLM_CRC_ADDR = 0x3F133, + .DMA_CRC_FLAG_ADDR = 0x3F134, +}; + + +static const struct nvt_spi_mem_map NT36675_memory_map = { + .EVENT_BUF_ADDR = 0x22D00, + .RAW_PIPE0_ADDR = 0x24000, + .RAW_PIPE1_ADDR = 0x24000, + .BASELINE_ADDR = 0x21B90, + .BASELINE_BTN_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x20C60, + .DIFF_PIPE1_ADDR = 0x24C60, + .RAW_BTN_PIPE0_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0, + .READ_FLASH_CHECKSUM_ADDR = 0x24000, + .RW_FLASH_DATA_ADDR = 0x24002, + /* Phase 2 Host Download */ + .BOOT_RDY_ADDR = 0x3F10D, + /* BLD CRC */ + .BLD_LENGTH_ADDR = 0x3F138, //0x3F138 ~ 0x3F13A (3 bytes) + .ILM_LENGTH_ADDR = 0x3F118, //0x3F118 ~ 0x3F11A (3 bytes) + .DLM_LENGTH_ADDR = 0x3F130, //0x3F130 ~ 0x3F132 (3 bytes) + .BLD_DES_ADDR = 0x3F114, //0x3F114 ~ 0x3F116 (3 bytes) + .ILM_DES_ADDR = 0x3F128, //0x3F128 ~ 0x3F12A (3 bytes) + .DLM_DES_ADDR = 0x3F12C, //0x3F12C ~ 0x3F12E (3 bytes) + .G_ILM_CHECKSUM_ADDR = 0x3F100, //0x3F100 ~ 0x3F103 (4 bytes) + .G_DLM_CHECKSUM_ADDR = 0x3F104, //0x3F104 ~ 0x3F107 (4 bytes) + .R_ILM_CHECKSUM_ADDR = 0x3F120, //0x3F120 ~ 0x3F123 (4 bytes) + .R_DLM_CHECKSUM_ADDR = 0x3F124, //0x3F124 ~ 0x3F127 (4 bytes) + .BLD_CRC_EN_ADDR = 0x3F30E, + .DMA_CRC_EN_ADDR = 0x3F136, + .BLD_ILM_DLM_CRC_ADDR = 0x3F133, + .DMA_CRC_FLAG_ADDR = 0x3F134, +}; + +static const struct nvt_spi_mem_map NT36672A_memory_map = { + .EVENT_BUF_ADDR = 0x21C00, + .RAW_PIPE0_ADDR = 0x20000, + .RAW_PIPE1_ADDR = 0x23000, + .BASELINE_ADDR = 0x20BFC, + .BASELINE_BTN_ADDR = 0x23BFC, + .DIFF_PIPE0_ADDR = 0x206DC, + .DIFF_PIPE1_ADDR = 0x236DC, + .RAW_BTN_PIPE0_ADDR = 0x20510, + .RAW_BTN_PIPE1_ADDR = 0x23510, + .DIFF_BTN_PIPE0_ADDR = 0x20BF0, + .DIFF_BTN_PIPE1_ADDR = 0x23BF0, + .READ_FLASH_CHECKSUM_ADDR = 0x24000, + .RW_FLASH_DATA_ADDR = 0x24002, + /* Phase 2 Host Download */ + .BOOT_RDY_ADDR = 0x3F10D, + /* BLD CRC */ + .BLD_LENGTH_ADDR = 0x3F10E, //0x3F10E ~ 0x3F10F (2 bytes) + .ILM_LENGTH_ADDR = 0x3F118, //0x3F118 ~ 0x3F119 (2 bytes) + .DLM_LENGTH_ADDR = 0x3F130, //0x3F130 ~ 0x3F131 (2 bytes) + .BLD_DES_ADDR = 0x3F114, //0x3F114 ~ 0x3F116 (3 bytes) + .ILM_DES_ADDR = 0x3F128, //0x3F128 ~ 0x3F12A (3 bytes) + .DLM_DES_ADDR = 0x3F12C, //0x3F12C ~ 0x3F12E (3 bytes) + .G_ILM_CHECKSUM_ADDR = 0x3F100, //0x3F100 ~ 0x3F103 (4 bytes) + .G_DLM_CHECKSUM_ADDR = 0x3F104, //0x3F104 ~ 0x3F107 (4 bytes) + .R_ILM_CHECKSUM_ADDR = 0x3F120, //0x3F120 ~ 0x3F123 (4 bytes) + .R_DLM_CHECKSUM_ADDR = 0x3F124, //0x3F124 ~ 0x3F127 (4 bytes) + .BLD_CRC_EN_ADDR = 0x3F30E, + .DMA_CRC_EN_ADDR = 0x3F132, + .BLD_ILM_DLM_CRC_ADDR = 0x3F133, + .DMA_CRC_FLAG_ADDR = 0x3F134, +}; + +static const struct nvt_spi_mem_map NT36772_memory_map = { + .EVENT_BUF_ADDR = 0x11E00, + .RAW_PIPE0_ADDR = 0x10000, + .RAW_PIPE1_ADDR = 0x12000, + .BASELINE_ADDR = 0x10E70, + .BASELINE_BTN_ADDR = 0x12E70, + .DIFF_PIPE0_ADDR = 0x10830, + .DIFF_PIPE1_ADDR = 0x12830, + .RAW_BTN_PIPE0_ADDR = 0x10E60, + .RAW_BTN_PIPE1_ADDR = 0x12E60, + .DIFF_BTN_PIPE0_ADDR = 0x10E68, + .DIFF_BTN_PIPE1_ADDR = 0x12E68, + .READ_FLASH_CHECKSUM_ADDR = 0x14000, + .RW_FLASH_DATA_ADDR = 0x14002, + /* Phase 2 Host Download */ + .BOOT_RDY_ADDR = 0x1F141, + .POR_CD_ADDR = 0x1F61C, + /* BLD CRC */ + .R_ILM_CHECKSUM_ADDR = 0x1BF00, +}; + +static const struct nvt_spi_mem_map NT36525_memory_map = { + .EVENT_BUF_ADDR = 0x11A00, + .RAW_PIPE0_ADDR = 0x10000, + .RAW_PIPE1_ADDR = 0x12000, + .BASELINE_ADDR = 0x10B08, + .BASELINE_BTN_ADDR = 0x12B08, + .DIFF_PIPE0_ADDR = 0x1064C, + .DIFF_PIPE1_ADDR = 0x1264C, + .RAW_BTN_PIPE0_ADDR = 0x10634, + .RAW_BTN_PIPE1_ADDR = 0x12634, + .DIFF_BTN_PIPE0_ADDR = 0x10AFC, + .DIFF_BTN_PIPE1_ADDR = 0x12AFC, + .READ_FLASH_CHECKSUM_ADDR = 0x14000, + .RW_FLASH_DATA_ADDR = 0x14002, + /* Phase 2 Host Download */ + .BOOT_RDY_ADDR = 0x1F141, + .POR_CD_ADDR = 0x1F61C, + /* BLD CRC */ + .R_ILM_CHECKSUM_ADDR = 0x1BF00, +}; + +static const struct nvt_spi_mem_map NT36676F_memory_map = { + .EVENT_BUF_ADDR = 0x11A00, + .RAW_PIPE0_ADDR = 0x10000, + .RAW_PIPE1_ADDR = 0x12000, + .BASELINE_ADDR = 0x10B08, + .BASELINE_BTN_ADDR = 0x12B08, + .DIFF_PIPE0_ADDR = 0x1064C, + .DIFF_PIPE1_ADDR = 0x1264C, + .RAW_BTN_PIPE0_ADDR = 0x10634, + .RAW_BTN_PIPE1_ADDR = 0x12634, + .DIFF_BTN_PIPE0_ADDR = 0x10AFC, + .DIFF_BTN_PIPE1_ADDR = 0x12AFC, + .READ_FLASH_CHECKSUM_ADDR = 0x14000, + .RW_FLASH_DATA_ADDR = 0x14002, +}; + +static struct nvt_spi_hw_info NT36523_hw_info = { + .hw_crc = 2, +}; + +static struct nvt_spi_hw_info NT36526_hw_info = { + .hw_crc = 2, +}; + +static struct nvt_spi_hw_info NT36675_hw_info = { + .hw_crc = 2, +}; + +static struct nvt_spi_hw_info NT36672A_hw_info = { + .hw_crc = 1, +}; + +static struct nvt_spi_hw_info NT36772_hw_info = { + .hw_crc = 0, +}; + +static struct nvt_spi_hw_info NT36525_hw_info = { + .hw_crc = 0, +}; + +static struct nvt_spi_hw_info NT36676F_hw_info = { + .hw_crc = 0, +}; + +#define NVT_SPI_ID_BYTE_MAX 6 +struct nvt_spi_trim_id_table_t { + uint8_t id[NVT_SPI_ID_BYTE_MAX]; + uint8_t mask[NVT_SPI_ID_BYTE_MAX]; + const struct nvt_spi_mem_map *mmap; + const struct nvt_spi_hw_info *hwinfo; +}; + +static const struct nvt_spi_trim_id_table_t nvt_spi_trim_id_table[] = { + {.id = {0x0D, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x20, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x00, 0xFF, 0xFF, 0x80, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x0C, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0E, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x20, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36523_memory_map, .hwinfo = &NT36523_hw_info}, + {.id = {0x0C, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36523_memory_map, .hwinfo = &NT36523_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36523_memory_map, .hwinfo = &NT36523_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36523_memory_map, .hwinfo = &NT36523_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x23, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36523_memory_map, .hwinfo = &NT36523_hw_info}, + {.id = {0x0C, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x26, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36526_memory_map, .hwinfo = &NT36526_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x75, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0B, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.id = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0x55, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xAA, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xAA, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36525_memory_map, .hwinfo = &NT36525_hw_info}, + {.id = {0xFF, 0xFF, 0xFF, 0x76, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36676F_memory_map, .hwinfo = &NT36676F_hw_info} +}; + +#endif diff --git a/nt36xxx/nt36xxx_mp_ctrlram.c b/nt36xxx/nt36xxx_mp_ctrlram.c new file mode 100644 index 0000000000..5c07488521 --- /dev/null +++ b/nt36xxx/nt36xxx_mp_ctrlram.c @@ -0,0 +1,1480 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 47247 $ + * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $ + * + * 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. + * + */ + +#include +#include +#include + +#include "nt36xxx.h" +#include "nt36xxx_mp_ctrlram.h" + +#if NVT_TOUCH_MP + +#define NORMAL_MODE 0x00 +#define TEST_MODE_1 0x21 +#define TEST_MODE_2 0x22 +#define MP_MODE_CC 0x41 +#define FREQ_HOP_DISABLE 0x66 +#define FREQ_HOP_ENABLE 0x65 + +#define SHORT_TEST_CSV_FILE "/data/local/tmp/ShortTest.csv" +#define OPEN_TEST_CSV_FILE "/data/local/tmp/OpenTest.csv" +#define FW_RAWDATA_CSV_FILE "/data/local/tmp/FWMutualTest.csv" +#define FW_CC_CSV_FILE "/data/local/tmp/FWCCTest.csv" +#define NOISE_TEST_CSV_FILE "/data/local/tmp/NoiseTest.csv" + +#define nvt_mp_seq_printf(m, fmt, args...) do { \ + seq_printf(m, fmt, ##args); \ + if (!nvt_mp_test_result_printed) \ + printk(fmt, ##args); \ +} while (0) + +static uint8_t *RecordResult_Short = NULL; +static uint8_t *RecordResult_Open = NULL; +static uint8_t *RecordResult_FWMutual = NULL; +static uint8_t *RecordResult_FW_CC = NULL; +static uint8_t *RecordResult_FW_DiffMax = NULL; +static uint8_t *RecordResult_FW_DiffMin = NULL; + +static int32_t TestResult_Short = 0; +static int32_t TestResult_Open = 0; +static int32_t TestResult_FW_Rawdata = 0; +static int32_t TestResult_FWMutual = 0; +static int32_t TestResult_FW_CC = 0; +static int32_t TestResult_Noise = 0; +static int32_t TestResult_FW_DiffMax = 0; +static int32_t TestResult_FW_DiffMin = 0; + +static int32_t *RawData_Short = NULL; +static int32_t *RawData_Open = NULL; +static int32_t *RawData_Diff = NULL; +static int32_t *RawData_Diff_Min = NULL; +static int32_t *RawData_Diff_Max = NULL; +static int32_t *RawData_FWMutual = NULL; +static int32_t *RawData_FW_CC = NULL; + +static struct proc_dir_entry *NVT_proc_selftest_entry = NULL; +static int8_t nvt_mp_test_result_printed = 0; +static uint8_t fw_ver = 0; + +extern void nvt_change_mode(uint8_t mode); +extern uint8_t nvt_get_fw_pipe(void); +extern void nvt_read_mdata(uint32_t xdata_addr, uint32_t xdata_btn_addr); +extern void nvt_get_mdata(int32_t *buf, uint8_t *m_x_num, uint8_t *m_y_num); +int32_t nvt_mp_parse_dt(struct device_node *root, const char *node_compatible); + +/******************************************************* +Description: + Novatek touchscreen allocate buffer for mp selftest. + +return: + Executive outcomes. 0---succeed. -12---Out of memory +*******************************************************/ +static int nvt_mp_buffer_init(void) +{ + size_t RecordResult_BufSize = IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE; + size_t RawData_BufSize = (IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE) * sizeof(int32_t); + + RecordResult_Short = kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_Short) { + NVT_ERR("kzalloc for RecordResult_Short failed!\n"); + return -ENOMEM; + } + + RecordResult_Open = kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_Open) { + NVT_ERR("kzalloc for RecordResult_Open failed!\n"); + return -ENOMEM; + } + + RecordResult_FWMutual = kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FWMutual) { + NVT_ERR("kzalloc for RecordResult_FWMutual failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_CC = kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_CC) { + NVT_ERR("kzalloc for RecordResult_FW_CC failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_DiffMax = kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_DiffMax) { + NVT_ERR("kzalloc for RecordResult_FW_DiffMax failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_DiffMin = kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_DiffMin) { + NVT_ERR("kzalloc for RecordResult_FW_DiffMin failed!\n"); + return -ENOMEM; + } + + RawData_Short = kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Short) { + NVT_ERR("kzalloc for RawData_Short failed!\n"); + return -ENOMEM; + } + + RawData_Open = kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Open) { + NVT_ERR("kzalloc for RawData_Open failed!\n"); + return -ENOMEM; + } + + RawData_Diff = kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff) { + NVT_ERR("kzalloc for RawData_Diff failed!\n"); + return -ENOMEM; + } + + RawData_Diff_Min = kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff_Min) { + NVT_ERR("kzalloc for RawData_Diff_Min failed!\n"); + return -ENOMEM; + } + + RawData_Diff_Max = kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff_Max) { + NVT_ERR("kzalloc for RawData_Diff_Max failed!\n"); + return -ENOMEM; + } + + RawData_FWMutual = kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_FWMutual) { + NVT_ERR("kzalloc for RawData_FWMutual failed!\n"); + return -ENOMEM; + } + + RawData_FW_CC = kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_FW_CC) { + NVT_ERR("kzalloc for RawData_FW_CC failed!\n"); + return -ENOMEM; + } + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen free buffer for mp selftest. + +return: + n.a. +*******************************************************/ +static void nvt_mp_buffer_deinit(void) +{ + if (RecordResult_Short) { + kfree(RecordResult_Short); + RecordResult_Short = NULL; + } + + if (RecordResult_Open) { + kfree(RecordResult_Open); + RecordResult_Open = NULL; + } + + if (RecordResult_FWMutual) { + kfree(RecordResult_FWMutual); + RecordResult_FWMutual = NULL; + } + + if (RecordResult_FW_CC) { + kfree(RecordResult_FW_CC); + RecordResult_FW_CC = NULL; + } + + if (RecordResult_FW_DiffMax) { + kfree(RecordResult_FW_DiffMax); + RecordResult_FW_DiffMax = NULL; + } + + if (RecordResult_FW_DiffMin) { + kfree(RecordResult_FW_DiffMin); + RecordResult_FW_DiffMin = NULL; + } + + if (RawData_Short) { + kfree(RawData_Short); + RawData_Short = NULL; + } + + if (RawData_Open) { + kfree(RawData_Open); + RawData_Open = NULL; + } + + if (RawData_Diff) { + kfree(RawData_Diff); + RawData_Diff = NULL; + } + + if (RawData_Diff_Min) { + kfree(RawData_Diff_Min); + RawData_Diff_Min = NULL; + } + + if (RawData_Diff_Max) { + kfree(RawData_Diff_Max); + RawData_Diff_Max = NULL; + } + + if (RawData_FWMutual) { + kfree(RawData_FWMutual); + RawData_FWMutual = NULL; + } + + if (RawData_FW_CC) { + kfree(RawData_FW_CC); + RawData_FW_CC = NULL; + } +} + +static void nvt_print_data_log_in_one_line(int32_t *data, int32_t data_num) +{ + char *tmp_log = NULL; + int32_t i = 0; + int32_t count = data_num * 7 + 1; + + tmp_log = kzalloc(count, GFP_KERNEL); + if (!tmp_log) { + NVT_ERR("kzalloc for tmp_log failed!\n "); + return; + } + + for (i = 0; i < data_num; i++) { + snprintf(tmp_log + i * 7, count - i * 7, "%5d, ", data[i]); + } + tmp_log[data_num * 7] = '\0'; + printk("%s", tmp_log); + if (tmp_log) { + kfree(tmp_log); + tmp_log = NULL; + } + + return; +} + +static void nvt_print_result_log_in_one_line(uint8_t *result, int32_t result_num) +{ + char *tmp_log = NULL; + int32_t i = 0; + int32_t count = 0; + + count = result_num * 6 + 1; + tmp_log = kzalloc(count, GFP_KERNEL); + if (!tmp_log) { + NVT_ERR("kzalloc for tmp_log failed!\n "); + return; + } + + for (i = 0; i < result_num; i++) { + snprintf(tmp_log + i * 6, count - i * 6, "0x%02X, ", result[i]); + } + tmp_log[result_num * 6] = '\0'; + printk("%s", tmp_log); + if (tmp_log) { + kfree(tmp_log); + tmp_log = NULL; + } + + return; +} + +/******************************************************* +Description: + Novatek touchscreen self-test criteria print function. + +return: + n.a. +*******************************************************/ +static void nvt_print_lmt_array(int32_t *array, int32_t x_ch, int32_t y_ch) +{ + int32_t j = 0; + + for (j = 0; j < y_ch; j++) { + nvt_print_data_log_in_one_line(array + j * x_ch, x_ch); + printk("\n"); + } +#if TOUCH_KEY_NUM > 0 + nvt_print_data_log_in_one_line(array + y_ch * x_ch, Key_Channel); + printk("\n"); +#endif /* #if TOUCH_KEY_NUM > 0 */ +} + +static void nvt_print_criteria(void) +{ + NVT_LOG("++\n"); + + //---PS_Config_Lmt_Short_Rawdata--- + printk("PS_Config_Lmt_Short_Rawdata_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Short_Rawdata_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_Short_Rawdata_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Short_Rawdata_N, X_Channel, Y_Channel); + + //---PS_Config_Lmt_Open_Rawdata--- + printk("PS_Config_Lmt_Open_Rawdata_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Open_Rawdata_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_Open_Rawdata_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Open_Rawdata_N, X_Channel, Y_Channel); + + //---PS_Config_Lmt_FW_Rawdata--- + printk("PS_Config_Lmt_FW_Rawdata_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_Rawdata_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_FW_Rawdata_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_Rawdata_N, X_Channel, Y_Channel); + + //---PS_Config_Lmt_FW_CC--- + printk("PS_Config_Lmt_FW_CC_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_CC_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_FW_CC_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_CC_N, X_Channel, Y_Channel); + + //---PS_Config_Lmt_FW_Diff--- + printk("PS_Config_Lmt_FW_Diff_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_Diff_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_FW_Diff_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_Diff_N, X_Channel, Y_Channel); + + NVT_LOG("--\n"); +} + +static int32_t nvt_polling_hand_shake_status(void) +{ + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 70; + + for (i = 0; i < retry; i++) { + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE); + + //---read fw status--- + buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0x00; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2); + + if ((buf[1] == 0xA0) || (buf[1] == 0xA1)) + break; + + usleep_range(10000, 10000); + } + + if (i >= retry) { + NVT_ERR("polling hand shake status failed, buf[1]=0x%02X\n", buf[1]); + + // Read back 5 bytes from offset EVENT_MAP_HOST_CMD for debug check + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x00; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x00; + buf[5] = 0x00; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 6); + NVT_ERR("Read back 5 bytes from offset EVENT_MAP_HOST_CMD: 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X\n", buf[1], buf[2], buf[3], buf[4], buf[5]); + + return -1; + } else { + return 0; + } +} + +static int8_t nvt_switch_FreqHopEnDis(uint8_t FreqHopEnDis) +{ + uint8_t buf[8] = {0}; + uint8_t retry = 0; + int8_t ret = 0; + + NVT_LOG("++\n"); + + for (retry = 0; retry < 20; retry++) { + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + + //---switch FreqHopEnDis--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = FreqHopEnDis; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); + + msleep(35); + + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0xFF; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, 2); + + if (buf[1] == 0x00) + break; + } + + if (unlikely(retry == 20)) { + NVT_ERR("switch FreqHopEnDis 0x%02X failed, buf[1]=0x%02X\n", FreqHopEnDis, buf[1]); + ret = -1; + } + + NVT_LOG("--\n"); + + return ret; +} + +static int32_t nvt_read_baseline(int32_t *xdata) +{ + uint8_t x_num = 0; + uint8_t y_num = 0; + uint32_t x = 0; + uint32_t y = 0; + int32_t iArrayIndex = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + NVT_LOG("++\n"); + + nvt_read_mdata(ts->mmap->BASELINE_ADDR, ts->mmap->BASELINE_BTN_ADDR); + + nvt_get_mdata(xdata, &x_num, &y_num); + + for (y = 0; y < y_num; y++) { + for (x = 0; x < x_num; x++) { + iArrayIndex = y * x_num + x; + xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex]; + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = Y_Channel * X_Channel + k; + xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex]; + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + NVT_LOG("--\n"); + + return 0; +} + +static int32_t nvt_read_CC(int32_t *xdata) +{ + uint8_t x_num = 0; + uint8_t y_num = 0; + uint32_t x = 0; + uint32_t y = 0; + int32_t iArrayIndex = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + NVT_LOG("++\n"); + + if (nvt_get_fw_pipe() == 0) + nvt_read_mdata(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_ADDR); + else + nvt_read_mdata(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_ADDR); + + nvt_get_mdata(xdata, &x_num, &y_num); + + for (y = 0; y < y_num; y++) { + for (x = 0; x < x_num; x++) { + iArrayIndex = y * x_num + x; + xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex]; + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = Y_Channel * X_Channel + k; + xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex]; + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + NVT_LOG("--\n"); + + return 0; +} + +static void nvt_enable_noise_collect(int32_t frame_num) +{ + uint8_t buf[8] = {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + + //---enable noise collect--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x47; + buf[2] = 0xAA; + buf[3] = frame_num; + buf[4] = 0x00; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 5); +} + +static int32_t nvt_read_fw_noise(int32_t *xdata) +{ + uint8_t x_num = 0; + uint8_t y_num = 0; + uint32_t x = 0; + uint32_t y = 0; + int32_t iArrayIndex = 0; + int32_t frame_num = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + NVT_LOG("++\n"); + + //---Enter Test Mode--- + if (nvt_clear_fw_status()) { + return -EAGAIN; + } + + frame_num = PS_Config_Diff_Test_Frame / 10; + if (frame_num <= 0) + frame_num = 1; + printk("%s: frame_num=%d\n", __func__, frame_num); + nvt_enable_noise_collect(frame_num); + // need wait PS_Config_Diff_Test_Frame * 8.3ms + msleep(frame_num * 83); + + if (nvt_polling_hand_shake_status()) { + return -EAGAIN; + } + + if (nvt_get_fw_info()) { + return -EAGAIN; + } + + if (nvt_get_fw_pipe() == 0) + nvt_read_mdata(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_ADDR); + else + nvt_read_mdata(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_ADDR); + + nvt_get_mdata(xdata, &x_num, &y_num); + + for (y = 0; y < y_num; y++) { + for (x = 0; x < x_num; x++) { + iArrayIndex = y * x_num + x; + RawData_Diff_Max[iArrayIndex] = (int8_t)((xdata[iArrayIndex] >> 8) & 0xFF); + RawData_Diff_Min[iArrayIndex] = (int8_t)(xdata[iArrayIndex] & 0xFF); + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = Y_Channel * X_Channel + k; + RawData_Diff_Max[iArrayIndex] = (int8_t)((xdata[iArrayIndex] >> 8) & 0xFF); + RawData_Diff_Min[iArrayIndex] = (int8_t)(xdata[iArrayIndex] & 0xFF); + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + //---Leave Test Mode--- + nvt_change_mode(NORMAL_MODE); + + NVT_LOG("--\n"); + + return 0; +} + +static void nvt_enable_open_test(void) +{ + uint8_t buf[8] = {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + + //---enable open test--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x45; + buf[2] = 0xAA; + buf[3] = 0x02; + buf[4] = 0x00; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 5); +} + +static void nvt_enable_short_test(void) +{ + uint8_t buf[8] = {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(I2C_FW_Address, ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + + //---enable short test--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x43; + buf[2] = 0xAA; + buf[3] = 0x02; + buf[4] = 0x00; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 5); +} + +static int32_t nvt_read_fw_open(int32_t *xdata) +{ + uint32_t raw_pipe_addr = 0; + uint8_t *rawdata_buf = NULL; + uint32_t x = 0; + uint32_t y = 0; + uint8_t buf[128] = {0}; +#if TOUCH_KEY_NUM > 0 + uint32_t raw_btn_pipe_addr = 0; + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + NVT_LOG("++\n"); + + //---Enter Test Mode--- + if (nvt_clear_fw_status()) { + return -EAGAIN; + } + + nvt_enable_open_test(); + + if (nvt_polling_hand_shake_status()) { + return -EAGAIN; + } + +#if TOUCH_KEY_NUM > 0 + rawdata_buf = kzalloc((IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE) * 2, GFP_KERNEL); +#else + rawdata_buf = kzalloc(IC_X_CFG_SIZE * IC_Y_CFG_SIZE * 2, GFP_KERNEL); +#endif /* #if TOUCH_KEY_NUM > 0 */ + if (!rawdata_buf) { + NVT_ERR("kzalloc for rawdata_buf failed!\n"); + return -ENOMEM; + } + + if (nvt_get_fw_pipe() == 0) + raw_pipe_addr = ts->mmap->RAW_PIPE0_ADDR; + else + raw_pipe_addr = ts->mmap->RAW_PIPE1_ADDR; + + for (y = 0; y < IC_Y_CFG_SIZE; y++) { + //---change xdata index--- + nvt_set_page(I2C_FW_Address, raw_pipe_addr + y * IC_X_CFG_SIZE * 2); + buf[0] = (uint8_t)((raw_pipe_addr + y * IC_X_CFG_SIZE * 2) & 0xFF); + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, IC_X_CFG_SIZE * 2 + 1); + memcpy(rawdata_buf + y * IC_X_CFG_SIZE * 2, buf + 1, IC_X_CFG_SIZE * 2); + } +#if TOUCH_KEY_NUM > 0 + if (nvt_get_fw_pipe() == 0) + raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE0_ADDR; + else + raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE1_ADDR; + + //---change xdata index--- + nvt_set_page(I2C_FW_Address, raw_btn_pipe_addr); + buf[0] = (uint8_t)(raw_btn_pipe_addr & 0xFF); + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, IC_KEY_CFG_SIZE * 2 + 1); + memcpy(rawdata_buf + IC_Y_CFG_SIZE * IC_X_CFG_SIZE * 2, buf + 1, IC_KEY_CFG_SIZE * 2); +#endif /* #if TOUCH_KEY_NUM > 0 */ + + for (y = 0; y < IC_Y_CFG_SIZE; y++) { + for (x = 0; x < IC_X_CFG_SIZE; x++) { + if ((AIN_Y[y] != 0xFF) && (AIN_X[x] != 0xFF)) { + xdata[AIN_Y[y] * X_Channel + AIN_X[x]] = (int16_t)((rawdata_buf[(y * IC_X_CFG_SIZE + x) * 2] + 256 * rawdata_buf[(y * IC_X_CFG_SIZE + x) * 2 + 1])); + } + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < IC_KEY_CFG_SIZE; k++) { + if (AIN_KEY[k] != 0xFF) + xdata[Y_Channel * X_Channel + AIN_KEY[k]] = (int16_t)(rawdata_buf[(IC_Y_CFG_SIZE * IC_X_CFG_SIZE + k) * 2] + 256 * rawdata_buf[(IC_Y_CFG_SIZE * IC_X_CFG_SIZE + k) * 2 + 1]); + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + if (rawdata_buf) { + kfree(rawdata_buf); + rawdata_buf = NULL; + } + + //---Leave Test Mode--- + nvt_change_mode(NORMAL_MODE); + + NVT_LOG("--\n"); + + return 0; +} + +static int32_t nvt_read_fw_short(int32_t *xdata) +{ + uint32_t raw_pipe_addr = 0; + uint8_t *rawdata_buf = NULL; + uint32_t x = 0; + uint32_t y = 0; + uint8_t buf[128] = {0}; + int32_t iArrayIndex = 0; +#if TOUCH_KEY_NUM > 0 + uint32_t raw_btn_pipe_addr = 0; + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + NVT_LOG("++\n"); + + //---Enter Test Mode--- + if (nvt_clear_fw_status()) { + return -EAGAIN; + } + + nvt_enable_short_test(); + + if (nvt_polling_hand_shake_status()) { + return -EAGAIN; + } + +#if TOUCH_KEY_NUM > 0 + rawdata_buf = kzalloc((X_Channel * Y_Channel + Key_Channel) * 2, GFP_KERNEL); +#else + rawdata_buf = kzalloc(X_Channel * Y_Channel * 2, GFP_KERNEL); +#endif /* #if TOUCH_KEY_NUM > 0 */ + if (!rawdata_buf) { + NVT_ERR("kzalloc for rawdata_buf failed!\n"); + return -ENOMEM; + } + + if (nvt_get_fw_pipe() == 0) + raw_pipe_addr = ts->mmap->RAW_PIPE0_ADDR; + else + raw_pipe_addr = ts->mmap->RAW_PIPE1_ADDR; + + for (y = 0; y < Y_Channel; y++) { + //---change xdata index--- + nvt_set_page(I2C_FW_Address, raw_pipe_addr + y * X_Channel * 2); + buf[0] = (uint8_t)((raw_pipe_addr + y * X_Channel * 2) & 0xFF); + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, X_Channel * 2 + 1); + memcpy(rawdata_buf + y * X_Channel * 2, buf + 1, X_Channel * 2); + } +#if TOUCH_KEY_NUM > 0 + if (nvt_get_fw_pipe() == 0) + raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE0_ADDR; + else + raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE1_ADDR; + + //---change xdata index--- + nvt_set_page(I2C_FW_Address, raw_btn_pipe_addr); + buf[0] = (uint8_t)(raw_btn_pipe_addr & 0xFF); + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, Key_Channel * 2 + 1); + memcpy(rawdata_buf + Y_Channel * X_Channel * 2, buf + 1, Key_Channel * 2); +#endif /* #if TOUCH_KEY_NUM > 0 */ + + for (y = 0; y < Y_Channel; y++) { + for (x = 0; x < X_Channel; x++) { + iArrayIndex = y * X_Channel + x; + xdata[iArrayIndex] = (int16_t)(rawdata_buf[iArrayIndex * 2] + 256 * rawdata_buf[iArrayIndex * 2 + 1]); + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = Y_Channel * X_Channel + k; + xdata[iArrayIndex] = (int16_t)(rawdata_buf[iArrayIndex * 2] + 256 * rawdata_buf[iArrayIndex * 2 + 1]); + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + if (rawdata_buf) { + kfree(rawdata_buf); + rawdata_buf = NULL; + } + + //---Leave Test Mode--- + nvt_change_mode(NORMAL_MODE); + + NVT_LOG("--\n"); + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen raw data test for each single point function. + +return: + Executive outcomes. 0---passed. negative---failed. +*******************************************************/ +static int32_t RawDataTest_SinglePoint_Sub(int32_t rawdata[], uint8_t RecordResult[], uint8_t x_ch, uint8_t y_ch, int32_t Rawdata_Limit_Postive[], int32_t Rawdata_Limit_Negative[]) +{ + int32_t i = 0; + int32_t j = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + int32_t iArrayIndex = 0; + bool isPass = true; + + for (j = 0; j < y_ch; j++) { + for (i = 0; i < x_ch; i++) { + iArrayIndex = j * x_ch + i; + + RecordResult[iArrayIndex] = 0x00; // default value for PASS + + if(rawdata[iArrayIndex] > Rawdata_Limit_Postive[iArrayIndex]) + RecordResult[iArrayIndex] |= 0x01; + + if(rawdata[iArrayIndex] < Rawdata_Limit_Negative[iArrayIndex]) + RecordResult[iArrayIndex] |= 0x02; + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_ch * x_ch + k; + + RecordResult[iArrayIndex] = 0x00; // default value for PASS + + if(rawdata[iArrayIndex] > Rawdata_Limit_Postive[iArrayIndex]) + RecordResult[iArrayIndex] |= 0x01; + + if(rawdata[iArrayIndex] < Rawdata_Limit_Negative[iArrayIndex]) + RecordResult[iArrayIndex] |= 0x02; + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + //---Check RecordResult--- + for (j = 0; j < y_ch; j++) { + for (i = 0; i < x_ch; i++) { + if (RecordResult[j * x_ch + i] != 0) { + isPass = false; + break; + } + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_ch * x_ch + k; + if (RecordResult[iArrayIndex] != 0) { + isPass = false; + break; + } + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + if (!isPass) { + return -1; // FAIL + } else { + return 0; // PASS + } +} + +/******************************************************* +Description: + Novatek touchscreen print self-test result function. + +return: + n.a. +*******************************************************/ +void print_selftest_result(struct seq_file *m, int32_t TestResult, uint8_t RecordResult[], int32_t rawdata[], uint8_t x_len, uint8_t y_len) +{ + int32_t i = 0; + int32_t j = 0; + int32_t iArrayIndex = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + switch (TestResult) { + case 0: + nvt_mp_seq_printf(m, " PASS!\n"); + break; + + case 1: + nvt_mp_seq_printf(m, " ERROR! Read Data FAIL!\n"); + break; + + case -1: + nvt_mp_seq_printf(m, " FAIL!\n"); + nvt_mp_seq_printf(m, "RecordResult:\n"); + for (i = 0; i < y_len; i++) { + for (j = 0; j < x_len; j++) { + iArrayIndex = i * x_len + j; + seq_printf(m, "0x%02X, ", RecordResult[iArrayIndex]); + } + if (!nvt_mp_test_result_printed) + nvt_print_result_log_in_one_line(RecordResult + i * x_len, x_len); + nvt_mp_seq_printf(m, "\n"); + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_len * x_len + k; + seq_printf(m, "0x%02X, ", RecordResult[iArrayIndex]); + } + if (!nvt_mp_test_result_printed) + nvt_print_result_log_in_one_line(RecordResult + y_len * x_len, Key_Channel); + nvt_mp_seq_printf(m, "\n"); +#endif /* #if TOUCH_KEY_NUM > 0 */ + nvt_mp_seq_printf(m, "ReadData:\n"); + for (i = 0; i < y_len; i++) { + for (j = 0; j < x_len; j++) { + iArrayIndex = i * x_len + j; + seq_printf(m, "%5d, ", rawdata[iArrayIndex]); + } + if (!nvt_mp_test_result_printed) + nvt_print_data_log_in_one_line(rawdata + i * x_len, x_len); + nvt_mp_seq_printf(m, "\n"); + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_len * x_len + k; + seq_printf(m, "%5d, ", rawdata[iArrayIndex]); + } + if (!nvt_mp_test_result_printed) + nvt_print_data_log_in_one_line(rawdata + y_len * x_len, Key_Channel); + nvt_mp_seq_printf(m, "\n"); +#endif /* #if TOUCH_KEY_NUM > 0 */ + break; + } + nvt_mp_seq_printf(m, "\n"); +} + +/******************************************************* +Description: + Novatek touchscreen self-test sequence print show + function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t c_show_selftest(struct seq_file *m, void *v) +{ + NVT_LOG("++\n"); + + nvt_mp_seq_printf(m, "FW Version: %d\n\n", fw_ver); + + nvt_mp_seq_printf(m, "Short Test"); + + print_selftest_result(m, TestResult_Short, RecordResult_Short, RawData_Short, X_Channel, Y_Channel); + + nvt_mp_seq_printf(m, "Open Test"); + print_selftest_result(m, TestResult_Open, RecordResult_Open, RawData_Open, X_Channel, Y_Channel); + + nvt_mp_seq_printf(m, "FW Rawdata Test"); + if ((TestResult_FW_Rawdata == 0) || (TestResult_FW_Rawdata == 1)) { + print_selftest_result(m, TestResult_FWMutual, RecordResult_FWMutual, RawData_FWMutual, X_Channel, Y_Channel); + } else { // TestResult_FW_Rawdata is -1 + nvt_mp_seq_printf(m, " FAIL!\n"); + if (TestResult_FWMutual == -1) { + nvt_mp_seq_printf(m, "FW Mutual"); + print_selftest_result(m, TestResult_FWMutual, RecordResult_FWMutual, RawData_FWMutual, X_Channel, Y_Channel); + } + if (TestResult_FW_CC == -1) { + nvt_mp_seq_printf(m, "FW CC"); + print_selftest_result(m, TestResult_FW_CC, RecordResult_FW_CC, RawData_FW_CC, X_Channel, Y_Channel); + } + } + + nvt_mp_seq_printf(m, "Noise Test"); + if ((TestResult_Noise == 0) || (TestResult_Noise == 1)) { + print_selftest_result(m, TestResult_FW_DiffMax, RecordResult_FW_DiffMax, RawData_Diff_Max, X_Channel, Y_Channel); + } else { // TestResult_Noise is -1 + nvt_mp_seq_printf(m, " FAIL!\n"); + + if (TestResult_FW_DiffMax == -1) { + nvt_mp_seq_printf(m, "FW Diff Max"); + print_selftest_result(m, TestResult_FW_DiffMax, RecordResult_FW_DiffMax, RawData_Diff_Max, X_Channel, Y_Channel); + } + if (TestResult_FW_DiffMin == -1) { + nvt_mp_seq_printf(m, "FW Diff Min"); + print_selftest_result(m, TestResult_FW_DiffMin, RecordResult_FW_DiffMin, RawData_Diff_Min, X_Channel, Y_Channel); + } + } + + nvt_mp_test_result_printed = 1; + + NVT_LOG("--\n"); + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen self-test sequence print start + function. + +return: + Executive outcomes. 1---call next function. + NULL---not call next function and sequence loop + stop. +*******************************************************/ +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < 1 ? (void *)1 : NULL; +} + +/******************************************************* +Description: + Novatek touchscreen self-test sequence print next + function. + +return: + Executive outcomes. NULL---no next and call sequence + stop function. +*******************************************************/ +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return NULL; +} + +/******************************************************* +Description: + Novatek touchscreen self-test sequence print stop + function. + +return: + n.a. +*******************************************************/ +static void c_stop(struct seq_file *m, void *v) +{ + return; +} + +const struct seq_operations nvt_selftest_seq_ops = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = c_show_selftest +}; + +/******************************************************* +Description: + Novatek touchscreen /proc/nvt_selftest open function. + +return: + Executive outcomes. 0---succeed. negative---failed. +*******************************************************/ +static int32_t nvt_selftest_open(struct inode *inode, struct file *file) +{ + struct device_node *np = ts->client->dev.of_node; + unsigned char mpcriteria[PAGE_SIZE] = {0}; //novatek-mp-criteria-default + + TestResult_Short = 0; + TestResult_Open = 0; + TestResult_FW_Rawdata = 0; + TestResult_FWMutual = 0; + TestResult_FW_CC = 0; + TestResult_Noise = 0; + TestResult_FW_DiffMax = 0; + TestResult_FW_DiffMin = 0; + + NVT_LOG("++\n"); + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + + if (nvt_get_fw_info()) { + mutex_unlock(&ts->lock); + NVT_ERR("get fw info failed!\n"); + return -EAGAIN; + } + + fw_ver = ts->fw_ver; + + /* Parsing criteria from dts */ + if(of_property_read_bool(np, "novatek,mp-support-dt")) { + /* + * Parsing Criteria by Novatek PID + * The string rule is "novatek-mp-criteria-" + * nvt_pid is 2 bytes (show hex). + * + * Ex. nvt_pid = 500A + * mpcriteria = "novatek-mp-criteria-500A" + */ + snprintf(mpcriteria, PAGE_SIZE, "novatek-mp-criteria-%04X", ts->nvt_pid); + + if (nvt_mp_parse_dt(np, mpcriteria)) { + mutex_unlock(&ts->lock); + NVT_ERR("mp parse device tree failed!\n"); + return -EINVAL; + } + } else { + NVT_LOG("Not found novatek,mp-support-dt, use default setting\n"); + //---Print Test Criteria--- + nvt_print_criteria(); + } + + if (nvt_switch_FreqHopEnDis(FREQ_HOP_DISABLE)) { + mutex_unlock(&ts->lock); + NVT_ERR("switch frequency hopping disable failed!\n"); + return -EAGAIN; + } + + if (nvt_check_fw_reset_state(RESET_STATE_NORMAL_RUN)) { + mutex_unlock(&ts->lock); + NVT_ERR("check fw reset state failed!\n"); + return -EAGAIN; + } + + msleep(100); + + //---Enter Test Mode--- + if (nvt_clear_fw_status()) { + mutex_unlock(&ts->lock); + NVT_ERR("clear fw status failed!\n"); + return -EAGAIN; + } + + nvt_change_mode(MP_MODE_CC); + + if (nvt_check_fw_status()) { + mutex_unlock(&ts->lock); + NVT_ERR("check fw status failed!\n"); + return -EAGAIN; + } + + //---FW Rawdata Test--- + if (nvt_read_baseline(RawData_FWMutual) != 0) { + TestResult_FWMutual = 1; + } else { + TestResult_FWMutual = RawDataTest_SinglePoint_Sub(RawData_FWMutual, RecordResult_FWMutual, X_Channel, Y_Channel, + PS_Config_Lmt_FW_Rawdata_P, PS_Config_Lmt_FW_Rawdata_N); + } + if (nvt_read_CC(RawData_FW_CC) != 0) { + TestResult_FW_CC = 1; + } else { + TestResult_FW_CC = RawDataTest_SinglePoint_Sub(RawData_FW_CC, RecordResult_FW_CC, X_Channel, Y_Channel, + PS_Config_Lmt_FW_CC_P, PS_Config_Lmt_FW_CC_N); + } + + if ((TestResult_FWMutual == 1) || (TestResult_FW_CC == 1)) { + TestResult_FW_Rawdata = 1; + } else { + if ((TestResult_FWMutual == -1) || (TestResult_FW_CC == -1)) + TestResult_FW_Rawdata = -1; + else + TestResult_FW_Rawdata = 0; + } + + //---Leave Test Mode--- + nvt_change_mode(NORMAL_MODE); + + //---Noise Test--- + if (nvt_read_fw_noise(RawData_Diff) != 0) { + TestResult_Noise = 1; // 1: ERROR + TestResult_FW_DiffMax = 1; + TestResult_FW_DiffMin = 1; + } else { + TestResult_FW_DiffMax = RawDataTest_SinglePoint_Sub(RawData_Diff_Max, RecordResult_FW_DiffMax, X_Channel, Y_Channel, + PS_Config_Lmt_FW_Diff_P, PS_Config_Lmt_FW_Diff_N); + + TestResult_FW_DiffMin = RawDataTest_SinglePoint_Sub(RawData_Diff_Min, RecordResult_FW_DiffMin, X_Channel, Y_Channel, + PS_Config_Lmt_FW_Diff_P, PS_Config_Lmt_FW_Diff_N); + + if ((TestResult_FW_DiffMax == -1) || (TestResult_FW_DiffMin == -1)) + TestResult_Noise = -1; + else + TestResult_Noise = 0; + } + + //--Short Test--- + if (nvt_read_fw_short(RawData_Short) != 0) { + TestResult_Short = 1; // 1:ERROR + } else { + //---Self Test Check --- // 0:PASS, -1:FAIL + TestResult_Short = RawDataTest_SinglePoint_Sub(RawData_Short, RecordResult_Short, X_Channel, Y_Channel, + PS_Config_Lmt_Short_Rawdata_P, PS_Config_Lmt_Short_Rawdata_N); + } + + //---Open Test--- + if (nvt_read_fw_open(RawData_Open) != 0) { + TestResult_Open = 1; // 1:ERROR + } else { + //---Self Test Check --- // 0:PASS, -1:FAIL + TestResult_Open = RawDataTest_SinglePoint_Sub(RawData_Open, RecordResult_Open, X_Channel, Y_Channel, + PS_Config_Lmt_Open_Rawdata_P, PS_Config_Lmt_Open_Rawdata_N); + } + + //---Reset IC--- + nvt_bootloader_reset(); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + nvt_mp_test_result_printed = 0; + + return seq_open(file, &nvt_selftest_seq_ops); +} + +static const struct proc_ops nvt_selftest_fops = { + .proc_open = nvt_selftest_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; + +#ifdef CONFIG_OF +/******************************************************* +Description: + Novatek touchscreen parse AIN setting for array type. + +return: + n.a. +*******************************************************/ +int32_t nvt_mp_parse_ain(struct device_node *np, const char *name, uint8_t *array, int32_t size) +{ + struct property *data; + int32_t len, ret; + int32_t tmp[40]; + int32_t i; + + data = of_find_property(np, name, &len); + len /= sizeof(u32); + if ((!data) || (!len) || (len != size)) { + NVT_ERR("error find %s. len=%d\n", name, len); + return -1; + } else { + NVT_LOG("%s. len=%d\n", name, len); + ret = of_property_read_u32_array(np, name, tmp, len); + if (ret) { + NVT_ERR("error reading %s. ret=%d\n", name, ret); + return -1; + } + + for (i = 0; i < len; i++) + array[i] = tmp[i]; + +#if NVT_DEBUG + printk("[NVT-ts] %s = ", name); + nvt_print_result_log_in_one_line(array, len); + printk("\n"); +#endif + } + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen parse criterion for u32 type. + +return: + n.a. +*******************************************************/ +int32_t nvt_mp_parse_u32(struct device_node *np, const char *name, int32_t *para) +{ + int32_t ret; + + ret = of_property_read_u32(np, name, para); + if (ret) { + NVT_ERR("error reading %s. ret=%d\n", name, ret); + return -1; + } else { +#if NVT_DEBUG + NVT_LOG("%s=%d\n", name, *para); +#endif + } + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen parse criterion for array type. + +return: + n.a. +*******************************************************/ +int32_t nvt_mp_parse_array(struct device_node *np, const char *name, int32_t *array, + int32_t size) +{ + struct property *data; + int32_t len, ret; +#if NVT_DEBUG + int32_t j = 0; +#endif + + data = of_find_property(np, name, &len); + len /= sizeof(u32); + if ((!data) || (!len) || (len < size)) { + NVT_ERR("error find %s. len=%d\n", name, len); + return -1; + } else { + NVT_LOG("%s. len=%d\n", name, len); + ret = of_property_read_u32_array(np, name, array, len); + if (ret) { + NVT_ERR("error reading %s. ret=%d\n", name, ret); + return -1; + } + +#if NVT_DEBUG + NVT_LOG("%s =\n", name); + for (j = 0; j < Y_Channel; j++) { + nvt_print_data_log_in_one_line(array + j * X_Channel, X_Channel); + printk("\n"); + } +#if TOUCH_KEY_NUM > 0 + nvt_print_data_log_in_one_line(array + Y_Channel * X_Channel, Key_Channel); + printk("\n"); +#endif +#endif + } + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen parse device tree mp function. + +return: + n.a. +*******************************************************/ +int32_t nvt_mp_parse_dt(struct device_node *root, const char *node_compatible) +{ + struct device_node *np = root; + struct device_node *child = NULL; + + NVT_LOG("Parse mp criteria for node %s\n", node_compatible); + + /* find each MP sub-nodes */ + for_each_child_of_node(root, child) { + /* find the specified node */ + if (of_device_is_compatible(child, node_compatible)) { + NVT_LOG("found child node %s\n", node_compatible); + np = child; + break; + } + } + if (child == NULL) { + NVT_ERR("Not found compatible node %s!\n", node_compatible); + return -1; + } + + /* MP Config*/ + if (nvt_mp_parse_u32(np, "IC_X_CFG_SIZE", &IC_X_CFG_SIZE)) + return -1; + + if (nvt_mp_parse_u32(np, "IC_Y_CFG_SIZE", &IC_Y_CFG_SIZE)) + return -1; + +#if TOUCH_KEY_NUM > 0 + if (nvt_mp_parse_u32(np, "IC_KEY_CFG_SIZE", &IC_KEY_CFG_SIZE)) + return -1; +#endif + + if (nvt_mp_parse_u32(np, "X_Channel", &X_Channel)) + return -1; + + if (nvt_mp_parse_u32(np, "Y_Channel", &Y_Channel)) + return -1; + + if (nvt_mp_parse_ain(np, "AIN_X", AIN_X, IC_X_CFG_SIZE)) + return -1; + + if (nvt_mp_parse_ain(np, "AIN_Y", AIN_Y, IC_Y_CFG_SIZE)) + return -1; + +#if TOUCH_KEY_NUM > 0 + if (nvt_mp_parse_ain(np, "AIN_KEY", AIN_KEY, IC_KEY_CFG_SIZE)) + return -1; +#endif + + /* MP Criteria */ + if (nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Rawdata_P", PS_Config_Lmt_Short_Rawdata_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Rawdata_N", PS_Config_Lmt_Short_Rawdata_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_Open_Rawdata_P", PS_Config_Lmt_Open_Rawdata_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_Open_Rawdata_N", PS_Config_Lmt_Open_Rawdata_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Rawdata_P", PS_Config_Lmt_FW_Rawdata_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Rawdata_N", PS_Config_Lmt_FW_Rawdata_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_P", PS_Config_Lmt_FW_CC_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_N", PS_Config_Lmt_FW_CC_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Diff_P", PS_Config_Lmt_FW_Diff_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Diff_N", PS_Config_Lmt_FW_Diff_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_u32(np, "PS_Config_Diff_Test_Frame", &PS_Config_Diff_Test_Frame)) + return -1; + + NVT_LOG("Parse mp criteria done!\n"); + + return 0; +} +#endif /* #ifdef CONFIG_OF */ + +/******************************************************* +Description: + Novatek touchscreen MP function proc. file node + initial function. + +return: + Executive outcomes. 0---succeed. -1---failed. +*******************************************************/ +int32_t nvt_mp_proc_init(void) +{ + NVT_proc_selftest_entry = proc_create("nvt_selftest", 0444, NULL, &nvt_selftest_fops); + if (NVT_proc_selftest_entry == NULL) { + NVT_ERR("create /proc/nvt_selftest Failed!\n"); + return -1; + } else { + if(nvt_mp_buffer_init()) { + NVT_ERR("Allocate mp memory failed\n"); + return -1; + } + else { + NVT_LOG("create /proc/nvt_selftest Succeeded!\n"); + } + return 0; + } +} + +/******************************************************* +Description: + Novatek touchscreen MP function proc. file node + deinitial function. + +return: + n.a. +*******************************************************/ +void nvt_mp_proc_deinit(void) +{ + nvt_mp_buffer_deinit(); + + if (NVT_proc_selftest_entry != NULL) { + remove_proc_entry("nvt_selftest", NULL); + NVT_proc_selftest_entry = NULL; + NVT_LOG("Removed /proc/%s\n", "nvt_selftest"); + } +} +#endif /* #if NVT_TOUCH_MP */ diff --git a/nt36xxx/nt36xxx_mp_ctrlram.h b/nt36xxx/nt36xxx_mp_ctrlram.h new file mode 100644 index 0000000000..69c27cabcc --- /dev/null +++ b/nt36xxx/nt36xxx_mp_ctrlram.h @@ -0,0 +1,460 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 46179 $ + * $Date: 2019-06-14 13:47:17 +0800 (Fri, 14 Jun 2019) $ + * + * 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. + * + */ + +#if NVT_TOUCH_MP + +static uint32_t IC_X_CFG_SIZE = 18; +static uint32_t IC_Y_CFG_SIZE = 36; +static uint32_t IC_KEY_CFG_SIZE = 4; +static uint32_t X_Channel = 18; +static uint32_t Y_Channel = 36; +static uint32_t Key_Channel = TOUCH_KEY_NUM; +static uint8_t AIN_X[40] = + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}; +static uint8_t AIN_Y[40] = + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}; + +#if TOUCH_KEY_NUM > 0 +static uint8_t AIN_KEY[8] = {0, 1, 2, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +#endif /* #if TOUCH_KEY_NUM > 0 */ + +static int32_t PS_Config_Lmt_Short_Rawdata_P[40 * 40] = { + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, + 14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008,14008, +#if TOUCH_KEY_NUM > 0 + 14008,14008,14008, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Short_Rawdata_N[40 * 40] = { + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, + 10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000, +#if TOUCH_KEY_NUM > 0 + 10000,10000,10000, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Open_Rawdata_P[40 * 40] = { + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, + 5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120,5120, +#if TOUCH_KEY_NUM > 0 + 5120,5120,5120, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Open_Rawdata_N[40 * 40] = { + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, + 50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50, +#if TOUCH_KEY_NUM > 0 + 50,50,50, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Rawdata_P[40 * 40] = { + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, + 2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560,2560, +#if TOUCH_KEY_NUM > 0 + 2560,2560,2560, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Rawdata_N[40 * 40] = { + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, + 240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240,240, +#if TOUCH_KEY_NUM > 0 + 240,240,240, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_P[40 * 40] = { + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, + 314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314,314, +#if TOUCH_KEY_NUM > 0 + 314,314,314, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_N[40 * 40] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +#if TOUCH_KEY_NUM > 0 + 0,0,0, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Diff_P[40 * 40] = { + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, + 75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75,75, +#if TOUCH_KEY_NUM > 0 + 75,75,75, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Diff_N[40 *40] = { + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, + -75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75,-75, +#if TOUCH_KEY_NUM > 0 + -75,-75,-75, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Diff_Test_Frame = 50; + +#endif /* #if NVT_TOUCH_MP */ diff --git a/nt36xxx/nt36xxx_spi.c b/nt36xxx/nt36xxx_spi.c new file mode 100644 index 0000000000..c26dbd70b2 --- /dev/null +++ b/nt36xxx/nt36xxx_spi.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define NVT_NT36XXX_SPI + +#include "nt36xxx.c" diff --git a/nt36xxx/nt36xxx_spi_ext_proc.c b/nt36xxx/nt36xxx_spi_ext_proc.c new file mode 100644 index 0000000000..a6ad2f6231 --- /dev/null +++ b/nt36xxx/nt36xxx_spi_ext_proc.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define NVT_NT36XXX_SPI + +#include "nt36xxx_ext_proc.c" diff --git a/nt36xxx/nt36xxx_spi_fw_update.c b/nt36xxx/nt36xxx_spi_fw_update.c new file mode 100644 index 0000000000..2a6c20e2fb --- /dev/null +++ b/nt36xxx/nt36xxx_spi_fw_update.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define NVT_NT36XXX_SPI + +#include "nt36xxx_fw_update.c" diff --git a/st/fts.c b/st/fts.c new file mode 100644 index 0000000000..f5dc14c681 --- /dev/null +++ b/st/fts.c @@ -0,0 +1,6066 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * marco.cali@st.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*#include */ +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_FB_MSM) +#include +#include +#else +#include +#endif + +#ifdef KERNEL_ABOVE_2_6_38 +#include +#endif + +#include "fts.h" +#include "fts_lib/ftsCompensation.h" +#include "fts_lib/ftsIO.h" +#include "fts_lib/ftsError.h" +#include "fts_lib/ftsFlash.h" +#include "fts_lib/ftsFrame.h" +#include "fts_lib/ftsGesture.h" +#include "fts_lib/ftsTest.h" +#include "fts_lib/ftsTime.h" +#include "fts_lib/ftsTool.h" +#include "linux/moduleparam.h" + +#if defined(CONFIG_ST_TRUSTED_TOUCH) + +#include +#include +#include + +#include +#include +#include +#include +#include +#include "linux/gunyah/gh_irq_lend.h" +#include "linux/gunyah/gh_msgq.h" +#include "linux/gunyah/gh_mem_notifier.h" +#include "linux/gunyah/gh_rm_drv.h" +#include +#endif + +#define LINK_KOBJ_NAME "tp" + +#define FTS_DVDD_VOL_MIN 1800000 +#define FTS_DVDD_VOL_MAX 1800000 +#define FTS_DVDD_LOAD 20000 +#define FTS_AVDD_VOL_MIN 3000000 +#define FTS_AVDD_VOL_MAX 3300000 +#define FTS_AVDD_LOAD 20000 + +/* + * Uncomment to use polling mode instead of interrupt mode. + * + */ +// #define FTS_USE_POLLING_MODE + +/* + * Event installer helpers + */ +#define event_id(_e) EVENTID_##_e +#define handler_name(_h) fts_##_h##_event_handler + +#define install_handler(_i, _evt, _hnd) \ + (_i->event_dispatch_table[event_id(_evt)].handler = handler_name(_hnd)) + +/* + * Asyncronouns command helper + */ +#define WAIT_WITH_TIMEOUT(_info, _timeout, _command) \ +do { \ + if (wait_for_completion_timeout(&_info->cmd_done, _timeout) == 0) { \ + dev_warn(_info->dev, "Waiting for %s command: timeout\n", \ + #_command); \ + } \ +} while (0) + +#ifdef KERNEL_ABOVE_2_6_38 +#define TYPE_B_PROTOCOL +#endif + +#if defined(SCRIPTLESS) || defined(DRIVER_TEST) +static struct class *fts_cmd_class; +#endif + +static void fts_interrupt_disable(struct fts_ts_info *info); +static void fts_interrupt_enable(struct fts_ts_info *info); +static irqreturn_t fts_interrupt_handler(int irq, void *handle); +static int fts_probe_delayed(struct fts_ts_info *info); + +#ifdef CONFIG_ST_TRUSTED_TOUCH + +static struct gh_acl_desc *fts_vm_get_acl(enum gh_vm_names vm_name) +{ + struct gh_acl_desc *acl_desc; + gh_vmid_t vmid; + + gh_rm_get_vmid(vm_name, &vmid); + + acl_desc = kzalloc(offsetof(struct gh_acl_desc, acl_entries[1]), + GFP_KERNEL); + if (!acl_desc) + return ERR_PTR(ENOMEM); + + acl_desc->n_acl_entries = 1; + acl_desc->acl_entries[0].vmid = vmid; + acl_desc->acl_entries[0].perms = GH_RM_ACL_R | GH_RM_ACL_W; + + return acl_desc; +} + +static struct gh_sgl_desc *fts_vm_get_sgl(struct trusted_touch_vm_info *vm_info) +{ + struct gh_sgl_desc *sgl_desc; + int i; + + sgl_desc = kzalloc(offsetof(struct gh_sgl_desc, + sgl_entries[vm_info->iomem_list_size]), GFP_KERNEL); + if (!sgl_desc) + return ERR_PTR(ENOMEM); + + sgl_desc->n_sgl_entries = vm_info->iomem_list_size; + + for (i = 0; i < vm_info->iomem_list_size; i++) { + sgl_desc->sgl_entries[i].ipa_base = vm_info->iomem_bases[i]; + sgl_desc->sgl_entries[i].size = vm_info->iomem_sizes[i]; + } + + return sgl_desc; +} + +static int fts_populate_vm_info(struct fts_ts_info *info) +{ + int rc = 0; + struct trusted_touch_vm_info *vm_info; + struct device_node *np = info->client->dev.of_node; + int num_regs, num_sizes = 0; + + vm_info = kzalloc(sizeof(struct trusted_touch_vm_info), GFP_KERNEL); + if (!vm_info) { + rc = -ENOMEM; + goto error; + } + + info->vm_info = vm_info; + vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH; + vm_info->vm_name = GH_TRUSTED_VM; + rc = of_property_read_u32(np, "st,trusted-touch-spi-irq", + &vm_info->hw_irq); + if (rc) { + pr_err("Failed to read trusted touch SPI irq:%d\n", rc); + goto vm_error; + } + num_regs = of_property_count_u32_elems(np, + "st,trusted-touch-io-bases"); + if (num_regs < 0) { + pr_err("Invalid number of IO regions specified\n"); + rc = -EINVAL; + goto vm_error; + } + + num_sizes = of_property_count_u32_elems(np, + "st,trusted-touch-io-sizes"); + if (num_sizes < 0) { + pr_err("Invalid number of IO regions specified\n"); + rc = -EINVAL; + goto vm_error; + } + + if (num_regs != num_sizes) { + pr_err("IO bases and sizes doe not match\n"); + rc = -EINVAL; + goto vm_error; + } + + vm_info->iomem_list_size = num_regs; + + vm_info->iomem_bases = kcalloc(num_regs, sizeof(*vm_info->iomem_bases), + GFP_KERNEL); + if (!vm_info->iomem_bases) { + rc = -ENOMEM; + goto vm_error; + } + + rc = of_property_read_u32_array(np, "st,trusted-touch-io-bases", + vm_info->iomem_bases, vm_info->iomem_list_size); + if (rc) { + pr_err("Failed to read trusted touch io bases:%d\n", rc); + goto io_bases_error; + } + + vm_info->iomem_sizes = kzalloc( + sizeof(*vm_info->iomem_sizes) * num_sizes, GFP_KERNEL); + if (!vm_info->iomem_sizes) { + rc = -ENOMEM; + goto io_bases_error; + } + + rc = of_property_read_u32_array(np, "st,trusted-touch-io-sizes", + vm_info->iomem_sizes, vm_info->iomem_list_size); + if (rc) { + pr_err("Failed to read trusted touch io sizes:%d\n", rc); + goto io_sizes_error; + } + return rc; + +io_sizes_error: + kfree(vm_info->iomem_sizes); +io_bases_error: + kfree(vm_info->iomem_bases); +vm_error: + kfree(vm_info); +error: + return rc; +} + +static void fts_destroy_vm_info(struct fts_ts_info *info) +{ + kfree(info->vm_info->iomem_sizes); + kfree(info->vm_info->iomem_bases); + kfree(info->vm_info); +} + +static void fts_vm_deinit(struct fts_ts_info *info) +{ + if (info->vm_info->mem_cookie) + gh_mem_notifier_unregister(info->vm_info->mem_cookie); + fts_destroy_vm_info(info); +} + +#ifdef CONFIG_ARCH_QTI_VM +static int fts_vm_mem_release(struct fts_ts_info *info); +static void fts_trusted_touch_vm_mode_disable(struct fts_ts_info *info); + +static int fts_sgl_cmp(const void *a, const void *b) +{ + struct gh_sgl_entry *left = (struct gh_sgl_entry *)a; + struct gh_sgl_entry *right = (struct gh_sgl_entry *)b; + + return (left->ipa_base - right->ipa_base); +} + +static int fts_vm_compare_sgl_desc(struct gh_sgl_desc *expected, + struct gh_sgl_desc *received) +{ + int idx; + + if (expected->n_sgl_entries != received->n_sgl_entries) + return -E2BIG; + sort(received->sgl_entries, received->n_sgl_entries, + sizeof(received->sgl_entries[0]), fts_sgl_cmp, NULL); + sort(expected->sgl_entries, expected->n_sgl_entries, + sizeof(expected->sgl_entries[0]), fts_sgl_cmp, NULL); + + for (idx = 0; idx < expected->n_sgl_entries; idx++) { + struct gh_sgl_entry *left = &expected->sgl_entries[idx]; + struct gh_sgl_entry *right = &received->sgl_entries[idx]; + + if ((left->ipa_base != right->ipa_base) || + (left->size != right->size)) { + pr_err("sgl mismatch: left_base:%d right base:%d left size:%d right size:%d\n", + left->ipa_base, right->ipa_base, + left->size, right->size); + return -EINVAL; + } + } + return 0; +} + +static int fts_vm_handle_vm_hardware(struct fts_ts_info *info) +{ + int rc = 0; + + if (atomic_read(&info->delayed_vm_probe_pending)) { + rc = fts_probe_delayed(info); + if (rc) { + pr_err(" Delayed probe failure on VM!\n"); + return rc; + } + atomic_set(&info->delayed_vm_probe_pending, 0); + return rc; + } + + queue_delayed_work(info->fwu_workqueue, &info->fwu_work, + msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + fts_interrupt_enable(info); + return rc; +} + +static void fts_vm_irq_on_lend_callback(void *data, + unsigned long notif_type, + enum gh_irq_label label) +{ + struct fts_ts_info *info = data; + struct irq_data *irq_data; + int irq = 0; + int const resource_timeout = msecs_to_jiffies(2000); + int rc = 0; + + irq = gh_irq_accept(info->vm_info->irq_label, -1, IRQ_TYPE_LEVEL_HIGH); + if (irq < 0) { + pr_err("failed to accept irq\n"); + goto irq_fail; + } + atomic_set(&info->vm_info->tvm_owns_irq, 1); + irq_data = irq_get_irq_data(irq); + if (!irq_data) { + pr_err("Invalid irq data for trusted touch\n"); + goto irq_fail; + } + if (!irq_data->hwirq) { + pr_err("Invalid irq in irq data\n"); + goto irq_fail; + } + if (irq_data->hwirq != info->vm_info->hw_irq) { + pr_err("Invalid irq lent\n"); + goto irq_fail; + } + + pr_debug("irq:returned from accept:%d\n", irq); + info->client->irq = irq; + if (!wait_for_completion_timeout(&info->resource_checkpoint, + resource_timeout)) { + pr_err("Resources not acquired in TVM\n"); + goto irq_fail; + } + + rc = fts_vm_handle_vm_hardware(info); + if (rc) { + pr_err(" Delayed probe failure on VM!\n"); + goto irq_fail; + } + + atomic_set(&info->trusted_touch_enabled, 1); + return; +irq_fail: + fts_trusted_touch_vm_mode_disable(info); +} + +static void fts_vm_mem_on_lend_handler(enum gh_mem_notifier_tag tag, + unsigned long notif_type, void *entry_data, void *notif_msg) +{ + struct gh_rm_notif_mem_shared_payload *payload; + struct gh_sgl_desc *sgl_desc, *expected_sgl_desc; + struct gh_acl_desc *acl_desc; + struct trusted_touch_vm_info *vm_info; + struct fts_ts_info *info; + int rc = 0; + + if (notif_type != GH_RM_NOTIF_MEM_SHARED || + tag != GH_MEM_NOTIFIER_TAG_TOUCH) { + pr_err("Invalid command passed from rm\n"); + return; + } + + if (!entry_data || !notif_msg) { + pr_err("Invalid entry data passed from rm\n"); + return; + } + + info = (struct fts_ts_info *)entry_data; + vm_info = info->vm_info; + if (!vm_info) { + pr_err("Invalid vm_info\n"); + return; + } + + payload = (struct gh_rm_notif_mem_shared_payload *)notif_msg; + if (payload->trans_type != GH_RM_TRANS_TYPE_LEND || + payload->label != TRUSTED_TOUCH_MEM_LABEL) { + pr_err("Invalid label or transaction type\n"); + goto onlend_fail; + } + + acl_desc = fts_vm_get_acl(GH_TRUSTED_VM); + if (IS_ERR(acl_desc)) { + pr_err("failed to populated acl data:rc=%d\n", + PTR_ERR(acl_desc)); + goto onlend_fail; + } + + sgl_desc = gh_rm_mem_accept(payload->mem_handle, GH_RM_MEM_TYPE_IO, + GH_RM_TRANS_TYPE_LEND, + GH_RM_MEM_ACCEPT_VALIDATE_ACL_ATTRS | + GH_RM_MEM_ACCEPT_VALIDATE_LABEL | + GH_RM_MEM_ACCEPT_DONE, payload->label, acl_desc, + NULL, NULL, 0); + if (IS_ERR_OR_NULL(sgl_desc)) { + pr_err("failed to do mem accept :rc=%d\n", + PTR_ERR(sgl_desc)); + goto acl_fail; + } + atomic_set(&vm_info->tvm_owns_iomem, 1); + + /* Initiate i2c session on tvm */ + rc = pm_runtime_get_sync(info->client->adapter->dev.parent); + if (rc < 0) { + pr_err("failed to get sync rc:%d\n", rc); + (void)fts_vm_mem_release(info); + atomic_set(&info->vm_info->tvm_owns_iomem, 0); + goto acl_fail; + } + complete(&info->resource_checkpoint); + + expected_sgl_desc = fts_vm_get_sgl(vm_info); + if (fts_vm_compare_sgl_desc(expected_sgl_desc, sgl_desc)) { + pr_err("IO sg list does not match\n"); + goto sgl_cmp_fail; + } + + vm_info->vm_mem_handle = payload->mem_handle; + kfree(expected_sgl_desc); + kfree(acl_desc); + return; + +sgl_cmp_fail: + kfree(expected_sgl_desc); +acl_fail: + kfree(acl_desc); +onlend_fail: + fts_trusted_touch_vm_mode_disable(info); +} + +static int fts_vm_mem_release(struct fts_ts_info *info) +{ + int rc = 0; + + rc = gh_rm_mem_release(info->vm_info->vm_mem_handle, 0); + if (rc) + pr_err("VM mem release failed: rc=%d\n", rc); + + rc = gh_rm_mem_notify(info->vm_info->vm_mem_handle, + GH_RM_MEM_NOTIFY_OWNER_RELEASED, + GH_MEM_NOTIFIER_TAG_TOUCH, 0); + if (rc) + pr_err("Failed to notify mem release to PVM: rc=%d\n"); + + info->vm_info->vm_mem_handle = 0; + return rc; +} + +static void fts_trusted_touch_vm_mode_disable(struct fts_ts_info *info) +{ + int rc = 0; + + + if (atomic_read(&info->vm_info->tvm_owns_iomem) && + atomic_read(&info->vm_info->tvm_owns_irq)) + fts_interrupt_disable(info); + + if (atomic_read(&info->vm_info->tvm_owns_iomem)) { + flushFIFO(); + release_all_touches(info); + rc = fts_vm_mem_release(info); + if (rc) + pr_err("Failed to release mem rc:%d\n", rc); + else + atomic_set(&info->vm_info->tvm_owns_iomem, 0); + pm_runtime_put_sync(info->client->adapter->dev.parent); + } + + if (atomic_read(&info->vm_info->tvm_owns_irq)) { + rc = gh_irq_release(info->vm_info->irq_label); + if (rc) + pr_err("Failed to release irq rc:%d\n", rc); + else + atomic_set(&info->vm_info->tvm_owns_irq, 0); + + rc = gh_irq_release_notify(info->vm_info->irq_label); + if (rc) + pr_err("Failed to notify release irq rc:%d\n", rc); + } + atomic_set(&info->trusted_touch_enabled, 0); + reinit_completion(&info->resource_checkpoint); + pr_debug("trusted touch disabled\n"); +} + +static int fts_handle_trusted_touch_tvm(struct fts_ts_info *info, int value) +{ + int err = 0; + + switch (value) { + case 0: + if (atomic_read(&info->trusted_touch_enabled) == 0) { + pr_err("Trusted touch is already disabled\n"); + break; + } + if (atomic_read(&info->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + fts_trusted_touch_vm_mode_disable(info); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + case 1: + if (atomic_read(&info->trusted_touch_enabled)) { + pr_err("Trusted touch usecase underway\n"); + err = -EBUSY; + break; + } + if (atomic_read(&info->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + pr_err("Cannot turnon trusted touch(vm mode) in VM\n"); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + default: + dev_err(&info->client->dev, "unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + + return err; +} + +#else + +static int fts_clk_prepare_enable(struct fts_ts_info *info) +{ + int ret; + + ret = clk_prepare_enable(info->iface_clk); + if (ret) { + dev_err(&info->client->dev, + "error on clk_prepare_enable(iface_clk):%d\n", ret); + return ret; + } + + ret = clk_prepare_enable(info->core_clk); + if (ret) { + clk_disable_unprepare(info->iface_clk); + dev_err(&info->client->dev, + "error clk_prepare_enable(core_clk):%d\n", ret); + } + return ret; +} + +static void fts_clk_disable_unprepare(struct fts_ts_info *info) +{ + clk_disable_unprepare(info->core_clk); + clk_disable_unprepare(info->iface_clk); +} + +static int fts_bus_get(struct fts_ts_info *info) +{ + int rc = 0; + + mutex_lock(&info->fts_clk_io_ctrl_mutex); + rc = pm_runtime_get_sync(info->client->adapter->dev.parent); + if (rc >= 0 && info->core_clk != NULL && info->iface_clk != NULL) { + rc = fts_clk_prepare_enable(info); + if (rc) + pm_runtime_put_sync(info->client->adapter->dev.parent); + } + mutex_unlock(&info->fts_clk_io_ctrl_mutex); + return rc; +} + +static void fts_bus_put(struct fts_ts_info *info) +{ + mutex_lock(&info->fts_clk_io_ctrl_mutex); + if (info->core_clk != NULL && info->iface_clk != NULL) + fts_clk_disable_unprepare(info); + pm_runtime_put_sync(info->client->adapter->dev.parent); + mutex_unlock(&info->fts_clk_io_ctrl_mutex); +} + +static struct gh_notify_vmid_desc *fts_vm_get_vmid(gh_vmid_t vmid) +{ + struct gh_notify_vmid_desc *vmid_desc; + + vmid_desc = kzalloc(offsetof(struct gh_notify_vmid_desc, + vmid_entries[1]), GFP_KERNEL); + if (!vmid_desc) + return ERR_PTR(ENOMEM); + + vmid_desc->n_vmid_entries = 1; + vmid_desc->vmid_entries[0].vmid = vmid; + return vmid_desc; +} + +static void fts_trusted_touch_complete(struct fts_ts_info *info) +{ + if (atomic_read(&info->vm_info->pvm_owns_iomem) && + atomic_read(&info->vm_info->pvm_owns_irq)) { + fts_interrupt_enable(info); + fts_bus_put(info); + complete(&info->trusted_touch_powerdown); + atomic_set(&info->trusted_touch_enabled, 0); + pr_debug("reenabled interrupts on PVM\n"); + } else { + pr_err("PVM does not own irq and IOMEM\n"); + } +} + +static void fts_vm_irq_on_release_callback(void *data, + unsigned long notif_type, + enum gh_irq_label label) +{ + struct fts_ts_info *info = data; + int rc = 0; + + rc = gh_irq_reclaim(info->vm_info->irq_label); + if (rc) + pr_err("failed to reclaim irq on pvm rc:%d\n", rc); + else + atomic_set(&info->vm_info->pvm_owns_irq, 1); +} + +static void fts_vm_mem_on_release_handler(enum gh_mem_notifier_tag tag, + unsigned long notif_type, void *entry_data, void *notif_msg) +{ + struct gh_rm_notif_mem_released_payload *payload; + struct trusted_touch_vm_info *vm_info; + struct fts_ts_info *info; + int rc = 0; + + if (notif_type != GH_RM_NOTIF_MEM_RELEASED || + tag != GH_MEM_NOTIFIER_TAG_TOUCH) { + pr_err(" Invalid tag or command passed\n"); + return; + } + + if (!entry_data || !notif_msg) { + pr_err(" Invalid data or notification message\n"); + return; + } + + payload = (struct gh_rm_notif_mem_released_payload *)notif_msg; + info = (struct fts_ts_info *)entry_data; + vm_info = info->vm_info; + if (!vm_info) { + pr_err(" Invalid vm_info\n"); + return; + } + + if (payload->mem_handle != vm_info->vm_mem_handle) { + pr_err("Invalid mem handle detected\n"); + return; + } + + rc = gh_rm_mem_reclaim(payload->mem_handle, 0); + if (rc) { + pr_err("Trusted touch VM mem release failed rc:%d\n", rc); + return; + } + atomic_set(&vm_info->pvm_owns_iomem, 1); + vm_info->vm_mem_handle = 0; +} + +static int fts_vm_mem_lend(struct fts_ts_info *info) +{ + struct gh_acl_desc *acl_desc; + struct gh_sgl_desc *sgl_desc; + struct gh_notify_vmid_desc *vmid_desc; + gh_memparcel_handle_t mem_handle; + gh_vmid_t trusted_vmid; + int rc = 0; + + acl_desc = fts_vm_get_acl(GH_TRUSTED_VM); + if (IS_ERR(acl_desc)) { + pr_err("Failed to get acl of IO memories for Trusted touch\n"); + PTR_ERR(acl_desc); + return -EINVAL; + } + + sgl_desc = fts_vm_get_sgl(info->vm_info); + if (IS_ERR(sgl_desc)) { + pr_err("Failed to get sgl of IO memories for Trusted touch\n"); + PTR_ERR(sgl_desc); + rc = -EINVAL; + goto sgl_error; + } + + rc = gh_rm_mem_lend(GH_RM_MEM_TYPE_IO, 0, TRUSTED_TOUCH_MEM_LABEL, + acl_desc, sgl_desc, NULL, &mem_handle); + if (rc) { + pr_err("Failed to lend IO memories for Trusted touch rc:%d\n", + rc); + goto error; + } + + gh_rm_get_vmid(GH_TRUSTED_VM, &trusted_vmid); + + vmid_desc = fts_vm_get_vmid(trusted_vmid); + + rc = gh_rm_mem_notify(mem_handle, GH_RM_MEM_NOTIFY_RECIPIENT_SHARED, + GH_MEM_NOTIFIER_TAG_TOUCH, vmid_desc); + if (rc) { + pr_err("Failed to notify mem lend to hypervisor rc:%d\n", rc); + goto vmid_error; + } + + info->vm_info->vm_mem_handle = mem_handle; +vmid_error: + kfree(vmid_desc); +error: + kfree(sgl_desc); +sgl_error: + kfree(acl_desc); + + return rc; +} + +static int fts_trusted_touch_vm_mode_enable(struct fts_ts_info *info) +{ + int rc = 0; + struct trusted_touch_vm_info *vm_info = info->vm_info; + + /* i2c session start and resource acquire */ + if (fts_bus_get(info) < 0) { + dev_err(&info->client->dev, "fts_bus_get failed\n"); + rc = -EIO; + return rc; + } + + /* flush pending interurpts from FIFO */ + fts_interrupt_disable(info); + flushFIFO(); + release_all_touches(info); + + rc = fts_vm_mem_lend(info); + if (rc) { + pr_err("Failed to lend memory\n"); + return -EINVAL; + } + atomic_set(&vm_info->pvm_owns_iomem, 0); + + rc = gh_irq_lend_v2(vm_info->irq_label, vm_info->vm_name, + info->client->irq, &fts_vm_irq_on_release_callback, info); + if (rc) { + pr_err("Failed to lend irq\n"); + return -EINVAL; + } + atomic_set(&vm_info->pvm_owns_irq, 0); + + rc = gh_irq_lend_notify(vm_info->irq_label); + if (rc) { + pr_err("Failed to notify irq\n"); + return -EINVAL; + } + + reinit_completion(&info->trusted_touch_powerdown); + atomic_set(&info->trusted_touch_enabled, 1); + pr_debug("trusted touch enabled\n"); + return rc; +} + +static int fts_handle_trusted_touch_pvm(struct fts_ts_info *info, int value) +{ + int err = 0; + + switch (value) { + case 0: + if (atomic_read(&info->trusted_touch_enabled) == 0) { + pr_err("Trusted touch is already disabled\n"); + break; + } + if (atomic_read(&info->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + fts_trusted_touch_complete(info); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + case 1: + if (atomic_read(&info->trusted_touch_enabled)) { + pr_err("Trusted touch usecase underway\n"); + err = -EBUSY; + break; + } + if (atomic_read(&info->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + err = fts_trusted_touch_vm_mode_enable(info); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + default: + dev_err(&info->client->dev, "unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + return err; +} +#endif + +static int fts_vm_init(struct fts_ts_info *info) +{ + int rc = 0; + struct trusted_touch_vm_info *vm_info; + void *mem_cookie; + + rc = fts_populate_vm_info(info); + if (rc) { + pr_err("Cannot setup vm pipeline\n"); + rc = -EINVAL; + goto fail; + } + + vm_info = info->vm_info; +#ifdef CONFIG_ARCH_QTI_VM + mem_cookie = gh_mem_notifier_register(GH_MEM_NOTIFIER_TAG_TOUCH, + fts_vm_mem_on_lend_handler, info); + if (!mem_cookie) { + pr_err("Failed to register on lend mem notifier\n"); + rc = -EINVAL; + goto init_fail; + } + vm_info->mem_cookie = mem_cookie; + rc = gh_irq_wait_for_lend_v2(vm_info->irq_label, GH_PRIMARY_VM, + &fts_vm_irq_on_lend_callback, info); + atomic_set(&vm_info->tvm_owns_irq, 0); + atomic_set(&vm_info->tvm_owns_iomem, 0); + init_completion(&info->resource_checkpoint); +#else + mem_cookie = gh_mem_notifier_register(GH_MEM_NOTIFIER_TAG_TOUCH, + fts_vm_mem_on_release_handler, info); + if (!mem_cookie) { + pr_err("Failed to register on release mem notifier\n"); + rc = -EINVAL; + goto init_fail; + } + vm_info->mem_cookie = mem_cookie; + atomic_set(&vm_info->pvm_owns_irq, 1); + atomic_set(&vm_info->pvm_owns_iomem, 1); +#endif + return rc; +init_fail: + fts_vm_deinit(info); +fail: + return rc; +} + +static void fts_dt_parse_trusted_touch_info(struct fts_ts_info *info) +{ + struct device_node *np = info->client->dev.of_node; + int rc = 0; + const char *selection; + const char *environment; + + rc = of_property_read_string(np, "st,trusted-touch-mode", + &selection); + if (rc) { + dev_warn(&info->client->dev, + "%s: No trusted touch mode selection made\n", __func__); + } + + if (!strcmp(selection, "vm_mode")) { + atomic_set(&info->trusted_touch_mode, TRUSTED_TOUCH_VM_MODE); + pr_err("Selected trusted touch mode to VM mode\n"); + } else { + atomic_set(&info->trusted_touch_mode, TRUSTED_TOUCH_MODE_NONE); + pr_err("Invalid trusted_touch mode\n"); + } + + rc = of_property_read_string(np, "st,touch-environment", + &environment); + if (rc) { + dev_warn(&info->client->dev, + "%s: No trusted touch mode environment\n", __func__); + } + info->touch_environment = environment; + pr_err("Trusted touch environment:%s\n", + info->touch_environment); +} + +static void fts_trusted_touch_init(struct fts_ts_info *info) +{ + int rc = 0; + + atomic_set(&info->trusted_touch_initialized, 0); + init_completion(&info->trusted_touch_powerdown); + fts_dt_parse_trusted_touch_info(info); + + /* Get clocks */ + info->core_clk = devm_clk_get(info->client->dev.parent, + "m-ahb"); + if (IS_ERR(info->core_clk)) { + info->core_clk = NULL; + dev_warn(&info->client->dev, + "%s: core_clk is not defined\n", __func__); + } + + info->iface_clk = devm_clk_get(info->client->dev.parent, + "se-clk"); + if (IS_ERR(info->iface_clk)) { + info->iface_clk = NULL; + dev_warn(&info->client->dev, + "%s: iface_clk is not defined\n", __func__); + } + if (atomic_read(&info->trusted_touch_mode) == TRUSTED_TOUCH_VM_MODE) { + rc = fts_vm_init(info); + if (rc) + pr_err("Failed to init VM\n"); + } + atomic_set(&info->trusted_touch_initialized, 1); +} + +static ssize_t fts_trusted_touch_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info; + + if (!client) + return scnprintf(buf, PAGE_SIZE, "client is null\n"); + + info = i2c_get_clientdata(client); + if (!info) { + logError(0, "info is null\n"); + return scnprintf(buf, PAGE_SIZE, "info is null\n"); + } + + return scnprintf(buf, PAGE_SIZE, "%d", + atomic_read(&info->trusted_touch_enabled)); +} + +static ssize_t fts_trusted_touch_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info; + unsigned long value; + int err = 0; + + if (!client) + return -EIO; + info = i2c_get_clientdata(client); + if (!info) { + logError(0, "info is null\n"); + return -EIO; + } + if (count > 2) + return -EINVAL; + err = kstrtoul(buf, 10, &value); + if (err != 0) + return err; + + if (!atomic_read(&info->trusted_touch_initialized)) + return -EIO; + +#ifdef CONFIG_ARCH_QTI_VM + err = fts_handle_trusted_touch_tvm(info, value); + if (err) { + pr_err("Failed to handle trusted touch in tvm\n"); + return -EINVAL; + } +#else + err = fts_handle_trusted_touch_pvm(info, value); + if (err) { + pr_err("Failed to handle trusted touch in pvm\n"); + return -EINVAL; + } +#endif + err = count; + return err; +} + +#endif + +//struct chipInfo ftsInfo; + +/** + * #ifdef PHONE_GESTURE + * extern struct mutex gestureMask_mutex; + * #endif + */ + +static char tag[8] = "[ FTS ]\0"; + +static char fts_ts_phys[64]; +static u32 typeOfComand[CMD_STR_LEN] = {0}; +static int numberParameters; +#ifdef USE_ONE_FILE_NODE +static int feature_feasibility = ERROR_OP_NOT_ALLOW; +#endif +#ifdef PHONE_GESTURE +static u8 mask[GESTURE_MASK_SIZE + 2]; +//extern u16 gesture_coordinates_x[GESTURE_COORDS_REPORT_MAX]; +//extern u16 gesture_coordinates_y[GESTURE_COORDS_REPORT_MAX]; +//extern int gesture_coords_reported; +//extern struct mutex gestureMask_mutex; +#ifdef USE_CUSTOM_GESTURES +static int custom_gesture_res; +#endif +#endif +#ifdef USE_NOISE_PARAM +static u8 noise_params[NOISE_PARAMETERS_SIZE] = {0}; +#endif +static void fts_interrupt_enable(struct fts_ts_info *info); +static int fts_init_afterProbe(struct fts_ts_info *info); +static int fts_mode_handler(struct fts_ts_info *info, int force); +static int fts_command(struct fts_ts_info *info, unsigned char cmd); +static int fts_chip_initialization(struct fts_ts_info *info); +static int fts_enable_reg(struct fts_ts_info *info, bool enable); + +static struct drm_panel *active_panel; + +void touch_callback(unsigned int status) +{ + /* Empty */ +} + +unsigned int le_to_uint(const unsigned char *ptr) +{ + return (unsigned int) ptr[0] + (unsigned int) ptr[1] * 0x100; +} + +unsigned int be_to_uint(const unsigned char *ptr) +{ + return (unsigned int) ptr[1] + (unsigned int) ptr[0] * 0x100; +} + +void release_all_touches(struct fts_ts_info *info) +{ + unsigned int type = MT_TOOL_FINGER; + int i; + + for (i = 0; i < TOUCH_ID_MAX; i++) { +#ifdef STYLUS_MODE + if (test_bit(i, &info->stylus_id)) + type = MT_TOOL_PEN; +#endif + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, type, 0); + input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, -1); + } + input_sync(info->input_dev); + info->touch_id = 0; +#ifdef STYLUS_MODE + info->stylus_id = 0; +#endif +} + +/************************* FW UPGGRADE *********************************/ +/* update firmware*/ +/** + * echo 01/00 > fwupdate perform a fw update taking the FW to burn always + * from a bin file stored in /system/etc/firmware, 01= force the FW update + * whicheve fw_version and config_id; 00=perform a fw update only if the fw + * in the file has a greater fw_version or config_id + */ + +/** + * cat fwupdate to show the result of the burning procedure + * (example output in the terminal = "AA00000001BB" if the switch is enabled) + */ + +/** + * echo 01/00 > fwupdate; cat fwupdate to perform both operation stated before + * in just one call + */ +static ssize_t fts_fwupdate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret, mode; + /*const struct firmware *fw = NULL;*/ + /*char *firmware_name = "st_fts.bin";*/ + struct Firmware fwD; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + int orig_size; + u8 *orig_data; + + /* reading out firmware upgrade mode */ + ret = kstrtoint(buf, 10, &mode); + if (ret != 0) { + pr_err("%s: ret = %d\n", __func__, ret); + return -EINVAL; + } + + fwD.data = NULL; + ret = getFWdata_nocheck(PATH_FILE_FW, &orig_data, &orig_size, 0); + if (ret < OK) { + logError(1, "%s %s: impossible retrieve FW... ERROR %08X\n", + tag, __func__, ERROR_MEMH_READ); + ret = (ret | ERROR_MEMH_READ); + goto END; + } + + ret = parseBinFile(orig_data, orig_size, &fwD, !mode); + if (ret < OK) { + logError(1, "%s %s: impossible parse ERROR %08X\n", + tag, __func__, ERROR_MEMH_READ); + ret = (ret | ERROR_MEMH_READ); + goto END; + } + + logError(0, "%s Starting flashing procedure...\n", tag); + ret = flash_burn(&fwD, mode, !mode); + + if (ret < OK && ret != (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED)) + logError(0, "%s flashProcedure: ERROR %02X\n", + tag, ERROR_FLASH_PROCEDURE); + logError(0, "%s flashing procedure Finished!\n", tag); + +END: + kfree(fwD.data); + info->fwupdate_stat = ret; + + if (ret < OK) + logError(1, "%s %s Unable to upgrade firmware! ERROR %08X\n", + tag, __func__, ret); + + return count; +} + +static ssize_t fts_fwupdate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + //fwupdate_stat: ERROR code Returned by flashProcedure. + return snprintf(buf, PAGE_SIZE, "AA%08XBB\n", info->fwupdate_stat); +} + +/****UTILITIES (current fw_ver/conf_id, active mode, file fw_ver/conf_id)****/ +/** + * cat appid show on the terminal fw_version.config_id of + * the FW running in the IC + */ +static ssize_t fts_appid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int error; + + error = snprintf(buf, PAGE_SIZE, "%x.%x\n", ftsInfo.u16_fwVer, + ftsInfo.u16_cfgId); + return error; +} + +/** + * cat mode_active to show the bitmask of which indicate the modes/features + * which are running on the IC in a specific istant oftime (example output in + * the terminal = "AA10000000BB" only senseOn performed) + */ +static ssize_t fts_mode_active_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(1, "%s Current mode active = %08X\n", tag, info->mode); + //return sprintf(buf, "AA%08XBB\n", info->mode); + return snprintf(buf, PAGE_SIZE, "AA%08XBB\n", info->mode); +} + +/** + * cat fw_file_test show on the terminal fw_version and config_id of the FW + * stored in the fw file/header file + */ +static ssize_t fts_fw_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct Firmware fw; + int ret; + + fw.data = NULL; + ret = readFwFile(PATH_FILE_FW, &fw, 0); + + if (ret < OK) + logError(1, "%s Error during reading FW file! ERROR %08X\n", + tag, ret); + else { + logError(1, "%s fw_version = %04X, config_version = %04X, ", + tag, fw.fw_ver, fw.config_id); + logError(1, "size = %dbytes\n", fw.data_size); + } + + kfree(fw.data); + return 0; +} + +/** + * cat lockdown_info to show the lockdown info on the terminal + * (example output in the terminal = "AA00000000X1X2..X10BB" ) + * where first 4 bytes correspond t + */ +static ssize_t fts_lockdown_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 data[LOCKDOWN_CODE_SIZE] = {0}; + int ret, size = 100; + char buff[CMD_STR_LEN] = {0}; + char all_strbuff[100] = {0}; + + ret = fts_disableInterrupt(); + if (ret < OK) + goto END; + + ret = lockDownInfo((u8 *)data, LOCKDOWN_CODE_SIZE); + if (ret < OK) + goto END; + +END: + ret |= fts_enableInterrupt(); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + snprintf(buff, sizeof(buff), "%08X", ret); + strlcat(all_strbuff, buff, size); + if (ret >= OK) { + for (ret = 0; ret < LOCKDOWN_CODE_SIZE; ret++) { + snprintf(buff, sizeof(buff), "%02X", data[ret]); + strlcat(all_strbuff, buff, size); + } + } else { + logError(1, "%s Error while reading lockdown info = %08X\n", + tag, ret); + } + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + return snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); +} + +/** + * cat strength_frame to obtain strength data + * the string returned in the shell is made up as follow: + * AA = start byte + * X1X2X3X4 = 4 bytes in HEX format which represent an + * error code (00000000 no error) + * + * if error code is all 0s + * FF = 1 byte in HEX format number of rows + * SS = 1 byte in HEX format number of columns + * N1, ... = the decimal value of each node separated by a coma + * + * BB = end byte + */ +static ssize_t fts_strength_frame_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + /*unsigned int temp;*/ + /*int res;*/ + /*struct i2c_client *client = to_i2c_client(dev); */ + /*struct fts_ts_info *info = i2c_get_clientdata(client); */ + + if (sscanf(p, "%x ", &typeOfComand[0]) != 1) + return -EINVAL; + + logError(1, "%s %s: Type of Strength Frame selected: %d\n", tag, + __func__, typeOfComand[0]); + return count; +} + +static ssize_t fts_strength_frame_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct MutualSenseFrame frame; + int res = ERROR_OP_NOT_ALLOW, j, size = 6*2; + int count = 0; + u16 type = 0; + char *all_strbuff = NULL; + char buff[CMD_STR_LEN] = {0}; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + frame.node_data = NULL; + + res = fts_disableInterrupt(); + if (res < OK) + goto END; + + res = senseOn(); +#ifdef PHONE_KEY + res = keyOn(); +#endif + if (res < OK) { + logError(1, "%s %s: could not start scanning! ERROR %08X\n", + tag, __func__, res); + goto END; + } + msleep(WAIT_FOR_FRESH_FRAMES); + + res = senseOff(); +#ifdef PHONE_KEY + res = keyOff(); +#endif + if (res < OK) { + logError(1, "%s %s: could not finish scanning! ERROR %08X\n", + tag, __func__, res); + goto END; + } + /* mdelay(WAIT_AFTER_SENSEOFF); */ + msleep(WAIT_AFTER_SENSEOFF); + flushFIFO(); + + switch (typeOfComand[0]) { + case 1: + type = ADDR_NORM_TOUCH; + break; +#ifdef PHONE_KEY + case 2: + type = ADDR_NORM_MS_KEY; + break; +#endif + default: + logError(1, "%s %s: Strength type %d not valid! ERROR %08X\n", + tag, __func__, typeOfComand[0], ERROR_OP_NOT_ALLOW); + res = ERROR_OP_NOT_ALLOW; + goto END; + } + + res = getMSFrame(type, &frame, 0); + if (res < OK) { + logError(1, "%s %s: could not get the frame! ERROR %08X\n", + tag, __func__, res); + goto END; + } else { + size += (res * 6); + logError(0, "%s The frame size is %d words\n", tag, res); + res = OK; + print_frame_short("MS Strength frame =", + array1dTo2d_short(frame.node_data, + frame.node_data_size, + frame.header.sense_node), + frame.header.force_node, + frame.header.sense_node); + } + +END: + flushFIFO(); + release_all_touches(info); + fts_mode_handler(info, 1); + + all_strbuff = kmalloc_array(size, sizeof(char), GFP_KERNEL); + + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + if (res >= OK) { + snprintf(buff, sizeof(buff), "%02X", + (u8) frame.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + (u8) frame.header.sense_node); + strlcat(all_strbuff, buff, size); + + for (j = 0; j < frame.node_data_size; j++) { + snprintf(buff, sizeof(buff), "%d,", + frame.node_data[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(frame.node_data); + } + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, "%s %s: Unable toallocate all_strbuff!ERROR %08X\n", + tag, ERROR_ALLOC); + } + + fts_enableInterrupt(); + return count; +} + +/********** FEATURES *********************/ + +/** + * TODO: edit this function according to the features policy to + * allow during the screen on/off, following is shown an example + * but check always with ST for more details + */ +int check_feature_feasibility(struct fts_ts_info *info, unsigned int feature) +{ + int res = OK; + + /** + * Example based on the status of the screen and + * on the feature that is trying to enable + */ + + /*Example based only on the feature that is going to be activated*/ + switch (feature) { + case FEAT_GESTURE: + if (info->cover_enabled == 1) { + res = ERROR_OP_NOT_ALLOW; + + logError(1, "%s %s:Feature not allowed when in Cover ", + tag, __func__); + logError(1, "mode %08X\n", res); + /** + * for example here can be place a code for + * disabling the cover mode when gesture is + * activated + */ + } + break; + + case FEAT_COVER: + if (info->gesture_enabled == 1) { + res = ERROR_OP_NOT_ALLOW; + /*logError(1,"Feature not allowed*/ + /*when Gestures enabled!");*/ + logError(1, "s %s: Feature not allowed when Gestures ", + tag, __func__); + logError(1, "enabled%08X\n", res); + /** + * for example here can be place a code for + * disabling the gesture mode when cover is + * activated (that means that cover mode has + * an higher priority on gesture mode) + */ + } + break; + + default: + logError(1, "%s %s: Feature Allowed!\n", tag, __func__); + } + return res; +} + +#ifdef USE_ONE_FILE_NODE +/** + * echo XXXX 00/01 > feature_enable + * set the feature to disable/enable. + * XXXX = 4 bytes which identify the feature + * + * cat feature_enable + * set the enabled mode/features in the IC + * and return an error code + * + * echo XXXX 00/01 > feature_enable; + * cat feature_enable to perform both action stated + * before in just one call + */ +static ssize_t fts_feature_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + char *p = (char *)buf; + unsigned int temp; + int res = OK; + + if ((count - 8 + 1) / 3 != 1) { + logError(1, "%s fts_feature_enable: ", tag); + logError(1, "Number of parameter wrong! %d > %d\n", + (count - 8 + 1) / 3, 1); + return -EINVAL; + } + + if (sscanf(p, "%08X ", &temp) != 1) + return -EINVAL; + p += 9; + res = check_feature_feasibility(info, temp); + if (res < OK) + return -EINVAL; + + switch (temp) { +#ifdef PHONE_GESTURE + case FEAT_GESTURE: + if (sscanf(p, "%02X ", &info->gesture_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: Gesture Enabled = %d\n", + tag, info->gesture_enabled); + + break; +#endif + +#ifdef GLOVE_MODE + case FEAT_GLOVE: + if (sscanf(p, "%02X ", &info->glove_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: Glove Enabled = %d\n", + tag, info->glove_enabled); + + break; +#endif + +#ifdef STYLUS_MODE + case FEAT_STYLUS: + if (sscanf(p, "%02X ", &info->stylus_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: Stylus Enabled = %d\n", + tag, info->stylus_enabled); + + break; +#endif + +#ifdef COVER_MODE + case FEAT_COVER: + if (sscanf(p, "%02X ", &info->cover_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: Cover Enabled = %d\n", + tag, info->cover_enabled); + + break; +#endif + +#ifdef CHARGER_MODE + case FEAT_CHARGER: + if (sscanf(p, "%02X ", &info->charger_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: Charger Enabled= %d\n", + tag, info->charger_enabled); + + break; +#endif + +#ifdef VR_MODE + case FEAT_VR: + if (sscanf(p, "%02X ", &info->vr_enabled) != 1) + return -EINVAL; + + logError(1, "%s fts_feature_enable: VR Enabled = %d\n", + tag, info->vr_enabled); + + break; +#endif + +#ifdef EDGE_REJ + case FEAT_EDGE_REJECTION: + if (sscanf(p, "%02X ", &info->edge_rej_enabled) != 1) + return -EINVAL; + logError(1, "%s %s: Edge Rejection Enabled= %d\n", + tag, __func__, info->edge_rej_enabled); + + break; +#endif + +#ifdef CORNER_REJ + case FEAT_CORNER_REJECTION: + if (sscanf(p, "%02X ", &info->corner_rej_enabled) != 1) + return -EINVAL; + + logError(1, "%s %s: Corner Rejection Enabled= %d\n", + tag, __func__, info->corner_rej_enabled); + + break; +#endif + +#ifdef EDGE_PALM_REJ + case FEAT_EDGE_PALM_REJECTION: + if (sscanf(p, "%02X", &info->edge_palm_rej_enabled) != 1) + return -EINVAL; + + logError(1, "%s %s:Edge Palm RejectionEnabled= %d\n", + tag, __func__, info->edge_palm_rej_enabled); + + break; +#endif + default: + logError(1, "%s %s: Feature %08X not valid! ERROR %08X\n", + tag, __func__, temp, ERROR_OP_NOT_ALLOW); + res = ERROR_OP_NOT_ALLOW; + } + feature_feasibility = res; + + if (feature_feasibility >= OK) + feature_feasibility = fts_mode_handler(info, 1); + else { + logError(1, "%s %s: Call echo XXXX 00/01 > feature_enable ", + tag, __func__); + logError(1, "with a correct feature! ERROR %08X\n", res); + } + return count; +} + +static ssize_t fts_feature_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + + if (feature_feasibility < OK) { + logError(1, + "%s %s:Call before echo 00/01 > feature_enable %08X\n", + tag, __func__, feature_feasibility); + } + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", feature_feasibility); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + feature_feasibility = ERROR_OP_NOT_ALLOW; + return count; +} + +#else + +#ifdef EDGE_REJ +/** + * echo 01/00 > edge_rej to enable/disable edge rejection + * cat edge_rej to show the status of the edge_rej_enabled + * switch (example output in the terminal = "AA00000001BB" + * if the switch is enabled) + * + * echo 01/00 > edge_rej; cat edge_rej to enable/disable + * edge rejection and see the switch status in just one call + */ +static ssize_t fts_edge_rej_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: edge_rej_enabled = %d\n", + tag, __func__, info->edge_rej_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->edge_rej_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + + return count; +} + +static ssize_t fts_edge_rej_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code + */ + if ((count + 1) / 3 != 1) { + logError(1, + "%s %s:Number bytes of parameter wrong!%d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be always + * used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * + * second step: call fts_mode_handler to actually enable it + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_EDGE_REJECTION); + if (res < OK && temp != FEAT_DISABLE) + return -EINVAL; + + info->edge_rej_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, + "%s %s: Error during fts_mode_handler! ERROR %08X\n", + tag, __func__, res); + } + } + + return count; +} +#endif + +#ifdef CORNER_REJ +/** + * echo 01/00 > corner_rej to enable/disable corner rejection + * cat corner_rej to show the status of the + * corner_rej_enabled switch (example output in the terminal + * = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > corner_rej; cat corner_rej to enable/disable + * corner rejection and see the switch status in just one call + */ +static ssize_t fts_corner_rej_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: corner_rej_enabled = %d\n", + tag, __func__, info->corner_rej_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->corner_rej_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, "%s%s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_corner_rej_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code according + * to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, + "%s %s:Number bytes of parameter wrong!%d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + + /*sscanf(p, "%02X ", &temp);*/ + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be always + * used when a feature is enabled! + * + * first step : check if the wanted feature + * can be enabled + * + * second step: call fts_mode_handler to + * actually enable it + * + * NOTE: Disabling a feature is always + * allowed by default + */ + res = check_feature_feasibility(info, FEAT_CORNER_REJECTION); + if (res >= OK || temp == FEAT_DISABLE) { + info->corner_rej_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, + "%s %s: During fts_mode_handler!ERROR %08X\n", + tag, __func__, res); + } + } + + return count; +} +#endif + +#ifdef EDGE_PALM_REJ +/** + * echo 01/00 > edge_palm_rej + * to enable/disable edge palm rejection + * + * cat edge_palm_rej to show the status of the + * edge_palm_rej_enabled switch (example output + * in the terminal = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > edge_palm_rej; cat edge_palm_rej + * to enable/disable edge palm rejection and see + * the switch status in just one call + */ +static ssize_t fts_edge_palm_rej_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: edge_palm_rej_enabled = %d\n", + tag, __func__, info->edge_palm_rej_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", + info->edge_palm_rej_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", + all_strbuff); + kfree(all_strbuff); + } else { + logError(1, "%s%s:Unable to allocate all_strbuff! %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_edge_palm_rej_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code according + * to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, + "%s%s:Number bytes of parameter wrong! %d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + /*sscanf(p, "%02X ", &temp);*/ + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be + * always used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * + * second step: call fts_mode_handler to actually enable it + * + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_EDGE_PALM_REJECTION); + if (res >= OK || temp == FEAT_DISABLE) { + info->edge_palm_rej_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, "%s%s:Error in fts_mode_handler!%08X\n", + tag, __func__, res); + } + } + + return count; +} +#endif + +#ifdef CHARGER_MODE +/** + * echo 01/00 > charger_mode to enable/disable charger mode + * + * cat charger_mode to show the status of + * the charger_enabled switch (example output in the terminal + * = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > charger_mode; cat charger_mode + * to enable/disable charger mode and see the + * switch status in just one call + */ +static ssize_t fts_charger_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s:charger_enabled = %d\n", + tag, __func__, info->charger_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->charger_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, "%s %s:Unable to allocate all_strbuff! %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_charger_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code + * according to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, "%s %s:Size of parameter wrong! %d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + /*sscanf(p, "%02X ", &temp);*/ + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be always + * used when a feature is enabled! + * + * first step : check if the wanted feature + * can be enabled + * second step: call fts_mode_handler to + * actually enable it + * + * NOTE: Disabling a feature is always + * allowed by default + */ + res = check_feature_feasibility(info, FEAT_CHARGER); + if (res >= OK || temp == FEAT_DISABLE) { + info->charger_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, "%s %s: Error during fts_mode_handler! ", + tag, __func__); + logError(1, "ERROR %08X\n", res); + } + } + + return count; +} +#endif + +#ifdef GLOVE_MODE +/** + * echo 01/00 > glove_mode + * to enable/disable glove mode + * + * cat glove_mode to show the status of + * the glove_enabled switch (example output in the + * terminal = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > glove_mode; cat glove_mode + * to enable/disable glove mode and see the + * switch status in just one call + */ +static ssize_t fts_glove_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s:glove_enabled = %d\n", + tag, __func__, info->glove_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->glove_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, "%s %s:Unable to allocate all_strbuff! %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_glove_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code + * according to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, "%s %s:Size of parameter wrong! %d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be + * always used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * + * second step: call fts_mode_handler to actually enable it + * + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_GLOVE); + if (res >= OK || temp == FEAT_DISABLE) { + info->glove_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, "%s %s: Error during fts_mode_handler! ", + tag, __func__); + logError(1, "ERROR %08X\n", res); + } + } + + return count; +} +#endif + +#ifdef VR_MODE +/** + * echo 01/00 > vr_mode to enable/disable vr mode + * + * cat vr_mode to show the status of + * the vr_enabled switch (example output in the + * terminal = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > vr_mode; cat vr_mode to enable/disable + * vr mode and see the switch status in just one call + */ +static ssize_t fts_vr_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: vr_enabled = %d\n", + tag, __func__, info->vr_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->vr_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_vr_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code + * according to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, + "%s %s:Number bytes of parameter wrong!%d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + + /** + * this is a standard code that should be always + * used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * second step: call fts_mode_handler to actually enable it + * + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_VR); + if (res >= OK || temp == FEAT_DISABLE) { + info->vr_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, "%s %s: Error in fts_mode_handler!%08X\n", + tag, __func__, res); + } + } + + return count; +} +#endif + +#ifdef COVER_MODE +/** + * echo 01/00 > cover_mode to enable/disable cover mode + * cat cover_mode to show the status of the + * cover_enabled switch (example output in the + * terminal = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > cover_mode; cat cover_mode to + * enable/disable cover mode and see the switch + * status in just one call + * + * NOTE: the cover can be handled also using a notifier, + * in this case the body of these functions + * should be copied in the notifier callback + */ +static ssize_t fts_cover_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: cover_enabled = %d\n", + tag, __func__, info->cover_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->cover_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_cover_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code according + * to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, + "%s %s:Number bytes of parameter wrong!%d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + p += 3; + + /** + * this is a standard code that should be + * always used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * second step: call fts_mode_handler to actually enable it + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_COVER); + if (res >= OK || temp == FEAT_DISABLE) { + info->cover_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, "%s%s:Error in fts_mode_handler!%08X\n", + tag, __func__, res); + } + } + + + return count; +} +#endif + +#ifdef STYLUS_MODE +/** + * echo 01/00 > stylus_mode to enable/disable stylus mode + * cat stylus_mode to show the status of + * the stylus_enabled switch (example output in the + * terminal = "AA00000001BB" if the switch is enabled) + * + * echo 01/00 > stylus_mode; cat stylus_mode to + * enable/disable stylus mode and see the + * switch status in just one call + */ +static ssize_t fts_stylus_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: stylus_enabled = %d\n", + tag, __func__, info->stylus_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->stylus_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_stylus_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + /** + * in case of a different elaboration of the input, + * just modify this initial part of the code + * according to customer needs + */ + if ((count + 1) / 3 != 1) { + logError(1, "%s %s:Size of parameter wrong! %d != %d byte\n", + tag, __func__, (count + 1) / 3, 1); + return -EINVAL; + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + p += 3; + + /** + * this is a standard code that should be + * always used when a feature is enabled! + * + * first step : check if the wanted feature can be enabled + * second step: call fts_mode_handler to actually enable it + * NOTE: Disabling a feature is always allowed by default + */ + res = check_feature_feasibility(info, FEAT_STYLUS); + if (res >= OK || temp == FEAT_DISABLE) { + info->stylus_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) { + logError(1, + "%s %s:Error during fts_mode_handler! %08X\n", + tag, __func__, res); + } + } + + return count; +} +#endif + +#endif + +/************** GESTURES *************/ +#ifdef PHONE_GESTURE +#ifdef USE_GESTURE_MASK + +/** + * if this define is used, a gesture bit mask + * is used as method to select the gestures + * to enable/disable + */ + +/** + * echo EE X1 X2 ... X8 > gesture_mask set + * the gesture mask to disable/enable; + * EE = 00(disable) or 01(enable); + * X1 ... X8 = gesture mask (example 06 00 ... 00 + * this gesture mask represent the gestures with ID = 1 and 2) + * can be specified from 1 to 8 bytes, if less than 8 bytes + * are specified the remaining bytes are kept as previous settings + * + * cat gesture_mask enable/disable the given mask, + * if one or more gestures is enabled the driver will + * automatically enable the gesture mode. + * If all the gestures are disabled the driver + * automatically will disable the gesture mode. + * At the end an error code will be printed + * (example output in the terminal = "AA00000000BB" + * if there are no errors) + * + * echo EE X1 X2 ... X8 > gesture_mask; + * cat gesture_mask perform in one + * command both actions stated before + */ +static ssize_t fts_gesture_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0, res, temp; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + if (mask[0] == 0) { + res = ERROR_OP_NOT_ALLOW; + logError(1, "%s %s:Call before echo enable/disable xx xx >", + tag, __func__); + logError(1, "%s %s: gesture_mask with a correct number of ", + tag, __func__); + logError(1, "parameters! ERROR %08X\n", res); + return -EINVAL; + } + + if (mask[1] == FEAT_ENABLE || mask[1] == FEAT_DISABLE) + res = updateGestureMask(&mask[2], mask[0], mask[1]); + else + res = ERROR_OP_NOT_ALLOW; + + if (res < OK) { + logError(1, "%s fts_gesture_mask_store: ERROR %08X\n", + tag, res); + } + + res |= check_feature_feasibility(info, FEAT_GESTURE); + temp = isAnyGestureActive(); + if (res >= OK || temp == FEAT_DISABLE) + info->gesture_enabled = temp; + + logError(1, "%s fts_gesture_mask_store:Gesture Enabled = %d\n", + tag, info->gesture_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + mask[0] = 0; + return count; +} + + +static ssize_t fts_gesture_mask_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + int n; + unsigned int temp; + + if ((count + 1) / 3 > GESTURE_MASK_SIZE + 1) { + logError(1, "%s %s: Number of bytes of parameter wrong! ", tag, + __func__); + logError(1, "%d > (enable/disable + %d )\n", (count + 1) / 3, + GESTURE_MASK_SIZE); + mask[0] = 0; + return -EINVAL; + } + mask[0] = ((count + 1) / 3) - 1; + for (n = 1; n <= (count + 1) / 3; n++) { + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + p += 3; + mask[n] = (u8)temp; + logError(1, "%s mask[%d] = %02X\n", tag, n, mask[n]); + } + + return count; +} + +#else + +/** + * if this define is not used, + * to select the gestures to enable/disable + * are used the IDs of the gestures + * + * echo EE X1 X2 ... > gesture_mask set + * the gesture to disable/enable; EE = 00(disable) + * or 01(enable); X1 ... = gesture IDs + * (example 01 02 05... represent the gestures with + * ID = 1, 2 and 5) there is no limit of the parameters + * that can be passed, but of course the gesture IDs + * should be valid (all the valid IDs are listed + * in ftsGesture.h) + * + * cat gesture_mask enable/disable the + * given gestures, if one or more gestures is enabled + * the driver will automatically enable the gesture mode. + * If all the gestures are disabled the driver automatically + * will disable the gesture mode. At the end an error code + * will be printed (example output in the terminal = + * "AA00000000BB" if there are no errors) + * + * echo EE X1 X2 ... > gesture_mask; cat gesture_mask + * perform in one command both actions stated before + */ +static ssize_t fts_gesture_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + logError(0, "%s %s: gesture_enabled = %d\n", tag, __func__, + info->gesture_enabled); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", info->gesture_enabled); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_gesture_mask_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + int n; + unsigned int temp; + int res; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + if ((count + 1) / 3 < 2 || (count + 1) / 3 > GESTURE_MASK_SIZE + 1) { + logError(1, + "%s %s:Number bytes of parameter wrong! %d %d bytes)\n", + tag, __func__, (count + 1) / 3, GESTURE_MASK_SIZE); + mask[0] = 0; + return -EINVAL; + } + + memset(mask, 0, GESTURE_MASK_SIZE + 2); + mask[0] = ((count + 1) / 3) - 1; + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + mask[1] = (u8)temp; + for (n = 1; n < (count + 1) / 3; n++) { + /*sscanf(p, "%02X ", &temp);*/ + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + gestureIDtoGestureMask((u8)temp, &mask[2]); + } + + for (n = 0; n < GESTURE_MASK_SIZE + 2; n++) + logError(1, "%s mask[%d] = %02X\n", tag, n, mask[n]); + + if (mask[0] == 0) { + res = ERROR_OP_NOT_ALLOW; + logError(1, "%s %s: Call before echo enable/disable xx xx ....", + tag, __func__); + logError(1, " > gesture_mask with parameters! ERROR %08X\n", + res); + + } else { + + if (mask[1] == FEAT_ENABLE || mask[1] == FEAT_DISABLE) + res = updateGestureMask(&mask[2], mask[0], mask[1]); + else + res = ERROR_OP_NOT_ALLOW; + + if (res < OK) + logError(1, "%s %s: ERROR %08X\n", tag, __func__, res); + + } + + res = check_feature_feasibility(info, FEAT_GESTURE); + temp = isAnyGestureActive(); + if (res >= OK || temp == FEAT_DISABLE) + info->gesture_enabled = temp; + res = fts_mode_handler(info, 0); + + return count; +} +#endif + +#ifdef USE_CUSTOM_GESTURES +/** + * allow to use user defined gestures + * + * echo ID X1 Y1 X2 Y2 ... X30 Y30 > + * add_custom_gesture add a custom gesture; + * ID = 1 byte that represent the gesture ID of + * the custom gesture (can be chosen only between + * the custom IDs defined in ftsGesture.h); + * X1 Y1 ... = a series of 30 points (x,y) which + * represent the gesture template. + * The loaded gesture is enabled automatically + * + * cat add_custom_gesture/remove_custom_gesture + * Print the error code of the last operation + * performed with the custom gestures + * (example output in the terminal = "AA00000000BB" + * if there are no errors) + * + * echo ID X1 Y1 X2 Y2 ... X30 Y30 > + * add_custom_gesture; cat add_custom_gesture + * perform in one command both actions stated before + */ +static ssize_t fts_add_custom_gesture_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + + logError(0, "%s %s:Last Operation Result = %08X\n", + tag, __func__, custom_gesture_res); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", custom_gesture_res); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_add_custom_gesture_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + int n; + unsigned int temp; + u8 gestureID; + u8 gestMask[GESTURE_MASK_SIZE] = {0}; + u8 template[GESTURE_CUSTOM_POINTS]; + int res; + /*struct i2c_client *client = to_i2c_client(dev);*/ + /*struct fts_ts_info *info = i2c_get_clientdata(client);*/ + + if ((count + 1) / 3 != GESTURE_CUSTOM_POINTS + 1) { + logError(1, + "%s %s: Number bytes of parameter wrong! %d != %d\n", + tag, __func__, (count + 1) / 3, + GESTURE_CUSTOM_POINTS + 1); + res = ERROR_OP_NOT_ALLOW; + return -EINVAL; + } + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + p += 3; + gestureID = (u8)temp; + + for (n = 1; n < (count + 1) / 3; n++) { + /*sscanf(p, "%02X ", &temp);*/ + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + + p += 3; + template[n-1] = (u8)temp; + logError(1, "%s template[%d] = %02X\n", + tag, n-1, template[n-1]); + } + + res = fts_disableInterrupt(); + if (res >= OK) { + logError(1, "%s %s: Adding custom gesture ID = %02X\n", + tag, __func__, gestureID); + res = addCustomGesture(template, + GESTURE_CUSTOM_POINTS, gestureID); + if (res < OK) { + logError(1, + "%s %s:error during add custom gesture ", + tag, __func__); + logError(1, "ERROR %08X\n", res); + } else { + logError(1, + "%s %s:Enabling in the gesture mask...\n", + tag, __func__); + gestureIDtoGestureMask(gestureID, gestMask); + res = enableGesture(gestMask, GESTURE_MASK_SIZE); + if (res < OK) { + logError(1, "%s %s:error during enable gesture", + tag, __func__); + logError(1, " mask: ERROR %08X\n", res); + } else { + /*if (check_feature_feasibility(info,*/ + /*FEAT_GESTURE)==OK)*/ + /*info->gesture_enabled =*/ + /*isAnyGestureActive();*/ + /*uncomment if you want to activate*/ + /* automatically*/ + /*the gesture mode when a custom gesture*/ + /*is loaded*/ + logError(1, "%s %s:Custom Gesture enabled!\n", + tag, __func__, res); + } + } + } + res |= fts_enableInterrupt(); + + custom_gesture_res = res; + + return count; +} + + +/** + * echo ID > remove_custom_gesture + * remove a custom gesture; + * ID = 1 byte that represent the gesture ID + * of the custom gesture (can be chosen only + * between the custom IDs defined in ftsGesture.h); + * the same gesture is disabled automatically + */ +static ssize_t fts_remove_custom_gesture_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + u8 *all_strbuff = NULL; + int count = 0; + + logError(0, "%s %s:Last Operation Result = %08X\n", + tag, __func__, custom_gesture_res); + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", custom_gesture_res); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, __func__, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_remove_custom_gesture_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + u8 gestureID; + u8 gestMask[GESTURE_MASK_SIZE] = {0}; + /*struct i2c_client *client = to_i2c_client(dev);*/ + /*struct fts_ts_info *info = i2c_get_clientdata(client);*/ + + if ((count + 1) / 3 < 1) { + logError(1, + "%s %s:Number bytes of parameter wrong! %d != %d\n", + tag, __func__, (count + 1) / 3, 1); + res = ERROR_OP_NOT_ALLOW; + return -EINVAL; + } + + if (sscanf(p, "%02X ", &temp) != 1) + return -EINVAL; + p += 3; + gestureID = (u8)temp; + res = fts_disableInterrupt(); + if (res >= OK) { + logError(1, + "%s %s: Removing custom gesture ID = %02X\n", + tag, __func__, gestureID); + res = removeCustomGesture(gestureID); + if (res < OK) { + logError(1, + "%s %s:error in custom gesture:%08X\n", + tag, __func__, res); + } else { + logError(1, "%s %s: Enabling in the gesture mask...\n", + tag, __func__); + gestureIDtoGestureMask(gestureID, gestMask); + res = disableGesture(gestMask, GESTURE_MASK_SIZE); + if (res < OK) { + logError(1, + "%s %s:error in enable gesture mask:%08X\n", + tag, __func__, res); + } else { + /*if (check_feature_feasibility*/ + /*(info,FEAT_GESTURE)==OK)*/ + /*info->gesture_enabled = */ + /*isAnyGestureActive();*/ + /** + * uncomment if you want to disable + * automatically + * the gesture mode when a custom gesture is + * removed and no other gestures were enabled + */ + logError(1, "%s %s: Custom Gesture disabled!\n", + tag, __func__, res); + } + + } + } + + res |= fts_enableInterrupt(); + + custom_gesture_res = res; + return count; +} +#endif + + +/** + * cat gesture_coordinates to obtain the gesture coordinates + * the string returned in the shell follow this up as follow: + * AA = start byte + * X1X2X3X4 = 4 bytes in HEX format + * which represent an error code (00000000 no error) + */ + /**** if error code is all 0s ****/ +/** + * CC = 1 byte in HEX format number of coords + * (pair of x,y) returned + * + * X1X2 Y1Y2 ... = X1X2 2 bytes in HEX format for + * x[i] and Y1Y2 2 bytes in HEX format for y[i] (MSB first) + */ +/********************************/ +/* BB = end byte*/ +static ssize_t fts_gesture_coordinates_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int size = 6 * 2; + //u8 coords_num; + u8 *all_strbuff = NULL; + int count = 0, res, i = 0; + + logError(0, "%s %s: Getting gestures coordinates...\n", tag, __func__); + + if (gesture_coords_reported < OK) { + logError(1, "%s %s:invalid coordinates! ERROR %08X\n", + tag, __func__, gesture_coords_reported); + res = gesture_coords_reported; + } else { + /*coords are pairs of x,y (*2) where each coord*/ + /*is a short(2bytes=4char)(*4) + 1 byte(2char) num*/ + /*of coords (+2)*/ + size += gesture_coords_reported * 2 * 4 + 2; + /*coords_num = res;*/ + res = OK; + /*set error code to OK*/ + } + + all_strbuff = kmalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + if (res >= OK) { + snprintf(buff, sizeof(buff), "%02X", + gesture_coords_reported); + strlcat(all_strbuff, buff, size); + + for (i = 0; i < gesture_coords_reported; i++) { + snprintf(buff, sizeof(buff), "%04X", + gesture_coordinates_x[i]); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%04X", + gesture_coordinates_y[i]); + strlcat(all_strbuff, buff, size); + } + } + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else { + logError(1, + "%s %s:Unable to allocate all_strbuff! ERROR %08X\n", + tag, ERROR_ALLOC); + } + + return count; +} + +static ssize_t fts_gesture_coordinates_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return 0; +} +#endif + +/***************** PRODUCTION TEST ****************/ +static ssize_t fts_stm_cmd_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int n; + char *p = (char *) buf; + + memset(typeOfComand, 0, CMD_STR_LEN * sizeof(u32)); + + logError(1, "%s\n", tag); + for (n = 0; n < (count + 1) / 3; n++) { + + if (sscanf(p, "%02X ", &typeOfComand[n]) != 1) + return -EINVAL; + p += 3; + logError(1, "%s typeOfComand[%d] = %02X\n", + tag, n, typeOfComand[n]); + + } + + numberParameters = n; + logError(1, "%s Number of Parameters = %d\n", tag, numberParameters); + return count; +} + +static ssize_t fts_stm_cmd_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int res, j, doClean = 0, count; + + int size = 6 * 2; + u8 *all_strbuff = NULL; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + struct MutualSenseData compData = {0}; + struct SelfSenseData comData = {0}; + struct MutualSenseFrame frameMS = {0}; + struct SelfSenseFrame frameSS = {0}; + + /** + * struct used for defining which test + * perform during the production test + */ + struct TestToDo todoDefault; + + todoDefault.MutualRaw = 1; + todoDefault.MutualRawGap = 1; + todoDefault.MutualCx1 = 0; + todoDefault.MutualCx2 = 1; + todoDefault.MutualCx2Adj = 1; + todoDefault.MutualCxTotal = 0; + todoDefault.MutualCxTotalAdj = 0; + + todoDefault.MutualKeyRaw = 0; + todoDefault.MutualKeyCx1 = 0; + todoDefault.MutualKeyCx2 = 0; + todoDefault.MutualKeyCxTotal = 0; + + todoDefault.SelfForceRaw = 1; + todoDefault.SelfForceRawGap = 0; + todoDefault.SelfForceIx1 = 0; + todoDefault.SelfForceIx2 = 0; + todoDefault.SelfForceIx2Adj = 0; + todoDefault.SelfForceIxTotal = 1; + todoDefault.SelfForceIxTotalAdj = 0; + todoDefault.SelfForceCx1 = 0; + todoDefault.SelfForceCx2 = 0; + todoDefault.SelfForceCx2Adj = 0; + todoDefault.SelfForceCxTotal = 0; + todoDefault.SelfForceCxTotalAdj = 0; + + todoDefault.SelfSenseRaw = 1; + todoDefault.SelfSenseRawGap = 0; + todoDefault.SelfSenseIx1 = 0; + todoDefault.SelfSenseIx2 = 0; + todoDefault.SelfSenseIx2Adj = 0; + todoDefault.SelfSenseIxTotal = 1; + todoDefault.SelfSenseIxTotalAdj = 0; + todoDefault.SelfSenseCx1 = 0; + todoDefault.SelfSenseCx2 = 0; + todoDefault.SelfSenseCx2Adj = 0; + todoDefault.SelfSenseCxTotal = 0; + todoDefault.SelfSenseCxTotalAdj = 0; + + + if (numberParameters >= 1) { + res = fts_disableInterrupt(); + if (res < 0) { + logError(0, "%s fts_disableInterrupt: ERROR %08X\n", + tag, res); + res = (res | ERROR_DISABLE_INTER); + goto END; + } + +#if defined(CONFIG_FB_MSM) + res = fb_unregister_client(&info->notifier); +#else + if (active_panel) + res = drm_panel_notifier_unregister(active_panel, + &info->notifier); +#endif + if (res < 0) { + logError(1, "%s ERROR: unregister notifier failed!\n", + tag); + goto END; + } + + switch (typeOfComand[0]) { + /*ITO TEST*/ + case 0x01: + res = production_test_ito(); + break; + /*PRODUCTION TEST*/ + case 0x00: + if (ftsInfo.u32_mpPassFlag != INIT_MP) { + logError(0, "%s MP Flag not set!\n", tag, res); + res = production_test_main(LIMITS_FILE, 1, 1, + &todoDefault, INIT_MP); + } else { + logError(0, "%s MP Flag set!\n", tag, res); + res = production_test_main(LIMITS_FILE, 1, 0, + &todoDefault, INIT_MP); + } + break; + /*read mutual raw*/ + case 0x13: + logError(0, "%s Get 1 MS Frame\n", tag); + //res = getMSFrame(ADDR_RAW_TOUCH, &frame, 0); + res = getMSFrame2(MS_TOUCH_ACTIVE, &frameMS); + if (res < 0) { + logError(0, + "%s Error in taking MS frame.%02X\n", + tag, res); + + } else { + logError(0, "%s The frame size is %d words\n", + tag, res); + size = (res * sizeof(short) + 8) * 2; + /* set res to OK because if getMSFrame is*/ + /* successful res = number of words read*/ + res = OK; + print_frame_short("MS frame =", + array1dTo2d_short(frameMS.node_data, + frameMS.node_data_size, + frameMS.header.sense_node), + frameMS.header.force_node, + frameMS.header.sense_node); + } + break; + /*read self raw*/ + case 0x15: + logError(0, "%s Get 1 SS Frame\n", tag); + res = getSSFrame2(SS_TOUCH, &frameSS); + + if (res < OK) { + logError(0, + "%s Error while taking the SS frame%02X\n", + tag, res); + + } else { + logError(0, "%s The frame size is %d words\n", + tag, res); + size = (res * sizeof(short) + 8) * 2 + 1; + /** + * set res to OK because if getMSFrame is + * successful res = number of words read + */ + res = OK; + print_frame_short("SS force frame =", + array1dTo2d_short(frameSS.force_data, + frameSS.header.force_node, 1), + frameSS.header.force_node, 1); + print_frame_short("SS sense frame =", + array1dTo2d_short(frameSS.sense_data, + frameSS.header.sense_node, + frameSS.header.sense_node), + 1, + frameSS.header.sense_node); + } + break; + + /*read mutual comp data*/ + case 0x14: + logError(0, "%s Get MS Compensation Data\n", tag); + res = readMutualSenseCompensationData(MS_TOUCH_ACTIVE, + &compData); + + if (res < 0) { + logError(0, + "%s Error MS compensation data%02X\n", + tag, res); + } else { + logError(0, + "%s MS Data Reading Finished!\n", + tag); + size = ((compData.node_data_size + 9) * + sizeof(u8)) * 2; + print_frame_u8("MS Data (Cx2) =", + array1dTo2d_u8(compData.node_data, + compData.node_data_size, + compData.header.sense_node), + compData.header.force_node, + compData.header.sense_node); + } + break; + + /*read self comp data*/ + case 0x16: + logError(0, "%s Get SS Compensation Data...\n", tag); + res = readSelfSenseCompensationData(SS_TOUCH, &comData); + if (res < 0) { + logError(0, "%s Error reading SS data%02X\n", + tag, res); + } else { + logError(0, "%s SS Data Reading Finished!\n", + tag); + size = ((comData.header.force_node + + comData.header.sense_node) * 2 + 12); + size *= sizeof(u8) * 2; + print_frame_u8("SS Data Ix2_fm = ", + array1dTo2d_u8(comData.ix2_fm, + comData.header.force_node, 1), + comData.header.force_node, + 1); + print_frame_u8("SS Data Cx2_fm = ", + array1dTo2d_u8(comData.cx2_fm, + comData.header.force_node, 1), + comData.header.force_node, + 1); + print_frame_u8("SS Data Ix2_sn = ", + array1dTo2d_u8(comData.ix2_sn, + comData.header.sense_node, + comData.header.sense_node), + 1, + comData.header.sense_node); + print_frame_u8("SS Data Cx2_sn = ", + array1dTo2d_u8(comData.cx2_sn, + comData.header.sense_node, + comData.header.sense_node), + 1, + comData.header.sense_node); + } + break; + + /* MS Raw DATA TEST */ + case 0x03: + res = fts_system_reset(); + if (res >= OK) + res = production_test_ms_raw(LIMITS_FILE, + 1, &todoDefault); + break; + /* MS CX DATA TEST */ + case 0x04: + res = fts_system_reset(); + if (res >= OK) + res = production_test_ms_cx(LIMITS_FILE, + 1, &todoDefault); + break; + /* SS RAW DATA TEST */ + case 0x05: + res = fts_system_reset(); + if (res >= OK) + res = production_test_ss_raw(LIMITS_FILE, + 1, &todoDefault); + break; + /* SS IX CX DATA TEST */ + case 0x06: + res = fts_system_reset(); + if (res >= OK) + res = production_test_ss_ix_cx(LIMITS_FILE, + 1, &todoDefault); + break; + + case 0xF0: + /* TOUCH ENABLE/DISABLE */ + case 0xF1: + doClean = (int) (typeOfComand[0] & 0x01); + res = cleanUp(doClean); + break; + + default: + logError(1, + "%s COMMAND NOT VALID!! Insert a proper value\n", + tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + doClean = fts_enableInterrupt(); + if (doClean < 0) { + logError(0, "%s fts_enableInterrupt: ERROR %08X\n", + tag, (doClean|ERROR_ENABLE_INTER)); + } + } else { + logError(1, "%s NO COMMAND SPECIFIED!!!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + +#if defined(CONFIG_FB_MSM) + if (fb_register_client(&info->notifier) < 0) + logError(1, "%s ERROR: register notifier failed!\n", tag); +#else + if (active_panel && + drm_panel_notifier_register(active_panel, &info->notifier) < 0) + logError(1, "%s ERROR: register notifier failed!\n", tag); +#endif + +END: + /*here start the reporting phase,*/ + /* assembling the data to send in the file node */ + all_strbuff = kmalloc(size, GFP_KERNEL); + if (!all_strbuff) + return 0; + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + if (res >= OK) { + /*all the other cases are already fine printing only the res.*/ + switch (typeOfComand[0]) { + case 0x13: + snprintf(buff, sizeof(buff), "%02X", + (u8) frameMS.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + (u8) frameMS.header.sense_node); + strlcat(all_strbuff, buff, size); + + for (j = 0; j < frameMS.node_data_size; j++) { + snprintf(buff, sizeof(buff), "%04X", + frameMS.node_data[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(frameMS.node_data); + break; + + case 0x15: + snprintf(buff, sizeof(buff), "%02X", + (u8) frameSS.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + (u8) frameSS.header.sense_node); + strlcat(all_strbuff, buff, size); + + /* Copying self raw data Force */ + for (j = 0; j < frameSS.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%04X", + frameSS.force_data[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying self raw data Sense */ + for (j = 0; j < frameSS.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%04X", + frameSS.sense_data[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(frameSS.force_data); + kfree(frameSS.sense_data); + break; + + case 0x14: + snprintf(buff, sizeof(buff), "%02X", + (u8) compData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + (u8) compData.header.sense_node); + strlcat(all_strbuff, buff, size); + + /* Cpying CX1 value */ + snprintf(buff, sizeof(buff), "%02X", compData.cx1); + strlcat(all_strbuff, buff, size); + + /* Copying CX2 values */ + for (j = 0; j < compData.node_data_size; j++) { + snprintf(buff, sizeof(buff), "%02X", + *(compData.node_data + j)); + strlcat(all_strbuff, buff, size); + } + + kfree(compData.node_data); + break; + + case 0x16: + snprintf(buff, sizeof(buff), "%02X", + comData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + comData.header.sense_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.f_ix1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.s_ix1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.f_cx1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.s_cx1); + strlcat(all_strbuff, buff, size); + + /* Copying IX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%02X", + comData.ix2_fm[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying IX2 Sense */ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%02X", + comData.ix2_sn[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying CX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%02X", + comData.cx2_fm[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying CX2 Sense */ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%02X", + comData.cx2_sn[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(comData.ix2_fm); + kfree(comData.ix2_sn); + kfree(comData.cx2_fm); + kfree(comData.cx2_sn); + break; + + default: + break; + } + } + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + /** + * need to reset the number of parameters + * in order to wait the next command, + * comment if you want to repeat + * the last command sent just doing a cat + */ + numberParameters = 0; + /* logError(0,"%s numberParameters = %d\n",tag, numberParameters);*/ + kfree(all_strbuff); + + return count; +} + +static DEVICE_ATTR_RW(fts_fwupdate); +static DEVICE_ATTR_RO(fts_appid); +static DEVICE_ATTR_RO(fts_mode_active); +static DEVICE_ATTR_RO(fts_lockdown_info); +static DEVICE_ATTR_RW(fts_strength_frame); +static DEVICE_ATTR_RO(fts_fw_test); +static DEVICE_ATTR_RW(fts_stm_cmd); +#ifdef USE_ONE_FILE_NODE +static DEVICE_ATTR_RW(fts_feature_enable); +#else + +#ifdef EDGE_REJ +static DEVICE_ATTR_RW(fts_edge_rej); +#endif + +#ifdef CORNER_REJ +static DEVICE_ATTR_RW(fts_corner_rej); +#endif + +#ifdef EDGE_PALM_REJ +static DEVICE_ATTR_RW(fts_edge_palm_rej); +#endif + +#ifdef CHARGER_MODE +static DEVICE_ATTR_RW(fts_charger_mode); +#endif + +#ifdef GLOVE_MODE +static DEVICE_ATTR_RW(fts_glove_mode); +#endif + +#ifdef VR_MODE +static DEVICE_ATTR_RW(fts_vr_mode); +#endif + +#ifdef COVER_MODE +static DEVICE_ATTR_RW(fts_cover_mode); +#endif + +#ifdef STYLUS_MODE +static DEVICE_ATTR_RW(fts_stylus_mode); +#endif + +#endif + +#ifdef PHONE_GESTURE +static DEVICE_ATTR_RW(fts_gesture_mask); +static DEVICE_ATTR_RW(fts_gesture_coordinates); +#ifdef USE_CUSTOM_GESTURES +static DEVICE_ATTR_RW(fts_add_custom_gesture); +static DEVICE_ATTR_RW(fts_remove_custom_gesture); +#endif +#endif + +#ifdef CONFIG_ST_TRUSTED_TOUCH +static DEVICE_ATTR(trusted_touch_enable, + 0664, + fts_trusted_touch_enable_show, + fts_trusted_touch_enable_store); +#endif + +/* /sys/devices/soc.0/f9928000.i2c/i2c-6/6-0049 */ +static struct attribute *fts_attr_group[] = { + &dev_attr_fts_fwupdate.attr, + &dev_attr_fts_appid.attr, + &dev_attr_fts_mode_active.attr, + &dev_attr_fts_lockdown_info.attr, + &dev_attr_fts_strength_frame.attr, + &dev_attr_fts_fw_test.attr, + &dev_attr_fts_stm_cmd.attr, +#ifdef USE_ONE_FILE_NODE + &dev_attr_fts_feature_enable.attr, +#else + +#ifdef EDGE_REJ + &dev_attr_fts_edge_rej.attr, +#endif +#ifdef CORNER_REJ + &dev_attr_fts_corner_rej.attr, +#endif +#ifdef EDGE_PALM_REJ + &dev_attr_fts_edge_palm_rej.attr, +#endif +#ifdef CHARGER_MODE + &dev_attr_fts_charger_mode.attr, +#endif +#ifdef GLOVE_MODE + &dev_attr_fts_glove_mode.attr, +#endif +#ifdef VR_MODE + &dev_attr_fts_vr_mode.attr, +#endif +#ifdef COVER_MODE + &dev_attr_fts_cover_mode.attr, +#endif +#ifdef STYLUS_MODE + &dev_attr_fts_stylus_mode.attr, +#endif + +#endif + +#ifdef PHONE_GESTURE + &dev_attr_fts_gesture_mask.attr, + &dev_attr_fts_gesture_coordinates.attr, +#ifdef USE_CUSTOM_GESTURES + &dev_attr_fts_add_custom_gesture.attr, + &dev_attr_fts_remove_custom_gesture.attr, +#endif + +#endif +#ifdef CONFIG_ST_TRUSTED_TOUCH + &dev_attr_trusted_touch_enable.attr, +#endif + NULL, +}; + +static int fts_command(struct fts_ts_info *info, unsigned char cmd) +{ + unsigned char regAdd; + int ret; + + regAdd = cmd; + + ret = fts_writeCmd(®Add, sizeof(regAdd)); /* 0 = ok */ + + logError(0, "%s Issued command 0x%02x, return value %08X\n", cmd, ret); + + return ret; +} + +void fts_input_report_key(struct fts_ts_info *info, int key_code) +{ + mutex_lock(&info->input_report_mutex); + input_report_key(info->input_dev, key_code, 1); + input_sync(info->input_dev); + input_report_key(info->input_dev, key_code, 0); + input_sync(info->input_dev); + mutex_unlock(&info->input_report_mutex); +} + +/* + * New Interrupt handle implementation + */ +static inline unsigned char *fts_next_event(unsigned char *evt) +{ + /* Nothing to do with this event, moving to the next one */ + evt += FIFO_EVENT_SIZE; + + /* the previous one was the last event ? */ + return (evt[-1] & 0x1F) ? evt : NULL; +} + +/* EventId : 0x00 */ +static void fts_nop_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + /** + * logError(1, + * "%s %s Doing nothing for event = + * %02X %02X %02X %02X %02X %02X %02X %02X\n", + * tag, __func__, event[0], event[1], event[2], + * event[3], event[4], event[5], event[6], event[7]); + */ + /* return fts_next_event(event); */ +} + +/* EventId : 0x03 */ +static void fts_enter_pointer_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + unsigned char touchId, touchcount; + int x, y; + int minor; + int major, distance = 0; + u8 touchsize; + + if (!info->resume_bit && !info->aoi_notify_enabled) + return; + + touchId = event[1] & 0x0F; + touchcount = (event[1] & 0xF0) >> 4; + touchsize = (event[5] & 0xC0) >> 6; + major = (event[5] & 0x1F); // bit0-bit4: major + minor = event[6]; // event6:minor + + __set_bit(touchId, &info->touch_id); + + x = (event[2] << 4) | (event[4] & 0xF0) >> 4; + y = (event[3] << 4) | (event[4] & 0x0F); + + if (info->bdata->x_flip) + x = X_AXIS_MAX - x; + if (info->bdata->y_flip) + y = Y_AXIS_MAX - y; + + if (x == X_AXIS_MAX) + x--; + + if (y == Y_AXIS_MAX) + y--; + + if (info->sensor_sleep && info->aoi_notify_enabled) + if ((x < info->aoi_left || x > info->aoi_right) + || (y < info->aoi_top || y > info->aoi_bottom)) { + x = -x; + y = -y; + } + + input_mt_slot(info->input_dev, touchId); + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 1); + + if (touchcount == 1) { + input_report_key(info->input_dev, BTN_TOUCH, 1); + input_report_key(info->input_dev, BTN_TOOL_FINGER, 1); + } + input_report_abs(info->input_dev, ABS_MT_POSITION_X, x); + input_report_abs(info->input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(info->input_dev, ABS_MT_TOUCH_MAJOR, major); + input_report_abs(info->input_dev, ABS_MT_TOUCH_MINOR, minor); + input_report_abs(info->input_dev, ABS_MT_DISTANCE, distance); + + return; +} + +/* EventId : 0x04 */ +static void fts_leave_pointer_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + unsigned char touchId, touchcount; + u8 touchsize; + + touchId = event[1] & 0x0F; + touchcount = (event[1] & 0xF0) >> 4; + touchsize = (event[5] & 0xC0) >> 6; + + input_mt_slot(info->input_dev, touchId); + + __clear_bit(touchId, &info->touch_id); + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 0); + + if (touchcount == 0) { + input_report_key(info->input_dev, BTN_TOUCH, 0); + input_report_key(info->input_dev, BTN_TOOL_FINGER, 0); + } + + input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, -1); + +} + +/* EventId : 0x05 */ +#define fts_motion_pointer_event_handler fts_enter_pointer_event_handler + +#ifdef PHONE_KEY +/* EventId : 0x0E */ +static void fts_key_status_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + int value; + + logError(0, + "%s %sReceived event %02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[0], event[1], event[2], event[3], + event[4], event[5], event[6], event[7]); + /* + * TODO: the customer should handle the events coming + * from the keys according his needs (this is an example + * that report only the single pressure of one key at time) + */ + /* event[2] contain the bitmask of the keys that are actually pressed */ + if (event[2] != 0) { + switch (event[2]) { + case KEY1: + value = KEY_HOMEPAGE; + logError(0, "%s %s: Button HOME!\n", tag, __func__); + break; + + case KEY2: + value = KEY_BACK; + logError(0, "%s %s: Button Back !\n", tag, __func__); + break; + + case KEY3: + value = KEY_MENU; + logError(0, "%s %s: Button Menu !\n", tag, __func__); + break; + + default: + logError(0, + "%s %s:No valid Button ID or more than one key pressed!\n", + tag, __func__); + return; + } + + fts_input_report_key(info, value); + } else { + logError(0, "%s %s: All buttons released!\n", tag, __func__); + } +} +#endif + +/* EventId : 0x0F */ +static void fts_error_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + int error = 0; + + logError(0, + "%s %sReceived event:%02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[0], event[1], event[2], event[3], + event[4], event[5], event[6], event[7]); + + switch (event[1]) { + case EVENT_TYPE_ESD_ERROR: /* esd */ + /* before reset clear all slot */ + release_all_touches(info); + + fts_chip_powercycle(info); + + error = fts_system_reset(); + error |= fts_mode_handler(info, 0); + error |= fts_enableInterrupt(); + if (error < OK) { + logError(1, + "%s %s Cannot restore the device ERROR %08X\n", + tag, __func__, error); + } + break; + case EVENT_TYPE_WATCHDOG_ERROR: /* watch dog timer */ + /* if (event[2] == 0) { */ + dumpErrorInfo(); + /* before reset clear all slot */ + release_all_touches(info); + error = fts_system_reset(); + error |= fts_mode_handler(info, 0); + error |= fts_enableInterrupt(); + if (error < OK) { + logError(1, + "%s %s Cannot reset the device ERROR %08X\n", + tag, __func__, error); + } + /* } */ + break; +} + /* return fts_next_event(event); */ +} + +/* EventId : 0x10 */ +static void fts_controller_ready_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + int error; + + logError(0, "%s %s Received event 0x%02x\n", tag, __func__, event[0]); + release_all_touches(info); + setSystemResettedUp(1); + setSystemResettedDown(1); + error = fts_mode_handler(info, 0); + if (error < OK) { + logError(1, + "%s %s Cannot restore the device status ERROR %08X\n", + tag, __func__, error); + } + /* return fts_next_event(event); */ +} + +/* EventId : 0x16 */ +static void fts_status_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + /* logError(1, "%s Received event 0x%02x\n", tag, event[0]); */ + + switch (event[1]) { + case EVENT_TYPE_MS_TUNING_CMPL: + case EVENT_TYPE_SS_TUNING_CMPL: + case FTS_FORCE_CAL_SELF_MUTUAL: + case FTS_FLASH_WRITE_CONFIG: + case FTS_FLASH_WRITE_COMP_MEMORY: + case FTS_FORCE_CAL_SELF: + case FTS_WATER_MODE_ON: + case FTS_WATER_MODE_OFF: + default: + logError(0, "%s %s Received unhandled status event = ", + tag, __func__); + logError(0, "%02X %02X %02X %02X %02X %02X %02X %02X\n", + event[0], event[1], event[2], event[3], event[4], + event[5], event[6], event[7]); + break; + } + + /* return fts_next_event(event); */ +} + +#ifdef PHONE_GESTURE +/** + * TODO: Customer should implement their own action + * in respons of a gesture event. + * This is an example that simply print the gesture received + */ +static void fts_gesture_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + unsigned char touchId; + int value; + int needCoords = 0; + + logError(0, + "%s gesture event: %02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, event[0], event[1], event[2], event[3], + event[4], event[5], event[6], event[7]); + + if (event[1] == 0x03) { + logError(1, "%s %s: Gesture ID %02X enable_status = %02X\n", + tag, __func__, event[2], event[3]); + } + + if (event[1] == EVENT_TYPE_ENB && event[2] == 0x00) { + switch (event[3]) { + case GESTURE_ENABLE: + logError(1, "%s %s: Gesture Enabled! res = %02X\n", + tag, __func__, event[4]); + break; + + case GESTURE_DISABLE: + logError(1, "%s %s: Gesture Disabled! res = %02X\n", + tag, __func__, event[4]); + break; + + default: + logError(1, "%s %s: Event not Valid!\n", tag, __func__); + } + } + + if (event[0] == EVENTID_GESTURE && (event[1] == EVENT_TYPE_GESTURE_DTC1 + || event[1] == EVENT_TYPE_GESTURE_DTC2)) { + /* always use touchId zero */ + touchId = 0; + __set_bit(touchId, &info->touch_id); + + /* by default read the coordinates*/ + /* for all gestures excluding double tap */ + needCoords = 1; + + switch (event[2]) { + case GES_ID_DBLTAP: + value = KEY_WAKEUP; + logError(0, "%s %s: double tap!\n", tag, __func__); + needCoords = 0; + break; + + case GES_ID_AT: + value = KEY_WWW; + logError(0, "%s %s: @!\n", tag, __func__); + break; + + case GES_ID_C: + value = KEY_C; + logError(0, "%s %s: C !\n", tag, __func__); + break; + + case GES_ID_E: + value = KEY_E; + logError(0, "%s %s: e !\n", tag, __func__); + break; + + case GES_ID_F: + value = KEY_F; + logError(0, "%s %s: F !\n", tag, __func__); + break; + + case GES_ID_L: + value = KEY_L; + logError(0, "%s %s: L !\n", tag, __func__); + break; + + case GES_ID_M: + value = KEY_M; + logError(0, "%s %s: M !\n", tag, __func__); + break; + + case GES_ID_O: + value = KEY_O; + logError(0, "%s %s: O !\n", tag, __func__); + break; + + case GES_ID_S: + value = KEY_S; + logError(0, "%s %s: S !\n", tag, __func__); + break; + + case GES_ID_V: + value = KEY_V; + logError(0, "%s %s: V !\n", tag, __func__); + break; + + case GES_ID_W: + value = KEY_W; + logError(0, "%s %s: W !\n", tag, __func__); + break; + + case GES_ID_Z: + value = KEY_Z; + logError(0, "%s %s: Z !\n", tag, __func__); + break; + + case GES_ID_HFLIP_L2R: + value = KEY_RIGHT; + logError(0, "%s %s: -> !\n", tag, __func__); + break; + + case GES_ID_HFLIP_R2L: + value = KEY_LEFT; + logError(0, "%s %s: <- !\n", tag, __func__); + break; + + case GES_ID_VFLIP_D2T: + value = KEY_UP; + logError(0, "%s %s: UP !\n", tag, __func__); + break; + + case GES_ID_VFLIP_T2D: + value = KEY_DOWN; + logError(0, "%s %s: DOWN !\n", tag, __func__); + break; + + case GES_ID_CUST1: + value = KEY_F1; + logError(0, "%s %s: F1 !\n", tag, __func__); + break; + + case GES_ID_CUST2: + value = KEY_F1; + logError(0, "%s %s: F2 !\n", tag, __func__); + break; + + case GES_ID_CUST3: + value = KEY_F3; + logError(0, "%s %s: F3 !\n", tag, __func__); + break; + + case GES_ID_CUST4: + value = KEY_F1; + logError(0, "%s %s: F4 !\n", tag, __func__); + break; + + case GES_ID_CUST5: + value = KEY_F1; + logError(0, "%s %s: F5 !\n", tag, __func__); + break; + + case GES_ID_LEFTBRACE: + value = KEY_LEFTBRACE; + logError(0, "%s %s: < !\n", tag, __func__); + break; + + case GES_ID_RIGHTBRACE: + value = KEY_RIGHTBRACE; + logError(0, "%s %s: > !\n", tag, __func__); + break; + default: + logError(0, "%s %s: No valid GestureID!\n", + tag, __func__); + goto gesture_done; + } + + /* no coordinates for gestures reported by FW */ + if (event[1] == EVENT_TYPE_GESTURE_DTC1) + needCoords = 0; + + if (needCoords == 1) + readGestureCoords(event); + + fts_input_report_key(info, value); + +gesture_done: + /* Done with gesture event, clear bit. */ + __clear_bit(touchId, &info->touch_id); + } + /* return fts_next_event(event); */ +} +#endif + +/* EventId : 0x05 */ +#define fts_motion_pointer_event_handler fts_enter_pointer_event_handler + +/* + * This handler is called each time there is at least + * one new event in the FIFO + */ +static void fts_event_handler(struct work_struct *work) +{ + struct fts_ts_info *info; + int error = 0, count = 0; + unsigned char regAdd; + unsigned char data[FIFO_EVENT_SIZE] = {0}; + unsigned char eventId; + + struct event_dispatch_handler_t event_handler; + + info = container_of(work, struct fts_ts_info, work); + /* + * read all the FIFO and parsing events + */ + + __pm_wakeup_event(info->wakeup_source, HZ); + regAdd = FIFO_CMD_READONE; + + for (count = 0; count < FIFO_DEPTH; count++) { + error = fts_readCmd(®Add, sizeof(regAdd), data, + FIFO_EVENT_SIZE); + if (error == OK && data[0] != EVENTID_NO_EVENT) + eventId = data[0]; + else + break; + + if (eventId < EVENTID_LAST) { + event_handler = info->event_dispatch_table[eventId]; + event_handler.handler(info, (data)); + } + } + input_sync(info->input_dev); + + fts_interrupt_enable(info); +} + +static void fts_fw_update_auto(struct work_struct *work) +{ + u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, SYSTEM_RESET_VALUE }; + int event_to_search[2] = {(int)EVENTID_ERROR_EVENT, + (int)EVENT_TYPE_CHECKSUM_ERROR}; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int flag_init = 0; + int retval = 0; + int retval1 = 0; + int ret; + struct fts_ts_info *info; + struct delayed_work *fwu_work = container_of(work, + struct delayed_work, work); + int crc_status = 0; + int error = 0; + struct Firmware fwD; + int orig_size; + u8 *orig_data; + + info = container_of(fwu_work, struct fts_ts_info, fwu_work); + logError(0, "%s Fw Auto Update is starting...\n", tag); + + ret = getFWdata(PATH_FILE_FW, &orig_data, &orig_size, 0); + if (ret < OK) { + logError(0, "%s %s: impossible retrieve FW... ERROR %08X\n", + tag, __func__, ERROR_MEMH_READ); + ret = (ret | ERROR_MEMH_READ); + goto NO_FIRMWARE_UPDATE; + } + + ret = parseBinFile(orig_data, orig_size, &fwD, 1); + if (ret < OK) { + logError(1, "%s %s: impossible parse ERROR %08X\n", + tag, __func__, ERROR_MEMH_READ); + ret = (ret | ERROR_MEMH_READ); + kfree(fwD.data); + goto NO_FIRMWARE_UPDATE; + } + + fts_chip_powercycle(info); + retval = flash_burn(&fwD, crc_status, 1); + + if ((retval & 0xFF000000) == ERROR_FLASH_PROCEDURE) { + logError(1, "%s %s:firmware update retry! ERROR %08X\n", + tag, __func__, retval); + fts_chip_powercycle(info); + + retval1 = flash_burn(&fwD, crc_status, 1); + + if ((retval1 & 0xFF000000) == ERROR_FLASH_PROCEDURE) { + logError(1, "%s %s: update failed again! ERROR %08X\n", + tag, __func__, retval1); + logError(1, "%s Fw Auto Update Failed!\n", tag); + } + } + + kfree(fwD.data); + u16ToU8_be(SYSTEM_RESET_ADDRESS, &cmd[1]); + ret = fts_writeCmd(cmd, 4); + if (ret < OK) { + logError(1, "%s %s Can't send reset command! ERROR %08X\n", + tag, __func__, ret); + } else { + setSystemResettedDown(1); + setSystemResettedUp(1); + ret = pollForEvent(event_to_search, 2, readData, + GENERAL_TIMEOUT); + if (ret < OK) { + logError(0, "%s %s: No CX CRC Found!\n", tag, __func__); + } else { + if (readData[2] == CRC_CX_MEMORY) { + logError(1, "%s %s: CRC Error! ERROR:%02X\n\n", + tag, __func__, readData[2]); + + flag_init = 1; + } + } + } + + if (ftsInfo.u8_msScrConfigTuneVer != ftsInfo.u8_msScrCxmemTuneVer || + ftsInfo.u8_ssTchConfigTuneVer != ftsInfo.u8_ssTchCxmemTuneVer) + ret = ERROR_GET_INIT_STATUS; + else if (((ftsInfo.u32_mpPassFlag != INIT_MP) + && (ftsInfo.u32_mpPassFlag != INIT_FIELD)) || flag_init == 1) + ret = ERROR_GET_INIT_STATUS; + else + ret = OK; + + if (ret == ERROR_GET_INIT_STATUS) { + error = fts_chip_initialization(info); + if (error < OK) + logError(1, "%s %s Can't initialize chip! ERROR %08X", + tag, __func__, error); + } + +NO_FIRMWARE_UPDATE: + error = fts_init_afterProbe(info); + if (error < OK) + logError(1, "%s Can't initialize hardware device ERROR %08X\n", + tag, error); + + logError(0, "%s Fw Auto Update Finished!\n", tag); +} + +static int fts_chip_initialization(struct fts_ts_info *info) +{ + int ret2 = 0; + int retry; + int initretrycnt = 0; + struct TestToDo todoDefault; + + todoDefault.MutualRaw = 1; + todoDefault.MutualRawGap = 1; + todoDefault.MutualCx1 = 0; + todoDefault.MutualCx2 = 0; + todoDefault.MutualCx2Adj = 0; + todoDefault.MutualCxTotal = 0; + todoDefault.MutualCxTotalAdj = 0; + + todoDefault.MutualKeyRaw = 0; + todoDefault.MutualKeyCx1 = 0; + todoDefault.MutualKeyCx2 = 0; + todoDefault.MutualKeyCxTotal = 0; + + todoDefault.SelfForceRaw = 0; + todoDefault.SelfForceRawGap = 0; + todoDefault.SelfForceIx1 = 0; + todoDefault.SelfForceIx2 = 0; + todoDefault.SelfForceIx2Adj = 0; + todoDefault.SelfForceIxTotal = 0; + todoDefault.SelfForceIxTotalAdj = 0; + todoDefault.SelfForceCx1 = 0; + todoDefault.SelfForceCx2 = 0; + todoDefault.SelfForceCx2Adj = 0; + todoDefault.SelfForceCxTotal = 0; + todoDefault.SelfForceCxTotalAdj = 0; + + todoDefault.SelfSenseRaw = 1; + todoDefault.SelfSenseRawGap = 0; + todoDefault.SelfSenseIx1 = 0; + todoDefault.SelfSenseIx2 = 0; + todoDefault.SelfSenseIx2Adj = 0; + todoDefault.SelfSenseIxTotal = 0; + todoDefault.SelfSenseIxTotalAdj = 0; + todoDefault.SelfSenseCx1 = 0; + todoDefault.SelfSenseCx2 = 0; + todoDefault.SelfSenseCx2Adj = 0; + todoDefault.SelfSenseCxTotal = 0; + todoDefault.SelfSenseCxTotalAdj = 0; + + for (retry = 0; retry <= INIT_FLAG_CNT; retry++) { + ret2 = production_test_main(LIMITS_FILE, 1, 1, &todoDefault, + INIT_FIELD); + if (ret2 == OK) + break; + initretrycnt++; + logError(1, "%s %s: cycle count = %04d - ERROR %08X\n", + tag, __func__, initretrycnt, ret2); + fts_chip_powercycle(info); + } + + if (ret2 < OK) + logError(1, "%s failed to initializate 3 times\n", tag); + + return ret2; +} + +#ifdef FTS_USE_POLLING_MODE + +static enum hrtimer_restart fts_timer_func(struct hrtimer *timer) +{ + struct fts_ts_info *info = + container_of(timer, struct fts_ts_info, timer); + + queue_work(info->event_wq, &info->work); + return HRTIMER_NORESTART; +} +#else + +static irqreturn_t fts_interrupt_handler(int irq, void *handle) +{ + struct fts_ts_info *info = handle; + + if (!info) { + pr_err("%s: Invalid info\n", __func__); + return IRQ_HANDLED; + } +#ifdef CONFIG_ST_TRUSTED_TOUCH +#ifndef CONFIG_ARCH_QTI_VM + if (atomic_read(&info->vm_info->pvm_owns_iomem) && + atomic_read(&info->vm_info->pvm_owns_irq) && + atomic_read(&info->trusted_touch_enabled)) { + pr_err("%s: Cannot service interrupts in PVM while trusted touch is enabled\n", + __func__); + return IRQ_HANDLED; + } +#endif +#endif + disable_irq_nosync(info->client->irq); + + queue_work(info->event_wq, &info->work); + + return IRQ_HANDLED; +} +#endif + +static int fts_interrupt_install(struct fts_ts_info *info) +{ + int i, error = 0; + size_t len; + + len = sizeof(struct event_dispatch_handler_t) * EVENTID_LAST; + info->event_dispatch_table = kzalloc(len, GFP_KERNEL); + + if (!info->event_dispatch_table) { + logError(1, "%s OOM allocating event dispatch table\n", tag); + return -ENOMEM; + } + + for (i = 0; i < EVENTID_LAST; i++) + info->event_dispatch_table[i].handler = fts_nop_event_handler; + install_handler(info, ENTER_POINTER, enter_pointer); + install_handler(info, LEAVE_POINTER, leave_pointer); + install_handler(info, MOTION_POINTER, motion_pointer); + install_handler(info, ERROR_EVENT, error); + install_handler(info, CONTROL_READY, controller_ready); + install_handler(info, STATUS_UPDATE, status); +#ifdef PHONE_GESTURE + install_handler(info, GESTURE, gesture); +#endif +#ifdef PHONE_KEY + install_handler(info, KEY_STATUS, key_status); +#endif + /* disable interrupts in any case */ + error = fts_disableInterrupt(); + +#ifdef FTS_USE_POLLING_MODE + logError(0, "%s Polling Mode\n"); + hrtimer_init(&info->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + info->timer.function = fts_timer_func; + hrtimer_start(&info->timer, ktime_set(1, 0), HRTIMER_MODE_REL); +#else +#ifdef CONFIG_ARCH_QTI_VM + logError(0, "%s Interrupt Mode\n", tag); + if (request_threaded_irq(info->client->irq, NULL, fts_interrupt_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, info->client->name, info)) { + logError(1, "%s Request irq failed\n", tag); + kfree(info->event_dispatch_table); + error = -EBUSY; + } +#else + logError(0, "%s Interrupt Mode\n", tag); + if (request_threaded_irq(info->client->irq, NULL, fts_interrupt_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, info->client->name, info)) { + logError(1, "%s Request irq failed\n", tag); + kfree(info->event_dispatch_table); + error = -EBUSY; + } +#endif +#endif + return error; +} + +static void fts_interrupt_uninstall(struct fts_ts_info *info) +{ + fts_disableInterrupt(); + + kfree(info->event_dispatch_table); +#ifdef FTS_USE_POLLING_MODE + hrtimer_cancel(&info->timer); +#else + free_irq(info->client->irq, info); +#endif +} + +static void fts_interrupt_enable(struct fts_ts_info *info) +{ +#ifdef FTS_USE_POLLING_MODE + hrtimer_start(&info->timer, ktime_set(0, 10000000), HRTIMER_MODE_REL); +#else + enable_irq(info->client->irq); +#endif + /* enable the touch IC irq */ + fts_enableInterrupt(); +} + +static void fts_interrupt_disable(struct fts_ts_info *info) +{ + /* disable the touch IC irq */ + fts_disableInterrupt(); + +#ifdef FTS_USE_POLLING_MODE + hrtimer_cancel(&info->timer); +#else + disable_irq(info->client->irq); +#endif + +} + +static int fts_init(struct fts_ts_info *info) +{ + int error; + + error = fts_system_reset(); + if (error < OK && error != (ERROR_TIMEOUT | ERROR_SYSTEM_RESET_FAIL)) { + logError(1, "%s Cannot reset the device! ERROR %08X\n", + tag, error); + return error; + } + if (error == (ERROR_TIMEOUT | ERROR_SYSTEM_RESET_FAIL)) { + logError(1, "%s Setting default Chip INFO!\n", tag); + defaultChipInfo(0); + } else { + error = readChipInfo(0); + if (error < OK) { + logError(1, "%s Cannot read Chip Info!ERROR:%08X\n", + tag, error); + } + } + + error = fts_interrupt_install(info); + + if (error != OK) + logError(1, "%s Init (1) error (ERROR = %08X)\n", tag, error); + + return error; +} + +int fts_chip_powercycle(struct fts_ts_info *info) +{ + int error = 0; + + logError(0, "%s %s: Power Cycle Starting...\n", tag, __func__); + + /* + * if IRQ pin is short with DVDD a call to + * the ISR will triggered when the regulator is turned off + */ + + logError(0, "%s %s: Disabling IRQ...\n", tag, __func__); + disable_irq_nosync(info->client->irq); + if (info->pwr_reg) { + error = regulator_disable(info->pwr_reg); + if (error < 0) { + logError(1, "%s %s: Failed to disable DVDD regulator\n", + tag, __func__); + } + } + + if (info->bus_reg) { + error = regulator_disable(info->bus_reg); + if (error < 0) { + logError(1, "%s %s: Failed to disable AVDD regulator\n", + tag, __func__); + } + } + + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) + gpio_set_value(info->bdata->reset_gpio, 0); + else + msleep(300); + + if (info->pwr_reg) { + error = regulator_enable(info->bus_reg); + if (error < 0) { + logError(1, "%s %s: Failed to enable AVDD regulator\n", + tag, __func__); + } + } + + if (info->bus_reg) { + error = regulator_enable(info->pwr_reg); + if (error < 0) { + logError(1, "%s %s: Failed to enable DVDD regulator\n", + tag, __func__); + } + } + /* time needed by the regulators for reaching the regime values */ + msleep(20); + + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) { + /* time to wait before bring up the reset */ + /* gpio after the power up of the regulators */ + msleep(20); + gpio_set_value(info->bdata->reset_gpio, 1); + /* mdelay(300); */ + } + + release_all_touches(info); + + logError(0, "%s %s: Enabling IRQ...\n", tag, __func__); + enable_irq(info->client->irq); + logError(0, "%s %s: Power Cycle Finished! ERROR CODE = %08x\n", + tag, __func__, error); + setSystemResettedUp(1); + setSystemResettedDown(1); + return error; +} + +int fts_chip_powercycle2(struct fts_ts_info *info, unsigned long sleep) +{ + int error = 0; + + logError(0, "%s %s: Power Cycle Starting...\n", tag, __func__); + + if (info->pwr_reg) { + error = regulator_disable(info->pwr_reg); + if (error < 0) { + logError(1, "%s %s: Failed to disable DVDD regulator\n", + tag, __func__); + } + } + + if (info->bus_reg) { + error = regulator_disable(info->bus_reg); + if (error < 0) { + logError(1, "%s %s: Failed to disable AVDD regulator\n", + tag, __func__); + } + } + + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) + gpio_set_value(info->bdata->reset_gpio, 0); + + msleep(sleep); + if (info->pwr_reg) { + error = regulator_enable(info->bus_reg); + if (error < 0) { + logError(1, "%s %s: Failed to enable AVDD regulator\n", + tag, __func__); + } + } + + if (info->bus_reg) { + error = regulator_enable(info->pwr_reg); + if (error < 0) { + logError(1, "%s %s: Failed to enable DVDD regulator\n", + tag, __func__); + } + } + /* time needed by the regulators for reaching the regime values */ + msleep(500); + + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) { + /* + * time to wait before bring up the reset + * gpio after the power up of the regulators + */ + msleep(20); + gpio_set_value(info->bdata->reset_gpio, 1); + /* msleep(300); */ + } + + /* before reset clear all slot */ + release_all_touches(info); + + logError(0, "%s %s: Power Cycle Finished! ERROR CODE = %08x\n", + tag, __func__, error); + setSystemResettedUp(1); + setSystemResettedDown(1); + return error; +} + +static int fts_init_afterProbe(struct fts_ts_info *info) +{ + int error = 0; + + /* system reset */ + error = cleanUp(0); + + /* enable the features and the sensing */ + error |= fts_mode_handler(info, 0); + + /* enable the interrupt */ + error |= fts_enableInterrupt(); + +#if defined(CONFIG_FB_MSM) + error |= fb_register_client(&info->notifier); +#else + if (active_panel) + error |= drm_panel_notifier_register(active_panel, + &info->notifier); +#endif + + if (error < OK) + logError(1, "%s %s Init after Probe error (ERROR = %08X)\n", + tag, __func__, error); + + return error; +} + +/* + * TODO: change this function according with the needs + * of customer in terms of feature to enable/disable + */ +static int fts_mode_handler(struct fts_ts_info *info, int force) +{ + int res = OK; + int ret = OK; + + /* initialize the mode to Nothing in order */ + /* to be updated depending on the features enabled */ + info->mode = MODE_NOTHING; + + logError(0, "%s %s: Mode Handler starting...\n", tag, __func__); + switch (info->resume_bit) { + case 0: + /* screen down */ + logError(0, "%s %s: Screen OFF...\n", tag, __func__); + /* + * do sense off in order to avoid the flooding + * of the fifo with touch events if someone is + * touching the panel during suspend + */ + logError(0, "%s %s: Sense OFF!\n", tag, __func__); + /* + *we need to use fts_command for speed reason + * (no need to check echo in this case and interrupt + * can be enabled) + */ + res |= fts_command(info, FTS_CMD_MS_MT_SENSE_OFF); +#ifdef PHONE_KEY + logError(0, "%s %s: Key OFF!\n", tag, __func__); + res |= fts_command(info, FTS_CMD_MS_KEY_OFF); +#endif + +#ifdef PHONE_GESTURE + if (info->gesture_enabled == 1) { + logError(0, "%s %s: enter in gesture mode!\n", + tag, __func__); + ret = enterGestureMode(isSystemResettedDown()); + if (ret >= OK) { + info->mode |= FEAT_GESTURE; + } else { + logError(1, + "%s %s:enterGestureMode failed!%08X recovery in senseOff\n", + tag, __func__, ret); + } + res |= ret; + } +#endif + if (info->mode != (FEAT_GESTURE|MODE_NOTHING) + || info->gesture_enabled == 0) + info->mode |= MODE_SENSEOFF; + setSystemResettedDown(0); + break; + + case 1: + /* screen up */ + logError(0, "%s %s: Screen ON...\n", tag, __func__); + +#ifdef FEAT_GLOVE + if ((info->glove_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Glove Mode setting...\n", + tag, __func__); + ret = featureEnableDisable(info->glove_enabled, + FEAT_GLOVE); + if (ret < OK) { + logError(1, + "%s %s:error in setting GLOVE_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->glove_enabled == FEAT_ENABLE) { + info->mode |= FEAT_GLOVE; + logError(1, "%s %s: GLOVE_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, "%s %s: GLOVE_MODE Disabled!\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_STYLUS + if ((info->stylus_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Stylus Mode setting...\n", + tag, __func__); + ret = featureEnableDisable(info->stylus_enabled, + FEAT_STYLUS); + if (ret < OK) { + logError(1, + "%s %s:error in set STYLUS_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->stylus_enabled == FEAT_ENABLE) { + info->mode |= FEAT_STYLUS; + logError(1, "%s %s: STYLUS_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, "%s %s: STYLUS_MODE Disabled!\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_COVER + if ((info->cover_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Cover Mode setting...\n", + tag, __func__); + ret = featureEnableDisable(info->cover_enabled, + FEAT_COVER); + if (ret < OK) { + logError(1, + "%s %s:error setting COVER_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->cover_enabled == FEAT_ENABLE) { + info->mode |= FEAT_COVER; + logError(1, "%s %s: COVER_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, "%s %s: COVER_MODE Disabled!\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_CHARGER + if ((info->charger_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Charger Mode setting...\n", + tag, __func__); + ret = featureEnableDisable(info->charger_enabled, + FEAT_CHARGER); + if (ret < OK) { + logError(1, + "%s %s:error set CHARGER_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->charger_enabled == FEAT_ENABLE) { + info->mode |= FEAT_CHARGER; + logError(1, "%s %s: CHARGER_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, "%s %s: CHARGER_MODE Disabled!\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_VR + if ((info->vr_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Vr Mode setting\n", tag, __func__); + ret = featureEnableDisable(info->vr_enabled, FEAT_VR); + if (ret < OK) { + logError(1, + "%s %s:error setting VR_MODE!:%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->vr_enabled == FEAT_ENABLE) { + info->mode |= FEAT_VR; + logError(1, "%s %s: VR_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, "%s %s: VR_MODE Disabled!\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_EDGE_REJECTION + if ((info->edge_rej_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Edge Rejection Mode setting\n", + tag, __func__); + ret = featureEnableDisable(info->edge_rej_enabled, + FEAT_EDGE_REJECTION); + if (ret < OK) { + logError(1, + "%s %s:err set EDGE_REJECTION_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->edge_rej_enabled == + FEAT_ENABLE) { + info->mode |= FEAT_EDGE_REJECTION; + logError(1, + "%s %s:EDGE_REJECTION_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, + "%s %s:EDGE_REJECTION_MODE Disabled!\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_CORNER_REJECTION + if ((info->corner_rej_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Corner rejection Mode setting\n", + tag, __func__); + ret = featureEnableDisable(info->corner_rej_enabled, + FEAT_CORNER_REJECTION); + if (ret < OK) { + logError(1, + "%s%s:err CORNER_REJECTION_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->corner_rej_enabled == + FEAT_ENABLE) { + info->mode |= FEAT_CORNER_REJECTION; + logError(1, + "%s%s:CORNER_REJECTION_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, + "%s%s:CORNER_REJECTION_MODE Disabled\n", + tag, __func__); + } + } +#endif +#ifdef FEAT_EDGE_PALM_REJECTION + if ((info->edge_palm_rej_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s:Edge Palm rejection Mode setting\n", + tag, __func__); + ret = featureEnableDisable(info->edge_palm_rej_enabled, + FEAT_EDGE_PALM_REJECTION); + if (ret < OK) { + logError(1, + "%s %s:err EDGE_PALM_REJECTION_MODE!%08X\n", + tag, __func__, ret); + } + res |= ret; + + if (ret >= OK && info->edge_palm_rej_enabled == + FEAT_ENABLE) { + info->mode |= FEAT_EDGE_PALM_REJECTION; + logError(1, + "%s %s:EDGE_PALM_REJECTION_MODE Enabled!\n", + tag, __func__); + } else { + logError(1, + "%s %s:EDGE_PALM_REJECTION_MODE Disabled!\n", + tag, __func__); + } + } +#endif + logError(0, "%s %s: Sense ON!\n", tag, __func__); + res |= fts_command(info, FTS_CMD_MS_MT_SENSE_ON); + info->mode |= MODE_SENSEON; +#ifdef PHONE_KEY + logError(0, "%s %s: Key ON!\n", tag, __func__); + res |= fts_command(info, FTS_CMD_MS_KEY_ON); +#endif + setSystemResettedUp(0); + break; + + default: + logError(1, + "%s %s: invalid resume_bit value = %d! ERROR %08X\n", + tag, __func__, info->resume_bit, ERROR_OP_NOT_ALLOW); + res = ERROR_OP_NOT_ALLOW; + } + logError(0, "%s %s: Mode Handler finished! res = %08X\n", tag, __func__, + res); + return res; +} + +static int fts_chip_power_switch(struct fts_ts_info *info, bool on) +{ + int error = -1; + + if (info->bdata->pwr_on_suspend) { + if (!info->ts_pinctrl) + return 0; + + if (on) { + error = pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_active); + if (error < 0) + logError(1, "%s: Failed to select %s\n", + __func__, PINCTRL_STATE_ACTIVE); + } else { + error = pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_suspend); + if (error < 0) + logError(1, "%s: Failed to select %s\n", + __func__, PINCTRL_STATE_SUSPEND); + } + + return 0; + } + + if (on) { + if (info->bus_reg) { + error = regulator_enable(info->bus_reg); + if (error < 0) + logError(1, "%s %s: Failed to enable AVDD\n", + tag, __func__); + } + + if (info->pwr_reg) { + error = regulator_enable(info->pwr_reg); + if (error < 0) + logError(1, "%s %s: Failed to enable DVDD\n", + tag, __func__); + } + + if (info->ts_pinctrl) { + if (pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_active) < 0) { + logError(1, "%s: Failed to select %s\n", + __func__, PINCTRL_STATE_ACTIVE); + } + } + } else { + if (info->bdata->reset_gpio != GPIO_NOT_DEFINED) + gpio_set_value(info->bdata->reset_gpio, 0); + else + msleep(300); + + if (info->ts_pinctrl) { + if (pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_suspend) < 0) { + logError(1, "%s: Failed to select %s\n", + __func__, PINCTRL_STATE_SUSPEND); + } + } + + if (info->pwr_reg) { + error = regulator_disable(info->pwr_reg); + if (error < 0) + logError(1, "%s %s: Failed to disable DVDD\n", + tag, __func__); + } + + if (info->bus_reg) { + error = regulator_disable(info->bus_reg); + if (error < 0) + logError(1, "%s %s: Failed to disable AVDD\n", + tag, __func__); + } + } + return error; +} + + +static void fts_resume_work(struct work_struct *work) +{ + struct fts_ts_info *info; + + info = container_of(work, struct fts_ts_info, resume_work); + + __pm_wakeup_event(info->wakeup_source, HZ); + + fts_chip_power_switch(info, true); + + info->resume_bit = 1; + + fts_system_reset(); +#ifdef USE_NOISE_PARAM + readNoiseParameters(noise_params); +#endif + +#ifdef USE_NOISE_PARAM + writeNoiseParameters(noise_params); +#endif + + release_all_touches(info); + + fts_mode_handler(info, 0); + + info->sensor_sleep = false; + + fts_interrupt_enable(info); +} + +static void fts_suspend_work(struct work_struct *work) +{ + struct fts_ts_info *info; + + info = container_of(work, struct fts_ts_info, suspend_work); + +#ifdef CONFIG_ST_TRUSTED_TOUCH + if (atomic_read(&info->trusted_touch_enabled)) + wait_for_completion_interruptible( + &info->trusted_touch_powerdown); +#endif + + __pm_wakeup_event(info->wakeup_source, HZ); + + info->resume_bit = 0; + + fts_mode_handler(info, 0); + + fts_interrupt_disable(info); + release_all_touches(info); + info->sensor_sleep = true; + + fts_chip_power_switch(info, false); +} + +#if defined(CONFIG_FB_MSM) +static int fts_fb_state_chg_callback(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct fts_ts_info *info = container_of(nb, + struct fts_ts_info, notifier); + struct fb_event *evdata = data; + unsigned int blank; + + if (!evdata || (evdata->id != 0)) + return 0; + + if (val != FB_EVENT_BLANK) + return 0; + + logError(0, "%s %s: fts notifier begin!\n", tag, __func__); + + if (evdata->data && val == FB_EVENT_BLANK && info) { + + blank = *(int *) (evdata->data); + + switch (blank) { + case FB_BLANK_POWERDOWN: + if (info->sensor_sleep) + break; + + logError(0, "%s %s: FB_BLANK_POWERDOWN\n", + tag, __func__); + + queue_work(info->event_wq, &info->suspend_work); + + break; + + case FB_BLANK_UNBLANK: + if (!info->sensor_sleep) + break; + + logError(0, "%s %s: FB_BLANK_UNBLANK\n", + tag, __func__); + + queue_work(info->event_wq, &info->resume_work); + break; + default: + break; + } + } + return NOTIFY_OK; +} + +#else +static int fts_fb_state_chg_callback(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct fts_ts_info *info = container_of(nb, struct fts_ts_info, + notifier); + struct drm_panel_notifier *evdata = data; + unsigned int blank; + + if (!evdata) + return 0; + + if (val != DRM_PANEL_EVENT_BLANK) + return 0; + + logError(0, "%s %s: fts notifier begin!\n", tag, __func__); + if (evdata->data && val == DRM_PANEL_EVENT_BLANK && info) { + blank = *(int *) (evdata->data); + + switch (blank) { + case DRM_PANEL_BLANK_POWERDOWN: + if (info->sensor_sleep && info->aoi_notify_enabled) + break; + + if (info->aoi_notify_enabled) + info->aoi_wake_on_suspend = true; + else + info->aoi_wake_on_suspend = false; + + if (info->aoi_wake_on_suspend) { + info->sensor_sleep = true; + __pm_stay_awake(info->wakeup_source); + } else { + queue_work(info->event_wq, &info->suspend_work); + } + break; + + case DRM_PANEL_BLANK_UNBLANK: + if (info->aoi_wake_on_suspend) + __pm_relax(info->wakeup_source); + + if (!info->sensor_sleep) + break; + + if (!info->resume_bit) + queue_work(info->event_wq, &info->resume_work); + + if (info->aoi_wake_on_suspend) + info->sensor_sleep = false; + + break; + default: + break; + } + } + return NOTIFY_OK; +} +#endif + +static struct notifier_block fts_noti_block = { + .notifier_call = fts_fb_state_chg_callback, +}; + +static int fts_pinctrl_init(struct fts_ts_info *info) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + info->ts_pinctrl = devm_pinctrl_get(info->dev); + if (IS_ERR_OR_NULL(info->ts_pinctrl)) { + retval = PTR_ERR(info->ts_pinctrl); + logError(1, "Target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; + } + + info->pinctrl_state_active + = pinctrl_lookup_state(info->ts_pinctrl, PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(info->pinctrl_state_active)) { + retval = PTR_ERR(info->pinctrl_state_active); + logError(1, "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + info->pinctrl_state_suspend + = pinctrl_lookup_state(info->ts_pinctrl, PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(info->pinctrl_state_suspend)) { + retval = PTR_ERR(info->pinctrl_state_suspend); + logError(1, "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + info->pinctrl_state_release + = pinctrl_lookup_state(info->ts_pinctrl, PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(info->pinctrl_state_release)) { + retval = PTR_ERR(info->pinctrl_state_release); + logError(1, "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(info->ts_pinctrl); +err_pinctrl_get: + info->ts_pinctrl = NULL; + return retval; +} + +static int fts_get_reg(struct fts_ts_info *info, bool get) +{ + int retval; + const struct fts_i2c_platform_data *bdata = info->bdata; + + if (!get) { + retval = 0; + goto regulator_put; + } + + if ((bdata->pwr_reg_name != NULL) && (*bdata->pwr_reg_name != 0)) { + info->pwr_reg = regulator_get(info->dev, + bdata->pwr_reg_name); + if (IS_ERR(info->pwr_reg)) { + logError(1, "%s %s: Failed to get power regulator\n", + tag, __func__); + retval = PTR_ERR(info->pwr_reg); + goto regulator_put; + } + + retval = regulator_set_load(info->pwr_reg, FTS_DVDD_LOAD); + if (retval < 0) { + logError(1, "%s %s: Failed to set power load\n", + tag, __func__); + goto regulator_put; + } + + retval = regulator_set_voltage(info->pwr_reg, + FTS_DVDD_VOL_MIN, FTS_DVDD_VOL_MAX); + if (retval < 0) { + logError(1, "%s %s: Failed to set power voltage\n", + tag, __func__); + goto regulator_put; + } + } + + if ((bdata->bus_reg_name != NULL) && (*bdata->bus_reg_name != 0)) { + info->bus_reg = regulator_get(info->dev, + bdata->bus_reg_name); + if (IS_ERR(info->bus_reg)) { + logError(1, + "%s %s:Failed to get bus pullup regulator\n", + tag, __func__); + retval = PTR_ERR(info->bus_reg); + goto regulator_put; + } + + retval = regulator_set_load(info->bus_reg, FTS_AVDD_LOAD); + if (retval < 0) { + logError(1, "%s %s: Failed to set power load\n", + tag, __func__); + goto regulator_put; + } + + retval = regulator_set_voltage(info->bus_reg, + FTS_AVDD_VOL_MIN, FTS_AVDD_VOL_MAX); + + if (retval < 0) { + logError(1, "%s %s: Failed to set power voltage\n", + tag, __func__); + goto regulator_put; + } + } + + return 0; + +regulator_put: + if (info->pwr_reg) { + regulator_put(info->pwr_reg); + info->pwr_reg = NULL; + } + + if (info->bus_reg) { + regulator_put(info->bus_reg); + info->bus_reg = NULL; + } + + return retval; +} + +static int fts_enable_reg(struct fts_ts_info *info, + bool enable) +{ + int retval; + + if (!enable) { + retval = 0; + goto disable_pwr_reg; + } + + if (info->bus_reg) { + retval = regulator_enable(info->bus_reg); + if (retval < 0) { + logError(1, "%s %s: Failed to enable bus regulator\n", + tag, __func__); + goto exit; + } + } + + if (info->pwr_reg) { + retval = regulator_enable(info->pwr_reg); + if (retval < 0) { + logError(1, "%s %s: Failed to enable power regulator\n", + tag, __func__); + goto disable_bus_reg; + } + } + + return OK; + +disable_pwr_reg: + if (info->pwr_reg) + regulator_disable(info->pwr_reg); + +disable_bus_reg: + if (info->bus_reg) + regulator_disable(info->bus_reg); + +exit: + return retval; +} + +static int fts_gpio_setup(int gpio, bool config, int dir, int state) +{ + int retval = 0; + unsigned char buf[16]; + + if (config) { + snprintf(buf, 16, "fts_gpio_%u\n", gpio); + + retval = gpio_request(gpio, buf); + if (retval) { + logError(1, "%s %s: Failed to get gpio %d (code: %d)", + tag, __func__, gpio, retval); + return retval; + } + + if (dir == 0) + retval = gpio_direction_input(gpio); + else + retval = gpio_direction_output(gpio, state); + if (retval) { + logError(1, "%s %s: Failed to set gpio %d direction", + tag, __func__, gpio); + return retval; + } + } else { + gpio_free(gpio); + } + + return retval; +} + +static int fts_set_gpio(struct fts_ts_info *info) +{ + int retval; + const struct fts_i2c_platform_data *bdata = + info->bdata; + + retval = fts_gpio_setup(bdata->irq_gpio, true, 0, 0); + if (retval < 0) { + logError(1, "%s %s: Failed to configure irq GPIO\n", + tag, __func__); + goto err_gpio_irq; + } + + if (bdata->reset_gpio >= 0) { + retval = fts_gpio_setup(bdata->reset_gpio, true, 1, 0); + if (retval < 0) { + logError(1, "%s %s: Failed to configure reset GPIO\n", + tag, __func__); + goto err_gpio_reset; + } + } + if (bdata->reset_gpio >= 0) { + gpio_set_value(bdata->reset_gpio, 0); + msleep(20); + gpio_set_value(bdata->reset_gpio, 1); + } + + setResetGpio(bdata->reset_gpio); + return OK; + +err_gpio_reset: + fts_gpio_setup(bdata->irq_gpio, false, 0, 0); + setResetGpio(GPIO_NOT_DEFINED); +err_gpio_irq: + return retval; +} + +static int parse_dt(struct device *dev, + struct fts_i2c_platform_data *bdata) +{ + int retval; + const char *name; + struct device_node *np = dev->of_node; + + bdata->irq_gpio = of_get_named_gpio_flags(np, + "st,irq-gpio", 0, NULL); + + logError(0, "%s irq_gpio = %d\n", tag, bdata->irq_gpio); + + bdata->pwr_on_suspend = + of_property_read_bool(np, "st,power_on_suspend"); + + retval = of_property_read_string(np, "st,regulator_dvdd", &name); + if (retval == -EINVAL) + bdata->pwr_reg_name = NULL; + else if (retval < 0) + return retval; + + bdata->pwr_reg_name = name; + logError(0, "%s pwr_reg_name = %s\n", tag, name); + + retval = of_property_read_string(np, "st,regulator_avdd", &name); + if (retval == -EINVAL) + bdata->bus_reg_name = NULL; + else if (retval < 0) + return retval; + + bdata->bus_reg_name = name; + logError(0, "%s bus_reg_name = %s\n", tag, name); + + if (of_property_read_bool(np, "st,reset-gpio")) { + bdata->reset_gpio = of_get_named_gpio_flags(np, + "st,reset-gpio", 0, NULL); + logError(0, "%s reset_gpio =%d\n", tag, bdata->reset_gpio); + } else { + bdata->reset_gpio = GPIO_NOT_DEFINED; + } + + bdata->x_flip = of_property_read_bool(np, "st,x-flip"); + bdata->y_flip = of_property_read_bool(np, "st,y-flip"); + + return OK; +} + +static int check_dt(struct device_node *np) +{ + int i; + int count; + struct device_node *node; + struct drm_panel *panel; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) + return OK; + + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + panel = of_drm_find_panel(node); + of_node_put(node); + if (!IS_ERR(panel)) { + active_panel = panel; + return OK; + } + } + + return PTR_ERR(panel); +} + +static int check_default_tp(struct device_node *dt, const char *prop) +{ + const char *active_tp; + const char *compatible; + char *start; + int ret; + + ret = of_property_read_string(dt->parent, prop, &active_tp); + if (ret) { + pr_err(" %s:fail to read %s %d\n", __func__, prop, ret); + return -ENODEV; + } + + ret = of_property_read_string(dt, "compatible", &compatible); + if (ret < 0) { + pr_err(" %s:fail to read %s %d\n", __func__, "compatible", ret); + return -ENODEV; + } + + start = strnstr(active_tp, compatible, strlen(active_tp)); + if (start == NULL) { + pr_err(" %s:no match compatible, %s, %s\n", + __func__, compatible, active_tp); + ret = -ENODEV; + } + + return ret; +} + +static int fts_probe_delayed(struct fts_ts_info *info) +{ + int error = 0; + int retval = 0; + +/* Avoid setting up hardware for TVM during probe */ +#ifdef CONFIG_ST_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + if (!atomic_read(&info->delayed_vm_probe_pending)) { + atomic_set(&info->delayed_vm_probe_pending, 1); + return 0; + } + goto tvm_setup; +#endif +#endif + logError(0, "%s SET Regulators:\n", tag); + retval = fts_get_reg(info, true); + if (retval < 0) { + logError(1, "%s ERROR: %s: Failed to get regulators\n", + tag, __func__); + goto Exit_1; + } + retval = fts_enable_reg(info, true); + if (retval < 0) { + logError(1, + "%s %s: ERROR Failed to enable regulators\n", + tag, __func__); + goto Exit_2; + } + + logError(0, "%s SET GPIOS:\n", tag); + retval = fts_set_gpio(info); + if (retval < 0) { + logError(1, "%s %s: ERROR Failed to set up GPIO's\n", + tag, __func__); + goto Exit_2; + } + + info->client->irq = gpio_to_irq(info->bdata->irq_gpio); + retval = fts_pinctrl_init(info); + if (!retval && info->ts_pinctrl) { + /* + * Pinctrl handle is optional. If pinctrl handle is + * found let pins to be configured in active state. + * If not found continue further without error. + */ + retval = pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_active); + if (retval < 0) { + logError(1, + "%s: Failed to select %s pinstate %d\n", + __func__, PINCTRL_STATE_ACTIVE, retval); + } + } + +#ifdef CONFIG_ARCH_QTI_VM +tvm_setup: +#endif + /* init hardware device */ + logError(0, "%s Device Initialization:\n", tag); + error = fts_init(info); + if (error < OK) { + logError(1, "%s Cannot initialize the device ERROR %08X\n", + tag, error); + error = -ENODEV; +#ifdef CONFIG_ARCH_QTI_VM + return error; +#endif + goto Exit_3; + } + + queue_delayed_work(info->fwu_workqueue, &info->fwu_work, + msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + return error; + +Exit_3: + if (info->ts_pinctrl) { + if (IS_ERR_OR_NULL(info->pinctrl_state_release)) { + devm_pinctrl_put(info->ts_pinctrl); + info->ts_pinctrl = NULL; + } else { + if (pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_release)) + logError(1, "%s:Failed to select %s pinstate\n", + __func__, PINCTRL_STATE_RELEASE); + } + } + fts_enable_reg(info, false); + fts_gpio_setup(info->bdata->irq_gpio, false, 0, 0); + fts_gpio_setup(info->bdata->reset_gpio, false, 0, 0); + +Exit_2: + fts_get_reg(info, false); +Exit_1: + return error; +} + +static int fts_probe_internal(struct i2c_client *client, + const struct i2c_device_id *idp) +{ + struct fts_ts_info *info = NULL; + int error = 0; + struct device_node *dp = client->dev.of_node; + int skip_5_1 = 0; + + logError(0, "%s %s: driver probe begin!\n", tag, __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + logError(1, "%s Unsupported I2C functionality\n", tag); + error = -EIO; + goto ProbeErrorExit_0; + } + + openChannel(client); + + info = kzalloc(sizeof(struct fts_ts_info), GFP_KERNEL); + if (!info) { + logError(1, + "%s can't allocate struct info!\n", + tag); + error = -ENOMEM; + goto ProbeErrorExit_0; + } + + info->client = client; + + i2c_set_clientdata(client, info); + + info->i2c_data = kmalloc(I2C_DATA_MAX_LEN, GFP_KERNEL); + if (info->i2c_data == NULL) { + error = -ENOMEM; + goto ProbeErrorExit_0P1; + } + info->i2c_data_len = I2C_DATA_MAX_LEN; + + logError(0, "%s i2c address: %x\n", tag, client->addr); + info->dev = &info->client->dev; + if (dp) { + info->bdata = devm_kzalloc(&client->dev, + sizeof(struct fts_i2c_platform_data), + GFP_KERNEL); + if (!info->bdata) { + logError(1, "%s ERROR:info.bdata kzalloc failed\n", + tag); + goto ProbeErrorExit_1; + } + parse_dt(&client->dev, info->bdata); + } + + logError(0, "%s SET Auto Fw Update:\n", tag); + info->fwu_workqueue = alloc_workqueue("fts-fwu-queue", + WQ_UNBOUND|WQ_HIGHPRI|WQ_CPU_INTENSIVE, 1); + if (!info->fwu_workqueue) { + logError(1, "%s ERROR: Cannot create fwu work thread\n", tag); + goto ProbeErrorExit_1; + } + + INIT_DELAYED_WORK(&info->fwu_work, fts_fw_update_auto); + + logError(0, "%s SET Event Handler:\n", tag); + info->wakeup_source = wakeup_source_register(&client->dev, + dev_name(&client->dev)); + + info->event_wq = alloc_workqueue("fts-event-queue", + WQ_UNBOUND|WQ_HIGHPRI|WQ_CPU_INTENSIVE, 1); + if (!info->event_wq) { + logError(1, "%s ERROR: Cannot create work thread\n", tag); + error = -ENOMEM; + goto ProbeErrorExit_4; + } + + INIT_WORK(&info->work, fts_event_handler); + + INIT_WORK(&info->resume_work, fts_resume_work); + INIT_WORK(&info->suspend_work, fts_suspend_work); + + logError(0, "%s SET Input Device Property:\n", tag); + /* info->dev = &info->client->dev; */ + info->input_dev = input_allocate_device(); + if (!info->input_dev) { + logError(1, "%s ERROR: No such input device defined!\n", + tag); + error = -ENODEV; + goto ProbeErrorExit_5; + } + info->input_dev->dev.parent = &client->dev; + info->input_dev->name = FTS_TS_DRV_NAME; + snprintf(fts_ts_phys, sizeof(fts_ts_phys), "%s/input0", + info->input_dev->name); + info->input_dev->phys = fts_ts_phys; + info->input_dev->id.bustype = BUS_I2C; + info->input_dev->id.vendor = 0x0001; + info->input_dev->id.product = 0x0002; + info->input_dev->id.version = 0x0100; + + __set_bit(EV_SYN, info->input_dev->evbit); + __set_bit(EV_KEY, info->input_dev->evbit); + __set_bit(EV_ABS, info->input_dev->evbit); + __set_bit(BTN_TOUCH, info->input_dev->keybit); + __set_bit(BTN_TOOL_FINGER, info->input_dev->keybit); + + input_mt_init_slots(info->input_dev, TOUCH_ID_MAX, INPUT_MT_DIRECT); + + input_set_abs_params(info->input_dev, ABS_MT_POSITION_X, + X_AXIS_MIN, X_AXIS_MAX, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_POSITION_Y, + Y_AXIS_MIN, Y_AXIS_MAX, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MAJOR, + AREA_MIN, AREA_MAX, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MINOR, + AREA_MIN, AREA_MAX, 0, 0); + +#ifdef PHONE_GESTURE + input_set_capability(info->input_dev, EV_KEY, KEY_WAKEUP); + + input_set_capability(info->input_dev, EV_KEY, KEY_M); + input_set_capability(info->input_dev, EV_KEY, KEY_O); + input_set_capability(info->input_dev, EV_KEY, KEY_E); + input_set_capability(info->input_dev, EV_KEY, KEY_W); + input_set_capability(info->input_dev, EV_KEY, KEY_C); + input_set_capability(info->input_dev, EV_KEY, KEY_L); + input_set_capability(info->input_dev, EV_KEY, KEY_F); + input_set_capability(info->input_dev, EV_KEY, KEY_V); + input_set_capability(info->input_dev, EV_KEY, KEY_S); + input_set_capability(info->input_dev, EV_KEY, KEY_Z); + input_set_capability(info->input_dev, EV_KEY, KEY_WWW); + + input_set_capability(info->input_dev, EV_KEY, KEY_LEFT); + input_set_capability(info->input_dev, EV_KEY, KEY_RIGHT); + input_set_capability(info->input_dev, EV_KEY, KEY_UP); + input_set_capability(info->input_dev, EV_KEY, KEY_DOWN); + + input_set_capability(info->input_dev, EV_KEY, KEY_F1); + input_set_capability(info->input_dev, EV_KEY, KEY_F2); + input_set_capability(info->input_dev, EV_KEY, KEY_F3); + input_set_capability(info->input_dev, EV_KEY, KEY_F4); + input_set_capability(info->input_dev, EV_KEY, KEY_F5); + + input_set_capability(info->input_dev, EV_KEY, KEY_LEFTBRACE); + input_set_capability(info->input_dev, EV_KEY, KEY_RIGHTBRACE); +#endif + +#ifdef PHONE_KEY + /* KEY associated to the touch screen buttons */ + input_set_capability(info->input_dev, EV_KEY, KEY_HOMEPAGE); + input_set_capability(info->input_dev, EV_KEY, KEY_BACK); + input_set_capability(info->input_dev, EV_KEY, KEY_MENU); +#endif + + mutex_init(&(info->input_report_mutex)); + +#ifdef PHONE_GESTURE + mutex_init(&gestureMask_mutex); +#endif + + /* register the multi-touch input device */ + error = input_register_device(info->input_dev); + if (error) { + logError(1, "%s ERROR: No such input device\n", tag); + error = -ENODEV; + goto ProbeErrorExit_5_1; + } + + skip_5_1 = 1; + /* track slots */ + info->touch_id = 0; +#ifdef STYLUS_MODE + info->stylus_id = 0; +#endif + + /* + * init feature switches (by default all the features + * are disable, if one feature want to be enabled from + * the start, set the corresponding value to 1) + */ + info->gesture_enabled = 0; + info->glove_enabled = 0; + info->charger_enabled = 0; + info->stylus_enabled = 0; + info->vr_enabled = 0; + info->cover_enabled = 0; + info->edge_rej_enabled = 0; + info->corner_rej_enabled = 0; + info->edge_palm_rej_enabled = 0; + + info->resume_bit = 1; + info->notifier = fts_noti_block; + +#ifdef CONFIG_ST_TRUSTED_TOUCH + fts_trusted_touch_init(info); + mutex_init(&(info->fts_clk_io_ctrl_mutex)); +#endif + + logError(0, "%s SET Device File Nodes:\n", tag); + /* sysfs stuff */ + info->attrs.attrs = fts_attr_group; + error = sysfs_create_group(&client->dev.kobj, &info->attrs); + if (error) { + logError(1, "%s ERROR: Cannot create sysfs structure!\n", tag); + error = -ENODEV; + goto ProbeErrorExit_7; + } + + /* I2C cmd */ + fts_cmd_class = class_create(THIS_MODULE, FTS_TS_DRV_NAME); + +#ifdef SCRIPTLESS + info->i2c_cmd_dev = device_create(fts_cmd_class, + NULL, DCHIP_ID_0, info, "fts_i2c"); + if (IS_ERR(info->i2c_cmd_dev)) { + logError(1, + "%s ERROR: Failed to create device for the sysfs!\n", + tag); + goto ProbeErrorExit_8; + } + + dev_set_drvdata(info->i2c_cmd_dev, info); + + error = sysfs_create_group(&info->i2c_cmd_dev->kobj, + &i2c_cmd_attr_group); + if (error) { + logError(1, "%s ERROR: Failed to create sysfs group!\n", tag); + goto ProbeErrorExit_9; + } +#endif + +#ifdef DRIVER_TEST + info->test_cmd_dev = device_create(fts_cmd_class, + NULL, DCHIP_ID_0, info, "fts_driver_test"); + if (IS_ERR(info->test_cmd_dev)) { + logError(1, + "%s ERROR: Failed to create device for the sysfs!\n", + tag); + goto ProbeErrorExit_10; + } + + dev_set_drvdata(info->test_cmd_dev, info); + + error = sysfs_create_group(&info->test_cmd_dev->kobj, + &test_cmd_attr_group); + if (error) { + logError(1, "%s ERROR: Failed to create sysfs group!\n", tag); + goto ProbeErrorExit_11; + } +#endif + + info->aoi_cmd_dev = device_create(fts_cmd_class, + NULL, DCHIP_ID_0, info, "touch_aoi"); + if (IS_ERR(info->aoi_cmd_dev)) { + logError(1, + "%s ERROR: Failed to create device for the sysfs\n", + tag); + goto ProbeErrorExit_10; + } + + dev_set_drvdata(info->aoi_cmd_dev, info); + + error = sysfs_create_group(&info->aoi_cmd_dev->kobj, + &aoi_cmd_attr_group); + if (error) { + logError(1, "%s ERROR: Failed to create sysfs group\n", tag); + goto ProbeErrorExit_11; + } + + info->aoi_class = class_create(THIS_MODULE, "android_touch"); + if (info->aoi_class) { + info->aoi_dev = device_create(info->aoi_class, + NULL, DCHIP_ID_0, info, "touch"); + if (!IS_ERR(info->aoi_dev)) { + dev_set_drvdata(info->aoi_dev, info); + + error = sysfs_create_group(&info->aoi_dev->kobj, + &aoi_enable_attr_group); + } + } + + error = fts_probe_delayed(info); + if (error) { + logError(1, "%s ERROR: Failed to enable resources\n", + tag); + goto ProbeErrorExit_11; + } + logError(1, "%s Probe Finished!\n", tag); + return OK; + + /* error exit path */ +#ifdef DRIVER_TEST +ProbeErrorExit_11: +#ifndef SCRIPTLESS + device_destroy(fts_cmd_class, DCHIP_ID_0); +#endif + +ProbeErrorExit_10: +#ifndef SCRIPTLESS + sysfs_remove_group(&client->dev.kobj, &info->attrs); +#endif +#endif + +#ifdef SCRIPTLESS +ProbeErrorExit_9: + device_destroy(fts_cmd_class, DCHIP_ID_0); + +ProbeErrorExit_8: + sysfs_remove_group(&client->dev.kobj, &info->attrs); +#endif + +ProbeErrorExit_7: +#ifdef CONFIG_ST_TRUSTED_TOUCH + fts_vm_deinit(info); +#endif + /* fb_unregister_client(&info->notifier); */ + input_unregister_device(info->input_dev); + +ProbeErrorExit_5_1: + if (skip_5_1 != 1) + input_free_device(info->input_dev); + +ProbeErrorExit_5: + destroy_workqueue(info->event_wq); + +ProbeErrorExit_4: + destroy_workqueue(info->fwu_workqueue); + wakeup_source_unregister(info->wakeup_source); + +ProbeErrorExit_1: + kfree(info->i2c_data); +ProbeErrorExit_0P1: + kfree(info); + +ProbeErrorExit_0: + logError(1, "%s Probe Failed!\n", tag); + + return error; +} + +static int fts_probe(struct i2c_client *client, const struct i2c_device_id *idp) +{ + int error = 0; + struct device_node *dp = client->dev.of_node; + + error = check_dt(dp); + if (error == -EPROBE_DEFER) + return error; + + if (error) { + if (!check_default_tp(dp, "qcom,i2c-touch-active")) + error = -EPROBE_DEFER; + else + error = -ENODEV; + + return error; + } + + device_init_wakeup(&client->dev, true); + return fts_probe_internal(client, idp); +} + +static int fts_remove(struct i2c_client *client) +{ + struct fts_ts_info *info = i2c_get_clientdata(client); + + if (info->aoi_dev) { + sysfs_remove_group(&info->aoi_dev->kobj, + &aoi_enable_attr_group); + info->aoi_dev = NULL; + } + + if (info->aoi_class) { + device_destroy(info->aoi_class, DCHIP_ID_0); + info->aoi_class = NULL; + } + +#ifdef DRIVER_TEST + sysfs_remove_group(&info->test_cmd_dev->kobj, + &test_cmd_attr_group); +#endif + +#ifdef SCRIPTLESS + /* I2C cmd */ + sysfs_remove_group(&info->i2c_cmd_dev->kobj, &i2c_cmd_attr_group); +#endif + +#if defined(SCRIPTLESS) || defined(DRIVER_TEST) + device_destroy(fts_cmd_class, DCHIP_ID_0); +#endif + + /* sysfs stuff */ + sysfs_remove_group(&client->dev.kobj, &info->attrs); + + /* remove interrupt and event handlers */ + fts_interrupt_uninstall(info); + +#if defined(CONFIG_FB_MSM) + fb_unregister_client(&info->notifier); +#else + if (active_panel) + drm_panel_notifier_register(active_panel, &info->notifier); +#endif + + /* unregister the device */ + input_unregister_device(info->input_dev); + + /* input_free_device(info->input_dev ); */ + + /* Empty the FIFO buffer */ + fts_command(info, FIFO_CMD_FLUSH); + /* flushFIFO(); */ + + /* Remove the work thread */ + destroy_workqueue(info->event_wq); + /* wake_lock_destroy(&info->wakelock); */ + wakeup_source_unregister(info->wakeup_source); + destroy_workqueue(info->fwu_workqueue); + + if (info->ts_pinctrl) { + if (IS_ERR_OR_NULL(info->pinctrl_state_release)) { + devm_pinctrl_put(info->ts_pinctrl); + info->ts_pinctrl = NULL; + } else { + pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_release); + } + } + fts_enable_reg(info, false); + fts_gpio_setup(info->bdata->irq_gpio, false, 0, 0); + fts_gpio_setup(info->bdata->reset_gpio, false, 0, 0); + + fts_get_reg(info, false); + + /* free all */ + kfree(info->i2c_data); + kfree(info); + + device_init_wakeup(&client->dev, false); + return OK; +} + +static const struct of_device_id fts_of_match_table[] = { + { + .compatible = "st,fts", + }, + {}, +}; +static const struct i2c_device_id fts_device_id[] = { + {FTS_TS_DRV_NAME, 0}, + {} +}; + +static struct i2c_driver fts_i2c_driver = { + .driver = { + .name = FTS_TS_DRV_NAME, + .of_match_table = fts_of_match_table, + }, + .probe = fts_probe, + .remove = fts_remove, + .id_table = fts_device_id, +}; + +static int __init fts_driver_init(void) +{ + return i2c_add_driver(&fts_i2c_driver); +} + +static void __exit fts_driver_exit(void) +{ + i2c_del_driver(&fts_i2c_driver); +} + +module_init(fts_driver_init); +module_exit(fts_driver_exit); + +MODULE_DESCRIPTION("STMicroelectronics MultiTouch IC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/st/fts.h b/st/fts.h new file mode 100644 index 0000000000..817096209a --- /dev/null +++ b/st/fts.h @@ -0,0 +1,396 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef _LINUX_FTS_I2C_H_ +#define _LINUX_FTS_I2C_H_ + +/*#include */ +#include +#include +#include + +#include "fts_lib/ftsSoftware.h" +#include "fts_lib/ftsHardware.h" +#include "fts_lib/ftsGesture.h" + +#define FTS_POWER_ON 1 +#define FTS_POWER_OFF 0 + +/****************** CONFIGURATION SECTION ******************/ + +/**** CODE CONFIGURATION ****/ + +#define FTS_TS_DRV_NAME "fts" +#define FTS_TS_DRV_VERSION "4.2.14" /* version */ + +/*#define SCRIPTLESS*/ /*allow to work in scriptless mode with the GUI*/ +#ifdef SCRIPTLESS +#define SCRIPTLESS_DEBUG +/** + * uncomment this macro definition to print debug + * message for script less support + */ +#endif + +#define DRIVER_TEST + +/* #define FW_H_FILE */ /*include the FW as header file*/ +#ifdef FW_H_FILE + #define FW_SIZE_NAME myArray_size + #define FW_ARRAY_NAME myArray +#endif + +/*#define LIMITS_H_FILE*/ /*include the limits file as header file*/ +#ifdef LIMITS_H_FILE + #define LIMITS_SIZE_NAME myArray2_size + #define LIMITS_ARRAY_NAME myArray2 +#endif + +/**** END ****/ + + +/**** FEATURES USED IN THE IC ***/ +/* #define PHONE_KEY enable the keys */ + +#define PHONE_GESTURE /*allow to use the gestures*/ +#ifdef PHONE_GESTURE + #define USE_GESTURE_MASK + #define USE_CUSTOM_GESTURES +#endif + +#define USE_ONE_FILE_NODE +/*allow to enable/disable all the features just using one file node*/ + +#define EDGE_REJ +/*allow edge rej feature (comment to disable)*/ + +#define CORNER_REJ +/*allow corn rej feature (comment to disable)*/ + +#define EDGE_PALM_REJ +/*allow edge palm rej feature (comment to disable)*/ + +#define CHARGER_MODE +/*allow charger mode feature (comment to disable)*/ + +#define GLOVE_MODE +/*allow glove mode feature (comment to disable)*/ + +#define VR_MODE +/*allow vr mode feature (comment to disable)*/ + +#define COVER_MODE +/*allow cover mode feature (comment to disable)*/ + +#define STYLUS_MODE +/*allow stylus mode feature (comment to disable)*/ + +#define USE_NOISE_PARAM +/*set noise params during resume (comment to disable)*/ + +/**** END ****/ + + +/**** PANEL SPECIFICATION ****/ +#define X_AXIS_MAX 1440 +#define X_AXIS_MIN 0 +#define Y_AXIS_MAX 2880 +#define Y_AXIS_MIN 0 + +#define PRESSURE_MIN 0 +#define PRESSURE_MAX 127 + +#define TOUCH_ID_MAX 10 + +#define AREA_MIN PRESSURE_MIN +#define AREA_MAX PRESSURE_MAX +/**** END ****/ + +/*********************************************************/ + +/* Flash programming */ + +#define INIT_FLAG_CNT 3 + +/* KEYS */ +#define KEY1 0x02 +#define KEY2 0x01 +#define KEY3 0x04 + +/* + * Configuration mode + */ +/** + * bitmask which can assume the value defined as + * features in ftsSoftware.h or the following values + */ + +#define MODE_NOTHING 0x00000000 +#define MODE_SENSEON 0x10000000 +#define MODE_SENSEOFF 0x20000000 +#define FEAT_GESTURE 0x40000000 + + +/* + * Status Event Field: + * id of command that triggered the event + */ + +#define FTS_FLASH_WRITE_CONFIG 0x03 +#define FTS_FLASH_WRITE_COMP_MEMORY 0x04 +#define FTS_FORCE_CAL_SELF_MUTUAL 0x05 +#define FTS_FORCE_CAL_SELF 0x06 +#define FTS_WATER_MODE_ON 0x07 +#define FTS_WATER_MODE_OFF 0x08 + + +#define EXP_FN_WORK_DELAY_MS 1000 + +#define CMD_STR_LEN 32 +#define I2C_DATA_MAX_LEN 32 + +#ifdef SCRIPTLESS +/* + * I2C Command Read/Write Function + */ + +#define CMD_RESULT_STR_LEN 2048 +#endif + +#define TSP_BUF_SIZE 4096 + +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" + +/*add by guchong*/ +#ifdef PHONE_GESTURE +extern u16 gesture_coordinates_x[GESTURE_COORDS_REPORT_MAX]; +extern u16 gesture_coordinates_y[GESTURE_COORDS_REPORT_MAX]; +extern int gesture_coords_reported; +extern struct mutex gestureMask_mutex; +#endif + +struct fts_i2c_platform_data { + bool x_flip; + bool y_flip; + int (*power)(bool on); + int irq_gpio; + int reset_gpio; + const char *pwr_reg_name; + const char *bus_reg_name; + bool pwr_on_suspend; +}; + +/* + * Forward declaration + */ +struct fts_ts_info; + +/* + * Dispatch event handler + */ +struct event_dispatch_handler_t { + void (*handler)(struct fts_ts_info *info, unsigned char *data); +}; + +enum trusted_touch_mode_config { + TRUSTED_TOUCH_VM_MODE, + TRUSTED_TOUCH_MODE_NONE +}; + +#ifdef CONFIG_ST_TRUSTED_TOUCH +#define TRUSTED_TOUCH_MEM_LABEL 0x7 + +struct trusted_touch_vm_info { + enum gh_irq_label irq_label; + enum gh_vm_names vm_name; + u32 hw_irq; + gh_memparcel_handle_t vm_mem_handle; + u32 *iomem_bases; + u32 *iomem_sizes; + u32 iomem_list_size; + void *mem_cookie; +#ifdef CONFIG_ARCH_QTI_VM + atomic_t tvm_owns_iomem; + atomic_t tvm_owns_irq; +#else + atomic_t pvm_owns_iomem; + atomic_t pvm_owns_irq; +#endif +}; +#endif + +/* + * struct fts_ts_info - FTS capacitive touch screen device information + * @dev: Pointer to the structure device + * @client: I2C client structure + * @input_dev Input device structure + * @work Work thread + * @event_wq Event queue for work thread + * @event_dispatch_table Event dispatch table handlers + * @attrs SysFS attributes + * @mode Device operating mode (bitmask) + * @touch_id Bitmask for touch id (mapped to input slots) + * @stylus_id Bitmask for tracking the stylus touches + * (mapped using the touchId) + * @timer Timer when operating in polling mode + * @power Power on/off routine + * @bdata HW info retrived from device tree + * @pwr_reg DVDD power regulator + * @bus_reg AVDD power regulator + * @resume_bit Indicate if screen off/on + * @fwupdate_stat Store the result of a fw update triggered by the host + * @notifier Used for be notified from a suspend/resume event + * @sensor_sleep true susped was called, false resume was called + * @wakelock Wake Lock struct + * @input_report_mutex mutex for handling the pressure of keys + * @series of switches to store the enabling status of a particular + * feature from the host + */ +struct fts_ts_info { + struct device *dev; + struct i2c_client *client; + struct input_dev *input_dev; + + struct work_struct work; + struct work_struct suspend_work; + struct work_struct resume_work; + struct workqueue_struct *event_wq; + + struct delayed_work fwu_work; + struct workqueue_struct *fwu_workqueue; + struct completion cmd_done; + + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; + + struct event_dispatch_handler_t *event_dispatch_table; + + struct attribute_group attrs; + + unsigned int mode; + unsigned long touch_id; +#ifdef STYLUS_MODE + unsigned long stylus_id; +#endif + + +#ifdef FTS_USE_POLLING_MODE + struct hrtimer timer; +#endif + + +#ifdef SCRIPTLESS + /*I2C cmd*/ + struct device *i2c_cmd_dev; + char cmd_read_result[CMD_RESULT_STR_LEN]; + char cmd_wr_result[CMD_RESULT_STR_LEN]; + char cmd_write_result[20]; +#endif + +#ifdef DRIVER_TEST + struct device *test_cmd_dev; +#endif + int (*power)(bool on); + + struct fts_i2c_platform_data *bdata; + struct regulator *pwr_reg; + struct regulator *bus_reg; + + int resume_bit; + int fwupdate_stat; + + struct notifier_block notifier; + bool sensor_sleep; + struct wakeup_source *wakeup_source; + + /* input lock */ + struct mutex input_report_mutex; + + /*switches for features*/ + unsigned int gesture_enabled; + unsigned int glove_enabled; + unsigned int charger_enabled; + unsigned int stylus_enabled; + unsigned int vr_enabled; + unsigned int cover_enabled; + unsigned int edge_rej_enabled; + unsigned int corner_rej_enabled; + unsigned int edge_palm_rej_enabled; + + uint8_t *i2c_data; + uint8_t i2c_data_len; + + struct device *aoi_cmd_dev; + bool aoi_notify_enabled; + bool aoi_wake_on_suspend; + + /* aoi region */ + struct class *aoi_class; + struct device *aoi_dev; + int aoi_left; + int aoi_top; + int aoi_bottom; + int aoi_right; + +#ifdef CONFIG_ST_TRUSTED_TOUCH + struct trusted_touch_vm_info *vm_info; + struct mutex fts_clk_io_ctrl_mutex; + const char *touch_environment; + struct completion trusted_touch_powerdown; + struct completion resource_checkpoint; + struct clk *core_clk; + struct clk *iface_clk; + atomic_t trusted_touch_initialized; + atomic_t trusted_touch_enabled; + atomic_t delayed_vm_probe_pending; + atomic_t trusted_touch_mode; +#endif +}; + +extern struct chipInfo ftsInfo; + +int fts_chip_powercycle(struct fts_ts_info *info); +int fts_chip_powercycle2(struct fts_ts_info *info, unsigned long sleep); +void release_all_touches(struct fts_ts_info *info); +/*int fts_get_fw_version(struct fts_ts_info *info);*/ +/*extern unsigned int le_to_uint(const unsigned char *ptr);*/ +/*extern unsigned int be_to_uint(const unsigned char *ptr);*/ +extern int input_register_notifier_client(struct notifier_block *nb); +extern int input_unregister_notifier_client(struct notifier_block *nb); + +extern struct attribute_group aoi_cmd_attr_group; +extern struct attribute_group aoi_enable_attr_group; + +#ifdef SCRIPTLESS +extern struct attribute_group i2c_cmd_attr_group; +#endif + +#ifdef DRIVER_TEST +extern struct attribute_group test_cmd_attr_group; +#endif + + +#endif + diff --git a/st/fts_aoi_event.c b/st/fts_aoi_event.c new file mode 100644 index 0000000000..1a37f659e1 --- /dev/null +++ b/st/fts_aoi_event.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019-2020, The Linux Foundation. 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 version 2 and + * only version 2 as published by the Free Software Foundation. + * + */ + +#include +#include +#include "fts.h" + +static ssize_t touch_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + return 0; +} + +ssize_t aoi_set_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + int left, top, right, bottom; + + ret = sscanf(buf, "%d %d %d %d", &left, &top, &right, &bottom); + if (ret != 4) + return -EINVAL; + + if (right > X_AXIS_MAX) + right = X_AXIS_MAX; + if (bottom > Y_AXIS_MAX) + bottom = Y_AXIS_MAX; + + if (left < 0 || left > X_AXIS_MAX || right < 0 || + top > Y_AXIS_MAX || bottom < 0) + return -EINVAL; + + if (left >= right || top >= bottom) { + info->aoi_left = 0; + info->aoi_top = 0; + info->aoi_right = 0; + info->aoi_bottom = 0; + info->aoi_notify_enabled = false; + return count; + } + + info->aoi_left = left; + info->aoi_top = top; + info->aoi_right = right; + info->aoi_bottom = bottom; + + info->aoi_notify_enabled = true; + return count; +} + +static ssize_t aoi_set_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + size_t len = 0; + + len = scnprintf(buf + len, PAGE_SIZE, + "%d %d %d %d", + info->aoi_left, + info->aoi_top, + info->aoi_right, + info->aoi_bottom); + + return len; +} + +static ssize_t power_set_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int enable; + + if (kstrtoint(buf, 10, &enable)) + return -EINVAL; + + return count; +} + +static DEVICE_ATTR_RO(touch_event); +static DEVICE_ATTR_RW(aoi_set); +static DEVICE_ATTR_WO(power_set); + +static struct attribute *aoi_cmd_attributes[] = { + &dev_attr_touch_event.attr, + &dev_attr_aoi_set.attr, + &dev_attr_power_set.attr, + NULL, +}; + +struct attribute_group aoi_cmd_attr_group = { + .attrs = aoi_cmd_attributes, +}; + +static ssize_t enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_info *info = dev_get_drvdata(dev); + int enable; + + if (kstrtoint(buf, 10, &enable)) + return -EINVAL; + + if (!enable && info->aoi_notify_enabled) { + info->aoi_left = 0; + info->aoi_top = 0; + info->aoi_right = 0; + info->aoi_bottom = 0; + info->aoi_notify_enabled = false; + } else { + info->aoi_left = 0; + info->aoi_top = 0; + info->aoi_right = X_AXIS_MAX; + info->aoi_bottom = Y_AXIS_MAX; + info->aoi_notify_enabled = true; + } + + return count; +} + +static ssize_t enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_info *info = dev_get_drvdata(dev); + size_t len = 0; + + len = scnprintf(buf, PAGE_SIZE, + "%d", + info->aoi_notify_enabled); + + return len; +} + +static DEVICE_ATTR_RW(enable); + +static struct attribute *aoi_enable_attributes[] = { + &dev_attr_aoi_set.attr, + &dev_attr_enable.attr, + NULL, +}; + +struct attribute_group aoi_enable_attr_group = { + .attrs = aoi_enable_attributes, +}; diff --git a/st/fts_driver_test.c b/st/fts_driver_test.c new file mode 100644 index 0000000000..fa9ed503cf --- /dev/null +++ b/st/fts_driver_test.c @@ -0,0 +1,1107 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*#include */ +#include + +#include +#include +#include +#include "fts.h" +#include "fts_lib/ftsCompensation.h" +#include "fts_lib/ftsIO.h" +#include "fts_lib/ftsError.h" +#include "fts_lib/ftsFrame.h" +#include "fts_lib/ftsFlash.h" +#include "fts_lib/ftsTest.h" +#include "fts_lib/ftsTime.h" +#include "fts_lib/ftsTool.h" + +#ifdef DRIVER_TEST + +#define MAX_PARAMS 50 + +/*DEFINE COMMANDS TO TEST*/ +#define CMD_READ 0x00 +#define CMD_WRITE 0x01 +#define CMD_READU16 0x02 +#define CMD_READB2 0x03 +#define CMD_READB2U16 0x04 +#define CMD_POLLFOREVENT 0x05 +#define CMD_SYSTEMRESET 0x06 +#define CMD_CLEANUP 0x07 +#define CMD_GETFORCELEN 0x08 +#define CMD_GETSENSELEN 0x09 +#define CMD_GETMSFRAME 0x0A +/*#define CMD_GETMSKEYFRAME 0x0B*/ +#define CMD_GETSSFRAME 0x0C +#define CMD_REQCOMPDATA 0x0D +#define CMD_READCOMPDATAHEAD 0x0E +#define CMD_READMSCOMPDATA 0x0F +#define CMD_READSSCOMPDATA 0x10 +#define CMD_READGNCOMPDATA 0x11 +#define CMD_GETFWVER 0x12 +#define CMD_FLASHSTATUS 0x13 +#define CMD_FLASHUNLOCK 0x14 +#define CMD_READFWFILE 0x15 +#define CMD_FLASHPROCEDURE 0x16 +#define CMD_ITOTEST 0x17 +#define CMD_INITTEST 0x18 +#define CMD_MSRAWTEST 0x19 +#define CMD_MSINITDATATEST 0x1A +#define CMD_SSRAWTEST 0x1B +#define CMD_SSINITDATATEST 0x1C +#define CMD_MAINTEST 0x1D +#define CMD_POWERCYCLE 0x1E +#define CMD_FWWRITE 0x1F +#define CMD_READCHIPINFO 0x20 +#define CMD_REQFRAME 0x21 + +static char tag[8] = "[ FTS ]\0"; +static u32 functionToTest[MAX_PARAMS]; +static int numberParam; + +static ssize_t stm_driver_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int n; + char *p = (char *)buf; + int ret; + + memset(functionToTest, 0, MAX_PARAMS * sizeof(u32)); + + for (n = 0; n < (count + 1) / 3 && n < MAX_PARAMS; n++) { + ret = sscanf(p, "%02X ", &functionToTest[n]); + if (ret != 1) + return -EINVAL; + p += 3; + logError(1, "%s functionToTest[%d] = %02X\n", tag, n, + functionToTest[n]); + } + + numberParam = n; + logError(1, "%s Number of Parameters = %d\n", tag, numberParam); + return count; +} + +static ssize_t stm_driver_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char buff[CMD_STR_LEN] = {0}; + int res = -1, j, count; + int size = 6 * 2; + int temp = 0; + int i; + int byteToRead = 0; + u8 *readData = NULL; + u8 *all_strbuff = NULL; + u8 *cmd = NULL; + + struct MutualSenseFrame frameMS = {0}; + struct SelfSenseFrame frameSS = {0}; + + struct DataHeader dataHead = {0}; + struct MutualSenseData compData = {0}; + struct SelfSenseData comData = {0}; + struct GeneralData gnData = {0}; + + u16 address = 0; + u16 fw_version = 0; + u16 config_id = 0; + + struct Firmware fw; + + /*struct used for defining which test*/ + /*perform during the MP test*/ + + struct TestToDo todoDefault; + + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + fw.data = NULL; + + todoDefault.MutualRaw = 1; + todoDefault.MutualRawGap = 1; + todoDefault.MutualCx1 = 0; + todoDefault.MutualCx2 = 1; + todoDefault.MutualCx2Adj = 1; + todoDefault.MutualCxTotal = 0; + todoDefault.MutualCxTotalAdj = 0; + + todoDefault.MutualKeyRaw = 0; + todoDefault.MutualKeyCx1 = 0; + todoDefault.MutualKeyCx2 = 0; + todoDefault.MutualKeyCxTotal = 0; + + todoDefault.SelfForceRaw = 1; + todoDefault.SelfForceRawGap = 0; + todoDefault.SelfForceIx1 = 0; + todoDefault.SelfForceIx2 = 0; + todoDefault.SelfForceIx2Adj = 0; + todoDefault.SelfForceIxTotal = 1; + todoDefault.SelfForceIxTotalAdj = 0; + todoDefault.SelfForceCx1 = 0; + todoDefault.SelfForceCx2 = 0; + todoDefault.SelfForceCx2Adj = 0; + todoDefault.SelfForceCxTotal = 0; + todoDefault.SelfForceCxTotalAdj = 0; + + todoDefault.SelfSenseRaw = 1; + todoDefault.SelfSenseRawGap = 0; + todoDefault.SelfSenseIx1 = 0; + todoDefault.SelfSenseIx2 = 0; + todoDefault.SelfSenseIx2Adj = 0; + todoDefault.SelfSenseIxTotal = 1; + todoDefault.SelfSenseIxTotalAdj = 0; + todoDefault.SelfSenseCx1 = 0; + todoDefault.SelfSenseCx2 = 0; + todoDefault.SelfSenseCx2Adj = 0; + todoDefault.SelfSenseCxTotal = 0; + todoDefault.SelfSenseCxTotalAdj = 0; + + if (numberParam < 1) { + logError(1, "%s NO COMMAND SPECIFIED!!! ", tag); + logError(1, "do: 'echo [cmd_code] [args] > stm_fts_cmd' "); + logError(1, "before looking for result!\n"); + res = ERROR_OP_NOT_ALLOW; + goto END; + } + + res = fts_disableInterrupt(); + if (res < 0) { + logError(0, "%s %s: ERROR %08X\n", tag, __func__, res); + res = (res | ERROR_DISABLE_INTER); + goto END; + } + switch (functionToTest[0]) { + case CMD_READ: + if (numberParam != 4) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /** + * need to pass:cmdLength + * cmd[0]cmd[1]…cmd[cmdLength-1] + * byteToRead + */ + temp = (int)functionToTest[1]; + if (numberParam == 4 + (temp - 1) && temp != 0) { + cmd = (u8 *)kmalloc_array(temp, sizeof(u8), GFP_KERNEL); + if (!cmd) { + res = ERROR_OP_NOT_ALLOW; + break; + } + for (i = 0; i < temp; i++) + cmd[i] = functionToTest[i + 2]; + byteToRead = functionToTest[i + 2]; + readData = (u8 *)kmalloc_array(byteToRead, sizeof(u8), + GFP_KERNEL); + if (!readData) { + kfree(cmd); + res = ERROR_OP_NOT_ALLOW; + break; + } + res = fts_readCmd(cmd, temp, readData, byteToRead); + size += (byteToRead * sizeof(u8)) * 2; + kfree(cmd); + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_WRITE: + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /** + * need to pass:cmdLength + * cmd[0] cmd[1]…cmd[cmdLength-1] + */ + temp = (int)functionToTest[1]; + if (numberParam == 3 + (temp - 1) && temp != 0) { + cmd = (u8 *)kmalloc_array(temp, sizeof(u8), GFP_KERNEL); + if (!cmd) { + res = ERROR_OP_NOT_ALLOW; + break; + } + for (i = 0; i < temp; i++) + cmd[i] = functionToTest[i + 2]; + res = fts_writeCmd(cmd, temp); + kfree(cmd); + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_FWWRITE: + if (numberParam != 3) { + logError(1, "%s Wrong number parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /** + * need to pass:cmdLength + * cmd[0] cmd[1]…cmd[cmdLength-1] + */ + temp = (int)functionToTest[1]; + if (numberParam == 3 + (temp - 1) && temp != 0) { + cmd = (u8 *)kmalloc_array(temp, sizeof(u8), GFP_KERNEL); + if (!cmd) { + res = ERROR_OP_NOT_ALLOW; + break; + } + for (i = 0; i < temp; i++) + cmd[i] = functionToTest[i + 2]; + res = fts_writeFwCmd(cmd, temp); + kfree(cmd); + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READU16: + if (numberParam != 6) { + logError(1, "%s Wrong number parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /** + * need to pass: cmd addr[0] addr[1] + * byteToRead hasDummyByte + */ + byteToRead = functionToTest[4]; + readData = kmalloc_array(byteToRead, + sizeof(u8), GFP_KERNEL); + if (!readData) { + res = ERROR_OP_NOT_ALLOW; + break; + } + res = readCmdU16((u8)functionToTest[1], + (u16)((((u8) functionToTest[2] + & 0x00FF) << 8) + ((u8) functionToTest[3] + & 0x00FF)), + readData, + byteToRead, + functionToTest[5]); + size += (byteToRead * sizeof(u8)) * 2; + break; + + case CMD_READB2: + if (numberParam != 4) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*need to pass: addr[0] addr[1] byteToRead*/ + byteToRead = functionToTest[3]; + readData = kmalloc_array(byteToRead, + sizeof(u8), GFP_KERNEL); + if (!readData) { + res = ERROR_OP_NOT_ALLOW; + break; + } + res = readB2((u16)( + (((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8) functionToTest[2] & 0x00FF)), + readData, + byteToRead); + size += (byteToRead * sizeof(u8)) * 2; + break; + + case CMD_READB2U16: + if (numberParam != 4) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*need to pass: addr[0] addr[1] byteToRead*/ + byteToRead = functionToTest[3]; + readData = (u8 *)kmalloc_array(byteToRead, + sizeof(u8), GFP_KERNEL); + if (!readData) { + res = ERROR_OP_NOT_ALLOW; + break; + } + res = readB2U16((u16)((((u8)functionToTest[1] + & 0x00FF) << 8) + ((u8)functionToTest[2] + & 0x00FF)), readData, byteToRead); + size += (byteToRead * sizeof(u8)) * 2; + break; + + case CMD_POLLFOREVENT: + if (numberParam < 5) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + /** + * need to pass: eventLength event[0] event[1] + * … event[eventLength-1] timeTowait + */ + temp = (int)functionToTest[1]; + if (numberParam == 5 + (temp - 1) && temp != 0) { + readData = (u8 *)kmalloc_array(FIFO_EVENT_SIZE, + sizeof(u8), GFP_KERNEL); + if (!readData) { + res = ERROR_OP_NOT_ALLOW; + break; + } + res = pollForEvent((int *)&functionToTest[2], + temp, + readData, + ((functionToTest[temp + 2] & 0x00FF) << 8) + + (functionToTest[temp + 3] & 0x00FF)); + //pollForEvent return the number of error found + if (res >= OK) + res = OK; + size += (FIFO_EVENT_SIZE * sizeof(u8)) * 2; + byteToRead = FIFO_EVENT_SIZE; + } else { + logError(1, "%s Wrong parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_SYSTEMRESET: + res = fts_system_reset(); + break; + + case CMD_READCHIPINFO: + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*need to pass: doRequest */ + res = readChipInfo(functionToTest[1]); + break; + + /* TOUCH ENABLE/DISABLE */ + case CMD_CLEANUP: + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /* need to pass: enableTouch*/ + res = cleanUp(functionToTest[1]); + break; + + case CMD_GETFORCELEN: + /*read number Tx channels */ + temp = getForceLen(); + if (temp < OK) + res = temp; + else { + size += (1 * sizeof(u8)) * 2; + res = OK; + } + break; + + case CMD_GETSENSELEN: + /* read number Rx channels */ + temp = getSenseLen(); + if (temp < OK) + res = temp; + else { + size += (1 * sizeof(u8)) * 2; + res = OK; + } + break; + + case CMD_REQFRAME: + /* request a frame */ + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + logError(0, "%s Requesting Frame\n", tag); + res = requestFrame((u16)((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF))); + + if (res < OK) { + logError(0, "%s Err requesting frame ERROR:%02X\n", + tag, res); + } else { + logError(0, "%s Requesting Frame Finished!\n", tag); + } + break; + + case CMD_GETMSFRAME: + if (numberParam != 3) { + logError(1, "%s Wrong number of param!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + logError(0, "%s Get 1 MS Frame\n", tag); + flushFIFO(); + /** + * delete the events related to some + * touch (allow to call this function + * while touching the sreen without + * having a flooding of the FIFO) + */ + res = getMSFrame2((u16)((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF)), &frameMS); + if (res < 0) { + logError(0, "%s Err while taking MS frame:%02X\n", + tag, res); + } else { + logError(0, "%s:frame size is %d words\n", tag, res); + size = (res * sizeof(short) + 8) * 2; + /*set res to OK because if getMSFrame is*/ + /*successful res = number of words read*/ + res = OK; + print_frame_short("MS frame =", + array1dTo2d_short(frameMS.node_data, + frameMS.node_data_size, + frameMS.header.sense_node), + frameMS.header.force_node, + frameMS.header.sense_node); + } + break; + + /*read self raw*/ + case CMD_GETSSFRAME: + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + logError(0, "%s Get 1 SS Frame\n", tag); + flushFIFO(); + /** + * delete the events related to some + * touch (allow to call this function + * while touching the sreen without + * having a flooding of the FIFO) + */ + res = getSSFrame2((u16)((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF)), &frameSS); + + if (res < OK) { + logError(0, + "%s Error while taking the SS frame... ERROR %02X\n", + tag, res); + } else { + logError(0, "%s The frame size is %d words\n", + tag, res); + size = (res * sizeof(short) + 8) * 2 + 1; + + /*set res to OK because if getMSFrame is*/ + /*successful res = number of words read*/ + res = OK; + print_frame_short("SS force frame =", + array1dTo2d_short(frameSS.force_data, + frameSS.header.force_node, 1), + frameSS.header.force_node, + 1); + print_frame_short("SS sense frame =", + array1dTo2d_short(frameSS.sense_data, + frameSS.header.sense_node, + frameSS.header.sense_node), + 1, + frameSS.header.sense_node); + } + break; + + case CMD_REQCOMPDATA: + /*request comp data*/ + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + logError(0, "%s Requesting Compensation Data\n", tag); + res = requestCompensationData((u16) + ((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF))); + + if (res < OK) { + logError(0, + "%s Error requesting compensation data ERROR %02X\n", + tag, res); + } else { + logError(0, + "%s Requesting Compensation Data Finished!\n", + tag); + } + break; + + case CMD_READCOMPDATAHEAD: + /*read comp data header*/ + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + logError(0, "%s Requesting Compensation Data\n", tag); + res = requestCompensationData( + (u16) ((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF))); + if (res < OK) { + logError(0, "%s Error requesting:%02X\n", tag, res); + } else { + logError(0, + "%s Requesting Compensation Data Finished!\n", tag); + res = readCompensationDataHeader( + (u16)((((u8)functionToTest[1] & 0x00FF) << 8) + +((u8)functionToTest[2] & 0x00FF)), + &dataHead, + &address); + if (res < OK) { + logError(0, "%s Read Header ERROR:%02X\n", + tag, res); + } else { + logError(0, "%s Read Header OK!\n", tag); + size += (2 * sizeof(u8)) * 2; + } + } + break; + case CMD_READMSCOMPDATA: + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*read mutual comp data */ + logError(0, "%s Get MS Compensation Data\n", tag); + res = readMutualSenseCompensationData( + (u16)((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF)), + &compData); + + if (res < OK) { + logError(0, "%s Error reading MS compe data:%02X\n", + tag, res); + } else { + logError(0, "%s MS Compensa Reading Finished!\n", + tag); + + size = ((compData.node_data_size + 9) * sizeof(u8)) * 2; + print_frame_u8("MS Data (Cx2) = ", + array1dTo2d_u8(compData.node_data, + compData.node_data_size, + compData.header.sense_node), + compData.header.force_node, + compData.header.sense_node); + } + break; + case CMD_READSSCOMPDATA: + if (numberParam != 3) { + logError(1, "%sWrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + /*read self comp data*/ + logError(0, "%s Get SS Compensation Data...\n", tag); + res = readSelfSenseCompensationData((u16) + ((((u8)functionToTest[1] & 0x00FF) << 8) + + ((u8)functionToTest[2] & 0x00FF)), + &comData); + if (res < OK) { + logError(0, "%s Error reading SS Compensa data %02X\n", + tag, res); + } else { + logError(0, "%s SS Compensa Reading Finished!\n", tag); + size = comData.header.force_node + + comData.header.sense_node; + size = (size * 2 + 12) * sizeof(u8) * 2; + print_frame_u8("SS Data Ix2_fm = ", + array1dTo2d_u8(comData.ix2_fm, + comData.header.force_node, + comData.header.force_node), + 1, + comData.header.force_node); + print_frame_u8("SS Data Cx2_fm = ", + array1dTo2d_u8(comData.cx2_fm, + comData.header.force_node, + comData.header.force_node), + 1, + comData.header.force_node); + print_frame_u8("SS Data Ix2_sn = ", + array1dTo2d_u8(comData.ix2_sn, + comData.header.sense_node, + comData.header.sense_node), + 1, + comData.header.sense_node); + print_frame_u8("SS Data Cx2_sn = ", + array1dTo2d_u8(comData.cx2_sn, + comData.header.sense_node, + comData.header.sense_node), + 1, + comData.header.sense_node); + } + break; + + case CMD_READGNCOMPDATA: + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*read self comp data */ + logError(0, "%s Get General Compensation Data...\n", tag); + res = readGeneralCompensationData((u16) + ((((u8)functionToTest[1] + & 0x00FF) << 8) + ((u8)functionToTest[2] + & 0x00FF)), &gnData); + if (res < OK) { + logError(0, + "%s Reading General compensa data ERROR %02X\n", + tag, res); + } else { + logError(0, "%s:General compensa Reading Finished!\n", + tag); + size = (14) * sizeof(u8) * 2; + } + break; + case CMD_GETFWVER: + res = getFirmwareVersion(&fw_version, &config_id); + if (res < OK) { + logError(1, "%s Reading firmware version ERROR %02X\n", + tag, res); + } else { + logError(0, "%s getFirmware Version Finished!\n", tag); + size += (4) * sizeof(u8) * 2; + } + break; +#ifdef FTM3_CHIP + case CMD_FLASHSTATUS: + res = flash_status(); + /*return 0 = flash ready, 1 = flash busy, <0 error*/ + if (res < OK) { + logError(1, "%s Reading flash status ERROR %02X\n", + tag, res); + } else { + logError(0, "%s Flash Status: %d\n", tag, res); + size += (1 * sizeof(u8)) * 2; + /*need to store the value for further display */ + temp = res; + + /*set res =ok for returning code*/ + res = OK; + } + break; +#endif + case CMD_FLASHUNLOCK: + res = flash_unlock(); + if (res < OK) { + logError(1, "%s:Impossible Unlock Flash ERROR %02X\n", + tag, res); + } else { + logError(0, "%s Flash Unlock OK!\n", tag); + } + break; + case CMD_READFWFILE: + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*read fw file */ + logError(0, "%s Reading FW File...\n", tag); + res = readFwFile(PATH_FILE_FW, &fw, functionToTest[1]); + if (res < OK) { + logError(0, "%s Error reading FW File:%02X\n", + tag, res); + } else { + logError(0, "%s Read FW File Finished!\n", tag); + } + kfree(fw.data); + break; + case CMD_FLASHPROCEDURE: + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*flashing procedure*/ + logError(0, "%s Starting Flashing Procedure\n", tag); + res = flashProcedure(PATH_FILE_FW, + functionToTest[1], functionToTest[2]); + if (res < OK) { + logError(0, "%s During flash procedure ERROR %02X", + tag, res); + } else { + logError(0, "%s Flash Procedure Finished!\n", tag); + } + break; + + /*ITO TEST*/ + case CMD_ITOTEST: + res = production_test_ito(); + break; + + /*Initialization*/ + case CMD_INITTEST: + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*need to specify if if save value on Flash*/ + if (functionToTest[1] == 0x01) + res = production_test_initialization(); + else + res = production_test_split_initialization(false); + break; + + case CMD_MSRAWTEST: + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /* MS Raw DATA TEST*/ + /* need to specify if stopOnFail */ + res = production_test_ms_raw(LIMITS_FILE, functionToTest[1], + &todoDefault); + break; + + case CMD_MSINITDATATEST: + /*MS CX DATA TEST*/ + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*need to specify if stopOnFail*/ + res = production_test_ms_cx(LIMITS_FILE, functionToTest[1], + &todoDefault); + break; + + case CMD_SSRAWTEST: + /*SS RAW DATA TEST*/ + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*need to specify if stopOnFail*/ + res = production_test_ss_raw(LIMITS_FILE, functionToTest[1], + &todoDefault); + break; + + case CMD_SSINITDATATEST: + /*SS IX CX DATA TEST*/ + if (numberParam != 2) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*need to specify if stopOnFail*/ + res = production_test_ss_ix_cx(LIMITS_FILE, functionToTest[1], + &todoDefault); + break; + + case CMD_MAINTEST: + /*PRODUCTION TEST*/ + if (numberParam != 3) { + logError(1, "%s Wrong number of parameters!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + /*need to specify if stopOnFail and saveInit*/ + res = production_test_main(LIMITS_FILE, functionToTest[1], + functionToTest[2], &todoDefault, INIT_FIELD); + break; + + case CMD_POWERCYCLE: + res = fts_chip_powercycle(info); + break; + + default: + logError(1, "%s COMMAND ID NOT VALID!!\n", tag); + logError(1, "%s Inset a value between 00 and 1E.\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + +END: + /** + * here start the reporting phase, + * assembling the data to send in the file node + */ + all_strbuff = kmalloc(size, GFP_KERNEL); + memset(all_strbuff, 0, size); + + snprintf(buff, sizeof(buff), "%02X", 0xAA); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%08X", res); + strlcat(all_strbuff, buff, size); + + if (res >= OK) { + /*all the other cases are already*/ + /*fine printing only the res.*/ + switch (functionToTest[0]) { + case CMD_READ: + case CMD_READU16: + case CMD_READB2: + case CMD_READB2U16: + case CMD_POLLFOREVENT: + for (j = 0; j < byteToRead; j++) { + snprintf(buff, sizeof(buff), "%02X", + readData[j]); + strlcat(all_strbuff, buff, size); + } + break; + + case CMD_GETFORCELEN: + case CMD_GETSENSELEN: + case CMD_FLASHSTATUS: + snprintf(buff, sizeof(buff), "%02X", (u8)temp); + strlcat(all_strbuff, buff, size); + break; + + case CMD_GETMSFRAME: + snprintf(buff, sizeof(buff), "%02X", + (u8) frameMS.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + (u8)frameMS.header.sense_node); + strlcat(all_strbuff, buff, size); + + for (j = 0; j < frameMS.node_data_size; j++) { + snprintf(buff, sizeof(buff), "%04X", + frameMS.node_data[j]); + strlcat(all_strbuff, buff, size); + } + kfree(frameMS.node_data); + break; + + case CMD_GETSSFRAME: + snprintf(buff, sizeof(buff), "%02X", + (u8) frameSS.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + (u8)frameSS.header.sense_node); + strlcat(all_strbuff, buff, size); + + /* Copying self raw data Force */ + for (j = 0; j < frameSS.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%04X", + frameSS.force_data[j]); + strlcat(all_strbuff, buff, size); + } + + + /* Copying self raw data Sense */ + for (j = 0; j < frameSS.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%04X", + frameSS.sense_data[j]); + strlcat(all_strbuff, buff, size); + } + kfree(frameSS.force_data); + kfree(frameSS.sense_data); + break; + + case CMD_READMSCOMPDATA: + snprintf(buff, sizeof(buff), "%02X", + (u8)compData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + (u8)compData.header.sense_node); + strlcat(all_strbuff, buff, size); + + /* Cpying CX1 value */ + snprintf(buff, sizeof(buff), "%02X", + compData.cx1); + strlcat(all_strbuff, buff, size); + + /* Copying CX2 values */ + for (j = 0; j < compData.node_data_size; j++) { + snprintf(buff, sizeof(buff), "%02X", + *(compData.node_data + j)); + strlcat(all_strbuff, buff, size); + } + kfree(compData.node_data); + break; + + case CMD_READSSCOMPDATA: + snprintf(buff, sizeof(buff), "%02X", + comData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + comData.header.sense_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.f_ix1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.s_ix1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.f_cx1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", comData.s_cx1); + strlcat(all_strbuff, buff, size); + + /* Copying IX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%02X", + comData.ix2_fm[j]); + strlcat(all_strbuff, buff, size); + } + /* Copying IX2 Sense*/ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%02X", + comData.ix2_sn[j]); + strlcat(all_strbuff, buff, size); + } + /* Copying CX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(buff, sizeof(buff), "%02X", + comData.cx2_fm[j]); + strlcat(all_strbuff, buff, size); + } + + /* Copying CX2 Sense */ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(buff, sizeof(buff), "%02X", + comData.cx2_sn[j]); + strlcat(all_strbuff, buff, size); + } + + kfree(comData.ix2_fm); + kfree(comData.ix2_sn); + kfree(comData.cx2_fm); + kfree(comData.cx2_sn); + break; + + case CMD_READGNCOMPDATA: + snprintf(buff, sizeof(buff), "%02X", + gnData.header.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + gnData.header.sense_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsd_lp_timer_cal0); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsd_lp_timer_cal1); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsd_lp_timer_cal2); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsd_lp_timer_cal3); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsa_lp_timer_cal0); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + gnData.ftsa_lp_timer_cal1); + strlcat(all_strbuff, buff, size); + break; + + case CMD_GETFWVER: + snprintf(buff, sizeof(buff), "%04X", fw_version); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%04X", config_id); + strlcat(all_strbuff, buff, size); + break; + + case CMD_READCOMPDATAHEAD: + snprintf(buff, sizeof(buff), "%02X", + dataHead.force_node); + strlcat(all_strbuff, buff, size); + + snprintf(buff, sizeof(buff), "%02X", + dataHead.sense_node); + strlcat(all_strbuff, buff, size); + break; + + default: + break; + } + } + + snprintf(buff, sizeof(buff), "%02X", 0xBB); + strlcat(all_strbuff, buff, size); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + numberParam = 0; + /** + * need to reset the number of parameters + * in order to wait the next command, + * comment if you want to repeat + * the last command sent just doing a cat + */ + + kfree(readData); + kfree(all_strbuff); + return count; +} + +static DEVICE_ATTR_RW(stm_driver_test); + +static struct attribute *test_cmd_attributes[] = { + &dev_attr_stm_driver_test.attr, + NULL, +}; + +struct attribute_group test_cmd_attr_group = { + .attrs = test_cmd_attributes, +}; +#endif diff --git a/st/fts_gui.c b/st/fts_gui.c new file mode 100644 index 0000000000..4adec7d96e --- /dev/null +++ b/st/fts_gui.c @@ -0,0 +1,412 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include "fts.h" +#include "fts_lib/ftsIO.h" + +#ifdef SCRIPTLESS + +static unsigned int fts_data[CMD_RESULT_STR_LEN] = {0}; +static unsigned char fts_pAddress_i2c[CMD_RESULT_STR_LEN] = {0}; +static int byte_count_read; +static char Out_buff[TSP_BUF_SIZE]; + + +/*I2C CMd functions: functions to interface with GUI without script */ + +ssize_t fts_i2c_wr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + int i; + char buff[16]; + + memset(Out_buff, 0x00, sizeof(Out_buff)); + if (byte_count_read == 0) { + snprintf(Out_buff, sizeof(Out_buff), "{FAILED}\n"); + return snprintf(buf, TSP_BUF_SIZE, "%s\n", Out_buff); + } +#ifdef SCRIPTLESS_DEBUG + pr_err("%s:DATA READ {\n", __func__); + for (i = 0; i < byte_count_read; i++) { + pr_err(" %02X\n", (unsigned int)info->cmd_wr_result[i]); + if (i < (byte_count_read - 1)) + pr_err("\n"); + } + pr_err("}\n"); +#endif + snprintf(buff, sizeof(buff), "{"); + strlcat(Out_buff, buff, sizeof(Out_buff)); + for (i = 0; i < (byte_count_read + 2); i++) { + char temp_byte_count_read; + + if (i == 0) { + temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + + snprintf(buff, sizeof(buff), "%02X", + temp_byte_count_read); + } else if (i == 1) { + temp_byte_count_read = (byte_count_read) & 0xFF; + + snprintf(buff, sizeof(buff), "%02X", + temp_byte_count_read); + + } else { + snprintf(buff, sizeof(buff), "%02X", + info->cmd_wr_result[i-2]); + } + //snprintf(buff, sizeof(buff), "%02X", info->cmd_wr_result[i]); + strlcat(Out_buff, buff, sizeof(Out_buff)); + if (i < (byte_count_read + 1)) { + snprintf(buff, sizeof(buff), " "); + strlcat(Out_buff, buff, sizeof(Out_buff)); + } + } + snprintf(buff, sizeof(buff), "}"); + strlcat(Out_buff, buff, sizeof(Out_buff)); + return snprintf(buf, TSP_BUF_SIZE, "%s\n", Out_buff); +} + +ssize_t fts_i2c_wr_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + unsigned char pAddress[9]; + unsigned int byte_count = 0; + int i; + unsigned int data[9]; + + memset(data, 0x00, sizeof(data)); + memset(pAddress, 0x00, sizeof(pAddress)); + memset(info->cmd_wr_result, 0x00, CMD_RESULT_STR_LEN); + ret = sscanf(buf, "%x %x %x %x %x %x %x %x %x ", + (data + 8), (data), (data + 1), (data + 2), (data + 3), + (data + 4), (data + 5), (data + 6), (data + 7)); + + byte_count = data[8]; + + /** + * if(sizeof(buf) != byte_count ) + * { + * printk("%s : Byte count is wrong\n",__func__); + * return count; + * } + */ + + if (byte_count > sizeof(pAddress)) + return -EINVAL; +#ifdef SCRIPTLESS_DEBUG + + pr_err("%s: Input Data 1:\n", __func__); + + for (i = 0 ; i < byte_count; i++) { + pr_err(" %02X\n", data[i]); + pAddress[i] = (unsigned char)data[i]; + } + pr_err("\n"); +#else + for (i = 0 ; i < byte_count; i++) + pAddress[i] = (unsigned char)data[i]; +#endif + byte_count_read = (((unsigned int)data[byte_count - 2]) << 8) + | data[byte_count - 1]; + ret = fts_writeCmd(pAddress, 3); + msleep(20); + ret = fts_readCmd(&pAddress[3], (byte_count - 5), + info->cmd_wr_result, byte_count_read); +#ifdef SCRIPTLESS_DEBUG + pr_err("%s:DATA READ {\n", __func__); + for (i = 0; i < (2 + byte_count_read); i++) { + char temp_byte_count_read; + + if (i == 0) { + temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + pr_err("%02X\n", (unsigned int)temp_byte_count_read); + } else if (i == 1) { + temp_byte_count_read = (byte_count_read) & 0xFF; + pr_err("%02X\n", (unsigned int)temp_byte_count_read); + + } else { + pr_err("%02X\n", + (unsigned int)info->cmd_read_result[i - 2]); + } + + if (i < (byte_count_read + 1)) + pr_err("\n"); + + } + pr_err("}\n"); +#endif + if (ret) + dev_err(dev, "Unable to read register\n"); + return count; +} + +ssize_t fts_i2c_read_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + int i; + char buff[16]; + + memset(Out_buff, 0x00, sizeof(Out_buff)); + if (byte_count_read == 0) { + snprintf(Out_buff, sizeof(Out_buff), "{FAILED}"); + return snprintf(buf, TSP_BUF_SIZE, "%s\n", Out_buff); + } +#ifdef SCRIPTLESS_DEBUG + pr_err("%s:DATA READ {\n", __func__); + for (i = 0; i < byte_count_read; i++) { + pr_err("%02X\n", (unsigned int)info->cmd_read_result[i]); + if (i < (byte_count_read - 1)) + pr_err("\n"); + } + pr_err("}\n"); +#endif + snprintf(buff, sizeof(buff), "{"); + strlcat(Out_buff, buff, sizeof(Out_buff)); + for (i = 0; i < (byte_count_read + 2); i++) { + char temp_byte_count_read; + + if (i == 0) { + temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + snprintf(buff, sizeof(buff), "%02X", + temp_byte_count_read); + } else if (i == 1) { + temp_byte_count_read = (byte_count_read) & 0xFF; + snprintf(buff, sizeof(buff), "%02X", + temp_byte_count_read); + } else { + snprintf(buff, sizeof(buff), "%02X", + info->cmd_read_result[i - 2]); + } + strlcat(Out_buff, buff, sizeof(Out_buff)); + if (i < (byte_count_read + 1)) { + snprintf(buff, sizeof(buff), " "); + strlcat(Out_buff, buff, sizeof(Out_buff)); + } + } + snprintf(buff, sizeof(buff), "}"); + strlcat(Out_buff, buff, sizeof(Out_buff)); + + return snprintf(buf, TSP_BUF_SIZE, "%s\n", Out_buff); +} + +ssize_t fts_i2c_read_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + unsigned char pAddress[9]; + unsigned int byte_count = 0; + int i; + unsigned int data[9]; + + byte_count_read = 0; + memset(data, 0x00, sizeof(data)); + memset(pAddress, 0x00, sizeof(pAddress)); + memset(info->cmd_read_result, 0x00, CMD_RESULT_STR_LEN); + ret = sscanf(buf, "%x %x %x %x %x %x %x %x %x ", + (data + 8), (data), (data + 1), (data + 2), (data + 3), + (data + 4), (data + 5), (data + 6), (data + 7)); + + byte_count = data[8]; + + if (byte_count > 8) { +#ifdef SCRIPTLESS_DEBUG + pr_err("%s:Byte count is more than 8\n", __func__); +#endif + return count; + } + /*if(sizeof(buf) != byte_count )*/ + /*{*/ + /* printk("%s : Byte count is wrong\n",__func__);*/ + /* return count;*/ + /*}*/ +#ifdef SCRIPTLESS_DEBUG + pr_err("%s: Input Data 1:\n", __func__); + for (i = 0 ; i < byte_count; i++) { + pr_err("%02X\n", data[i]); + pAddress[i] = (unsigned char)data[i]; + } + pr_err("\n"); +#else + for (i = 0 ; i < byte_count; i++) + pAddress[i] = (unsigned char)data[i]; +#endif + byte_count_read = (((unsigned int)data[byte_count - 2]) << 8) + | data[byte_count - 1]; + ret = fts_readCmd(pAddress, (byte_count - 2), info->cmd_read_result, + byte_count_read); +#ifdef SCRIPTLESS_DEBUG + pr_err("%s:DATA READ\n{\n", __func__); + for (i = 0; i < (byte_count_read + 2); i++) { + char temp_byte_count_read; + + if (i == 0) { + temp_byte_count_read = (byte_count_read >> 8) & 0xFF; + + pr_err("%02X\n", (unsigned int)temp_byte_count_read); + } else if (i == 1) { + temp_byte_count_read = (byte_count_read) & 0xFF; + + pr_err("%02X\n", (unsigned int)temp_byte_count_read); + + } else { + pr_err("%02X\n", + (unsigned int)info->cmd_read_result[i - 2]); + } + if (i < (byte_count_read + 1)) + pr_err("\n"); + } + pr_err("}\n"); +#endif + if (ret) + dev_err(dev, "Unable to read register\n"); + return count; +} + + +ssize_t fts_i2c_write_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + + return snprintf(buf, TSP_BUF_SIZE, "%s", info->cmd_write_result); + +} + +ssize_t fts_i2c_write_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info = i2c_get_clientdata(client); + unsigned int byte_count = 0; + int i; + unsigned int *data = &fts_data[0]; + + memset(fts_data, 0x00, sizeof(fts_data)); + memset(fts_pAddress_i2c, 0x00, sizeof(fts_pAddress_i2c)); + memset(info->cmd_write_result, 0x00, sizeof(info->cmd_write_result)); + ret = sscanf(buf, "%x %x", data, (data + 1)); + if (ret != 2) + return -EINVAL; + byte_count = data[0] << 8 | data[1]; + + if (byte_count <= sizeof(fts_pAddress_i2c)) { + for (i = 0; i < (byte_count); i++) { + ret = sscanf(&buf[3 * (i + 2)], "%x ", (data + i)); + if (ret != 1) + return -EINVAL; + } + } else { +#ifdef SCRIPTLESS_DEBUG + pr_err("%s:message size is > allowed limit of 512 bytes\n", + __func__); +#endif + snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), + "{Write NOT OK}\n"); + return -EINVAL; + } +#ifdef SCRIPTLESS_DEBUG + pr_err("\n"); + pr_err("%s:Byte_count = %02d|Count = %02d |size of buf:%02d\n", + __func__, byte_count, (int)count, (int)sizeof(buf)); + pr_err("%s: Input Data 1:\n", __func__); + for (i = 0 ; i < byte_count; i++) { + pr_err(" %02X\n", data[i]); + fts_pAddress_i2c[i] = (unsigned char)data[i]; + } + pr_err("\n"); +#else + for (i = 0 ; i < byte_count; i++) + fts_pAddress_i2c[i] = (unsigned char)data[i]; +#endif + if ((fts_pAddress_i2c[0] == 0xb3) && (fts_pAddress_i2c[3] == 0xb1)) { + ret = fts_writeCmd(fts_pAddress_i2c, 3); + msleep(20); + ret = fts_writeCmd(&fts_pAddress_i2c[3], byte_count-3); + } else + ret = fts_writeCmd(fts_pAddress_i2c, byte_count); + +#ifdef SCRIPTLESS_DEBUG + pr_err("%s:DATA :\n", __func__); + for (i = 0; i < byte_count; i++) + pr_err(" %02X\n", (unsigned int)fts_pAddress_i2c[i]); + pr_err(" byte_count: %02X\n", byte_count); +#endif + if (ret < 0) { + dev_err(dev, "{Write NOT OK}\n"); + snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), + "{Write NOT OK}\n"); + } else { + snprintf(info->cmd_write_result, sizeof(info->cmd_write_result), + "{Write OK}\n"); +#ifdef SCRIPTLESS_DEBUG + pr_err("%s : {Write OK}\n", __func__); +#endif + } + return count; +} + +static DEVICE_ATTR_RW(fts_i2c_read); +static DEVICE_ATTR_RW(fts_i2c_wr); +static DEVICE_ATTR_RW(fts_i2c_write); + +static struct attribute *i2c_cmd_attributes[] = { + &dev_attr_fts_i2c_read.attr, + &dev_attr_fts_i2c_wr.attr, + &dev_attr_fts_i2c_write.attr, + NULL, +}; + +struct attribute_group i2c_cmd_attr_group = { + .attrs = i2c_cmd_attributes, +}; + + #endif diff --git a/st/fts_lib/ftsCompensation.c b/st/fts_lib/ftsCompensation.c new file mode 100644 index 0000000000..2bffaa9060 --- /dev/null +++ b/st/fts_lib/ftsCompensation.c @@ -0,0 +1,744 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS functions for getting Initialization Data * + * * + ************************************************************************** + ************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +#include "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTool.h" + + +static char tag[8] = "[ FTS ]\0"; +struct chipInfo ftsInfo; + +int requestCompensationData(u16 type) +{ + int retry = 0; + int ret; + char *temp = NULL; + u16 answer; + + int event_to_search[3]; + u8 readEvent[FIFO_EVENT_SIZE]; + + u8 cmd[3] = { FTS_CMD_REQU_COMP_DATA, 0x00, 0x00}; + /* B8 is the command for asking compensation data*/ + u16ToU8(type, &cmd[1]); + + event_to_search[0] = (int)EVENTID_COMP_DATA_READ; + event_to_search[1] = cmd[1]; + event_to_search[2] = cmd[2]; + + while (retry < COMP_DATA_READ_RETRY) { + temp = printHex("Command = ", cmd, 3); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + ret = fts_writeFwCmd(cmd, 3); + /*send the request to the chip to load*/ + /*in memory the Compensation Data*/ + if (ret < OK) { + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + ret = pollForEvent(event_to_search, 3, readEvent, + TIMEOUT_REQU_COMP_DATA); + if (ret < OK) { + logError(0, "%s Event did not Found at %d attemp!\n", + tag, retry + 1); + retry += 1; + } else { + retry = 0; + break; + } + } + + if (retry == COMP_DATA_READ_RETRY) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + + u8ToU16_le(&readEvent[1], &answer); + + if (answer == type) + return OK; + + logError(1, "%sThe event found has a different type of ", tag); + logError(1, "Compensation data %02X\n", ERROR_DIFF_COMP_TYPE); + return ERROR_DIFF_COMP_TYPE; + +} + +int readCompensationDataHeader(u16 type, struct DataHeader *header, + u16 *address) +{ + u16 offset = ADDR_FRAMEBUFFER_DATA; + u16 answer; + u8 data[COMP_DATA_HEADER]; + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, offset, data, COMP_DATA_HEADER, + DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; + } + logError(0, "%s Read Data Header done!\n", tag); + + if (data[0] != HEADER_SIGNATURE) { + logError(1, "%s %s:%02X The Header Signature was wrong!", + tag, __func__, ERROR_WRONG_COMP_SIGN); + logError(1, "%02X != %02X\n", data[0], HEADER_SIGNATURE); + + return ERROR_WRONG_COMP_SIGN; + } + + + u8ToU16_le(&data[1], &answer); + + if (answer != type) { + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_DIFF_COMP_TYPE); + + return ERROR_DIFF_COMP_TYPE; + } + + logError(0, "%s Type of Compensation data OK!\n", tag); + + header->type = type; + header->force_node = (int)data[4]; + header->sense_node = (int)data[5]; + + *address = offset + COMP_DATA_HEADER; + + return OK; + +} + +int readMutualSenseGlobalData(u16 *address, struct MutualSenseData *global) +{ + + u8 data[COMP_DATA_GLOBAL]; + + logError(0, "%s Address for Global data= %02X\n", tag, *address); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, *address, data, + COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); + + return ERROR_I2C_R; + } + logError(0, "%s Global data Read!\n", tag); + + global->tuning_ver = data[0]; + global->cx1 = data[1]; + + logError(0, "%s tuning_ver = %d CX1 = %d\n", + tag, global->tuning_ver, global->cx1); + + *address += COMP_DATA_GLOBAL; + return OK; +} + + + +int readMutualSenseNodeData(u16 address, struct MutualSenseData *node) +{ + int size = node->header.force_node*node->header.sense_node; + + logError(0, "%s Address for Node data = %02X\n", tag, address); + + node->node_data = (u8 *)kmalloc_array(size, (sizeof(u8)), GFP_KERNEL); + + if (node->node_data == NULL) { + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + logError(0, "%s Node Data to read %d bytes\n", tag, size); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, node->node_data, + size, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s:ERROR %02X\n", tag, __func__, ERROR_I2C_R); + kfree(node->node_data); + return ERROR_I2C_R; + } + node->node_data_size = size; + + logError(0, "%s Read node data ok!\n", tag); + + return size; +} + + +int readMutualSenseCompensationData(u16 type, struct MutualSenseData *data) +{ + int ret; + u16 address; + + data->node_data = NULL; + + if (!(type == MS_TOUCH_ACTIVE || type == MS_TOUCH_LOW_POWER + || type == MS_TOUCH_ULTRA_LOW_POWER || type == MS_KEY)) { + logError(1, "%s %s: Choose a MS type of compensation data ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestCompensationData(type); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_REQU_COMP_DATA); + return (ret|ERROR_REQU_COMP_DATA); + } + + ret = readCompensationDataHeader(type, &(data->header), &address); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_HEADER); + return (ret | ERROR_COMP_DATA_HEADER); + } + + ret = readMutualSenseGlobalData(&address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_GLOBAL); + return (ret|ERROR_COMP_DATA_GLOBAL); + } + + ret = readMutualSenseNodeData(address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_NODE); + return (ret | ERROR_COMP_DATA_NODE); + } + + return OK; +} + + +int readSelfSenseGlobalData(u16 *address, struct SelfSenseData *global) +{ + u8 data[COMP_DATA_GLOBAL]; + + logError(0, "%s Address for Global data= %02X\n", tag, *address); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, *address, data, + COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; + } + + logError(0, "%s Global data Read!\n", tag); + + global->tuning_ver = data[0]; + global->f_ix1 = data[1]; + global->s_ix1 = data[2]; + global->f_cx1 = data[3]; + global->s_cx1 = data[4]; + global->f_max_n = data[5]; + global->s_max_n = data[6]; + + logError(0, + "%stuning_ver = %df_ix1 = %ds_ix1 = %df_cx1 = %d s_cx1 = %d\n", + tag, global->tuning_ver, global->f_ix1, + global->s_ix1, global->f_cx1, global->s_cx1); + logError(0, "%s max_n = %d s_max_n = %d\n", + tag, global->f_max_n, global->s_max_n); + + *address += COMP_DATA_GLOBAL; + return OK; +} + +int readSelfSenseNodeData(u16 address, struct SelfSenseData *node) +{ + int size = node->header.force_node * 2 + node->header.sense_node * 2; + u8 *data; + + node->ix2_fm = (u8 *)kmalloc_array(node->header.force_node, + sizeof(u8), GFP_KERNEL); + if (node->ix2_fm == NULL) { + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + node->cx2_fm = (u8 *)kmalloc_array(node->header.force_node, + sizeof(u8), GFP_KERNEL); + if (node->cx2_fm == NULL) { + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); + kfree(node->ix2_fm); + return ERROR_ALLOC; + } + node->ix2_sn = (u8 *)kmalloc_array(node->header.sense_node, + sizeof(u8), GFP_KERNEL); + if (node->ix2_sn == NULL) { + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); + kfree(node->ix2_fm); + kfree(node->cx2_fm); + return ERROR_ALLOC; + } + node->cx2_sn = (u8 *)kmalloc_array(node->header.sense_node, + sizeof(u8), GFP_KERNEL); + if (node->cx2_sn == NULL) { + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); + kfree(node->ix2_fm); + kfree(node->cx2_fm); + kfree(node->ix2_sn); + return ERROR_ALLOC; + } + + logError(0, "%s Address for Node data = %02X\n", tag, address); + + logError(0, "%s Node Data to read %d bytes\n", tag, size); + + data = (u8 *)kmalloc_array(size, sizeof(u8), GFP_KERNEL); + if (data == NULL) { + logError(1, "%s %s: ERROR %02X", tag, __func__, ERROR_ALLOC); + kfree(node->ix2_fm); + kfree(node->cx2_fm); + kfree(node->ix2_sn); + kfree(node->cx2_sn); + return ERROR_ALLOC; + } + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, data, size, + DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); + kfree(node->ix2_fm); + kfree(node->cx2_fm); + kfree(node->ix2_sn); + kfree(node->cx2_sn); + kfree(data); + return ERROR_I2C_R; + } + + logError(0, "%s Read node data ok!\n", tag); + + memcpy(node->ix2_fm, data, node->header.force_node); + memcpy(node->ix2_sn, &data[node->header.force_node], + node->header.sense_node); + memcpy(node->cx2_fm, + &data[node->header.force_node + node->header.sense_node], + node->header.force_node); + memcpy(node->cx2_sn, + &data[node->header.force_node * 2 + node->header.sense_node], + node->header.sense_node); + + kfree(data); + + return OK; +} + +int readSelfSenseCompensationData(u16 type, struct SelfSenseData *data) +{ + + int ret; + u16 address; + + data->ix2_fm = NULL; + data->cx2_fm = NULL; + data->ix2_sn = NULL; + data->cx2_sn = NULL; + + if (!(type == SS_TOUCH || type == SS_KEY || type == SS_HOVER + || type == SS_PROXIMITY)) { + logError(1, "%s %s:Choose a SS type of compensation data ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestCompensationData(type); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_REQU_COMP_DATA); + return (ret | ERROR_REQU_COMP_DATA); + } + + ret = readCompensationDataHeader(type, &(data->header), &address); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_HEADER); + return (ret|ERROR_COMP_DATA_HEADER); + } + + ret = readSelfSenseGlobalData(&address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_GLOBAL); + return (ret | ERROR_COMP_DATA_GLOBAL); + } + + ret = readSelfSenseNodeData(address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_NODE); + return (ret | ERROR_COMP_DATA_NODE); + } + + return OK; +} + + +int readGeneralGlobalData(u16 address, struct GeneralData *global) +{ + u8 data[COMP_DATA_GLOBAL]; + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, data, COMP_DATA_GLOBAL, + DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; + } + + global->ftsd_lp_timer_cal0 = data[0]; + global->ftsd_lp_timer_cal1 = data[1]; + global->ftsd_lp_timer_cal2 = data[2]; + global->ftsd_lp_timer_cal3 = data[3]; + global->ftsa_lp_timer_cal0 = data[4]; + global->ftsa_lp_timer_cal1 = data[5]; + + return OK; +} + + +int readGeneralCompensationData(u16 type, struct GeneralData *data) +{ + int ret; + u16 address; + + if (!(type == GENERAL_TUNING)) { + logError(1, "%s %s:Choose a GENERAL type of compensation data ", + tag); + logError(1, "ERROR %02X\n", ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestCompensationData(type); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_REQU_COMP_DATA); + return ERROR_REQU_COMP_DATA; + } + + ret = readCompensationDataHeader(type, &(data->header), &address); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_HEADER); + return ERROR_COMP_DATA_HEADER; + } + + ret = readGeneralGlobalData(address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_GLOBAL); + return ERROR_COMP_DATA_GLOBAL; + } + + return OK; + +} + + +int defaultChipInfo(int i2cError) +{ + int i; + + logError(0, "%s Setting default Chip Info...\n", tag); + ftsInfo.u32_echoEn = 0x00000000; + ftsInfo.u8_msScrConfigTuneVer = 0; + ftsInfo.u8_ssTchConfigTuneVer = 0; + ftsInfo.u8_msScrCxmemTuneVer = 0; + ftsInfo.u8_ssTchCxmemTuneVer = 0; + if (i2cError == 1) { + ftsInfo.u16_fwVer = 0xFFFF; + ftsInfo.u16_cfgId = 0xFFFF; + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) + ftsInfo.u8_extReleaseInfo[i] = 0xFF; + } else { + ftsInfo.u16_fwVer = 0x0000; + ftsInfo.u16_cfgId = 0x0000; + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) + ftsInfo.u8_extReleaseInfo[i] = 0x00; + } + ftsInfo.u32_mpPassFlag = INIT_FIELD; + ftsInfo.u16_errOffset = INVALID_ERROR_OFFS; + logError(0, "%s default Chip Info DONE!\n", tag); + return OK; +} + +int readChipInfo(int doRequest) +{ + int ret, i; + u16 answer; + u8 data[CHIP_INFO_SIZE + 3]; + /*+3 because need to read all the field of*/ + /*the struct plus the signature and 2 address bytes*/ + int index = 0; + + logError(0, "%s Starting Read Chip Info...\n", tag); + if (doRequest == 1) { + ret = requestCompensationData(CHIP_INFO); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_REQU_COMP_DATA); + ret = (ret | ERROR_REQU_COMP_DATA); + goto FAIL; + } + } + + logError(0, "%s Byte to read = %d bytes\n", tag, CHIP_INFO_SIZE + 3); + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, ADDR_FRAMEBUFFER_DATA, data, + CHIP_INFO_SIZE + 3, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); + ret = ERROR_I2C_R; + goto FAIL; + } + + logError(0, "%s Read data ok!\n", tag); + + logError(0, "%s Starting parsing of data...\n", tag); + + if (data[0] != HEADER_SIGNATURE) { + logError(1, "%s %s:ERROR ", tag, __func__); + logError(1, "%02X The Header Signature is wrong!%02X != %02X\n", + ERROR_WRONG_COMP_SIGN, data[0], HEADER_SIGNATURE); + ret = ERROR_WRONG_COMP_SIGN; + goto FAIL; + } + + u8ToU16_le(&data[1], &answer); + + if (answer != CHIP_INFO) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_DIFF_COMP_TYPE); + ret = ERROR_DIFF_COMP_TYPE; + goto FAIL; + } + + index += 3; + ftsInfo.u8_loadCnt = data[index++]; + ftsInfo.u8_infoVer = data[index++]; + u8ToU16(&data[index], &ftsInfo.u16_ftsdId); + index += 2; + ftsInfo.u8_ftsdVer = data[index++]; + ftsInfo.u8_ftsaId = data[index++]; + ftsInfo.u8_ftsaVer = data[index++]; + ftsInfo.u8_tchRptVer = data[index++]; + + logError(0, "%s External Release = ", tag); + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) { + ftsInfo.u8_extReleaseInfo[i] = data[index++]; + logError(0, "%02X ", ftsInfo.u8_extReleaseInfo[i]); + } + logError(0, "\n"); + + for (i = 0; i < sizeof(ftsInfo.u8_custInfo); i++) + ftsInfo.u8_custInfo[i] = data[index++]; + + u8ToU16(&data[index], &ftsInfo.u16_fwVer); + index += 2; + logError(1, "%s FW VERSION = %04X\n", tag, ftsInfo.u16_fwVer); + + u8ToU16(&data[index], &ftsInfo.u16_cfgId); + index += 2; + logError(1, "%s CONFIG ID = %04X\n", tag, ftsInfo.u16_cfgId); + + ftsInfo.u32_projId = ((data[index + 3] & 0x000000FF) << 24) + + ((data[index + 2] & 0x000000FF) << 16) + + ((data[index + 1] & 0x000000FF) << 8) + + (data[index] & 0x000000FF); + index += 4; + + u8ToU16(&data[index], &ftsInfo.u16_scrXRes); + index += 2; + + u8ToU16(&data[index], &ftsInfo.u16_scrYRes); + index += 2; + + ftsInfo.u8_scrForceLen = data[index++]; + logError(0, "%s Force Len = %d\n", tag, ftsInfo.u8_scrForceLen); + + ftsInfo.u8_scrSenseLen = data[index++]; + logError(0, "%s Sense Len = %d\n", tag, ftsInfo.u8_scrSenseLen); + + for (i = 0; i < 8; i++) + ftsInfo.u64_scrForceEn[i] = data[index++]; + + for (i = 0; i < 8; i++) + ftsInfo.u64_scrSenseEn[i] = data[index++]; + + ftsInfo.u8_msKeyLen = data[index++]; + logError(0, "%s MS Key Len = %d\n", tag, ftsInfo.u8_msKeyLen); + + for (i = 0; i < 8; i++) + ftsInfo.u64_msKeyForceEn[i] = data[index++]; + + for (i = 0; i < 8; i++) + ftsInfo.u64_msKeySenseEn[i] = data[index++]; + + ftsInfo.u8_ssKeyLen = data[index++]; + logError(0, "%s SS Key Len = %d\n", tag, ftsInfo.u8_ssKeyLen); + + for (i = 0; i < 8; i++) + ftsInfo.u64_ssKeyForceEn[i] = data[index++]; + + for (i = 0; i < 8; i++) + ftsInfo.u64_ssKeySenseEn[i] = data[index++]; + + ftsInfo.u8_frcTchXLen = data[index++]; + + ftsInfo.u8_frcTchYLen = data[index++]; + + for (i = 0; i < 8; i++) + ftsInfo.u64_frcTchForceEn[i] = data[index++]; + + for (i = 0; i < 8; i++) + ftsInfo.u64_frcTchSenseEn[i] = data[index++]; + + + ftsInfo.u8_msScrConfigTuneVer = data[index++]; + logError(0, "%s CFG MS TUNING VERSION = %02X\n", + tag, ftsInfo.u8_msScrConfigTuneVer); + ftsInfo.u8_msScrLpConfigTuneVer = data[index++]; + ftsInfo.u8_msScrHwulpConfigTuneVer = data[index++]; + ftsInfo.u8_msKeyConfigTuneVer = data[index++]; + ftsInfo.u8_ssTchConfigTuneVer = data[index++]; + logError(0, "%s CFG SS TUNING VERSION = %02X\n", + tag, ftsInfo.u8_ssTchConfigTuneVer); + ftsInfo.u8_ssKeyConfigTuneVer = data[index++]; + ftsInfo.u8_ssHvrConfigTuneVer = data[index++]; + ftsInfo.u8_frcTchConfigTuneVer = data[index++]; + ftsInfo.u8_msScrCxmemTuneVer = data[index++]; + logError(0, "%s CX MS TUNING VERSION = %02X\n", + tag, ftsInfo.u8_msScrCxmemTuneVer); + ftsInfo.u8_msScrLpCxmemTuneVer = data[index++]; + ftsInfo.u8_msScrHwulpCxmemTuneVer = data[index++]; + ftsInfo.u8_msKeyCxmemTuneVer = data[index++]; + ftsInfo.u8_ssTchCxmemTuneVer = data[index++]; + logError(0, "%s CX SS TUNING VERSION = %02X\n", + tag, ftsInfo.u8_ssTchCxmemTuneVer); + ftsInfo.u8_ssKeyCxmemTuneVer = data[index++]; + ftsInfo.u8_ssHvrCxmemTuneVer = data[index++]; + ftsInfo.u8_frcTchCxmemTuneVer = data[index++]; + ftsInfo.u32_mpPassFlag = ((data[index + 3] & 0x000000FF) << 24) + + ((data[index + 2] & 0x000000FF) << 16) + + ((data[index + 1] & 0x000000FF) << 8) + + (data[index] & 0x000000FF); + index += 4; + logError(0, "%s MP SIGNATURE = %08X\n", tag, ftsInfo.u32_mpPassFlag); + ftsInfo.u32_featEn = ((data[index + 3] & 0x000000FF) << 24) + + ((data[index + 2] & 0x000000FF) << 16) + + ((data[index + 1] & 0x000000FF) << 8) + + (data[index] & 0x000000FF); + index += 4; + ftsInfo.u32_echoEn = ((data[index + 3] & 0x000000FF) << 24) + + ((data[index + 2] & 0x000000FF) << 16) + + ((data[index + 1] & 0x000000FF) << 8) + + (data[index] & 0x000000FF); + index += 4; + logError(0, "%s FEATURES = %08X\n", tag, ftsInfo.u32_echoEn); + ftsInfo.u8_sideTchConfigTuneVer = data[index++]; + ftsInfo.u8_sideTchCxmemTuneVer = data[index++]; + ftsInfo.u8_sideTchForceLen = data[index++]; + logError(0, "%s Side Touch Force Len = %d\n", + tag, ftsInfo.u8_sideTchForceLen); + ftsInfo.u8_sideTchSenseLen = data[index++]; + logError(0, "%s Side Touch Sense Len = %d\n", + tag, ftsInfo.u8_sideTchSenseLen); + for (i = 0; i < 8; i++) + ftsInfo.u64_sideTchForceEn[i] = data[index++]; + for (i = 0; i < 8; i++) + ftsInfo.u64_sideTchSenseEn[i] = data[index++]; + ftsInfo.u8_errSign = data[index++]; + logError(0, "%s ERROR SIGNATURE = %02X\n", tag, ftsInfo.u8_errSign); + if (ftsInfo.u8_errSign == ERROR_SIGN_HEAD) { + logError(0, "%s Correct Error Signature found!\n", tag); + u8ToU16(&data[index], &ftsInfo.u16_errOffset); + } else { + logError(1, "%s Error Signature NOT FOUND!\n", tag); + ftsInfo.u16_errOffset = INVALID_ERROR_OFFS; + } + logError(0, "%s ERROR OFFSET = %04X\n", tag, ftsInfo.u16_errOffset); + index += 2; + logError(0, "%s Parsed %d bytes!\n", tag, index); + + + if (index != CHIP_INFO_SIZE + 3) { + logError(1, "%s %s: index = %d different from %d ERROR %02X\n", + tag, __func__, index, CHIP_INFO_SIZE + 3, + ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + logError(0, "%s Chip Info Read DONE!\n", tag); + return OK; + +FAIL: + defaultChipInfo(isI2cError(ret)); + return ret; +} + diff --git a/st/fts_lib/ftsCompensation.h b/st/fts_lib/ftsCompensation.h new file mode 100644 index 0000000000..fcd1cccfe5 --- /dev/null +++ b/st/fts_lib/ftsCompensation.h @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for Flashing the IC * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#ifndef __FTS_COMPENSATION_H +#define __FTS_COMPENSATION_H + + +#include "ftsCrossCompile.h" +#include "ftsSoftware.h" + + +#define COMP_DATA_READ_RETRY 2 + +//Bytes dimension of Compensation Data Format + +#define COMP_DATA_HEADER 8 +#define COMP_DATA_GLOBAL 8 + + +#define HEADER_SIGNATURE 0xA5 +#define INVALID_ERROR_OFFS 0xFFFF + +//Possible Compensation/Frame Data Type +#define GENERAL_TUNING 0x0100 +#define MS_TOUCH_ACTIVE 0x0200 +#define MS_TOUCH_LOW_POWER 0x0400 +#define MS_TOUCH_ULTRA_LOW_POWER 0x0800 +#define MS_KEY 0x1000 +#define SS_TOUCH 0x2000 +#define SS_KEY 0x4000 +#define SS_HOVER 0x8000 +#define SS_PROXIMITY 0x0001 +#define CHIP_INFO 0xFFFF + + +#define TIMEOUT_REQU_COMP_DATA 1000 //ms + +//CHIP INFO +#define CHIP_INFO_SIZE 161 +/*bytes to read from framebuffer (exclude the signature and the type*/ +/*because already checked during the reading)*/ +#define EXTERNAL_RELEASE_INFO_SIZE 8 //bytes + +struct DataHeader { + int force_node, sense_node; + u16 type; +}; + + +struct MutualSenseData { + struct DataHeader header; + u8 tuning_ver; + u8 cx1; + u8 *node_data; + int node_data_size; +}; + + +struct SelfSenseData { + struct DataHeader header; + u8 tuning_ver; + u8 f_ix1, s_ix1; + u8 f_cx1, s_cx1; + u8 f_max_n, s_max_n; + + u8 *ix2_fm; + u8 *ix2_sn; + u8 *cx2_fm; + u8 *cx2_sn; +}; + + +struct GeneralData { + struct DataHeader header; + u8 ftsd_lp_timer_cal0; + u8 ftsd_lp_timer_cal1; + u8 ftsd_lp_timer_cal2; + + u8 ftsd_lp_timer_cal3; + u8 ftsa_lp_timer_cal0; + u8 ftsa_lp_timer_cal1; +}; + +struct chipInfo { + u8 u8_loadCnt; ///< 03 - Load Counter + u8 u8_infoVer; ///< 04 - New chip info version + u16 u16_ftsdId; ///< 05 - FTSD ID + u8 u8_ftsdVer; ///< 07 - FTSD version + u8 u8_ftsaId; ///< 08 - FTSA ID + u8 u8_ftsaVer; ///< 09 - FTSA version + u8 u8_tchRptVer; ///< 0A - Touch report version (e.g. ST, Samsung etc) + + ///< 0B - External release information + u8 u8_extReleaseInfo[EXTERNAL_RELEASE_INFO_SIZE]; + u8 u8_custInfo[12]; ///< 13 - Customer information + u16 u16_fwVer; ///< 1F - Firmware version + u16 u16_cfgId; ///< 21 - Configuration ID + u32 u32_projId; ///< 23 - Project ID + u16 u16_scrXRes; ///< 27 - X resolution on main screen + u16 u16_scrYRes; ///< 29 - Y resolution on main screen + u8 u8_scrForceLen; ///< 2B - Number of force channel on main screen + u8 u8_scrSenseLen; ///< 2C - Number of sense channel on main screen + u8 u64_scrForceEn[8]; ///< 2D - Force channel enabled on main screen + u8 u64_scrSenseEn[8]; ///< 35 - Sense channel enabled on main screen + u8 u8_msKeyLen; ///< 3D - Number of MS Key channel + u8 u64_msKeyForceEn[8]; ///< 3E - MS Key force channel enable + u8 u64_msKeySenseEn[8]; ///< 46 - MS Key sense channel enable + u8 u8_ssKeyLen; ///< 4E - Number of SS Key channel + u8 u64_ssKeyForceEn[8]; ///< 4F - SS Key force channel enable + u8 u64_ssKeySenseEn[8]; ///< 57 - SS Key sense channel enable + u8 u8_frcTchXLen; ///< 5F - Number of force touch force channel + u8 u8_frcTchYLen; ///< 60 - Number of force touch sense channel + u8 u64_frcTchForceEn[8];///< 61 - Force touch force channel enable + u8 u64_frcTchSenseEn[8];///< 69 - Force touch sense channel enable + u8 u8_msScrConfigTuneVer; ///< 71 - MS screen tuning version in config + + ///< 72 - MS screen LP mode tuning version in config + u8 u8_msScrLpConfigTuneVer; + + ///< 73 - MS screen ultra low power mode tuning version in config + u8 u8_msScrHwulpConfigTuneVer; + u8 u8_msKeyConfigTuneVer; ///< 74 - MS Key tuning version in config + u8 u8_ssTchConfigTuneVer; ///< 75 - SS touch tuning version in config + u8 u8_ssKeyConfigTuneVer; ///< 76 - SS Key tuning version in config + u8 u8_ssHvrConfigTuneVer; ///< 77 - SS hover tuning version in config + + ///< 78 - Force touch tuning version in config + u8 u8_frcTchConfigTuneVer; + u8 u8_msScrCxmemTuneVer; ///< 79 - MS screen tuning version in cxmem + + ///< 7A - MS screen LP mode tuning version in cxmem + u8 u8_msScrLpCxmemTuneVer; + + ///< 7B - MS screen ultra low power mode tuning version in cxmem + u8 u8_msScrHwulpCxmemTuneVer; + u8 u8_msKeyCxmemTuneVer; ///< 7C - MS Key tuning version in cxmem + u8 u8_ssTchCxmemTuneVer; ///< 7D - SS touch tuning version in cxmem + u8 u8_ssKeyCxmemTuneVer; ///< 7E - SS Key tuning version in cxmem + u8 u8_ssHvrCxmemTuneVer; ///< 7F - SS hover tuning version in cxmem + + ///< 80 - Force touch tuning version in cxmem + u8 u8_frcTchCxmemTuneVer; + u32 u32_mpPassFlag; ///< 81 - Mass production pass flag + u32 u32_featEn; ///< 85 - Supported features + + ///< 89 - enable of particular features: first bit is Echo Enables + u32 u32_echoEn; + + ///< 8D - Side Touch tuning version in config + u8 u8_sideTchConfigTuneVer; + u8 u8_sideTchCxmemTuneVer; ///< 8E - Side Touch tuning version in cxmem + u8 u8_sideTchForceLen; ///< 8F - Number of force channel on side touch + u8 u8_sideTchSenseLen; ///< 90 - Number of sense channel on side touch + u8 u64_sideTchForceEn[8];///< 91 - Side touch force channel enable + u8 u64_sideTchSenseEn[8];///< 99 - Side touch sense channel enable + u8 u8_errSign; ///< A1 - Signature for error field + u16 u16_errOffset; ///< A2 - Error Offset +}; + +int requestCompensationData(u16 type); +int readCompensationDataHeader(u16 type, struct DataHeader *header, + u16 *address); +int readMutualSenseGlobalData(u16 *address, struct MutualSenseData *global); +int readMutualSenseNodeData(u16 address, struct MutualSenseData *node); +int readMutualSenseCompensationData(u16 type, struct MutualSenseData *data); +int readSelfSenseGlobalData(u16 *address, struct SelfSenseData *global); +int readSelfSenseNodeData(u16 address, struct SelfSenseData *node); +int readSelfSenseCompensationData(u16 type, struct SelfSenseData *data); +int readGeneralGlobalData(u16 address, struct GeneralData *global); +int readGeneralCompensationData(u16 type, struct GeneralData *data); +int defaultChipInfo(int i2cError); +int readChipInfo(int doRequest); + +#endif diff --git a/st/fts_lib/ftsCrossCompile.c b/st/fts_lib/ftsCrossCompile.c new file mode 100644 index 0000000000..e44899c0a6 --- /dev/null +++ b/st/fts_lib/ftsCrossCompile.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Cross Compile * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include + +#include "ftsCrossCompile.h" +#include "ftsError.h" + +void *stmalloc(size_t size) +{ + return kmalloc(size, GFP_KERNEL); +} + +void stfree(void *ptr) +{ + kfree(ptr); +} diff --git a/st/fts_lib/ftsCrossCompile.h b/st/fts_lib/ftsCrossCompile.h new file mode 100644 index 0000000000..15f51b3d72 --- /dev/null +++ b/st/fts_lib/ftsCrossCompile.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS cross compile * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#ifndef __FTS_CROSS_COMPILE_H +#define __FTS_CROSS_COMPILE_H + +//#define NDK +//#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include + +void *stmalloc(size_t size); +void stfree(void *ptr); + +#endif diff --git a/st/fts_lib/ftsError.c b/st/fts_lib/ftsError.c new file mode 100644 index 0000000000..80919322e2 --- /dev/null +++ b/st/fts_lib/ftsError.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS error/info kernel log reporting * + * * + ************************************************************************** + ************************************************************************** + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include + +#include +#include +#include + +#include "../fts.h" +#include "ftsCrossCompile.h" +#include "ftsError.h" +#include "ftsIO.h" +#include "ftsTool.h" +#include "ftsCompensation.h" + +static char tag[8] = "[ FTS ]\0"; + + +void logError(int force, const char *msg, ...) +{ + + if (force == 1 +#ifdef DEBUG + || 1 +#endif + ) { + va_list args; + + va_start(args, msg); + vprintk(msg, args); + va_end(args); + } +} + +int isI2cError(int error) +{ + if (((error & 0x000000FF) >= (ERROR_I2C_R & 0x000000FF)) + && ((error & 0x000000FF) <= (ERROR_I2C_O & 0x000000FF))) + return 1; + else + return 0; +} + +int dumpErrorInfo(void) +{ + int ret, i; + u8 data[ERROR_INFO_SIZE] = {0}; + u32 sign = 0; + + logError(0, "%s %s: Starting dump of error info...\n", tag, __func__); + if (ftsInfo.u16_errOffset == INVALID_ERROR_OFFS) { + logError(1, "%s %s: Invalid error offset ERROR %02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = readCmdU16(FTS_CMD_FRAMEBUFFER_R, ftsInfo.u16_errOffset, + data, ERROR_INFO_SIZE, DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, "%s %s: reading data ERROR %02X\n", + tag, __func__, ret); + return ret; + } + logError(0, "%s %s: Error Info =\n", tag, __func__); + u8ToU32(data, &sign); + if (sign != ERROR_SIGNATURE) + logError(1, "%s %s:Wrong Signature! Data may be invalid!\n", + tag, __func__); + else + logError(1, "%s %s: Error Signature OK! Data are valid!\n", + tag, __func__); + + for (i = 0; i < ERROR_INFO_SIZE; i++) { + if (i % 4 == 0) + logError(1, KERN_ERR "\n%s %s: %d) ", + tag, __func__, i / 4); + logError(1, "%02X ", data[i]); + } + logError(1, "\n"); + + logError(0, "%s %s: dump of error info FINISHED!\n", tag, __func__); + return OK; + +} + +int errorHandler(u8 *event, int size) +{ + int res = OK; + struct fts_ts_info *info = NULL; + + if (getClient() != NULL) + info = i2c_get_clientdata(getClient()); + + if (info == NULL || event == NULL || size <= 1 || event[0] != + EVENTID_ERROR_EVENT) { + logError(1, "%s %s: event Null or not correct size! ", + tag, __func__, ERROR_OP_NOT_ALLOW); + logError(1, "ERROR %08X\n", ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + logError(0, "%s %s: Starting handling...\n", tag, __func__); + //TODO: write an error log for undefinied command subtype 0xBA + switch (event[1]) { + case EVENT_TYPE_ESD_ERROR: //esd + res = fts_chip_powercycle(info); + if (res < OK) { + logError(1, "%s %s: ", tag, res); + logError(1, "Error performing powercycle ERROR %08X\n"); + } + + res = fts_system_reset(); + if (res < OK) + logError(1, "%s %s:Cannot reset device ERROR%08X\n", + tag, __func__, res); + res = (ERROR_HANDLER_STOP_PROC | res); + break; + + case EVENT_TYPE_WATCHDOG_ERROR: //watchdog + dumpErrorInfo(); + res = fts_system_reset(); + if (res < OK) + logError(1, "%s %s:Cannot reset device:ERROR%08X\n", + tag, __func__, res); + res = (ERROR_HANDLER_STOP_PROC | res); + break; + case EVENT_TYPE_CHECKSUM_ERROR: //CRC ERRORS + switch (event[2]) { + case CRC_CONFIG_SIGNATURE: + logError(1, "%s %s: Config Signature ERROR!\n", + tag, __func__); + break; + case CRC_CONFIG: + logError(1, "%s %s:Config CRC ERROR!\n", tag, __func__); + break; + case CRC_CX_MEMORY: + logError(1, "%s %s: CX CRC ERROR!\n", tag, __func__); + break; + } + break; + case EVENT_TYPE_LOCKDOWN_ERROR: + //res = (ERROR_HANDLER_STOP_PROC|res); + //stop lockdown code routines in order to retry + switch (event[2]) { + case 0x01: + logError(1, "%s %s:Lockdown code alredy ", + tag, __func__); + logError(1, "written into the IC!\n"); + break; + case 0x02: + logError(1, "%s %s:Lockdown CRC ", tag, __func__); + logError(1, "check fail during a WRITE!\n"); + break; + + case 0x03: + logError(1, + "%s %s:Lockdown WRITE command format wrong!\n", + tag, __func__); + break; + case 0x04: + pr_err("Lockdown Memory Corrupted!\n"); + logError(1, "%s %s:Please contact ST for support!\n", + tag, __func__); + break; + case 0x11: + logError(1, + "%s %s:NO Lockdown code to READ into the IC!\n", + tag, __func__); + break; + case 0x12: + logError(1, + "%s %s:Lockdown code data corrupted\n", + tag, __func__); + break; + case 0x13: + logError(1, + "%s %s:Lockdown READ command format wrong!\n", + tag, __func__); + break; + case 0x21: + pr_err("Exceeded maximum number of\n"); + logError(1, + "%s %s:Lockdown code REWRITE into IC!\n", + tag, __func__); + break; + case 0x22: + logError(1, "%s %s:Lockdown CRC check", tag, __func__); + logError(1, " fail during a REWRITE!\n"); + break; + case 0x23: + logError(1, "%s %s:", tag, __func__); + logError(1, "Lockdown REWRITE command format wrong!\n"); + break; + case 0x24: + pr_err("Lockdown Memory Corrupted!\n"); + logError(1, "%s %s:Please contact ST for support!\n", + tag, __func__); + break; + default: + logError(1, "%s %s:No valid error type for LOCKDOWN!\n", + tag, __func__); + } + break; + + default: + logError(0, "%s %s: No Action taken!\n", tag, __func__); + break; + } + logError(0, "%s %s: handling Finished! res = %08X\n", + tag, __func__, res); + return res; +} + diff --git a/st/fts_lib/ftsError.h b/st/fts_lib/ftsError.h new file mode 100644 index 0000000000..c96a587691 --- /dev/null +++ b/st/fts_lib/ftsError.h @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS error/info kernel log reporting * + * * + ************************************************************************** + ************************************************************************** + */ + +#ifndef __FTS_ERROR_H +#define __FTS_ERROR_H + + +//FIRST LEVEL ERROR CODE +#define OK (int)(0x00000000)/* No ERROR*/ +/*allocation of memory failed*/ +#define ERROR_ALLOC (int)(0x80000001) +#define ERROR_I2C_R (int)(0x80000002)//i2c read failed +#define ERROR_I2C_W (int)(0x80000003)//i2c write failed +#define ERROR_I2C_WR (int)(0x80000004)//i2c write/read failed + +//error during opening i2c device +#define ERROR_I2C_O (int)(0x80000005) +#define ERROR_OP_NOT_ALLOW (int)(0x80000006)//operation not allowed + +//timeout expired! exceed the max number of +//retries or the max waiting time +#define ERROR_TIMEOUT (int)(0x80000007) + +//the file that i want to open is not found +#define ERROR_FILE_NOT_FOUND (int)(0x80000008) +//error during parsing the file +#define ERROR_FILE_PARSE (int)(0x80000009) +//error during reading the file +#define ERROR_FILE_READ (int)(0x8000000A) +#define ERROR_LABEL_NOT_FOUND (int)(0x8000000B)//label not found + +//fw in the chip newer than the one in the memmh +#define ERROR_FW_NO_UPDATE (int)(0x8000000C) +//flash status busy or unknown +#define ERROR_FLASH_UNKNOWN (int)(0x8000000D) + +//SECOND LEVEL ERROR CODE +//unable to disable the interrupt +#define ERROR_DISABLE_INTER (int)(0x80000200) + +//unable to activate the interrupt +#define ERROR_ENABLE_INTER (int)(0x80000300) + +#define ERROR_READ_B2 (int)(0x80000400)//B2 command failed + +//unable to read an offset from memory +#define ERROR_GET_OFFSET (int)(0x80000500) + +//unable to retrieve the data of a required frame +#define ERROR_GET_FRAME_DATA (int)(0x80000600) + +//FW answers with an event that has a +//different address respect the request done +#define ERROR_DIFF_COMP_TYPE (int)(0x80000700) + +//the signature of the compensation data is not A5 +#define ERROR_WRONG_COMP_SIGN (int)(0x80000800) +//the command Sense On failed +#define ERROR_SENSE_ON_FAIL (int)(0x80000900) +//the command Sense Off failed +#define ERROR_SENSE_OFF_FAIL (int)(0x80000A00) + +//the command SYSTEM RESET failed +#define ERROR_SYSTEM_RESET_FAIL (int)(0x80000B00) + +//flash status not ready within a timeout +#define ERROR_FLASH_NOT_READY (int)(0x80000C00) + +//unable to retrieve fw_vers or the config_id +#define ERROR_FW_VER_READ (int)(0x80000D00) + +//unable to enable/disable the gesture +#define ERROR_GESTURE_ENABLE_FAIL (int)(0x80000E00) + +//unable to start to add custom gesture +#define ERROR_GESTURE_START_ADD (int)(0x80000F00) + +//unable to finish to add custom gesture +#define ERROR_GESTURE_FINISH_ADD (int)(0x80001000) + +//unable to add custom gesture data +#define ERROR_GESTURE_DATA_ADD (int)(0x80001100) + +//unable to remove custom gesture data +#define ERROR_GESTURE_REMOVE (int)(0x80001200) + +//unable to enable/disable a feature mode in the IC +#define ERROR_FEATURE_ENABLE_DISABLE (int)(0x80001300) + +//unable to set/read noise parameter in the IC +#define ERROR_NOISE_PARAMETERS (int)(0x80001400) + +//unable to write/rewrite/read lockdown code in the IC +#define ERROR_LOCKDOWN_CODE (int)(0x80001500) + +//THIRD LEVEL ERROR CODE +//unable to retrieve the force and/or sense length +#define ERROR_CH_LEN (int)(0x80010000) + +//compensation data request failed +#define ERROR_REQU_COMP_DATA (int)(0x80020000) + +//unable to retrieve the compensation data header +#define ERROR_COMP_DATA_HEADER (int)(0x80030000) + +//unable to retrieve the global compensation data +#define ERROR_COMP_DATA_GLOBAL (int)(0x80040000) + +//unable to retrieve the compensation data for each node +#define ERROR_COMP_DATA_NODE (int)(0x80050000) + +//check of production limits or of fw answers failed +#define ERROR_TEST_CHECK_FAIL (int)(0x80060000) +#define ERROR_MEMH_READ (int)(0x80070000)//memh reading failed +#define ERROR_FLASH_BURN_FAILED (int)(0x80080000)//flash burn failed +#define ERROR_MS_TUNING (int)(0x80090000)//ms tuning failed +#define ERROR_SS_TUNING (int)(0x800A0000)//ss tuning failed +//lp timer calibration failed +#define ERROR_LP_TIMER_TUNING (int)(0x800B0000) +//save cx data to flash failed +#define ERROR_SAVE_CX_TUNING (int)(0x800C0000) + +//stop the poll of the FIFO if particular errors are found +#define ERROR_HANDLER_STOP_PROC (int)(0x800D0000) +//unable to retrieve echo event +#define ERROR_CHECK_ECHO_FAIL (int)(0x800E0000) + +//FOURTH LEVEL ERROR CODE +//production data test failed +#define ERROR_PROD_TEST_DATA (int)(0x81000000) + +//complete flash procedure failed +#define ERROR_FLASH_PROCEDURE (int)(0x82000000) +//production ito test failed +#define ERROR_PROD_TEST_ITO (int)(0x83000000) + +//production initialization test failed +#define ERROR_PROD_TEST_INITIALIZATION (int)(0x84000000) + +//mismatch of the MS or SS tuning_version +#define ERROR_GET_INIT_STATUS (int)(0x85000000) + + +void logError(int force, const char *msg, ...); +int isI2cError(int error); +int dumpErrorInfo(void); +int errorHandler(u8 *event, int size); + +#endif diff --git a/st/fts_lib/ftsFlash.c b/st/fts_lib/ftsFlash.c new file mode 100644 index 0000000000..9fc3926dec --- /dev/null +++ b/st/fts_lib/ftsFlash.c @@ -0,0 +1,1178 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for Flashing the IC * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFlash.h" +#include "ftsFrame.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTest.h" +#include "ftsTime.h" +#include "ftsTool.h" +#include "../fts.h"//needed for including the define FW_H_FILE + +#ifdef FW_H_FILE +#include <../fts_fw.h> +#define LOAD_FW_FROM 1 +#else +#define LOAD_FW_FROM 0 +#endif + +#define FTS_LATEST_VERSION 0x1101 + +static char tag[8] = "[ FTS ]\0"; + +int getFirmwareVersion(u16 *fw_vers, u16 *config_id) +{ + u8 fwvers[DCHIP_FW_VER_BYTE]; + u8 confid[CONFIG_ID_BYTE]; + int res; + + res = readCmdU16(FTS_CMD_HW_REG_R, DCHIP_FW_VER_ADDR, + fwvers, DCHIP_FW_VER_BYTE, DUMMY_HW_REG); + if (res < OK) { + logError(1, + "%s %s:unable to read fw_version ERROR %02X\n", + tag, __func__, ERROR_FW_VER_READ); + return (res | ERROR_FW_VER_READ); + } + + u8ToU16(fwvers, fw_vers); //fw version use big endian + if (*fw_vers != 0) { + // if fw_version is 00 00 means that there is + //no firmware running in the chip therefore will be + //impossible find the config_id + res = readB2(CONFIG_ID_ADDR, confid, CONFIG_ID_BYTE); + if (res < OK) { + logError(1, "%s %s:unable to read config_id ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_FW_VER_READ); + return (res | ERROR_FW_VER_READ); + } + u8ToU16(confid, config_id); //config id use little endian + } else { + *config_id = 0x0000; + } + + logError(0, "%s FW VERS = %04X\n", tag, *fw_vers); + logError(0, "%s CONFIG ID = %04X\n", tag, *config_id); + return OK; +} + +int getFWdata_nocheck(const char *pathToFile, u8 **data, int *size, int from) +{ + const struct firmware *fw = NULL; + struct device *dev = getDev(); + int res; + + if (dev == NULL) + return ERROR_OP_NOT_ALLOW; + + logError(0, "%s Read FW from BIN file!\n", tag); + + res = firmware_request_nowarn(&fw, pathToFile, dev); + if (res) { + logError(1, "%s %s:No File found! ERROR %08X\n", + tag, __func__, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } + + *size = fw->size; + *data = (u8 *)kmalloc_array((*size), sizeof(u8), GFP_KERNEL); + if (*data == NULL) { + logError(1, "%s %s:Impossible to allocate! %08X\n", __func__); + release_firmware(fw); + return ERROR_ALLOC; + } + memcpy(*data, (u8 *)fw->data, (*size)); + release_firmware(fw); + + logError(0, "%s %s:Finshed!\n", tag, __func__); + return OK; +} + +int getFWdata(const char *pathToFile, u8 **data, int *size, int from) +{ + const struct firmware *fw = NULL; + struct device *dev = NULL; + int res; + + logError(0, "%s %s starting...\n", tag, __func__); + switch (from) { +#ifdef FW_H_FILE + case 1: + logError(1, "%s Read FW from .h file!\n", tag); + *size = FW_SIZE_NAME; + *data = (u8 *)kmalloc_array((*size), sizeof(u8), GFP_KERNEL); + if (*data == NULL) { + logError(1, "%s %s:Impossible to allocate memory! ", + tag, __func__); + logError(1, "ERROR %08X\n", ERROR_ALLOC); + + return ERROR_ALLOC; + } + memcpy(*data, (u8 *)FW_ARRAY_NAME, (*size)); + break; +#endif + default: + logError(0, "%s Read FW from BIN file!\n", tag); + + if (ftsInfo.u16_fwVer >= FTS_LATEST_VERSION) + return ERROR_FW_NO_UPDATE; + + dev = getDev(); + + if (dev != NULL) { + res = firmware_request_nowarn(&fw, pathToFile, dev); + if (res == 0) { + *size = fw->size; + *data = (u8 *)kmalloc_array((*size), sizeof(u8), + GFP_KERNEL); + if (*data == NULL) { + logError(1, "%s %s:Impossible to ", + tag, __func__); + logError(1, "%allocate! %08X\n", + ERROR_ALLOC); + release_firmware(fw); + return ERROR_ALLOC; + } + memcpy(*data, (u8 *)fw->data, (*size)); + release_firmware(fw); + } else { + logError(0, "%s %s:No File found! ERROR %08X\n", + tag, __func__, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } + } else { + logError(1, "%s %s:No device found! ERROR %08X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + /* break; */ + } + + logError(0, "%s %s:Finshed!\n", tag, __func__); + return OK; +} + +int readFwFile(const char *path, struct Firmware *fw, int keep_cx) +{ + int res; + int orig_size; + u8 *orig_data = NULL; + + + res = getFWdata(path, &orig_data, &orig_size, LOAD_FW_FROM); + if (res < OK) { + logError(0, "%s %s:impossible retrieve FW... ERROR %08X\n", + tag, __func__, ERROR_MEMH_READ); + + return (res | ERROR_MEMH_READ); + } + res = parseBinFile(orig_data, orig_size, fw, keep_cx); + + if (res < OK) { + logError(1, "%s %s:impossible parse ERROR %08X\n", + tag, __func__, ERROR_MEMH_READ); + return (res | ERROR_MEMH_READ); + } + + return OK; +} + +int flashProcedure(const char *path, int force, int keep_cx) +{ + struct Firmware fw; + int res; + + fw.data = NULL; + logError(0, "%s Reading Fw file...\n", tag); + res = readFwFile(path, &fw, keep_cx); + if (res < OK) { + logError(0, "%s %s: ERROR %02X\n", + tag, __func__, (res | ERROR_FLASH_PROCEDURE)); + kfree(fw.data); + return (res | ERROR_FLASH_PROCEDURE); + } + logError(0, "%s Fw file read COMPLETED!\n", tag); + + logError(0, "%s Starting flashing procedure...\n", tag); + res = flash_burn(&fw, force, keep_cx); + if (res < OK && res != (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED)) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_FLASH_PROCEDURE); + kfree(fw.data); + return (res | ERROR_FLASH_PROCEDURE); + } + logError(0, "%s flashing procedure Finished!\n", tag); + kfree(fw.data); + + return res; +} + +#ifdef FTM3_CHIP +int flash_status(void) +{ + u8 cmd[2] = {FLASH_CMD_READSTATUS, 0x00}; + u8 readData = 0; + + logError(0, "%s %s:Reading ...\n", tag, __func__); + if (fts_readCmd(cmd, 2, &readData, FLASH_STATUS_BYTES) < 0) { + logError(1, "%s %s: ERROR % 02X\n", tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; + } + + readData &= 0x01; + logError(0, "%s %s = %d\n", tag, __func__, readData); + return (int) readData; +} + +int flash_status_ready(void) +{ + + int status = flash_status(); + + if (status == ERROR_I2C_R) { + logError(1, "%s %s: ERROR % 02X\n", tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; + } + + if (status != FLASH_READY) { + //logError(1, + //"%s %s:flash busy or unknown STATUS = % 02X\n", + //tag, status); + return ERROR_FLASH_UNKNOWN; + } + + return FLASH_READY; +} + +int wait_for_flash_ready(void) +{ + int status; + int (*code)(void); + + code = flash_status_ready; + + logError(0, "%s Waiting for flash ready...\n", tag); + status = attempt_function(code, FLASH_WAIT_BEFORE_RETRY, + FLASH_RETRY_COUNT); + + if (status != FLASH_READY) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + return (status | ERROR_FLASH_NOT_READY); + } + + logError(0, "%s Flash ready!\n", tag); + return OK; +} + +int flash_unlock(void) +{ + int status; + //write the command to perform the unlock + u8 cmd[3] = {FLASH_CMD_UNLOCK, FLASH_UNLOCK_CODE0, + FLASH_UNLOCK_CODE1}; + + logError(0, "%s Try to unlock flash...\n", tag); + status = wait_for_flash_ready(); + + if (status != OK) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + //Flash not ready within the chosen time, better exit! + return (status | ERROR_FLASH_NOT_READY); + } + + logError(0, "%s Command unlock ...\n", tag); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + + status = wait_for_flash_ready(); + + if (status != OK) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + //Flash not ready within the chosen time, + //better exit! + return (status | ERROR_FLASH_NOT_READY); + } + logError(0, "%s Unlock flash DONE!\n", tag); + + return OK; +} + +int parseBinFile(u8 *fw_data, int fw_size, Firmware *fwData, int keep_cx) +{ + int dimension; + + if (keep_cx) { + dimension = FW_SIZE - FW_CX_SIZE; + logError(1, "%s %s: Selected 124k Configuration!\n", + tag, __func__); + } else { + dimension = FW_SIZE; + logError(1, "%s %s: Selected 128k Configuration!\n", + tag, __func__); + } + + if (fw_size - FW_HEADER_SIZE != FW_SIZE || fw_data == NULL) { + logError(1, "%s %s:Read only %d instead of %d... ERROR %02X\n", + tag, __func__, + fw_size - FW_HEADER_SIZE, + FW_SIZE, ERROR_FILE_PARSE); + kfree(fw_data); + return ERROR_FILE_PARSE; + } + + fwData->data = (u8 *)kmalloc_array(dimension, sizeof(u8), GFP_KERNEL); + if (fwData->data == NULL) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); + + kfree(fw_data); + return ERROR_ALLOC; + } + + memcpy(fwData->data, ((u8 *)(fw_data) + FW_HEADER_SIZE), + dimension); + fwData->data_size = dimension; + + fwData->fw_ver = (u16)(((fwData->data[FW_VER_MEMH_BYTE1] & 0x00FF) << 8) + + (fwData->data[FW_VER_MEMH_BYTE0] & 0x00FF)); + + fwData->config_id = (u16)(((fwData->data[(FW_CODE_SIZE) + + FW_OFF_CONFID_MEMH_BYTE1] & 0x00FF) << 8) + + (fwData->data[(FW_CODE_SIZE) + + FW_OFF_CONFID_MEMH_BYTE0] & 0x00FF)); + + logError(0, "%s %s: FW VERS File = %04X\n", + tag, __func__, fwData->fw_ver); + logError(0, "%s %s: CONFIG ID File = %04X\n", + tag, __func__, fwData->config_id); + + logError(0, "%s READ FW DONE %d bytes!\n", tag, fwData->data_size); + + kfree(fw_data); + return OK; +} + +int fillMemory(u32 address, u8 *data, int size) +{ + int remaining = size; + int toWrite = 0; + int delta; + + u8 *buff = (u8 *)kmalloc_array((MEMORY_CHUNK + 3), sizeof(u8), + GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + if (remaining >= MEMORY_CHUNK) { + if ((address + MEMORY_CHUNK) < FLASH_ADDR_SWITCH_CMD) { + buff[0] = FLASH_CMD_WRITE_LOWER_64; + toWrite = MEMORY_CHUNK; + remaining -= MEMORY_CHUNK; + } else { + if (address < FLASH_ADDR_SWITCH_CMD) { + delta = FLASH_ADDR_SWITCH_CMD - address; + + buff[0] = FLASH_CMD_WRITE_LOWER_64; + toWrite = delta; + remaining -= delta; + } else { + buff[0] = FLASH_CMD_WRITE_UPPER_64; + toWrite = MEMORY_CHUNK; + remaining -= MEMORY_CHUNK; + } + } + } else { + if ((address + remaining) < FLASH_ADDR_SWITCH_CMD) { + buff[0] = FLASH_CMD_WRITE_LOWER_64; + toWrite = remaining; + remaining = 0; + } else { + if (address < FLASH_ADDR_SWITCH_CMD) { + delta = FLASH_ADDR_SWITCH_CMD - address; + + buff[0] = FLASH_CMD_WRITE_LOWER_64; + toWrite = delta; + remaining -= delta; + } else { + buff[0] = FLASH_CMD_WRITE_UPPER_64; + toWrite = remaining; + remaining = 0; + } + } + } + + buff[1] = (u8) ((address & 0x0000FF00) >> 8); + buff[2] = (u8) (address & 0x000000FF); + memcpy(buff + 3, data, toWrite); + //logError(0, + //"%s Command = %02X , address = %02X %02X, bytes = %d\n", + //tag, buff[0], buff[1], buff[2], toWrite); + if (fts_writeCmd(buff, 3 + toWrite) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + kfree(buff); + return ERROR_I2C_W; + } + address += toWrite; + data += toWrite; + } + kfree(buff); + return OK; +} + +int flash_burn(Firmware *fw, int force_burn, int keep_cx) +{ + u8 cmd; + int res; + + if (!force_burn && (ftsInfo.u16_fwVer >= fw->fw_ver) + && (ftsInfo.u16_cfgId >= fw->config_id)) { + logError(0, "Firmware in the chip newer"); + logError(0, " or equal to the one to burn! "); + logError(0, "%s %s:NO UPDATE ERROR %02X\n", + tag, __func__, ERROR_FW_NO_UPDATE); + return (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED); + } + + //programming procedure start + + logError(0, "%s Programming Procedure for flashing started:\n", tag); + + logError(0, "%s 1) SYSTEM RESET:\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s system reset FAILED!\n", tag); + //if there is no firmware i will not + //get the controller ready event and + //there will be a timeout but i can + //keep going, but if there is + //an I2C error i have to exit + if (res != (ERROR_SYSTEM_RESET_FAIL | ERROR_TIMEOUT)) + return (res | ERROR_FLASH_BURN_FAILED); + } else + logError(0, "%s system reset COMPLETED!\n\n", tag); + + logError(0, "%s 2) FLASH UNLOCK:\n", tag); + res = flash_unlock(); + if (res < 0) { + logError(1, "%s flash unlock FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s flash unlock COMPLETED!\n\n", tag); + + + //Write the lower part of the Program RAM + logError(0, "%s 3) PREPARING DATA FOR FLASH BURN:\n", tag); + + res = fillMemory(FLASH_ADDR_CODE, fw->data, fw->data_size); + if (res < 0) { + logError(1, "%s Error During filling the memory!%02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s Data copy COMPLETED!\n\n", tag); + + logError(0, "%s 4) ERASE FLASH:\n", tag); + res = wait_for_flash_ready(); + if (res < 0) { + logError(1, "%s Flash not ready! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + logError(0, "%s Command erase ...\n", tag); + cmd = FLASH_CMD_ERASE; + if (fts_writeCmd(&cmd, 1) < 0) { + logError(1, "%s Error during erasing flash! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (ERROR_I2C_W | ERROR_FLASH_BURN_FAILED); + } + + res = wait_for_flash_ready(); + if (res < 0) { + logError(1, "%s Flash not ready 2! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + logError(0, "%s Flash erase COMPLETED!\n\n", tag); + + logError(0, "%s 5) BURN FLASH:\n", tag); + logError(0, "%s Command burn ...\n", tag); + cmd = FLASH_CMD_BURN; + if (fts_writeCmd(&cmd, 1) < 0) { + logError(1, "%s Error during burning data! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (ERROR_I2C_W | ERROR_FLASH_BURN_FAILED); + } + + res = wait_for_flash_ready(); + if (res < 0) { + logError(1, "%s Flash not ready! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + logError(0, "%s Flash burn COMPLETED!\n\n", tag); + + logError(0, "%s 6) SYSTEM RESET:\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s system reset FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s system reset COMPLETED!\n\n", tag); + + + logError(0, "%s 7) FINAL CHECK:\n", tag); + res = readChipInfo(0); + if (res < 0) { + logError(1, "%s %s:Unable to retrieve Chip INFO!%02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + if ((ftsInfo.u16_fwVer != fw->fw_ver) + && (ftsInfo.u16_cfgId != fw->config_id)) { + logError(1, "Firmware in the chip different"); + logError(1, " from the one that was burn!"); + logError(1, "%s fw: %x != %x , conf: %x != %x\n", + tag, ftsInfo.u16_fwVer, + fw->fw_ver, + ftsInfo.u16_cfgId, + fw->config_id); + return ERROR_FLASH_BURN_FAILED; + } + + logError(0, "%s Final check OK! fw: %02X, conf: %02X\n", + tag, ftsInfo.u16_fwVer, ftsInfo.u16_cfgId); + + return OK; +} + +#else + +int wait_for_flash_ready(u8 type) +{ + u8 cmd[2] = {FLASH_CMD_READ_REGISTER, type}; + u8 readData = 0; + int i, res = -1; + + logError(0, "%s Waiting for flash ready ...\n", tag); + for (i = 0; i < FLASH_RETRY_COUNT && res != 0; i++) { + if (fts_readCmd(cmd, sizeof(cmd), &readData, 1) < 0) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_I2C_R); + } else { + res = readData & 0x80; + //logError(0, "%s flash status = %d\n", tag, res); + } + msleep(FLASH_WAIT_BEFORE_RETRY); + } + + if (i == FLASH_RETRY_COUNT && res != 0) { + logError(1, "%s Wait for flash TIMEOUT! ERROR %02X\n", + tag, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + + logError(0, "%s Flash READY!\n", tag); + return OK; +} + +int fts_warm_boot(void) +{ + //write the command to perform the warm boot + u8 cmd[4] = {FTS_CMD_HW_REG_W, 0x00, 0x00, WARM_BOOT_VALUE}; + + u16ToU8_be(ADDR_WARM_BOOT, &cmd[1]); + + logError(0, "%s Command warm boot ...\n", tag); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s flash_unlock: ERROR % 02X\n", tag, ERROR_I2C_W); + return ERROR_I2C_W; + } + + logError(0, "%s Warm boot DONE!\n", tag); + + return OK; +} + +int parseBinFile(u8 *data, int fw_size, + struct Firmware *fwData, int keep_cx) +{ + int dimension, index = 0; + u32 temp; + int res, i; + + //the file should contain at least the header plus the content_crc + if (fw_size < FW_HEADER_SIZE+FW_BYTES_ALIGN || data == NULL) { + logError(1, "%s %s:Read only %d instead of %d...ERROR %02X\n", + tag, __func__, fw_size, + FW_HEADER_SIZE + FW_BYTES_ALIGN, + ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } else { + //start parsing of bytes + u8ToU32(&data[index], &temp); + if (temp != FW_HEADER_SIGNATURE) { + logError(1, "%s %s:Wrong Signature %08X...ERROR %02X\n", + tag, __func__, temp, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + + logError(0, "%s %s: Fw Signature OK!\n", tag, __func__); + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + if (temp != FW_FTB_VER) { + logError(1, "%s %s:Wrong ftb_version %08X.ERROR %02X\n", + tag, __func__, temp, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + logError(0, "%s %s:ftb_version OK!\n", __func__, tag); + index += FW_BYTES_ALIGN; + if (data[index] != DCHIP_ID_0 || data[index+1] != DCHIP_ID_1) { + logError(1, "%s %s:Wrong target %02X != %02X ", + tag, __func__, data[index]); + logError(1, "%%02X != %02X:%08X\n", + DCHIP_ID_0, data[index+1], + DCHIP_ID_1, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + logError(0, "%s %s: Fw ID = %08X\n", tag, __func__, temp); + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->fw_ver = temp; + logError(0, "%s %s:FILE Fw Version = %04X\n", + tag, __func__, fwData->fw_ver); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->config_id = temp; + logError(0, "%s %s:FILE Config ID = %04X\n", + tag, __func__, fwData->config_id); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + logError(0, "%s %s:Config Version = %08X\n", + tag, __func__, temp); + //skip reserved data + index += FW_BYTES_ALIGN * 2; + index += FW_BYTES_ALIGN; + logError(0, "%s %s:File External Release = ", + tag, __func__); + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) { + fwData->externalRelease[i] = data[index++]; + logError(0, "%02X", fwData->externalRelease[i]); + } + logError(0, "\n"); + + //index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->sec0_size = temp; + logError(0, "%s %s:sec0_size = %08X (%d bytes)\n", + tag, __func__, fwData->sec0_size, fwData->sec0_size); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->sec1_size = temp; + logError(0, "%s %s:sec1_size = %08X (%d bytes)\n", + tag, __func__, fwData->sec1_size, fwData->sec1_size); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->sec2_size = temp; + logError(0, "%s %s:sec2_size = %08X (%d bytes)\n", + tag, __func__, fwData->sec2_size, fwData->sec2_size); + + index += FW_BYTES_ALIGN; + u8ToU32(&data[index], &temp); + fwData->sec3_size = temp; + logError(0, "%s %s:sec3_size = %08X (%d bytes)\n", + tag, __func__, fwData->sec3_size, fwData->sec3_size); + + //skip header crc + index += FW_BYTES_ALIGN; + if (!keep_cx) { + dimension = fwData->sec0_size + fwData->sec1_size + + fwData->sec2_size + fwData->sec3_size; + temp = fw_size; + } else { + //sec2 may contain cx data (future implementation) + //sec3 atm not used + dimension = fwData->sec0_size + fwData->sec1_size; + temp = fw_size - fwData->sec2_size - fwData->sec3_size; + fwData->sec2_size = 0; + fwData->sec3_size = 0; + } + if (dimension + FW_HEADER_SIZE + FW_BYTES_ALIGN != temp) { + logError(1, "%s %s:Read only %d instead of %d...", + tag, __func__, fw_size, + dimension + FW_HEADER_SIZE + FW_BYTES_ALIGN); + logError(1, "ERROR %02X\n", ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + fwData->data = (u8 *)kmalloc_array(dimension, sizeof(u8), + GFP_KERNEL); + if (fwData->data == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + res = ERROR_ALLOC; + goto END; + } + index += FW_BYTES_ALIGN; + memcpy(fwData->data, &data[index], dimension); + fwData->data_size = dimension; + logError(0, "%s READ FW DONE %d bytes!\n", + tag, fwData->data_size); + res = OK; + goto END; + } +END: + kfree(data); + return res; +} + +int flash_unlock(void) +{ + //write the command to perform the unlock + u8 cmd[3] = {FLASH_CMD_UNLOCK, + FLASH_UNLOCK_CODE0, FLASH_UNLOCK_CODE1}; + logError(0, "%s Command unlock ...\n", tag); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s %s: ERROR % 02X\n", tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + //mdelay(FLASH_WAIT_TIME); + logError(0, "%s Unlock flash DONE!\n", tag); + + return OK; +} + +int flash_erase_unlock(void) +{ + //write the command to perform + //the unlock for erasing the flash + u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_UNLOCK_CODE0, + FLASH_ERASE_UNLOCK_CODE1}; + + logError(0, "%s Try to erase unlock flash...\n", tag); + + logError(0, "%s Command erase unlock ...\n", tag); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s %s:ERROR % 02X\n", tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + + logError(0, "%s Erase Unlock flash DONE!\n", tag); + + return OK; +} + +int flash_full_erase(void) +{ + int status; + //write the command to erase the flash + u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_CODE0, + FLASH_ERASE_CODE1}; + + logError(0, "%s Command full erase sent...\n", + tag); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s %s:ERROR % 02X\n", tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + + status = wait_for_flash_ready(FLASH_ERASE_CODE0); + + if (status != OK) { + logError(1, "%s %s:ERROR % 02X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + //Flash not ready within the chosen time, + //better exit! + return (status | ERROR_FLASH_NOT_READY); + } + + logError(0, "%s Full Erase flash DONE!\n", tag); + + return OK; +} + +int flash_erase_page_by_page(int keep_cx) +{ + u8 status, i = 0; + //write the command to erase the flash + u8 cmd[4] = {FLASH_CMD_WRITE_REGISTER, FLASH_ERASE_CODE0, 0x00, 0x00}; + + for (i = 0; i < FLASH_NUM_PAGE; i++) { + if (i >= FLASH_CX_PAGE_START && i <= FLASH_CX_PAGE_END + && keep_cx == 1) { + logError(0, "%s Skipping erase page %d!\n", tag, i); + continue; + } + cmd[2] = (0x3F & i) | FLASH_ERASE_START; + logError(0, "Command erase page %d sent", i); + logError(0, "%s:%02X %02X %02X %02X\n", + tag, i, cmd[0], cmd[1], cmd[2], cmd[3]); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, + "%s %s:ERROR % 08X\n", + tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + + status = wait_for_flash_ready(FLASH_ERASE_CODE0); + if (status != OK) { + logError(1, "%s %s:ERROR % 08X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + //Flash not ready within the chosen time, + //better exit! + return (status | ERROR_FLASH_NOT_READY); + } + } + + logError(0, "%s Erase flash page by page DONE!\n", tag); + + return OK; +} + +int start_flash_dma(void) +{ + int status; + //write the command to erase the flash + u8 cmd[3] = {FLASH_CMD_WRITE_REGISTER, FLASH_DMA_CODE0, + FLASH_DMA_CODE1}; + + logError(0, "%s Command flash DMA ...\n", tag); + if (fts_writeCmd(cmd, sizeof(cmd)) < 0) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + + status = wait_for_flash_ready(FLASH_DMA_CODE0); + + if (status != OK) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_FLASH_NOT_READY); + //Flash not ready within the chosen time, better exit! + return (status | ERROR_FLASH_NOT_READY); + } + + logError(0, "%s flash DMA DONE!\n", tag); + + return OK; +} + +int fillFlash(u32 address, u8 *data, int size) +{ + int remaining = size; + int toWrite = 0; + int byteBlock = 0; + int wheel = 0; + u32 addr = 0; + int res; + int delta; + u8 *buff = NULL; + u8 buff2[9] = {0}; + + + buff = (u8 *)kmalloc_array((DMA_CHUNK + 3), sizeof(u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + byteBlock = 0; + addr = 0; + while (byteBlock < FLASH_CHUNK && remaining > 0) { + buff[0] = FLASH_CMD_WRITE_64K; + if (remaining >= DMA_CHUNK) { + if ((byteBlock + DMA_CHUNK) <= FLASH_CHUNK) { + //logError(1, "%s fillFlash:1\n", tag); + toWrite = DMA_CHUNK; + remaining -= DMA_CHUNK; + byteBlock += DMA_CHUNK; + } else { + //logError(1, "%s fillFlash:2\n", tag); + delta = FLASH_CHUNK - byteBlock; + toWrite = delta; + remaining -= delta; + byteBlock += delta; + } + } else { + if ((byteBlock + remaining) <= FLASH_CHUNK) { + //logError(1, "%s fillFlash:3\n", tag); + toWrite = remaining; + byteBlock += remaining; + remaining = 0; + } else { + //logError(1, "%s fillFlash:4\n", tag); + delta = FLASH_CHUNK - byteBlock; + toWrite = delta; + remaining -= delta; + byteBlock += delta; + } + } + + buff[1] = (u8) ((addr & 0x0000FF00) >> 8); + buff[2] = (u8) (addr & 0x000000FF); + memcpy(&buff[3], data, toWrite); + //logError(0, + //"%s Command = %02X, address = %02X %02X, + //bytes = %d, data = %02X %02X, %02X %02X\n", + //tag, buff[0], buff[1], buff[2], toWrite, + //buff[3], buff[4], buff[3 + toWrite-2], + //buff[3 + toWrite-1]); + if (fts_writeCmd(buff, 3 + toWrite) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + kfree(buff); + return ERROR_I2C_W; + } + + addr += toWrite; + data += toWrite; + } + //configuring the DMA + byteBlock = byteBlock / 4 - 1; + + buff2[0] = FLASH_CMD_WRITE_REGISTER; + buff2[1] = FLASH_DMA_CONFIG; + buff2[2] = 0x00; + buff2[3] = 0x00; + + addr = address + ((wheel * FLASH_CHUNK)/4); + buff2[4] = (u8) ((addr & 0x000000FF)); + buff2[5] = (u8) ((addr & 0x0000FF00) >> 8); + buff2[6] = (u8) (byteBlock & 0x000000FF); + buff2[7] = (u8) ((byteBlock & 0x0000FF00) >> 8); + buff2[8] = 0x00; + + logError(0, "%s:Command:%02X, address:%02X %02X, ", + tag, buff2[0], buff2[5], buff2[4]); + logError(0, "words:%02X %02X\n", buff2[7], buff2[6]); + if (fts_writeCmd(buff2, 9) < OK) { + logError(1, "%s Error during filling Flash!:%02X\n", + tag, ERROR_I2C_W); + kfree(buff); + return ERROR_I2C_W; + } + //mdelay(FLASH_WAIT_TIME); + res = start_flash_dma(); + if (res < OK) { + logError(1, "%s Error during flashing DMA!:%02X\n", + tag, res); + kfree(buff); + return res; + } + wheel++; + } + kfree(buff); + return OK; +} + +int flash_burn(struct Firmware *fw, int force_burn, int keep_cx) +{ + int res; + + if (!force_burn && (ftsInfo.u16_fwVer >= fw->fw_ver) + && (ftsInfo.u16_cfgId >= fw->config_id)) { + for (res = EXTERNAL_RELEASE_INFO_SIZE-1; res >= 0; res--) { + if (fw->externalRelease[res] > + ftsInfo.u8_extReleaseInfo[res]) + goto start; + } + + logError(0, "Firmware in the chip newer or "); + logError(0, "equal to the one to burn!"); + logError(0, "%s %s:NO UPDATE ERROR %02X\n", + tag, __func__, ERROR_FW_NO_UPDATE); + return (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED); + } + + //programming procedure start +start: + logError(0, "%s Programming Procedure for flashing started:\n\n", tag); + + logError(0, "%s 1) SYSTEM RESET:\n", tag); + + logError(0, "%s 2) WARM BOOT:\n", tag); + res = fts_warm_boot(); + if (res < OK) { + logError(1, "%s warm boot FAILED!\n", tag); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s warm boot COMPLETED!\n\n", tag); + + //mdelay(FLASH_WAIT_TIME); + logError(0, "%s 3) FLASH UNLOCK:\n", tag); + res = flash_unlock(); + if (res < OK) { + logError(1, "%s flash unlock FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s flash unlock COMPLETED!\n\n", tag); + + //mdelay(200); + logError(0, "%s 4) FLASH ERASE UNLOCK:\n", tag); + res = flash_erase_unlock(); + if (res < 0) { + logError(1, "%s flash unlock FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s flash unlock COMPLETED!\n\n", tag); + + //mdelay(FLASH_WAIT_TIME); + logError(0, "%s 5) FLASH ERASE:\n", tag); + if (keep_cx == 1) + res = flash_erase_page_by_page(keep_cx); + else + res = flash_full_erase(); + if (res < 0) { + logError(1, "%s flash erase FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s flash erase COMPLETED!\n\n", tag); + + + //mdelay(FLASH_WAIT_TIME); + logError(0, "%s 6) LOAD PROGRAM:\n", tag); + res = fillFlash(FLASH_ADDR_CODE, (u8 *)(&fw->data[0]), + fw->sec0_size); + if (res < OK) { + logError(1, "%s load program ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s load program DONE!\n", tag); + logError(0, "%s 7) LOAD CONFIG:\n", tag); + res = fillFlash(FLASH_ADDR_CONFIG, + &(fw->data[fw->sec0_size]), fw->sec1_size); + if (res < OK) { + logError(1, "%s load config ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s load config DONE!\n", tag); + + logError(0, "%s Flash burn COMPLETED!\n\n", tag); + + logError(0, "%s 8) SYSTEM RESET:\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s system reset FAILED! ERROR %02X\n", + tag, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + logError(0, "%s system reset COMPLETED!\n\n", tag); + + + logError(0, "%s 9) FINAL CHECK:\n", tag); + res = readChipInfo(0); + if (res < 0) { + logError(1, "%s %s:Unable to retrieve Chip INFO!:%02X\n", + tag, __func__, ERROR_FLASH_BURN_FAILED); + return (res | ERROR_FLASH_BURN_FAILED); + } + + if ((ftsInfo.u16_fwVer != fw->fw_ver) + && (ftsInfo.u16_cfgId != fw->config_id)) { + pr_err("Firmware is different from the old!\n"); + logError(1, "%s fw: %x != %x, conf: %x != %x\n", + tag, ftsInfo.u16_fwVer, fw->fw_ver, + ftsInfo.u16_cfgId, fw->config_id); + return ERROR_FLASH_BURN_FAILED; + } + + logError(0, "%s Final check OK! fw: %02X , conf: %02X\n", + tag, ftsInfo.u16_fwVer, ftsInfo.u16_cfgId); + + return OK; +} + +#endif diff --git a/st/fts_lib/ftsFlash.h b/st/fts_lib/ftsFlash.h new file mode 100644 index 0000000000..844c5da532 --- /dev/null +++ b/st/fts_lib/ftsFlash.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for Flashing the IC * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#ifndef __FTS_FLASH_H +#define __FTS_FLASH_H + + +#include "ftsSoftware.h" + +//Flash possible status +#define FLASH_READY 0 +#define FLASH_BUSY 1 +#define FLASH_UNKNOWN -1 +#define FLASH_STATUS_BYTES 1 + + +//Flash timing parameters +#define FLASH_RETRY_COUNT 1000 +#define FLASH_WAIT_BEFORE_RETRY 50 //ms +#define FLASH_WAIT_TIME 200 //ms + + +//PATHS FW FILES +//#define PATH_FILE_FW "fw.memh" +#ifdef FTM3_CHIP +#define PATH_FILE_FW "st_fts.bin" +#else +#define PATH_FILE_FW "st_fts.ftb"//new bin file structure +#endif + +#ifndef FTM3_CHIP +#define FLASH_CHUNK (64 * 1024) +#define DMA_CHUNK (2 * 1024) +#endif + + +struct Firmware { + u8 *data; + u16 fw_ver; + u16 config_id; + u8 externalRelease[EXTERNAL_RELEASE_INFO_SIZE]; + int data_size; +#ifndef FTM3_CHIP + u32 sec0_size; + u32 sec1_size; + u32 sec2_size; + u32 sec3_size; +#endif +}; + +#ifdef FTM3_CHIP +int flash_status(void); +int flash_status_ready(void); +int wait_for_flash_ready(void); +#else +int wait_for_flash_ready(u8 type); +int fts_warm_boot(void); +int flash_erase_unlock(void); +int flash_full_erase(void); +int flash_erase_page_by_page(int keep_cx); +//int flash_erase_page_by_page_info(int page); +int start_flash_dma(void); +int fillFlash(u32 address, u8 *data, int size); +#endif + +int flash_unlock(void); +int fillMemory(u32 address, u8 *data, int size); +int getFirmwareVersion(u16 *fw_vers, u16 *config_id); +int getFWdata(const char *pathToFile, u8 **data, int *size, int from); +int getFWdata_nocheck(const char *pathToFile, u8 **data, int *size, int from); +int parseBinFile(u8 *fw_data, int fw_size, struct Firmware *fw, int keep_cx); +int readFwFile(const char *path, struct Firmware *fw, int keep_cx); +int flash_burn(struct Firmware *fw, int force_burn, int keep_cx); +int flashProcedure(const char *path, int force, int keep_cx); + +#endif diff --git a/st/fts_lib/ftsFrame.c b/st/fts_lib/ftsFrame.c new file mode 100644 index 0000000000..ffcf34925e --- /dev/null +++ b/st/fts_lib/ftsFrame.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS functions for getting frames * + * * + ************************************************************************** + ************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +#include "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTool.h" +#include "ftsTime.h" +#include "../fts.h" + +static char tag[8] = "[ FTS ]\0"; +static int sense_len, force_len; + +int getOffsetFrame(u16 address, u16 *offset) +{ + u8 data[2]; + u8 cmd = { FTS_CMD_FRAMEBUFFER_R }; + char *temp = NULL; + + if (readCmdU16(cmd, address, data, OFFSET_LENGTH, + DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %S: ERROR %02X\n", tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; + } + + u8ToU16(data, offset); + temp = printHex("Offest = ", data, OFFSET_LENGTH); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + return OK; +} + +int getChannelsLength(void) +{ + int ret; + u8 *data = (u8 *)kmalloc_array(2, sizeof(u8), GFP_KERNEL); + + if (data == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = readB2(ADDR_SENSE_LEN, data, 2); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_READ_B2); + kfree(data); + return (ret|ERROR_READ_B2); + } + + sense_len = (int)data[0]; + force_len = (int)data[1]; + + logError(0, "%s Force_len = %d Sense_Len = %d\n", + tag, force_len, sense_len); + kfree(data); + + return OK; +} + + +int getFrameData(u16 address, int size, short **frame) +{ + int i, j, ret; + u8 *data = (u8 *)kmalloc_array(size, sizeof(u8), GFP_KERNEL); + + if (data == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + ret = readCmdU16(FTS_CMD_FRAMEBUFFER_R, address, + data, size, DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_R); + kfree(data); + return ERROR_I2C_R; + } + j = 0; + for (i = 0; i < size; i += 2) { + (*frame)[j] = (short)((data[i + 1] << 8) + data[i]); + j++; + } + kfree(data); + return OK; +} + + +int getMSFrame(u16 type, struct MutualSenseFrame *frame, int keep_first_row) +{ + u16 offset; + int ret; + + if (getSenseLen() == 0 || getForceLen() == 0) { + ret = getChannelsLength(); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_CH_LEN); + return (ret|ERROR_CH_LEN); + } + } + + ret = getOffsetFrame(type, &offset); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_GET_OFFSET); + return (ret | ERROR_GET_OFFSET); + } + + switch (type) { + case ADDR_RAW_TOUCH: + case ADDR_FILTER_TOUCH: + case ADDR_NORM_TOUCH: + case ADDR_CALIB_TOUCH: + if (keep_first_row == 1) { + frame->node_data_size = ((force_len + 1) * sense_len); + frame->header.force_node = force_len + 1; + } else { + frame->node_data_size = ((force_len) * sense_len); + offset += (sense_len * BYTES_PER_NODE); + frame->header.force_node = force_len; + } + frame->header.sense_node = sense_len; + break; + case ADDR_NORM_MS_KEY: + case ADDR_RAW_MS_KEY: + frame->header.force_node = 1; + frame->header.sense_node = ftsInfo.u8_msKeyLen; + frame->node_data_size = ftsInfo.u8_msKeyLen; + break; + default: + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + frame->node_data = (short *)kmalloc_array(frame->node_data_size, + sizeof(short), GFP_KERNEL); + if (frame->node_data == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = getFrameData(offset, + frame->node_data_size * BYTES_PER_NODE, + &(frame->node_data)); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_GET_FRAME_DATA); + kfree(frame->node_data); + return (ret | ERROR_GET_FRAME_DATA); + } + // if you want to access one node i,j, + //you should compute the offset like: + //offset = i * columns + j => frame[i, j] + logError(0, "%s Frame acquired!\n", tag); + //return the number of data put inside frame + return frame->node_data_size; +} + +int getSenseLen(void) +{ + int ret; + + if (sense_len != 0) + return sense_len; + + if (ftsInfo.u8_scrSenseLen != 0) { + sense_len = ftsInfo.u8_scrSenseLen; + } else { + ret = getChannelsLength(); + if (ret < OK) + return ret; + } + return sense_len; +} + +int getForceLen(void) +{ + int ret; + + if (force_len != 0) + return force_len; + + if (ftsInfo.u8_scrForceLen != 0) { + force_len = ftsInfo.u8_scrForceLen; + } else { + ret = getChannelsLength(); + if (ret < OK) + return ret; + } + return force_len; +} + +int requestFrame(u16 type) +{ + int retry = 0; + int ret; + u16 answer; + char *temp = NULL; + + int event_to_search[1]; + u8 readEvent[FIFO_EVENT_SIZE]; + + u8 cmd[3] = { FTS_CMD_REQU_FRAME_DATA, 0x00, 0x00}; + // B7 is the command for asking frame data + event_to_search[0] = (int)EVENTID_FRAME_DATA_READ; + + + u16ToU8(type, &cmd[1]); + + while (retry < FRAME_DATA_READ_RETRY) { + temp = printHex("Command = ", cmd, 3); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + + //send the request to the chip to load in memory the Frame Data + ret = fts_writeFwCmd(cmd, 3); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + ret = pollForEvent(event_to_search, + 1, + readEvent, + TIMEOUT_REQU_COMP_DATA); + if (ret < OK) { + logError(0, "%s Event did not Found at %d attemp!\n", + tag, retry + 1); + retry += 1; + } else { + retry = 0; + break; + } + } + if (retry == FRAME_DATA_READ_RETRY) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + u8ToU16_le(&readEvent[1], &answer); + + if (answer == type) + return OK; + + logError(1, "%s The event found has a different type of ", tag); + logError(1, "Frame data:%02X\n", ERROR_DIFF_COMP_TYPE); + return ERROR_DIFF_COMP_TYPE; + +} + + +int readFrameDataHeader(u16 type, struct DataHeader *header) +{ + u16 offset = ADDR_FRAMEBUFFER_DATA; + u16 answer; + u8 data[FRAME_DATA_HEADER]; + + if (readCmdU16(FTS_CMD_FRAMEBUFFER_R, offset, data, + FRAME_DATA_HEADER, DUMMY_FRAMEBUFFER) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; + } + logError(0, "%s Read Data Header done!\n", tag); + + if (data[0] != FRAME_HEADER_SIGNATURE) { + logError(1, "%s %s %02X Wrong Header Signature !%02X != %02X\n", + tag, __func__, ERROR_WRONG_COMP_SIGN, data[0], + HEADER_SIGNATURE); + return ERROR_WRONG_COMP_SIGN; + } + + u8ToU16_le(&data[1], &answer); + + if (answer != type) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_DIFF_COMP_TYPE); + return ERROR_DIFF_COMP_TYPE; + } + + logError(0, "%s Type of Frame data OK!\n", tag); + + header->type = type; + header->force_node = (int)data[4]; + header->sense_node = (int)data[5]; + + return OK; +} + +int getMSFrame2(u16 type, struct MutualSenseFrame *frame) +{ + u16 offset = ADDR_FRAMEBUFFER_DATA+FRAME_DATA_HEADER; + int size, ret; + + frame->node_data = NULL; + + if (!(type == MS_TOUCH_ACTIVE || type == MS_TOUCH_LOW_POWER + || type == MS_TOUCH_ULTRA_LOW_POWER + || type == MS_KEY)) { + logError(1, "%s %s:Choose a MS type of frame data ERROR %02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestFrame(type); + if (ret < 0) { + logError(1, "%s readMutualSenseCompensation:ERROR %02X\n", + tag, ERROR_REQU_COMP_DATA); + return (ret | ERROR_REQU_COMP_DATA); + } + + ret = readFrameDataHeader(type, &(frame->header)); + if (ret < 0) { + logError(1, "%s readMutualSenseCompensationData:ERROR %02X\n", + tag, ERROR_COMP_DATA_HEADER); + return (ret | ERROR_COMP_DATA_HEADER); + } + + switch (type) { + case MS_TOUCH_ACTIVE: + case MS_TOUCH_LOW_POWER: + case MS_TOUCH_ULTRA_LOW_POWER: + size = frame->header.force_node * frame->header.sense_node; + break; + case MS_KEY: + //or use directly the number in the ftsChip + if (frame->header.force_node > frame->header.sense_node) + size = frame->header.force_node; + else + size = frame->header.sense_node; + frame->header.force_node = 1; + frame->header.sense_node = size; + break; + + default: + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + frame->node_data = (short *)kmalloc_array(size, + sizeof(short), GFP_KERNEL); + if (frame->node_data == NULL) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = getFrameData(offset, size*BYTES_PER_NODE, &(frame->node_data)); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_GET_FRAME_DATA); + kfree(frame->node_data); + return (ret | ERROR_GET_FRAME_DATA); + } + // if you want to access one node i,j, + //you should compute the offset like: + //offset = i * columns + j = > frame[i, j] + logError(0, "%s Frame acquired!\n", tag); + frame->node_data_size = size; + return size;//return the number of data put inside frame + +} + +int getSSFrame2(u16 type, struct SelfSenseFrame *frame) +{ + u16 offset = ADDR_FRAMEBUFFER_DATA + FRAME_DATA_HEADER; + int size, ret; + short *temp = NULL; + + frame->force_data = NULL; + frame->sense_data = NULL; + + if (!(type == SS_TOUCH || type == SS_KEY || type == SS_HOVER + || type == SS_PROXIMITY)) { + logError(1, "%s %s:Choose a SS type of frame data ERROR %02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestFrame(type); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_REQU_COMP_DATA); + return (ret | ERROR_REQU_COMP_DATA); + } + + ret = readFrameDataHeader(type, &(frame->header)); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_COMP_DATA_HEADER); + return (ret | ERROR_COMP_DATA_HEADER); + } + + switch (type) { + case SS_TOUCH: + case SS_HOVER: + case SS_PROXIMITY: + size = frame->header.force_node + frame->header.sense_node; + break; + + default: + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + + temp = (short *)kmalloc_array(size, sizeof(short), GFP_KERNEL); + if (temp == NULL) { + logError(1, "%s %s: temp ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = getFrameData(offset, size*BYTES_PER_NODE, &temp); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_GET_FRAME_DATA); + kfree(temp); + return (ret | ERROR_GET_FRAME_DATA); + } + + frame->force_data = (short *)kmalloc_array(frame->header.force_node, + sizeof(short), GFP_KERNEL); + if (frame->force_data == NULL) { + logError(1, "%s %s: frame->force_data ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + kfree(temp); + return ERROR_ALLOC; + } + + memcpy(frame->force_data, temp, + frame->header.force_node * sizeof(short)); + + frame->sense_data = (short *)kmalloc_array(frame->header.sense_node, + sizeof(short), GFP_KERNEL); + if (frame->sense_data == NULL) { + logError(1, "%s %s: frame->sense_data ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + kfree(temp); + kfree(frame->force_data); + return ERROR_ALLOC; + } + + memcpy(frame->sense_data, &temp[frame->header.force_node], + frame->header.sense_node * sizeof(short)); + + logError(0, "%s Frame acquired!\n", tag); + kfree(temp); + return size; //return the number of data put inside frame +} diff --git a/st/fts_lib/ftsFrame.h b/st/fts_lib/ftsFrame.h new file mode 100644 index 0000000000..6945a619eb --- /dev/null +++ b/st/fts_lib/ftsFrame.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS functions for getting frames * + * * + ************************************************************************** + ************************************************************************** + */ + +#ifndef __FTS_FRAME_H +#define __FTS_FRAME_H + +#include "ftsSoftware.h" + +//Number of data bytes for each node +#define BYTES_PER_NODE 2 +#define OFFSET_LENGTH 2 +#define FRAME_DATA_HEADER 8 +#define FRAME_HEADER_SIGNATURE 0xB5 +#define FRAME_DATA_READ_RETRY 2 + +struct MutualSenseFrame { + struct DataHeader header; + short *node_data; + int node_data_size; +}; + +struct SelfSenseFrame { + struct DataHeader header; + short *force_data; + short *sense_data; +}; + +int getOffsetFrame(u16 address, u16 *offset); +int getChannelsLength(void); +int getFrameData(u16 address, int size, short **frame); +int getMSFrame(u16 type, struct MutualSenseFrame *frame, int keep_first_row); +//int getMSKeyFrame(u16 type, short **frame); +//int getSSFrame(u16 type, short **frame); +//int getNmsFrame(u16 type, short ***frames, int * sizes, +//int keep_first_row, int fs, int n); +int getSenseLen(void); +int getForceLen(void); +int requestFrame(u16 type); +int readFrameDataHeader(u16 type, struct DataHeader *header); +int getMSFrame2(u16 type, struct MutualSenseFrame *frame); +int getSSFrame2(u16 type, struct SelfSenseFrame *frame); + +#endif + diff --git a/st/fts_lib/ftsGesture.c b/st/fts_lib/ftsGesture.c new file mode 100644 index 0000000000..dfd2dae00c --- /dev/null +++ b/st/fts_lib/ftsGesture.c @@ -0,0 +1,651 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Gesture Utilities * + * * + ************************************************************************** + ************************************************************************** + */ + +#include "ftsSoftware.h" +#include "ftsError.h" +#include "ftsGesture.h" +#include "ftsIO.h" +#include "ftsTool.h" + + +static char tag[8] = "[ FTS ]\0"; + +static u8 gesture_mask[GESTURE_MASK_SIZE] = { 0 }; +static u8 custom_gestures[GESTURE_CUSTOM_NUMBER][GESTURE_CUSTOM_POINTS]; +static u8 custom_gesture_index[GESTURE_CUSTOM_NUMBER] = { 0 }; +static int refreshGestureMask; + +u16 gesture_coordinates_x[GESTURE_COORDS_REPORT_MAX] = {0}; +u16 gesture_coordinates_y[GESTURE_COORDS_REPORT_MAX] = {0}; +int gesture_coords_reported = ERROR_OP_NOT_ALLOW; +struct mutex gestureMask_mutex; + + +int updateGestureMask(u8 *mask, int size, int en) +{ + u8 temp; + int i; + + if (mask == NULL) { + logError(1, "%s %s: Mask NULL! ERROR %08X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + if (size > GESTURE_MASK_SIZE) { + logError(1, "%s %s:Size not valid! %d > %d ERROR %08X\n", + tag, __func__, size, GESTURE_MASK_SIZE); + return ERROR_OP_NOT_ALLOW; + } + + if (en == FEAT_ENABLE) { + mutex_lock(&gestureMask_mutex); + logError(0, "%s %s:setting gesture mask to enable..\n", + tag, __func__); + if (mask != NULL) { + for (i = 0; i < size; i++) { + //back up the gesture enabled + gesture_mask[i] = gesture_mask[i] | mask[i]; + } + } + refreshGestureMask = 1; + logError(0, "%s %s:gesture mask to enable SET!\n", + tag, __func__); + mutex_unlock(&gestureMask_mutex); + return OK; + } + if (en == FEAT_DISABLE) { + mutex_lock(&gestureMask_mutex); + logError(0, "%s %s:setting gesture ", tag, __func__); + logError(0, "mask to disable...\n"); + for (i = 0; i < size; i++) { + // enabled XOR disabled + temp = gesture_mask[i] ^ mask[i]; + gesture_mask[i] = temp & gesture_mask[i]; + } + logError(0, "%s %s:gesture mask to disable SET!\n", + tag, __func__); + refreshGestureMask = 1; + mutex_unlock(&gestureMask_mutex); + return OK; + } + logError(1, "%s%s:Enable parameter Invalid%d!=%d or%d%:08X", + tag, __func__, FEAT_DISABLE, + FEAT_ENABLE, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + +} + +int enableGesture(u8 *mask, int size) +{ + u8 cmd[GESTURE_MASK_SIZE + 2]; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int i, res; + int event_to_search[4] = { EVENTID_GESTURE, + EVENT_TYPE_ENB, 0x00, GESTURE_ENABLE }; + + logError(0, "%s Trying to enable gesture...\n", tag); + cmd[0] = FTS_CMD_GESTURE_CMD; + cmd[1] = GESTURE_ENABLE; + + + if (size > GESTURE_MASK_SIZE) { + logError(1, "%s %s: Size not valid! %d > %d ERROR %08X\n", + tag, __func__, size, GESTURE_MASK_SIZE); + return ERROR_OP_NOT_ALLOW; + } + + mutex_lock(&gestureMask_mutex); + if (mask != NULL) { + for (i = 0; i < size; i++) { + cmd[i + 2] = mask[i]; + //back up of the gesture enabled + gesture_mask[i] = gesture_mask[i] | mask[i]; + } + while (i < GESTURE_MASK_SIZE) { + cmd[i + 2] = gesture_mask[i]; + i++; + } + } else { + for (i = 0; i < GESTURE_MASK_SIZE; i++) + cmd[i + 2] = gesture_mask[i]; + } + + res = fts_writeFwCmd(cmd, GESTURE_MASK_SIZE + 2); + if (res < OK) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, res); + goto END; + } + + res = pollForEvent(event_to_search, 4, + readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s %s: pollForEvent ERROR %08X\n", + tag, __func__, res); + goto END; + } + + if (readData[4] != 0x00) { + logError(1, "%s %s: ERROR %08X\n", + tag, __func__, ERROR_GESTURE_ENABLE_FAIL); + res = ERROR_GESTURE_ENABLE_FAIL; + goto END; + } + + logError(0, "%s %s: DONE!\n", tag, __func__); + res = OK; + +END: + mutex_unlock(&gestureMask_mutex); + return res; +} + + +int disableGesture(u8 *mask, int size) +{ + u8 cmd[2 + GESTURE_MASK_SIZE]; + u8 readData[FIFO_EVENT_SIZE] = {0}; + u8 temp; + int i, res; + int event_to_search[4] = { EVENTID_GESTURE, + EVENT_TYPE_ENB, 0x00, GESTURE_DISABLE }; + + logError(0, "%s Trying to disable gesture...\n", tag); + cmd[0] = FTS_CMD_GESTURE_CMD; + cmd[1] = GESTURE_DISABLE; + + if (size > GESTURE_MASK_SIZE) { + logError(1, "%s %s: Size not valid! %d > %d ERROR %08X\n", + tag, __func__, size, GESTURE_MASK_SIZE); + return ERROR_OP_NOT_ALLOW; + } + mutex_lock(&gestureMask_mutex); + if (mask != NULL) { + for (i = 0; i < size; i++) { + cmd[i + 2] = mask[i]; + // enabled XOR disabled + temp = gesture_mask[i] ^ mask[i]; + gesture_mask[i] = temp & gesture_mask[i]; + } + while (i < GESTURE_MASK_SIZE) { + //cmd[i + 2] = gesture_mask[i]; + //gesture_mask[i] = 0x00; + + cmd[i + 2] = 0x00; + //leave untouched the gestures not specified + + i++; + } + } else { + for (i = 0; i < GESTURE_MASK_SIZE; i++) { + //cmd[i + 2] = gesture_mask[i]; + cmd[i + 2] = 0xFF; + } + } + + res = fts_writeFwCmd(cmd, 2 + GESTURE_MASK_SIZE); + if (res < OK) { + logError(1, "%s %s:ERROR %08X\n", tag, __func__, res); + goto END; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s %s: pollForEvent ERROR %08X\n", + tag, __func__, res); + goto END; + } + + if (readData[4] != 0x00) { + logError(1, "%s %s:ERROR %08X\n", + tag, __func__, ERROR_GESTURE_ENABLE_FAIL); + res = ERROR_GESTURE_ENABLE_FAIL; + goto END; + } + + logError(0, "%s %s: DONE!\n", tag, __func__); + res = OK; +END: + mutex_unlock(&gestureMask_mutex); + return res; + +} + +int startAddCustomGesture(u8 gestureID) +{ + u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GESTURE_START_ADD, gestureID }; + int res; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, + gestureID, GESTURE_START_ADD }; + + res = fts_writeFwCmd(cmd, 3); + if (res < OK) { + logError(1, + "%s%s:Impossible to start adding custom gesture ID:%02X %08X\n", + tag, __func__, gestureID, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s %s:start add event not found! ERROR %08X\n", + tag, __func__, res); + return res; + } + //check of gestureID is redundant + if (readData[2] != gestureID || readData[4] != 0x00) { + logError(1, "%s %s:start add event status not OK! ERROR %08X\n", + tag, __func__, readData[4]); + return ERROR_GESTURE_START_ADD; + } + + return OK; +} + +int finishAddCustomGesture(u8 gestureID) +{ + u8 cmd[3] = { FTS_CMD_GESTURE_CMD, + GESTURE_FINISH_ADD, gestureID }; + int res; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, + gestureID, GESTURE_FINISH_ADD }; + + res = fts_writeFwCmd(cmd, 3); + if (res < OK) { + logError(1, + "%s%s:Impossible to finish adding custom gestureID:%02X %08X\n", + tag, __func__, gestureID, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s %s: finish add event not found! ERROR %08X\n", + tag, __func__, res); + return res; + } + //check of gestureID is redundant + if (readData[2] != gestureID || readData[4] != 0x00) { + logError(1, + "%s %s:finish add event status not OK! ERROR %08X\n", + tag, __func__, readData[4]); + return ERROR_GESTURE_FINISH_ADD; + } + + return OK; +} + +int loadCustomGesture(u8 *template, u8 gestureID) +{ + int res, i, wheel; + int remaining = GESTURE_CUSTOM_POINTS; + int toWrite, offset = 0; + u8 cmd[TEMPLATE_CHUNK + 5]; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, + gestureID, GESTURE_DATA_ADD }; + u8 readData[FIFO_EVENT_SIZE] = {0}; + + logError(0, "%s Starting adding custom gesture procedure...\n", tag); + + res = startAddCustomGesture(gestureID); + if (res < OK) { + logError(1, "%s %s:unable to start adding procedure %08X\n", + tag, __func__, res); + return res; + } + + cmd[0] = FTS_CMD_GESTURE_CMD; + cmd[1] = GESTURE_DATA_ADD; + cmd[2] = gestureID; + wheel = 0; + while (remaining > 0) { + if (remaining > TEMPLATE_CHUNK) + toWrite = TEMPLATE_CHUNK; + else + toWrite = remaining; + + cmd[3] = toWrite; + cmd[4] = offset; + for (i = 0; i < toWrite; i++) + cmd[i + 5] = template[wheel++]; + + res = fts_writeFwCmd(cmd, toWrite + 5); + if (res < OK) { + logError(1, "%s %s:unable to start ", tag, __func__); + logError(1, "adding procedure %08X\n", res); + return res; + } + + res = pollForEvent(event_to_search, + 4, + readData, + GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s %s: add event not found! ERROR %08X\n", + tag, __func__, res); + return res; + } + //check of gestureID is redundant + if (readData[2] != gestureID || readData[4] != 0x00) { + logError(1, "%s %s:add event status not OK! ", + tag, __func__); + logError(1, "ERROR %08X\n", readData[4]); + return ERROR_GESTURE_DATA_ADD; + } + + remaining -= toWrite; + offset += toWrite / 2; + } + + res = finishAddCustomGesture(gestureID); + if (res < OK) { + logError(1, "%s %s:unable to finish adding procedure! ", + tag, __func__); + logError(1, "ERROR %08X\n", res); + return res; + } + + logError(0, "%s Adding custom gesture procedure DONE!\n", tag); + return OK; + +} + + +int reloadCustomGesture(void) +{ + int res, i; + + logError(0, "%s Starting reload Gesture Template...\n", tag); + + for (i = 0; i < GESTURE_CUSTOM_NUMBER; i++) { + if (custom_gesture_index[i] == 1) { + res = loadCustomGesture(custom_gestures[i], + GESTURE_CUSTOM_OFFSET + i); + if (res < OK) { + logError(1, "%s %s:Impossible load gesture ", + tag, __func__); + logError(1, "D:%02X %08X\n", + GESTURE_CUSTOM_OFFSET + i, res); + return res; + } + } + } + + logError(0, "%s Reload Gesture Template DONE!\n", tag); + return OK; + +} + +int enterGestureMode(int reload) +{ + u8 cmd = FTS_CMD_GESTURE_MODE; + int res, ret; + + res = fts_disableInterrupt(); + if (res < OK) { + logError(1, "%s %s: ERROR %08X\n", + tag, __func__, res | ERROR_DISABLE_INTER); + return res | ERROR_DISABLE_INTER; + } + + if (reload == 1 || refreshGestureMask == 1) { + if (reload == 1) { + res = reloadCustomGesture(); + if (res < OK) { + logError(1, "%s %s:impossible reload ", + tag, __func__); + logError(1, "custom gesture %08X\n", res); + goto END; + } + } + + /** + * mandatory steps to set the correct gesture + * mask defined by the user + */ + res = disableGesture(NULL, 0); + if (res < OK) { + logError(1, "%s %s:disableGesture ERROR %08X\n", + tag, res); + goto END; + } + + res = enableGesture(NULL, 0); + if (res < OK) { + logError(1, "%s %s:enableGesture ERROR %08X\n", + tag, __func__, res); + goto END; + } + + refreshGestureMask = 0; + /**************************************************/ + } + + res = fts_writeFwCmd(&cmd, 1); + if (res < OK) { + logError(1, "%s %s:enter gesture mode ERROR %08X\n", + tag, __func__, res); + goto END; + } + + res = OK; +END: + ret = fts_enableInterrupt(); + if (ret < OK) { + logError(1, "%s %s:fts_enableInterrupt ERROR %08X\n", + tag, __func__, res | ERROR_ENABLE_INTER); + res |= ret | ERROR_ENABLE_INTER; + } + + return res; +} + +int addCustomGesture(u8 *data, int size, u8 gestureID) +{ + int index, res, i; + + index = gestureID - GESTURE_CUSTOM_OFFSET; + + logError(0, "%s Starting Custom Gesture Adding procedure...\n", tag); + if (size != GESTURE_CUSTOM_POINTS || (gestureID != GES_ID_CUST1 + && gestureID != GES_ID_CUST2 && gestureID != GES_ID_CUST3 + && gestureID != GES_ID_CUST4 && gestureID != GES_ID_CUST5)) { + logError(1, "%s %s:Invalid size(%d) or Custom GestureID ", + tag, __func__, size); + logError(1, "(%02X)!ERROR%08X\n", + size, gestureID, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + for (i = 0; i < GESTURE_CUSTOM_POINTS; i++) + custom_gestures[index][i] = data[i]; + + res = loadCustomGesture(custom_gestures[index], gestureID); + if (res < OK) { + logError(1, "%s %s:impossible to load the custom gesture! ", + tag, __func__); + logError(1, "ERROR %08X\n", res); + return res; + } + + custom_gesture_index[index] = 1; + logError(0, "%s Custom Gesture Adding procedure DONE!\n", tag); + return OK; +} + +int removeCustomGesture(u8 gestureID) +{ + int res, index; + u8 cmd[3] = { FTS_CMD_GESTURE_CMD, GETURE_REMOVE_CUSTOM, gestureID }; + int event_to_search[4] = { EVENTID_GESTURE, EVENT_TYPE_ENB, + gestureID, GETURE_REMOVE_CUSTOM }; + u8 readData[FIFO_EVENT_SIZE] = {0}; + + index = gestureID - GESTURE_CUSTOM_OFFSET; + + logError(0, "%s Starting Custom Gesture Removing procedure...\n", tag); + if (gestureID != GES_ID_CUST1 && gestureID != GES_ID_CUST2 && + gestureID != GES_ID_CUST3 && gestureID != + GES_ID_CUST4 && gestureID != GES_ID_CUST5) { + logError(1, "%s %s:Invalid Custom GestureID (%02X)! ", + tag, __func__, gestureID); + logError(1, "ERROR %08X\n", ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + //when a gesture is removed, it is also disabled automatically + res = fts_writeFwCmd(cmd, 3); + if (res < OK) { + logError(1, "%s %s:Impossible to remove custom ", + tag, __func__); + logError(1, "%gesture ID:%02X %08X\n", gestureID, res); + return res; + } + + res = pollForEvent(event_to_search, 4, readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(1, "%s %s:remove event not found! ERROR %08X\n", + tag, __func__, res); + return res; + } + //check of gestureID is redundant + if (readData[2] != gestureID || readData[4] != 0x00) { + logError(1, "%s %s:remove event status not OK! ERROR %08X\n", + tag, __func__, readData[4]); + return ERROR_GESTURE_REMOVE; + } + + custom_gesture_index[index] = 0; + logError(0, "%s Custom Gesture Remove procedure DONE!\n", tag); + return OK; + +} + +int isAnyGestureActive(void) +{ + int res = 0; + /*-1 because in any case the last gesture mask byte will*/ + /*be evaluated with the following if*/ + while (res < (GESTURE_MASK_SIZE - 1) && gesture_mask[res] == 0) + res++; + + if (gesture_mask[res] == 0) { + logError(0, "%s %s: All Gestures Disabled!\n", tag, __func__); + return FEAT_DISABLE; + } + + logError(0, "%s %s:Active Gestures Found! gesture_mask[%d] = %02X!\n", + tag, __func__, res, gesture_mask[res]); + return FEAT_ENABLE; +} + +int gestureIDtoGestureMask(u8 id, u8 *mask) +{ + logError(0, "%s %s: Index = %d Position = %d!\n", + tag, __func__, ((int)((id) / 8)), (id % 8)); + mask[((int)((id) / 8))] |= 0x01 << (id % 8); + return OK; +} + + +int readGestureCoords(u8 *event) +{ + int i = 0; + u8 rCmd[3] = {FTS_CMD_FRAMEBUFFER_R, 0x00, 0x00 }; + int res; + unsigned char val[GESTURE_COORDS_REPORT_MAX * 4 + 1]; + + //the max coordinates to read are GESTURE_COORDS_REPORT_MAX*4 + //(because each coordinate is a short(*2) and we have x and y) + //+ dummy byte + if (event[0] != EVENTID_GESTURE + || event[1] != EVENT_TYPE_GESTURE_DTC2) { + + logError(1, "%s %s:The event passsed as argument is invalid! ", + tag, __func__); + logError(1, "ERROR %08X\n", ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + rCmd[1] = event[4]; // Offset address L + rCmd[2] = event[3]; // Offset address H + //number of coords reported L + gesture_coords_reported = event[6]; + //number of coords reported H + gesture_coords_reported = (gesture_coords_reported << 8) | event[5]; + if (gesture_coords_reported > GESTURE_COORDS_REPORT_MAX) { + logError(1, "%s%s:FW reported more than:%d points ", + tag, __func__, gesture_coords_reported); + logError(1, "for gestures!\n"); + logError(1, " Decreasing to %d\n", GESTURE_COORDS_REPORT_MAX); + gesture_coords_reported = GESTURE_COORDS_REPORT_MAX; + } + + logError(1, "%s %s: Offset: %02X %02X points = %d\n", tag, + __func__, rCmd[1], rCmd[2], gesture_coords_reported); + res = fts_readCmd(rCmd, 3, (unsigned char *)val, + 1 + (gesture_coords_reported * 2)); + if (res < OK) { + logError(1, "%s %s: Cannot read the coordinates! ERROR %08X\n", + tag, __func__, res); + gesture_coords_reported = ERROR_OP_NOT_ALLOW; + return res; + } + //all the points of the gesture are stored in val + for (i = 0; i < gesture_coords_reported; i++) { + //ignore first byte data because it is a dummy byte + gesture_coordinates_x[i] = (((u16) val[i * 2 + 1 + 1]) + & 0x0F) << 8 | (((u16) val[i * 2 + 1]) & 0xFF); + gesture_coordinates_y[i] = + (((u16)val[gesture_coords_reported * 2 + + i * 2 + 1 + 1]) & 0x0F) << 8 + | (((u16)val[gesture_coords_reported * 2 + + i * 2 + 1]) & 0xFF); + } + + logError(1, "%s %s: Reading Gesture Coordinates DONE!\n", + tag, __func__); + return OK; +} + +int getGestureCoords(u16 *x, u16 *y) +{ + x = gesture_coordinates_x; + y = gesture_coordinates_y; + logError(1, "%s %s:Number of gesture coordinates returned = %d\n", + tag, __func__, gesture_coords_reported); + return gesture_coords_reported; +} diff --git a/st/fts_lib/ftsGesture.h b/st/fts_lib/ftsGesture.h new file mode 100644 index 0000000000..c5c2a6abbf --- /dev/null +++ b/st/fts_lib/ftsGesture.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Gesture Utilities * + * * + ************************************************************************** + ************************************************************************** + */ + +#ifndef _FTS_GESTURE_H_ +#define _FTS_GESTURE_H_ + +#include "ftsHardware.h" + +#define GESTURE_MASK_SIZE 8 + +//max number of gestures coordinates reported +#define GESTURE_COORDS_REPORT_MAX 32 + +// for each custom gesture should be provided 30 +//points (each point is a couple of x,y) +#define GESTURE_CUSTOM_POINTS (30 * 2) + +//fw support up to 5 custom gestures +#define GESTURE_CUSTOM_NUMBER 5 + +#ifdef FTM3 + +//number of points to transfer with each I2C transaction +#define TEMPLATE_CHUNK (10 * 2) +#else +//number of points to transfer with each I2C transaction +#define TEMPLATE_CHUNK (5 * 2) +#endif + + +//Gesture IDs +#define GES_ID_UNKNOWN 0x00 //no meaningful gesture +#define GES_ID_DBLTAP 0x01 //Double Tap +#define GES_ID_O 0x02 //'O' +#define GES_ID_C 0x03 //'C' +#define GES_ID_M 0x04 //'M' +#define GES_ID_W 0x05 //'W' +#define GES_ID_E 0x06 //'e' +#define GES_ID_HFLIP_L2R 0x07 //Left to right line +#define GES_ID_HFLIP_R2L 0x08 //Right to left line +#define GES_ID_VFLIP_T2D 0x09 //Top to bottom line +#define GES_ID_VFLIP_D2T 0x0A //Bottom to Top line +#define GES_ID_L 0x0B //'L' +#define GES_ID_F 0x0C //'F' +#define GES_ID_V 0x0D //'V' +#define GES_ID_AT 0x0E //'@' +#define GES_ID_S 0x0F //'S' +#define GES_ID_Z 0x10 //'Z' +#define GES_ID_CUST1 0x11 //Custom gesture 1 +#define GES_ID_CUST2 0x12 //Custom gesture 2 +#define GES_ID_CUST3 0x13 //Custom gesture 3 +#define GES_ID_CUST4 0x14 //Custom gesture 4 +#define GES_ID_CUST5 0x15 //Custom gesture 5 +#define GES_ID_LEFTBRACE 0x20 //'<' +#define GES_ID_RIGHTBRACE 0x21 //'>' + +#define GESTURE_CUSTOM_OFFSET GES_ID_CUST1 + +//Command sub-type +#define GESTURE_ENABLE 0x01 +#define GESTURE_DISABLE 0x02 +#define GESTURE_ENB_CHECK 0x03 +#define GESTURE_START_ADD 0x10 +#define GESTURE_DATA_ADD 0x11 +#define GESTURE_FINISH_ADD 0x12 +#define GETURE_REMOVE_CUSTOM 0x13 +#define GESTURE_CHECK_CUSTOM 0x14 + + +//Event sub-type +#define EVENT_TYPE_ENB 0x04 +#define EVENT_TYPE_CHECK_ENB 0x03 +#define EVENT_TYPE_GESTURE_DTC1 0x01 +#define EVENT_TYPE_GESTURE_DTC2 0x02 + +int updateGestureMask(u8 *mask, int size, int en); +int disableGesture(u8 *mask, int size); +int enableGesture(u8 *mask, int size); +int startAddCustomGesture(u8 gestureID); +int finishAddCustomGesture(u8 gestureID); +int loadCustomGesture(u8 *template, u8 gestureID); +int reloadCustomGesture(void); +int enterGestureMode(int reload); +int addCustomGesture(u8 *data, int size, u8 gestureID); +int removeCustomGesture(u8 gestureID); +int isAnyGestureActive(void); +int gestureIDtoGestureMask(u8 id, u8 *mask); +int readGestureCoords(u8 *event); +int getGestureCoords(u16 *x, u16 *y); + +#endif diff --git a/st/fts_lib/ftsHardware.h b/st/fts_lib/ftsHardware.h new file mode 100644 index 0000000000..553cf6d576 --- /dev/null +++ b/st/fts_lib/ftsHardware.h @@ -0,0 +1,213 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * HW related data ** + * * + ************************************************************************** + ************************************************************************** + */ + +#ifndef __FTS_HARDWARE_H +#define __FTS_HARDWARE_H + +//DUMMY BYTES DATA +#define DUMMY_HW_REG 1 +#define DUMMY_FRAMEBUFFER 1 +#define DUMMY_MEMORY 1 + +//DIGITAL CHIP INFO +#ifdef FTM3_CHIP +#define DCHIP_ID_0 0x39 +#define DCHIP_ID_1 0x6C +#else +#define DCHIP_ID_0 0x36 +#define DCHIP_ID_1 0x70 +#endif + +#ifdef FTM3_CHIP +#define DCHIP_ID_ADDR 0x0007 +#define DCHIP_FW_VER_ADDR 0x000A +#else +#define DCHIP_ID_ADDR 0x0004 +#define DCHIP_FW_VER_ADDR 0x0008 +#endif + +#define DCHIP_FW_VER_BYTE 2 + +//CHUNKS +#define READ_CHUNK (2 * 1024) +#define WRITE_CHUNK (2 * 1024) +#define MEMORY_CHUNK (2 * 1024) + +//PROTOCOL INFO +#ifdef FTM3_CHIP +#define I2C_SAD 0x49 +#else +#define I2C_SAD 0x49 +#endif + +#define I2C_INTERFACE //comment if the chip use SPI +#define ICR_ADDR 0x0024 +#define ICR_SPI_VALUE 0x02 + +//SYSTEM RESET INFO +#ifdef FTM3_CHIP +#define SYSTEM_RESET_ADDRESS 0x0023 +#define SYSTEM_RESET_VALUE 0x01 +#else +#define SYSTEM_RESET_ADDRESS 0x0028 +#define SYSTEM_RESET_VALUE 0x80 +#endif + +//INTERRUPT INFO +#ifdef FTM3_CHIP +#define IER_ADDR 0x001C +#else +#define IER_ADDR 0x002C +#endif + +#define IER_ENABLE 0x41 +#define IER_DISABLE 0x00 + +//FLASH COMMAND + +#define FLASH_CMD_UNLOCK 0xF7 + + +#ifdef FTM3_CHIP +#define FLASH_CMD_WRITE_LOWER_64 0xF0 +#define FLASH_CMD_WRITE_UPPER_64 0xF1 +#define FLASH_CMD_BURN 0xF2 +#define FLASH_CMD_ERASE 0xF3 +#define FLASH_CMD_READSTATUS 0xF4 +#else +#define FLASH_CMD_WRITE_64K 0xF8 +#define FLASH_CMD_READ_REGISTER 0xF9 +#define FLASH_CMD_WRITE_REGISTER 0xFA +#endif + +//FLASH UNLOCK PARAMETER +#define FLASH_UNLOCK_CODE0 0x74 +#define FLASH_UNLOCK_CODE1 0x45 + +#ifndef FTM3_CHIP +//FLASH ERASE and DMA PARAMETER +#define FLASH_ERASE_UNLOCK_CODE0 0x72 +#define FLASH_ERASE_UNLOCK_CODE1 0x03 +#define FLASH_ERASE_UNLOCK_CODE2 0x02 +#define FLASH_ERASE_CODE0 0x02 +#define FLASH_ERASE_CODE1 0xC0 +#define FLASH_DMA_CODE0 0x05 +#define FLASH_DMA_CODE1 0xC0 +#define FLASH_DMA_CONFIG 0x06 +#define FLASH_ERASE_START 0x80 +#define FLASH_NUM_PAGE 64//number of pages +#define FLASH_CX_PAGE_START 61 +#define FLASH_CX_PAGE_END 62 +#endif + + +//FLASH ADDRESS +#ifdef FTM3_CHIP +#define FLASH_ADDR_SWITCH_CMD 0x00010000 +#define FLASH_ADDR_CODE 0x00000000 +#define FLASH_ADDR_CONFIG 0x0001E800 +#define FLASH_ADDR_CX 0x0001F000 +#else +#define ADDR_WARM_BOOT 0x001E +#define WARM_BOOT_VALUE 0x38 +#define FLASH_ADDR_CODE 0x00000000 +#define FLASH_ADDR_CONFIG 0x0000FC00 +#endif + + +//CRC ADDR +#ifdef FTM3_CHIP +#define ADDR_CRC_BYTE0 0x00 +#define ADDR_CRC_BYTE1 0x86 +#define CRC_MASK 0x02 +#else +#define ADDR_CRC_BYTE0 0x00 +#define ADDR_CRC_BYTE1 0x74 +#define CRC_MASK 0x03 +#endif + +//SIZES FW, CODE, CONFIG, MEMH +#ifdef FTM3_CHIP +#define FW_HEADER_SIZE 32 +#define FW_SIZE (int)(128*1024) +#define FW_CODE_SIZE (int)(122*1024) +#define FW_CONFIG_SIZE (int)(2*1024) +#define FW_CX_SIZE (int)(FW_SIZE-FW_CODE_SIZE-FW_CONFIG_SIZE) +#define FW_VER_MEMH_BYTE1 193 +#define FW_VER_MEMH_BYTE0 192 +#define FW_OFF_CONFID_MEMH_BYTE1 2 +#define FW_OFF_CONFID_MEMH_BYTE0 1 +#define FW_BIN_VER_OFFSET 4 +#define FW_BIN_CONFIG_VER_OFFSET (FW_HEADER_SIZE+FW_CODE_SIZE+1) +#else +#define FW_HEADER_SIZE 64 +#define FW_HEADER_SIGNATURE 0xAA55AA55 +#define FW_FTB_VER 0x00000001 +#define FW_BYTES_ALIGN 4 +#define FW_BIN_VER_OFFSET 16 +#define FW_BIN_CONFIG_VER_OFFSET 20 +#endif + +//FIFO +#define FIFO_EVENT_SIZE 8 + +#ifdef FTM3_CHIP +#define FIFO_DEPTH 32 +#else +#define FIFO_DEPTH 64 +#endif + +#define FIFO_CMD_READONE 0x85 +#define FIFO_CMD_READALL 0x86 +#define FIFO_CMD_LAST 0x87 +#define FIFO_CMD_FLUSH 0xA1 + + +//CONSTANT TOTAL CX +#ifdef FTM3_CHIP +#define CX1_WEIGHT 4 +#define CX2_WEIGHT 1 +#else +#define CX1_WEIGHT 8 +#define CX2_WEIGHT 1 +#endif + +//OP CODES FOR MEMORY (based on protocol) + +#define FTS_CMD_HW_REG_R 0xB6 +#define FTS_CMD_HW_REG_W 0xB6 +#define FTS_CMD_FRAMEBUFFER_R 0xD0 +#define FTS_CMD_FRAMEBUFFER_W 0xD0 + +#endif diff --git a/st/fts_lib/ftsIO.c b/st/fts_lib/ftsIO.c new file mode 100644 index 0000000000..ba0fb18e33 --- /dev/null +++ b/st/fts_lib/ftsIO.c @@ -0,0 +1,526 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * I2C/SPI Communication ** + * * + ************************************************************************** + ************************************************************************** + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ftsSoftware.h" +#include "ftsCrossCompile.h" +#include "ftsError.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsTool.h" +#include "../fts.h" + +static char tag[8] = "[ FTS ]\0"; +static struct i2c_client *client; +static u16 I2CSAD; + +int openChannel(struct i2c_client *clt) +{ + client = clt; + I2CSAD = clt->addr; + logError(0, "%s %s: SAD: %02X\n", tag, __func__, I2CSAD); + return OK; +} + +struct device *getDev() +{ + if (client != NULL) + return &(client->dev); + else + return NULL; +} + +struct i2c_client *getClient() +{ + if (client != NULL) + return client; + else + return NULL; +} + +int fts_readCmd(u8 *cmd, int cmdLength, u8 *outBuf, int byteToRead) +{ + int ret = -1; + int retry = 0; + struct i2c_msg I2CMsg[2]; + struct fts_ts_info *info = i2c_get_clientdata(client); + uint8_t *buf = info->i2c_data; + + /* + * Reassign memory for i2c_data in case length is greater than 32 bytes + */ + if (info->i2c_data_len < cmdLength + byteToRead + 1) { + kfree(info->i2c_data); + info->i2c_data = kmalloc(cmdLength + byteToRead + 1, + GFP_KERNEL); + if (!info->i2c_data) { + info->i2c_data_len = 0; + return -ENOMEM; + } + info->i2c_data_len = cmdLength + byteToRead + 1; + buf = info->i2c_data; + } + + //write msg + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)cmdLength; + I2CMsg[0].buf = buf; + + //read msg + I2CMsg[1].addr = (__u16)I2CSAD; + I2CMsg[1].flags = I2C_M_RD; + I2CMsg[1].len = byteToRead; + I2CMsg[1].buf = buf + cmdLength; + + memcpy(buf, cmd, cmdLength); + + if (client == NULL) + return ERROR_I2C_O; + while (retry < I2C_RETRY && ret < OK) { + ret = i2c_transfer(client->adapter, I2CMsg, 2); + retry++; + if (ret < OK) + msleep(I2C_WAIT_BEFORE_RETRY); + } + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; + } + + memcpy(outBuf, buf + cmdLength, byteToRead); + + return OK; +} + +int fts_writeCmd(u8 *cmd, int cmdLength) +{ + int ret = -1; + int retry = 0; + struct i2c_msg I2CMsg[1]; + struct fts_ts_info *info = i2c_get_clientdata(client); + uint8_t *buf = info->i2c_data; + + /* + * Reassign memory for i2c_data in case length is greater than 32 bytes + */ + if (info->i2c_data_len < cmdLength + 1) { + kfree(info->i2c_data); + info->i2c_data = kmalloc(cmdLength + 1, GFP_KERNEL); + if (!info->i2c_data) { + info->i2c_data_len = 0; + return -ENOMEM; + } + info->i2c_data_len = cmdLength + 1; + buf = info->i2c_data; + } + + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)cmdLength; + I2CMsg[0].buf = buf; + + memcpy(buf, cmd, cmdLength); + + if (client == NULL) + return ERROR_I2C_O; + while (retry < I2C_RETRY && ret < OK) { + ret = i2c_transfer(client->adapter, I2CMsg, 1); + retry++; + if (ret < OK) + msleep(I2C_WAIT_BEFORE_RETRY); + //logError(1,"%s fts_writeCmd: attempt %d\n", tag, retry); + } + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + return OK; +} + +int fts_writeFwCmd(u8 *cmd, int cmdLength) +{ + int ret = -1; + int ret2 = -1; + int retry = 0; + struct i2c_msg I2CMsg[1]; + struct fts_ts_info *info = i2c_get_clientdata(client); + uint8_t *buf = info->i2c_data; + + /* + * Reassign memory for i2c_data in case length is greater than 32 bytes + */ + if (info->i2c_data_len < cmdLength + 1) { + kfree(info->i2c_data); + info->i2c_data = kmalloc(cmdLength + 1, GFP_KERNEL); + if (!info->i2c_data) { + info->i2c_data_len = 0; + return -ENOMEM; + } + info->i2c_data_len = cmdLength + 1; + buf = info->i2c_data; + } + + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)cmdLength; + I2CMsg[0].buf = buf; + + memcpy(buf, cmd, cmdLength); + + if (client == NULL) + return ERROR_I2C_O; + while (retry < I2C_RETRY && (ret < OK || ret2 < OK)) { + ret = i2c_transfer(client->adapter, I2CMsg, 1); + retry++; + if (ret >= 0) + ret2 = checkEcho(cmd, cmdLength); + if (ret < OK || ret2 < OK) + msleep(I2C_WAIT_BEFORE_RETRY); + //logError(1,"%s fts_writeCmd: attempt %d\n", tag, retry); + } + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + if (ret2 < OK) { + logError(1, "%s %s: check echo ERROR %02X\n", + tag, __func__, ret2); + return (ret | ERROR_I2C_W); + } + return OK; +} + + +int writeReadCmd(u8 *writeCmd1, int writeCmdLength, u8 *readCmd1, + int readCmdLength, u8 *outBuf, int byteToRead) +{ + int ret = -1; + int retry = 0; + struct i2c_msg I2CMsg[3]; + struct fts_ts_info *info = i2c_get_clientdata(client); + uint8_t *buf = info->i2c_data; + uint8_t wr_len = writeCmdLength + readCmdLength + byteToRead; + + /* + * Reassign memory for i2c_data in case length is greater than 32 bytes + */ + if (info->i2c_data_len < wr_len + 1) { + kfree(info->i2c_data); + info->i2c_data = kmalloc(wr_len + 1, GFP_KERNEL); + if (!info->i2c_data) { + info->i2c_data_len = 0; + return -ENOMEM; + } + info->i2c_data_len = wr_len + 1; + buf = info->i2c_data; + } + + //write msg + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)writeCmdLength; + I2CMsg[0].buf = buf; + + //write msg + I2CMsg[1].addr = (__u16)I2CSAD; + I2CMsg[1].flags = (__u16)0; + I2CMsg[1].len = (__u16)readCmdLength; + I2CMsg[1].buf = buf + writeCmdLength; + + //read msg + I2CMsg[2].addr = (__u16)I2CSAD; + I2CMsg[2].flags = I2C_M_RD; + I2CMsg[2].len = byteToRead; + I2CMsg[2].buf = buf + writeCmdLength + readCmdLength; + + memcpy(buf, writeCmd1, writeCmdLength); + memcpy(buf + writeCmdLength, readCmd1, readCmdLength); + + if (client == NULL) + return ERROR_I2C_O; + while (retry < I2C_RETRY && ret < OK) { + ret = i2c_transfer(client->adapter, I2CMsg, 3); + retry++; + if (ret < OK) + msleep(I2C_WAIT_BEFORE_RETRY); + } + + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_WR); + return ERROR_I2C_WR; + } + + memcpy(outBuf, buf + writeCmdLength + readCmdLength, byteToRead); + + return OK; +} + + +int readCmdU16(u8 cmd, u16 address, u8 *outBuf, int byteToRead, + int hasDummyByte) +{ + int remaining = byteToRead; + int toRead = 0; + u8 rCmd[3] = { cmd, 0x00, 0x00 }; + + u8 *buff = (u8 *)kmalloc_array(READ_CHUNK + 1, sizeof(u8), GFP_KERNEL); + + if (buff == NULL) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + if (remaining >= READ_CHUNK) { + toRead = READ_CHUNK; + remaining -= READ_CHUNK; + } else { + toRead = remaining; + remaining = 0; + } + + rCmd[1] = (u8)((address & 0xFF00) >> 8); + rCmd[2] = (u8)(address & 0xFF); + + if (hasDummyByte) { + if (fts_readCmd(rCmd, 3, buff, toRead + 1) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_R); + kfree(buff); + return ERROR_I2C_R; + } + memcpy(outBuf, buff + 1, toRead); + } else { + if (fts_readCmd(rCmd, 3, buff, toRead) < 0) + return ERROR_I2C_R; + memcpy(outBuf, buff, toRead); + } + + address += toRead; + outBuf += toRead; + } + kfree(buff); + + return OK; +} + + +int writeCmdU16(u8 WriteCmd, u16 address, u8 *dataToWrite, int byteToWrite) +{ + int remaining = byteToWrite; + int toWrite = 0; + + u8 *buff = (u8 *)kmalloc_array(WRITE_CHUNK + 3, sizeof(u8), GFP_KERNEL); + + if (buff == NULL) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + buff[0] = WriteCmd; + + while (remaining > 0) { + if (remaining >= WRITE_CHUNK) { + toWrite = WRITE_CHUNK; + remaining -= WRITE_CHUNK; + } else { + toWrite = remaining; + remaining = 0; + } + + buff[1] = (u8)((address & 0xFF00) >> 8); + buff[2] = (u8)(address & 0xFF); + memcpy(buff + 3, dataToWrite, toWrite); + if (fts_writeCmd(buff, 3 + toWrite) < 0) { + logError(1, "%s %s: ERROR %02\n", + tag, __func__, ERROR_I2C_W); + kfree(buff); + return ERROR_I2C_W; + } + address += toWrite; + dataToWrite += toWrite; + } + + kfree(buff); + return OK; +} + +int writeCmdU32(u8 writeCmd1, u8 writeCmd2, u32 address, u8 *dataToWrite, + int byteToWrite) +{ + int remaining = byteToWrite; + int toWrite = 0; + int ret; + + u8 buff1[3] = { writeCmd1, 0x00, 0x00 }; + u8 *buff2 = (u8 *)kmalloc_array(WRITE_CHUNK + 3, + sizeof(u8), GFP_KERNEL); + + if (buff2 == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + buff2[0] = writeCmd2; + + + while (remaining > 0) { + if (remaining >= WRITE_CHUNK) { + toWrite = WRITE_CHUNK; + remaining -= WRITE_CHUNK; + } else { + toWrite = remaining; + remaining = 0; + } + + buff1[1] = (u8)((address & 0xFF000000) >> 24); + buff1[2] = (u8)((address & 0x00FF0000) >> 16); + buff2[1] = (u8)((address & 0x0000FF00) >> 8); + buff2[2] = (u8)(address & 0xFF); + memcpy(buff2 + 3, dataToWrite, toWrite); + + if (fts_writeCmd(buff1, 3) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + ret = ERROR_I2C_W; + goto END; + } + if (fts_writeCmd(buff2, 3 + toWrite) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + ret = ERROR_I2C_W; + goto END; + } + + address += toWrite; + dataToWrite += toWrite; + } + + ret = OK; +END: + kfree(buff2); + return ret; +} + +int writeReadCmdU32(u8 wCmd, u8 rCmd, u32 address, u8 *outBuf, + int byteToRead, int hasDummyByte) +{ + int remaining = byteToRead; + int toRead = 0; + u8 reaCmd[3]; + u8 wriCmd[3]; + + u8 *buff = (u8 *)kmalloc_array(READ_CHUNK + 1, sizeof(u8), GFP_KERNEL); + + if (buff == NULL) { + logError(1, "%s writereadCmd32: ERROR %02X\n", + tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + reaCmd[0] = rCmd; + wriCmd[0] = wCmd; + + while (remaining > 0) { + if (remaining >= READ_CHUNK) { + toRead = READ_CHUNK; + remaining -= READ_CHUNK; + } else { + toRead = remaining; + remaining = 0; + } + + wriCmd[1] = (u8)((address & 0xFF000000) >> 24); + wriCmd[2] = (u8)((address & 0x00FF0000) >> 16); + + reaCmd[1] = (u8)((address & 0x0000FF00) >> 8); + reaCmd[2] = (u8)(address & 0x000000FF); + + if (hasDummyByte) { + if (writeReadCmd(wriCmd, 3, reaCmd, 3, + buff, toRead + 1) < 0) { + logError(1, "%s writeCmdU32: ERROR %02X\n", + tag, ERROR_I2C_WR); + kfree(buff); + return ERROR_I2C_WR; + } + memcpy(outBuf, buff + 1, toRead); + } else { + if (writeReadCmd(wriCmd, 3, reaCmd, + 3, buff, toRead) < 0) + return ERROR_I2C_WR; + memcpy(outBuf, buff, toRead); + } + + address += toRead; + outBuf += toRead; + } + + kfree(buff); + return OK; +} diff --git a/st/fts_lib/ftsIO.h b/st/fts_lib/ftsIO.h new file mode 100644 index 0000000000..efba899ea2 --- /dev/null +++ b/st/fts_lib/ftsIO.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * I2C/SPI Communication ** + * * + ************************************************************************** + ************************************************************************** + */ + +#ifndef __FTS_IO_H +#define __FTS_IO_H + +#include +#include + +#include "ftsSoftware.h" +#include "ftsCrossCompile.h" + +#define I2C_RETRY 3 //number of retry +#define I2C_WAIT_BEFORE_RETRY 2 //ms + +int openChannel(struct i2c_client *clt); +struct device *getDev(void); +struct i2c_client *getClient(void); +int fts_readCmd(u8 *cmd, int cmdLenght, u8 *outBuf, int byteToRead); +int fts_writeCmd(u8 *cmd, int cmdLenght); +int fts_writeFwCmd(u8 *cmd, int cmdLenght); +int writeReadCmd(u8 *writeCmd, int writeCmdLenght, u8 *readCmd, + int readCmdLenght, u8 *outBuf, int byteToRead); +int readCmdU16(u8 cmd, u16 address, u8 *outBuf, int byteToRead, + int hasDummyByte); +int writeCmdU16(u8 WriteCmd, u16 address, u8 *dataToWrite, int byteToWrite); +int writeCmdU32(u8 writeCmd1, u8 writeCmd2, u32 address, u8 *dataToWrite, + int byteToWrite); +int writeReadCmdU32(u8 wCmd, u8 rCmd, u32 address, u8 *outBuf, int byteToRead, + int hasDummyByte); + +#endif diff --git a/st/fts_lib/ftsSoftware.h b/st/fts_lib/ftsSoftware.h new file mode 100644 index 0000000000..0c52982856 --- /dev/null +++ b/st/fts_lib/ftsSoftware.h @@ -0,0 +1,191 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************* + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FW related data * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#ifndef __FTS_SOFTWARE_H +#define __FTS_SOFTWARE_H + +#include + +#include "ftsHardware.h" + +#define ECHO_ENABLED 0x00000001 + +//FTS FW COMMAND +#define FTS_CMD_MS_MT_SENSE_OFF 0x92 +#define FTS_CMD_MS_MT_SENSE_ON 0x93 +#define FTS_CMD_SS_HOVER_OFF 0x94 +#define FTS_CMD_SS_HOVER_ON 0x95 +#define FTS_CMD_LP_TIMER_CALIB 0x97 +#define FTS_CMD_MS_KEY_OFF 0x9A +#define FTS_CMD_MS_KEY_ON 0x9B +#define FTS_CMD_MS_COMP_TUNING 0xA3 +#define FTS_CMD_SS_COMP_TUNING 0xA4 +#define FTS_CMD_FULL_INITIALIZATION 0xA5 +#define FTS_CMD_ITO_CHECK 0xA7 +#define FTS_CMD_RELEASE_INFO 0xAA +#define FTS_CMD_GESTURE_MODE 0xAD +#define FTS_CMD_REQU_FW_CONF 0xB2 +#define FTS_CMD_REQU_FRAME_DATA 0xB7 +#define FTS_CMD_REQU_COMP_DATA 0xB8 +#define FTS_CMD_WRITE_MP_FLAG 0xC0 +#define FTS_CMD_FEATURE_ENABLE 0xC1 +#define FTS_CMD_FEATURE_DISABLE 0xC2 +#define FTS_CMD_GESTURE_CMD 0xC3 +#define FTS_CMD_LOCKDOWN_CMD 0xC4 +#define FTS_CMD_NOISE_WRITE 0xC7 +#define FTS_CMD_NOISE_READ 0xC8 +#define FTS_CMD_LOCKDOWN_FILL 0xCA +#define FTS_CMD_LOCKDOWN_WRITE 0xCB +#define FTS_CMD_LOCKDOWN_READ 0xCC +#define FTS_CMD_SAVE_CX_TUNING 0xFC + +//Event ID +#define EVENTID_NO_EVENT 0x00 +#define EVENTID_ERROR_EVENT 0x0F +#define EVENTID_CONTROL_READY 0x10 +#define EVENTID_FW_CONFIGURATION 0x12 +#define EVENTID_COMP_DATA_READ 0x13 +#define EVENTID_STATUS_UPDATE 0x16 +#define EVENTID_RELEASE_INFO 0x1C +#define EVENTID_LOCKDOWN_INFO 0x1E +#define EVENTID_ENTER_POINTER 0x03 +#define EVENTID_LEAVE_POINTER 0x04 +#define EVENTID_MOTION_POINTER 0x05 +#define EVENTID_HOVER_ENTER_POINTER 0x07 +#define EVENTID_HOVER_LEAVE_POINTER 0x08 +#define EVENTID_HOVER_MOTION_POINTER 0x09 +#define EVENTID_PROXIMITY_ENTER 0x0B +#define EVENTID_PROXIMITY_LEAVE 0x0C +#define EVENTID_KEY_STATUS 0x0E +#define EVENTID_LOCKDOWN_INFO_READ 0x1E +#define EVENTID_NOISE_READ 0x17 +#define EVENTID_NOISE_WRITE 0x18 +#define EVENTID_GESTURE 0x22 +#define EVENTID_FRAME_DATA_READ 0x25 +#define EVENTID_ECHO 0xEC +#define EVENTID_LAST (EVENTID_FRAME_DATA_READ+1) + +//EVENT TYPE +#define EVENT_TYPE_MS_TUNING_CMPL 0x01 +#define EVENT_TYPE_SS_TUNING_CMPL 0x02 +#define EVENT_TYPE_COMP_DATA_SAVED 0x04 +#define EVENT_TYPE_ITO 0x05 +#define EVENT_TYPE_FULL_INITIALIZATION 0x07 +#define EVENT_TYPE_LPTIMER_TUNING_CMPL 0x20 +#define EVENT_TYPE_ESD_ERROR 0x0A +#define EVENT_TYPE_WATCHDOG_ERROR 0x01 +#define EVENT_TYPE_CHECKSUM_ERROR 0x03 +#define EVENT_TYPE_LOCKDOWN 0x0A +#define EVENT_TYPE_LOCKDOWN_WRITE 0x08 +#define EVENT_TYPE_LOCKDOWN_ERROR 0x0B + +//CRC type +#define CRC_CONFIG_SIGNATURE 0x01 +#define CRC_CONFIG 0x02 +#define CRC_CX_MEMORY 0x03 + +// CONFIG ID INFO +#define CONFIG_ID_ADDR 0x0001 +#define CONFIG_ID_BYTE 2 + +//ADDRESS OFFSET IN SYSINFO +#define ADDR_RAW_TOUCH 0x0000 +#define ADDR_FILTER_TOUCH 0x0002 +#define ADDR_NORM_TOUCH 0x0004 +#define ADDR_CALIB_TOUCH 0x0006 +#define ADDR_RAW_HOVER_FORCE 0x000A +#define ADDR_RAW_HOVER_SENSE 0x000C +#define ADDR_FILTER_HOVER_FORCE 0x000E +#define ADDR_FILTER_HOVER_SENSE 0x0010 +#define ADDR_NORM_HOVER_FORCE 0x0012 +#define ADDR_NORM_HOVER_SENSE 0x0014 +#define ADDR_CALIB_HOVER_FORCE 0x0016 +#define ADDR_CALIB_HOVER_SENSE 0x0018 +#define ADDR_RAW_PRX_FORCE 0x001A +#define ADDR_RAW_PRX_SENSE 0x001C +#define ADDR_FILTER_PRX_FORCE 0x001E +#define ADDR_FILTER_PRX_SENSE 0x0020 +#define ADDR_NORM_PRX_FORCE 0x0022 +#define ADDR_NORM_PRX_SENSE 0x0024 +#define ADDR_CALIB_PRX_FORCE 0x0026 +#define ADDR_CALIB_PRX_SENSE 0x0028 +#define ADDR_RAW_MS_KEY 0x0032 +#define ADDR_NORM_MS_KEY 0x0036 +#define ADDR_COMP_DATA 0x0050 +#define ADDR_FRAMEBUFFER_DATA 0x8000 + +//ADDRESS FW REGISTER +#define ADDR_SENSE_LEN 0x0014 +#define ADDR_FORCE_LEN 0x0015 +#define ADDR_MS_TUNING_VER 0x0729 +#define ADDR_SS_TUNING_VER 0x074E + +//B2 INFO +#define B2_DATA_BYTES 4 +//number of bytes +#define B2_CHUNK ((FIFO_DEPTH/2)*B2_DATA_BYTES) + +//FEATURES +#define FEAT_GLOVE 0x00000001 +#define FEAT_STYLUS 0x00000002 +#define FEAT_COVER 0x00000004 +#define FEAT_CHARGER 0x00000008 +#define FEAT_VR 0x00000010 +#define FEAT_EDGE_REJECTION 0x00000020 +#define FEAT_CORNER_REJECTION 0x00000040 +#define FEAT_EDGE_PALM_REJECTION 0x00000100 + +//TOUCH SIZE +#define STYLUS_SIZE 0x01 +#define FINGER_SIZE 0x02 +#define LARGE_SIZE 0x03 + +//C7/C8 parameters +#define NOISE_PARAMETERS 0x01 + +//MP_FLAG_VALUE +#define INIT_MP 0xA5A5A501 +#define INIT_FIELD 0xA5A5A502 + +//NOISE PARAMS +#define NOISE_PARAMETERS_SIZE 4 //bytes + +//ERROR INFO +#define ERROR_INFO_SIZE (20*4) // bytes +#define ERROR_SIGNATURE 0xFA5005AF +#define ERROR_SIGN_HEAD 0xA5 + +#endif diff --git a/st/fts_lib/ftsTest.c b/st/fts_lib/ftsTest.c new file mode 100644 index 0000000000..baa0772cdb --- /dev/null +++ b/st/fts_lib/ftsTest.c @@ -0,0 +1,3844 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for MP test *** + * * + ************************************************************************** + ************************************************************************** + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +#include "ftsCrossCompile.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTest.h" +#include "ftsTime.h" +#include "ftsTool.h" +#include "../fts.h" + +#ifdef LIMITS_H_FILE +#include <../fts_limits.h> +#endif + +static char tag[8] = "[ FTS ]\0"; + +int computeAdjHoriz(u8 *data, int row, int column, u8 **result) +{ + int i, j; + int size = row * (column - 1); + + if (column < 2) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u8 *) kmalloc_array(size, sizeof(u8), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 0; i < row; i++) { + for (j = 1; j < column; j++) { + *(*result + (i * (column - 1) + (j - 1))) = + abs(data[i * column + j] - + data[i * column + (j - 1)]); + } + } + return OK; +} + +int computeAdjHorizTotal(u16 *data, int row, int column, u16 **result) +{ + int i, j; + int size = row * (column - 1); + + if (column < 2) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u16 *)kmalloc_array(size, sizeof(u16), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 0; i < row; i++) { + for (j = 1; j < column; j++) { + *(*result + (i * (column - 1) + (j - 1))) = + abs(data[i * column + j] - + data[i * column + (j - 1)]); + } + } + + return OK; +} + +int computeAdjVert(u8 *data, int row, int column, u8 **result) +{ + int i, j; + int size = (row - 1) * (column); + + if (row < 2) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u8 *)kmalloc_array(size, sizeof(u8), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 1; i < row; i++) { + for (j = 0; j < column; j++) { + *(*result + ((i - 1) * column + j)) = + abs(data[i * column + j] - + data[(i - 1) * column + j]); + } + } + + return OK; +} + +int computeAdjVertTotal(u16 *data, int row, int column, u16 **result) +{ + int i, j; + int size = (row - 1) * (column); + + if (row < 2) { + logError(1, "%s %s: ERROR % 02X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u16 *)kmalloc_array(size, sizeof(u16), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 1; i < row; i++) { + for (j = 0; j < column; j++) { + *(*result + ((i - 1) * column + j)) = + abs(data[i * column + j] - + data[(i - 1) * column + j]); + } + } + + return OK; +} + +int computeTotal(u8 *data, u8 main, int row, int column, + int m, int n, u16 **result) +{ + int i, j; + int size = (row) * (column); + + *result = (u16 *)kmalloc_array(size, sizeof(u16), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s %s : ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + *(*result + (i * column + j)) = + m * main + n * data[i * column + j]; + } + } + + return OK; +} + +int checkLimitsMinMax(short *data, int row, int column, int min, int max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min + || data[i * column + j] > max) { + logError(1, "%s %s:Node[%d,%d] = %d ", tag, + __func__, i, j, data[i * column + j]); + logError(1, "exceed limit [%d,%d]\n", min, max); + count++; + } + } + } + + return count;//if count is 0 = OK, test completed successfully +} + +int checkLimitsGap(short *data, int row, int column, int threshold) +{ + int i, j; + int min_node; + int max_node; + + if (row == 0 || column == 0) { + logError(1, "%s %s:invalid number of rows = %d ", + tag, __func__, row); + logError(1, "or columns = %d %02\n", + column, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + min_node = data[0]; + max_node = data[0]; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min_node) { + min_node = data[i * column + j]; + } else { + if (data[i * column + j] > max_node) + max_node = data[i * column + j]; + } + } + } + + if (max_node - min_node > threshold) { + logError(1, "%s %s: GAP = %d exceed limit %d\n", + tag, __func__, max_node - min_node, threshold); + return ERROR_TEST_CHECK_FAIL; + } + return OK; +} + +int checkLimitsMap(u8 *data, int row, int column, int *min, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min[i * column + j] + || data[i * column + j] > max[i * column + j]) { + logError(1, "%s %s: Node[%d,%d] = %d ", + tag, __func__, + i, j, + data[i * column + j]); + logError(1, "exceed limit [%d, %d]\n", + min[i * column + j], + max[i * column + j]); + count++; + } + } + } + + return count; //if count is 0 = OK, test completed successfully +} + +int checkLimitsMapTotal(u16 *data, int row, int column, int *min, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min[i * column + j] + || data[i * column + j] > max[i * column + j]) { + logError(1, "%s %s:Node[%d,%d] = %d\n", + tag, __func__, i, j, + data[i * column + j]); + logError(1, "exceed limit [%d, %d]\n", + min[i * column + j], + max[i * column + j]); + count++; + } + } + } + + return count; //if count is 0 = OK, test completed successfully +} + +int checkLimitsMapAdj(u8 *data, int row, int column, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] > max[i * column + j]) { + logError(1, "%s %s:Node[%d,%d] = %d ", + tag, __func__, i, j); + logError(1, "exceed limit > %d\n", + data[i * column + j], + max[i * column + j]); + count++; + } + } + } + //if count is 0 = OK, test completed successfully + return count; +} + +int checkLimitsMapAdjTotal(u16 *data, int row, int column, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] > max[i * column + j]) { + logError(1, "%s %s:Node[%d,%d] = %d ", + tag, __func__, i, j); + logError(1, "exceed limit > %d\n", + data[i * column + j], + max[i * column + j]); + count++; + } + } + } + //if count is 0 = OK, test completed successfully + return count; +} + +int production_test_ito(void) +{ + int res = OK; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + //look for ito event + int eventToSearch[2] = {EVENTID_ERROR_EVENT, EVENT_TYPE_ITO}; + + logError(0, "%s ITO Production test is starting...\n", tag); + + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_ITO); + return (res | ERROR_PROD_TEST_ITO); + } + + cmd = FTS_CMD_ITO_CHECK; + + logError(0, "%s ITO Check command sent...\n", tag); + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, (ERROR_I2C_W | ERROR_PROD_TEST_ITO)); + return (ERROR_I2C_W | ERROR_PROD_TEST_ITO); + } + + logError(0, "%s Looking for ITO Event...\n", tag); + res = pollForEvent(eventToSearch, 2, + readData, TIMEOUT_ITO_TEST_RESULT); + if (res < 0) { + logError(1, "%s %s: ITO Production test failed ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_ITO); + return (res | ERROR_PROD_TEST_ITO); + } + + if (readData[2] != 0x00 || readData[3] != 0x00) { + logError(0, "%s ITO Production testes finished! ERROR %02X\n", + tag, (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_ITO)); + res = (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_ITO); + } else { + logError(0, "%s ITO Production test finished!..OK\n", tag); + res = OK; + } + + res |= fts_system_reset(); + if (res < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_ITO); + res = (res | ERROR_PROD_TEST_ITO); + } + return res; +} + +int production_test_initialization(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_FULL_INITIALIZATION}; + logError(0, "%s INITIALIZATION Production test is starting\n", tag); + + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s INITIALIZATION command sent...\n", tag); + cmd = FTS_CMD_FULL_INITIALIZATION; + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, + (ERROR_I2C_W | ERROR_PROD_TEST_INITIALIZATION)); + return (ERROR_I2C_W | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s Looking for INITIALIZATION Event...\n", tag); + res = pollForEvent(eventToSearch, 2, readData, + TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s %s: INITIALIZATION Production ", tag, __func__); + logError(1, "test failed %02X\n", + ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + if (readData[2] != 0x00) { + logError(0, "%sINITIALIZATION Production ", tag); + logError(0, "testes finished! FAILED %02X\n", + (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_INITIALIZATION)); + res = (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_INITIALIZATION); + } else { + logError(0, "%s INITIALIZATION Production test...OK\n", tag); + res = OK; + } + + logError(0, "%s Refresh Chip Info...\n", tag); + //need to update the chipInfo in order to refresh the tuning_versione + res |= readChipInfo(1); + + if (res < 0) { + logError(1, "%s %s: read chip info ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_INITIALIZATION); + res = (res | ERROR_PROD_TEST_INITIALIZATION); + } + + return res; +} + +int ms_compensation_tuning(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_MS_TUNING_CMPL}; + + logError(0, "%s MS INITIALIZATION command sent...\n", tag); + cmd = FTS_CMD_MS_COMP_TUNING; + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s %s 2: ERROR %02X\n", + tag, __func__, (ERROR_I2C_W | ERROR_MS_TUNING)); + return (ERROR_I2C_W | ERROR_MS_TUNING); + } + + logError(0, "%s Looking for MS INITIALIZATION Event...\n", tag); + res = pollForEvent(eventToSearch, 2, readData, + TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s %s:MS INITIALIZATION Production\n", + tag, __func__); + logError(1, "test failed %02X\n", ERROR_MS_TUNING); + return (res | ERROR_MS_TUNING); + } + + if (readData[2] != 0x00 || readData[3] != 0x00) { + logError(0, "%s MS INITIALIZATION Production ", tag); + logError(0, "test finished! FAILED %02X\n", ERROR_MS_TUNING); + res = ERROR_MS_TUNING; + } else { + logError(0, + "%s MS INITIALIZATION Production test finished! OK\n", + tag); + res = OK; + } + + return res; +} + +int ss_compensation_tuning(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_SS_TUNING_CMPL}; + + logError(0, "%s SS INITIALIZATION command sent...\n", tag); + cmd = FTS_CMD_SS_COMP_TUNING; + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s %s 2: ERROR %02X\n", + tag, __func__, (ERROR_I2C_W | ERROR_SS_TUNING)); + return (ERROR_I2C_W | ERROR_SS_TUNING); + } + + logError(0, "%s Looking for SS INITIALIZATION Event...\n", tag); + res = pollForEvent(eventToSearch, + 2, + readData, + TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s %s:SS INITIALIZATION Production ", + tag, __func__); + logError(1, "test failed %02X\n", ERROR_SS_TUNING); + return (res | ERROR_SS_TUNING); + } + logError(0, "%s SS INITIALIZATION Production test finished!", tag); + if (readData[2] != 0x00 || readData[3] != 0x00) { + logError(0, "%s.................FAILED ERROR %02X\n", + tag, ERROR_SS_TUNING); + res = ERROR_SS_TUNING; + } else { + logError(0, "%s.................OK\n", tag); + res = OK; + } + + return res; +} + +int lp_timer_calibration(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_LPTIMER_TUNING_CMPL}; + + logError(0, "%s LP TIMER CALIBRATION command sent...\n", tag); + cmd = FTS_CMD_LP_TIMER_CALIB; + if (fts_writeFwCmd(&cmd, 1) < 0) { + logError(1, "%s %s 2:ERROR %02X\n", tag, __func__, + (ERROR_I2C_W | ERROR_LP_TIMER_TUNING)); + return (ERROR_I2C_W | ERROR_LP_TIMER_TUNING); + } + + logError(0, "%s Looking for LP TIMER CALIBRATION Event...\n", tag); + res = pollForEvent(eventToSearch, + 2, + readData, + TIMEOUT_INITIALIZATION_TEST_RESULT); + + if (res < 0) { + logError(1, "%s:LP TIMER CALIBRATION Production test failed\n", + tag); + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_LP_TIMER_TUNING); + return (res | ERROR_LP_TIMER_TUNING); + } + + logError(0, "LP TIMER CALIBRATION Production test finished!"); + if (readData[2] != 0x00 || readData[3] != 0x01) { + logError(0, "%s........FAILED ERROR %02X\n", + tag, ERROR_LP_TIMER_TUNING); + res = ERROR_LP_TIMER_TUNING; + } else { + logError(0, "%s.................OK\n", tag); + res = OK; + } + + return res; +} + +int save_cx_tuning(void) +{ + int res; + u8 cmd; + u8 readData[FIFO_EVENT_SIZE] = {0}; + int eventToSearch[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_COMP_DATA_SAVED}; + + logError(0, "%s SAVE CX command sent...\n", tag); + cmd = FTS_CMD_SAVE_CX_TUNING; + if (fts_writeCmd(&cmd, 1) < 0) { + logError(1, "%s %s 2:ERROR %02X\n", tag, __func__, + (ERROR_I2C_W | ERROR_SAVE_CX_TUNING)); + return (ERROR_I2C_W | ERROR_SAVE_CX_TUNING); + } + + logError(0, "%s Looking for SAVE CX Event...\n", tag); + res = pollForEvent(eventToSearch, + 2, + readData, + TIMEOUT_INITIALIZATION_TEST_RESULT); + if (res < 0) { + logError(1, "%s %s: SAVE CX failed... ERROR %02X\n", + tag, ERROR_SAVE_CX_TUNING); + return (res | ERROR_SAVE_CX_TUNING); + } + + if (readData[2] != 0x00 || readData[3] != 0x00) { + logError(0, "%s SAVE CX finished! FAILED ERROR %02X\n", + tag, ERROR_SAVE_CX_TUNING); + res = ERROR_SAVE_CX_TUNING; + } else { + logError(0, "%s SAVE CX finished!.................OK\n", tag); + res = OK; + } + + return res; +} + +int production_test_split_initialization(int saveToFlash) +{ + int res; + + logError(0, "%s Split Initialization test is starting...\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, + ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s MS INITIALIZATION TEST:\n", tag); + res = ms_compensation_tuning(); + if (res < 0) { + logError(0, "%s %s:MS INITIALIZATION TEST FAILED! ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s MS INITIALIZATION TEST OK!\n", tag); + logError(0, "%s\n", tag); + logError(0, "%s SS INITIALIZATION TEST:\n", tag); + res = ss_compensation_tuning(); + if (res < 0) { + logError(0, "%s %s: SS INITIALIZATION TEST FAILED! ", + tag, __func__); + logError(0, "ERROR %02X\n", ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s SS INITIALIZATION TEST OK!\n", tag); + logError(0, "%s\n", tag); + logError(0, "%s LP INITIALIZATION TEST:\n", tag); + res = lp_timer_calibration(); + if (res < 0) { + logError(0, "%s %s: LP INITIALIZATION TEST FAILED! ", + tag, __func__); + logError(0, "ERROR %02X\n", ERROR_PROD_TEST_INITIALIZATION); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + + logError(0, "%s LP INITIALIZATION TEST OK!\n", tag); + if (saveToFlash) { + logError(0, "%s\n", tag); + logError(0, "%s SAVE CX TEST:\n", tag); + res = save_cx_tuning(); + if (res < 0) { + logError(0, "%s %s: SAVE CX TEST FAILED! ERROR %02X\n", + tag, __func__, res); + return (res | ERROR_PROD_TEST_INITIALIZATION); + } + logError(0, "%s SAVE CX TEST OK!\n", tag); + } + + logError(0, "%s Refresh Chip Info...\n", tag); + res |= readChipInfo(1); + if (res < 0) { + logError(1, "%s %s: read chip info ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_INITIALIZATION); + res = (res | ERROR_PROD_TEST_INITIALIZATION); + } else { + logError(0, "%s Split Initialization test finished! OK\n", tag); + } + return res; + +} + +int production_test_main(char *pathThresholds, int stop_on_fail, int saveInit, + struct TestToDo *todo, u32 signature) +{ + int res, ret; + + logError(0, "%s MAIN Production test is starting...\n", tag); + logError(0, "%s\n", tag); + logError(0, "%s ITO TEST:\n", tag); + + res = production_test_ito(); + if (res < 0) { + logError(0, "%s Error during ITO TEST! ERROR %08X\n", tag, res); + //in case of ITO TEST failure is no sense keep going + goto END; + } + logError(0, "%s ITO TEST OK!\n", tag); + + logError(0, "%s:\n", tag); + logError(0, "%s INITIALIZATION TEST:\n", tag); + + if (saveInit == 1) { + res = production_test_initialization(); + if (res < 0) { + logError(0, "%s Error during INITIALIZATION TEST!", + tag); + logError(0, "ERROR %08X\n", res); + if (stop_on_fail) + goto END; + } else { + logError(0, "%s INITIALIZATION TEST OK!\n", tag); + } + } else + logError(0, "%s INITIALIZATION TEST:..SKIPPED\n", tag); + + logError(0, "%s\n", tag); + if (saveInit == 1) { + logError(0, "%s Cleaning up...\n", tag); + ret = cleanUp(0); + if (ret < 0) { + logError(1, "%s %s: clean up ERROR %02X\n", + tag, __func__, ret); + res |= ret; + if (stop_on_fail) + goto END; + } + logError(0, "%s\n", tag); + } + + logError(0, "%s PRODUCTION DATA TEST:\n", tag); + ret = production_test_data(pathThresholds, stop_on_fail, todo); + if (ret < 0) { + logError(0, "%sError during PRODUCTION DATA TEST %08X\n", + tag, ret); + } else { + logError(0, "%s PRODUCTION DATA TEST OK!\n", tag); + } + res |= ret; + // the OR is important because if + //the data test is OK but the inizialization + //test fail, the main production + //test result should = FAIL + + if (ret == OK && saveInit == 1) { + logError(0, "%s SAVE FLAG:\n", tag); + ret = save_mp_flag(signature); + if (ret < OK) + logError(0, "%s SAVE FLAG:FAIL! ERROR %08X\n", + tag, ret); + else + logError(0, "%s SAVE FLAG:OK!\n", tag); + res |= ret; + // need to update the MP Flag + ret = readChipInfo(1); + if (ret < OK) + logError(1, "%s %s:read chip info ERROR %08X\n", + tag, __func__, ret); + res |= ret; + } + logError(0, "%s\n", tag); +END: + if (res < 0) { + logError(0, "%s MAIN Production test finished..FAILED\n", tag); + return res; + } + logError(0, "%s MAIN Production test finished..OK\n", tag); + return OK; +} + +int production_test_ms_raw(char *path_limits, int stop_on_fail, + struct TestToDo *todo) +{ + int ret, count_fail = 0; + struct MutualSenseFrame msRawFrame = {0}; + + int *thresholds = NULL; + int trows, tcolumns; + + //****** Mutual Sense Test ************/ + logError(0, "%s\n", tag); + logError(0, "%s MS RAW DATA TEST is starting...\n", tag); + if (todo->MutualRaw == 1 || todo->MutualRawGap == 1) { + ret = getMSFrame2(MS_TOUCH_ACTIVE, &msRawFrame); + if (ret < 0) { + logError(1, "%s %s:getMSFrame failed... ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s MS RAW MIN MAX TEST:\n", tag); + if (todo->MutualRaw == 1) { + ret = parseProductionTestLimits(path_limits, + MS_RAW_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(0, "%s %s:MS_RAW_MIN_MAX failed...", + tag, __func__); + logError(0, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMinMax(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(0, "%s %s:MS RAW failed...", + tag, __func__); + logError(0, "ERROR COUNT = %d\n", ret); + logError(0, "%s MS RAW MIN MAX TEST:...", tag); + logError(0, "FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else + logError(0, "%s MS RAW MIN MAX TEST:OK\n", tag); + kfree(thresholds); + thresholds = NULL; + } else + logError(0, "%s MS RAW MIN MAX TEST:SKIPPED\n", tag); + + logError(0, "%s\n", tag); + logError(0, "%s MS RAW GAP TEST:\n", tag); + if (todo->MutualRawGap == 1) { + ret = parseProductionTestLimits(path_limits, + MS_RAW_GAP, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s %s: MS_RAW_GAP failed... ", + tag, __func__); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsGap(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + thresholds[0]); + if (ret != OK) { + logError(1, "%s %s:checkLimitsGap MS RAW ", + tag, __func__); + logError(1, "failed ERROR:%02X\n", ret); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else + logError(0, "%s MS RAW GAP TEST:....OK\n\n", + tag); + kfree(thresholds); + thresholds = NULL; + } else + logError(0, "%s MS RAW GAP TEST:..SKIPPED\n", tag); + } else + logError(0, "%s MS RAW FRAME TEST:.SKIPPED\n", tag); + + logError(0, "%s\n", tag); + logError(0, "%s MS KEY RAW TEST:\n", tag); + if (todo->MutualKeyRaw == 1) { + ret = production_test_ms_key_raw(path_limits); + if (ret < 0) { + logError(1, "%s %s:production_test_ms_key_raw ", + tag, __func__); + logError(1, "failed ERROR:%02X\n", ret); + count_fail += 1; + if (count_fail == 1) { + logError(0, "%s MS RAW DATA TEST:FAIL ", tag); + logError(0, "fails_count:%d\n\n", count_fail); + goto ERROR_LIMITS; + } + } + } else + logError(0, "%s MS KEY RAW TEST:....SKIPPED\n", tag); +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + kfree(msRawFrame.node_data); + msRawFrame.node_data = NULL; + logError(0, "%s MS RAW DATA TEST finished!.OK\n", tag); + return OK; + } + print_frame_short("MS Raw frame =", + array1dTo2d_short(msRawFrame.node_data, + msRawFrame.node_data_size, + msRawFrame.header.sense_node), + msRawFrame.header.force_node, + msRawFrame.header.sense_node); + kfree(msRawFrame.node_data); + kfree(thresholds); + logError(0, "%s MS RAW DATA TEST: FAIL fails_count = %d\n\n", + tag, count_fail); + return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); + +ERROR_LIMITS: + kfree(msRawFrame.node_data); + kfree(thresholds); + return ret; +} + +int production_test_ms_key_raw(char *path_limits) +{ + int ret; + struct MutualSenseFrame msRawFrame; + + int *thresholds = NULL; + int trows, tcolumns; + + //************* Mutual Sense Test ************/ + logError(0, "%s MS KEY RAW DATA TEST is starting...\n", tag); + + ret = getMSFrame2(MS_KEY, &msRawFrame); + if (ret < 0) { + logError(1, "%s %s:getMSKeyFrame failed...ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + ret = parseProductionTestLimits(path_limits, + MS_KEY_RAW_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s %s: MS_KEY_RAW_MIN_MAX failed...ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMinMax(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, "%s %s:checkLimitsMinMax failed..ERROR COUNT:%d\n", + tag, __func__, ret); + goto ERROR; + } else + logError(0, "%s MS KEY RAW TEST:.................OK\n\n", tag); + + kfree(thresholds); + thresholds = NULL; + + kfree(msRawFrame.node_data); + msRawFrame.node_data = NULL; + return OK; +ERROR: + print_frame_short("MS Key Raw frame =", + array1dTo2d_short(msRawFrame.node_data, + msRawFrame.node_data_size, + msRawFrame.header.sense_node), + msRawFrame.header.force_node, + msRawFrame.header.sense_node); + kfree(msRawFrame.node_data); + kfree(thresholds); + logError(0, "%s MS KEY RAW TEST:......FAIL\n\n", tag); + return (ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL); +ERROR_LIMITS: + kfree(msRawFrame.node_data); + kfree(thresholds); + return ret; +} + +int production_test_ms_cx(char *path_limits, + int stop_on_fail, struct TestToDo *todo) +{ + int ret; + int count_fail = 0; + + int *thresholds = NULL; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + int trows, tcolumns; + + struct MutualSenseData msCompData; + + u8 *adjhor = NULL; + + u8 *adjvert = NULL; + + u16 container; + u16 *total_cx = NULL; + u16 *total_adjhor = NULL; + u16 *total_adjvert = NULL; + + //MS CX TEST + logError(0, "%s\n", tag); + logError(0, "%s MS CX Testes are starting...\n", tag); + //read MS compensation data + ret = readMutualSenseCompensationData(MS_TOUCH_ACTIVE, &msCompData); + if (ret < 0) { + logError(1, "%s %s:readMutualSenseCompensationData ", + tag, __func__); + logError(1, "failed %02X\n", ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s MS CX1 TEST:\n", tag); + if (todo->MutualCx1 == 1) { + ret = parseProductionTestLimits(path_limits, + MS_CX1_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s %s:parseProductionTestLimits failed ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (u16)msCompData.cx1; + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); //check the limits + if (ret != OK) { + logError(1, "%s %s:checkLimitsMinMax MS CX1 failed ", + tag, __func__); + logError(1, "ERROR COUNT:%d\n", ret); + logError(0, "%s MS CX1 TEST:.........FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX1 TEST:..........OK\n\n", tag); + } else + logError(0, "%s MS CX1 TEST:.......SKIPPED\n\n", tag); + + kfree(thresholds); + thresholds = NULL; + + logError(0, "%s MS CX2 MIN MAX TEST:\n", tag); + if (todo->MutualCx2 == 1) { + ret = parseProductionTestLimits(path_limits, + MS_CX2_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); //load min thresholds + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s %s:parseProductionTestLimits ", + tag, __func__); + logError(1, "failed %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, + MS_CX2_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); //load max thresholds + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s %s: MS_CX2_MAP_MAX failed ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMap(msCompData.node_data, + msCompData.header.force_node, + msCompData.header.sense_node, + thresholds_min, + thresholds_max);//check the limits + if (ret != OK) { + logError(1, "%s %s:checkLimitsMap MS CX2 MIN MAX ", + tag, __func__); + logError(1, "failed ERR_COUNT:%d\n", ret); + logError(0, "%s MS CX2 MIN MAX TEST:......FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX2 MIN MAX TEST:....OK\n\n", tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, "%s MS CX2 MIN MAX TEST:....SKIPPED\n\n", tag); + + logError(0, "%s MS CX2 ADJ TEST:\n", tag); + if (todo->MutualCx2Adj == 1) { + //MS CX2 ADJ HORIZ + logError(0, "%s MS CX2 ADJ HORIZ TEST:\n", tag); + + ret = computeAdjHoriz(msCompData.node_data, + msCompData.header.force_node, + msCompData.header.sense_node, + &adjhor); + if (ret < 0) { + logError(1, "%s %s:computeAdjHoriz failed...", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS CX2 ADJ HORIZ computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, + MS_CX2_ADJH_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node - 1)) { + + logError(1, "%s %s: MS_CX2_ADJH_MAP_MAX failed...", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdj(adjhor, + msCompData.header.force_node, + msCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s %s:checkLimitsMapAdj CX2 ADJH failed ", + tag, __func__); + logError(1, "ERROR COUNT:%d\n", ret); + logError(0, "%s MS CX2 ADJ HORIZ TEST:..FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX2 ADJ HORIZ TEST:..OK\n\n", tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjhor); + adjhor = NULL; + + //MS CX2 ADJ VERT + logError(0, "%s MS CX2 ADJ VERT TEST:\n", tag); + ret = computeAdjVert(msCompData.node_data, + msCompData.header.force_node, + msCompData.header.sense_node, + &adjvert); + if (ret < 0) { + logError(1, "%s %s:computeAdjVert failed... ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS CX2 ADJ VERT computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, + MS_CX2_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node - 1 + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s %s:MS_CX2_ADJV_MAP_MAX failed ", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdj(adjvert, + msCompData.header.force_node - 1, + msCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s %s:checkLimitsMapAdj CX2 ADJV failed ", + tag, __func__); + logError(1, "COUNT:%d\n", ret); + logError(0, "%s MS CX2 ADJ HORIZ TEST:FAIL\n\n", tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX2 ADJ VERT TEST:OK\n\n", tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjvert); + adjvert = NULL; + } else + logError(0, "%s MS CX2 ADJ TEST:SKIPPED\n\n", tag); + + //START OF TOTAL CHECK + logError(0, "%s MS TOTAL CX TEST:\n", tag); + + if (todo->MutualCxTotal == 1 || todo->MutualCxTotalAdj == 1) { + ret = computeTotal(msCompData.node_data, + msCompData.cx1, + msCompData.header.force_node, + msCompData.header.sense_node, + CX1_WEIGHT, + CX2_WEIGHT, + &total_cx); + if (ret < 0) { + logError(1, "%s %s:computeTotalCx failed...", + tag, __func__); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS TOTAL CX MIN MAX TEST:\n", tag); + if (todo->MutualCxTotal == 1) { + ret = parseProductionTestLimits(path_limits, + MS_TOTAL_CX_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns);//load min thresholds + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s %s:parseProductionTestLimits ", + tag, __func__); + logError(1, "failed %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + //load max thresholds + ret = parseProductionTestLimits(path_limits, + MS_TOTAL_CX_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s %s:MS_TOTAL_CX_MAP_MAX failed", + tag, __func__); + logError(1, "...ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotal(total_cx, + msCompData.header.force_node, + msCompData.header.sense_node, + thresholds_min, + thresholds_max);//check the limits + if (ret != OK) { + logError(1, "%s %s:MS TOTAL CX TEST failed ", + tag, __func__); + logError(1, "COUNT:%d\n", ret); + logError(0, "%s MS TOTAL CX MIN MAX ", tag); + logError(0, "TEST:FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s MS TOTAL CX MIN MAX TEST", tag); + logError(0, ":OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, "%s MS TOTAL CX MIN MAX TEST:SKIPPED\n\n", + tag); + + + logError(0, "%s MS TOTAL CX ADJ TEST:\n", tag); + if (todo->MutualCxTotalAdj == 1) { + //MS TOTAL CX ADJ HORIZ + logError(0, "%s MS TOTAL CX ADJ HORIZ TEST:\n", tag); + + //thresholds_max = NULL; + ret = computeAdjHorizTotal(total_cx, + msCompData.header.force_node, + msCompData.header.sense_node, + &total_adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjHoriz failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS TOTAL CX ADJ HORIZ computed!\n", + tag); + ret = parseProductionTestLimits(path_limits, + MS_TOTAL_CX_ADJH_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node + || tcolumns != msCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "MS_TOTAL_CX_ADJH_MAP_MAX "); + logError(1, "failed...RROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdjTotal(total_adjhor, + msCompData.header.force_node, + msCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj MS TOTAL "); + logError(1, "CX ADJH failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s MS TOTAL CX ADJ HORIZ ", tag); + logError(0, "TEST:.................FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s MS TOTAL CX ADJ HORIZ ", tag); + logError(0, "TEST:.................OK\n\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjhor); + total_adjhor = NULL; + + //MS TOTAL CX ADJ VERT + logError(0, "%s MS TOTAL CX ADJ VERT TEST:\n", tag); + + ret = computeAdjVertTotal(total_cx, + msCompData.header.force_node, + msCompData.header.sense_node, + &total_adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjVert failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS TOTAL CX ADJ VERT computed!\n", + tag); + + ret = parseProductionTestLimits(path_limits, + MS_TOTAL_CX_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || + (trows != msCompData.header.force_node - 1 + || tcolumns != msCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "MS_TOTAL_CX_ADJV_MAP_MAX failed"); + logError(1, "... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdjTotal(total_adjvert, + msCompData.header.force_node - 1, + msCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj MS TOTAL "); + logError(1, "CX ADJV failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s MS TOTAL CX ADJ HORIZ ", tag); + logError(0, "TEST:.................FAIL\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s MS TOTAL CX ADJ VERT ", tag); + logError(0, "TEST:.................OK\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjvert); + total_adjvert = NULL; + } else { + logError(0, "%s MS TOTAL CX ADJ ", tag); + logError(0, "TEST:.................SKIPPED\n"); + } + + kfree(total_cx); + total_cx = NULL; + } else + logError(0, "%s MS TOTAL CX TEST:.................SKIPPED\n", + tag); + + if ((todo->MutualKeyCx1 + | todo->MutualKeyCx2 | todo->MutualKeyCxTotal) == 1) { + ret = production_test_ms_key_cx(path_limits, + stop_on_fail, + todo); + if (ret < 0) { + count_fail += 1; + logError(1, "%s production_test_data: ", tag); + logError(1, "production_test_ms_key_cx failed..."); + logError(1, "ERROR = %02X\n", ret); + logError(0, "%s MS CX testes finished!", tag); + logError(0, ".................FAILED "); + logError(0, "fails_count = %d\n\n", count_fail); + return ret; + } + } else + logError(0, "%s MS KEY CX TEST:.................SKIPPED\n", + tag); + + if ((todo->MutualKeyCx1 | todo->MutualKeyCx2 + | todo->MutualKeyCxTotal) == 1) { + ret = production_test_ms_key_cx(path_limits, + stop_on_fail, + todo); + + if (ret < 0) { + count_fail += 1; + logError(1, "%s %s:production_test_ms_key_cx ", + tag, __func__); + logError(1, "failed :%02X\n", ret); + logError(0, "%s MS CX testes finished! ", tag); + logError(0, "fails_count = %d\n\n", count_fail); + return ret; + } + } else + logError(0, "%s MS KEY CX TEST:..SKIPPED\n", tag); +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s MS CX testes finished! OK\n", tag); + kfree(msCompData.node_data); + msCompData.node_data = NULL; + return OK; + } + print_frame_u8("MS Init Data (Cx2) =", + array1dTo2d_u8(msCompData.node_data, + msCompData.node_data_size, + msCompData.header.sense_node), + msCompData.header.force_node, + msCompData.header.sense_node); + logError(0, "%s MS CX testes finished! fails_count = %d\n\n", + tag, count_fail); + kfree(thresholds); + kfree(thresholds_min); + kfree(thresholds_max); + kfree(adjhor); + kfree(adjvert); + kfree(total_cx); + kfree(total_adjhor); + kfree(total_adjvert); + kfree(msCompData.node_data); + + return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); + +ERROR_LIMITS: + kfree(thresholds); + kfree(thresholds_min); + kfree(thresholds_max); + kfree(adjhor); + kfree(adjvert); + kfree(total_cx); + kfree(total_adjhor); + kfree(total_adjvert); + kfree(msCompData.node_data); + return ret; +} + +int production_test_ms_key_cx(char *path_limits, int stop_on_fail, + struct TestToDo *todo) +{ + int ret; + int count_fail = 0; + int num_keys = 0; + + int *thresholds = NULL; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + int trows, tcolumns; + + struct MutualSenseData msCompData; + + u16 container; + u16 *total_cx = NULL; + + + //MS CX TEST + logError(0, "%s MS KEY CX Testes are starting...\n", tag); + + //read MS compensation data + ret = readMutualSenseCompensationData(MS_KEY, &msCompData); + if (ret < 0) { + logError(0, "%s production_test_data: ", tag); + logError(0, "readMutualSenseCompensationData failed... "); + logError(0, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + //the meaningful data are only in the first row, the other rows are + // only a copy of the first one + if (msCompData.header.force_node > msCompData.header.sense_node) + num_keys = msCompData.header.force_node; + else + num_keys = msCompData.header.sense_node; + + logError(0, "%s MS KEY CX1 TEST:\n", tag); + if (todo->MutualKeyCx1 == 1) { + + ret = parseProductionTestLimits(path_limits, + MS_KEY_CX1_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(0, "%s production_test_data: ", tag); + logError(0, "parseProductionTestLimits "); + logError(0, "MS_KEY_CX1_MIN_MAX failed... "); + logError(0, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (u16) msCompData.cx1; + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); //check the limits + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax MS CX1 failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s MS KEY CX1 TEST:................", tag); + logError(0, ".FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s MS KEY CX1 TEST:................", tag); + logError(0, ".OK\n\n"); + } + } else + logError(0, "%s MS KEY CX1 TEST:.................SKIPPED\n\n", + tag); + + kfree(thresholds); + thresholds = NULL; + + logError(0, "%s MS KEY CX2 TEST:\n", tag); + if (todo->MutualKeyCx2 == 1) { + //load min thresholds + ret = parseProductionTestLimits(path_limits, + MS_KEY_CX2_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "MS_KEY_CX2_MAP_MIN failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load max thresholds + ret = parseProductionTestLimits(path_limits, + MS_KEY_CX2_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "MS_KEY_CX2_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the limits + ret = checkLimitsMap(msCompData.node_data, + 1, + num_keys, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap MS KEY CX2 failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s MS KEY CX2 TEST:................", tag); + logError(0, ".FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s MS KEY CX2 TEST:...............", tag); + logError(0, "..OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, "%s MS CX2 TEST:.................SKIPPED\n\n", + tag); + + //START OF TOTAL CHECK + logError(0, "%s MS KEY TOTAL CX TEST:\n", tag); + + if (todo->MutualKeyCxTotal == 1) { + ret = computeTotal(msCompData.node_data, + msCompData.cx1, 1, + num_keys, + CX1_WEIGHT, + CX2_WEIGHT, + &total_cx); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeTotalCx failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load min thresholds + ret = parseProductionTestLimits(path_limits, + MS_KEY_TOTAL_CX_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "%parseProductionTestLimits "); + logError(1, "MS_KEY_TOTAL_CX_MAP_MIN failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load max thresholds + ret = parseProductionTestLimits(path_limits, + MS_KEY_TOTAL_CX_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != num_keys)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "MS_KEY_TOTAL_CX_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the limits + ret = checkLimitsMapTotal(total_cx, + 1, + num_keys, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap MS TOTAL "); + logError(1, "KEY CX TEST failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s MS KEY TOTAL CX TEST:...........", tag); + logError(0, "......FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s MS KEY TOTAL CX TEST:...........", tag); + logError(0, "......OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + + kfree(total_cx); + total_cx = NULL; + } else { + logError(0, "%s MS KEY TOTAL CX TEST:.................", tag); + logError(0, "SKIPPED\n"); + } + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, + "%s MS KEY CX testes finished! OK\n", tag); + kfree(msCompData.node_data); + msCompData.node_data = NULL; + return OK; + } + print_frame_u8("MS Key Init Data (Cx2) =", + array1dTo2d_u8(msCompData.node_data, + msCompData.node_data_size, + msCompData.header.sense_node), + 1, + msCompData.header.sense_node); + logError(0, "%s MS Key CX testes finished!..............", tag); + logError(0, "...FAILED fails_count = %d\n\n", count_fail); + kfree(thresholds); + kfree(thresholds_min); + kfree(thresholds_max); + kfree(msCompData.node_data); + kfree(total_cx); + return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); +ERROR_LIMITS: + kfree(thresholds); + kfree(thresholds_min); + kfree(thresholds_max); + kfree(msCompData.node_data); + kfree(total_cx); + return ret; +} + +int production_test_ss_raw(char *path_limits, + int stop_on_fail, struct TestToDo *todo) +{ + int ret; + int count_fail = 0; + int rows, columns; + + //short *ssRawFrame = NULL; + struct SelfSenseFrame ssRawFrame; + + int *thresholds = NULL; + int trows, tcolumns; + + //MS SS TEST + logError(0, "%s\n", tag); + logError(0, "%s SS RAW Testes are starting...\n", tag); + + //******* Self Sense Test ***************/ + logError(0, "%s Getting SS Frame...\n", tag); + ret = getSSFrame2(SS_TOUCH, &ssRawFrame); + if (ret < 0) { + logError(1, "%s %s:getSSFrame failed...ERROR %02X\n", + tag, __func__, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + //SS RAW (PROXIMITY) FORCE TEST + logError(0, "%s SS RAW (PROXIMITY) FORCE TEST:\n", tag); + + if (todo->SelfForceRaw == 1 || todo->SelfForceRawGap == 1) { + //there are no data for the sense + //channels due to the fact that + //the force frame is analized + columns = 1; + rows = ssRawFrame.header.force_node; + + logError(0, "%s SS RAW (PROXIMITY) FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceRaw == 1) { + ret = parseProductionTestLimits(path_limits, + SS_RAW_FORCE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s %s:parseProductionTestLimits ", + tag, __func__); + logError(1, "failed %02X\n", + ERROR_PROD_TEST_DATA); + //return (ret | ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMinMax(ssRawFrame.force_data, + rows, columns, + thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, "%s %s:checkLimitsMinMax ", + tag, __func__); + logError(1, "failed ERROR COUNT:%d\n", ret); + logError(0, "%s SS RAW (PROXIMITY) FORCE", tag); + logError(0, " MIN MAX TEST:FAIL\n\n"); + count_fail += 1; + print_frame_short("SS Raw force frame =", + array1dTo2d_short(ssRawFrame.force_data, + rows*columns, columns), + rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA + | ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else { + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "FORCE MIN MAX TEST:............."); + logError(0, "....OK\n\n"); + } + + kfree(thresholds); + thresholds = NULL; + } else { + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "FORCE MIN MAX TEST:................."); + logError(0, "SKIPPED\n\n"); + } + + logError(0, "%s\n", tag); + logError(0, "%s SS RAW (PROXIMITY) FORCE GAP TEST:\n", tag); + if (todo->SelfForceRawGap == 1) { + ret = parseProductionTestLimits(path_limits, + SS_RAW_FORCE_GAP, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_RAW_FORCE_GAP failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsGap(ssRawFrame.force_data, + rows, + columns, + thresholds[0]); + + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsGap SS RAW "); + logError(1, "(PROXIMITY) FORCE GAP failed..."); + logError(1, "ERROR = %02X\n", ret); + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "FORCE GAP TEST:................."); + logError(0, "FAIL\n\n"); + count_fail += 1; + print_frame_short("SS Raw force frame =", + array1dTo2d_short(ssRawFrame.force_data, + rows*columns, columns), + rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA + | ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else { + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "FORCE GAP TEST:................."); + logError(0, "OK\n\n"); + } + + kfree(thresholds); + thresholds = NULL; + } else { + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "FORCE GAP TEST:................."); + logError(0, "SKIPPED\n\n"); + } + + kfree(ssRawFrame.force_data); + ssRawFrame.force_data = NULL; + } else { + logError(0, "%s SS RAW (PROXIMITY) FORCE ", tag); + logError(0, "TEST:.................SKIPPED\n\n"); + } + + logError(0, "%s\n", tag); + //SS RAW (PROXIMITY) SENSE TEST + logError(0, "%s SS RAW (PROXIMITY) SENSE TEST:\n", tag); + + if (todo->SelfSenseRaw == 1 || todo->SelfSenseRawGap == 1) { + columns = ssRawFrame.header.sense_node; + // there are no data for the force channels due + // to the fact that the sense frame is analized + rows = 1; + + logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseRaw == 1) { + ret = parseProductionTestLimits(path_limits, + SS_RAW_SENSE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_RAW_SENSE_MIN_MAX failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMinMax(ssRawFrame.sense_data, + rows, + columns, + thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax SS RAW "); + logError(1, "(PROXIMITY) SENSE failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "SENSE MIN MAX TEST:............."); + logError(0, "....FAIL\n"); + count_fail += 1; + print_frame_short("SS Raw sense frame =", + array1dTo2d_short(ssRawFrame.sense_data, + rows*columns, columns), + rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA + | ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else { + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "SENSE MIN MAX TEST:............."); + logError(0, "....OK\n"); + } + + kfree(thresholds); + thresholds = NULL; + } else { + logError(0, "%s SS RAW (PROXIMITY) SENSE MIN MAX", tag); + logError(0, " TEST:.................SKIPPED\n"); + } + + logError(0, "%s\n", tag); + logError(0, "%s SS RAW (PROXIMITY) SENSE GAP TEST:\n", tag); + if (todo->SelfSenseRawGap == 1) { + ret = parseProductionTestLimits(path_limits, + SS_RAW_SENSE_GAP, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_RAW_SENSE_GAP failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsGap(ssRawFrame.sense_data, + rows, + columns, + thresholds[0]); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsGap SS RAW "); + logError(1, "(PROXIMITY) SENSE GAP failed... "); + logError(1, "ERROR = %02X\n", ret); + logError(0, "%s SS RAW (PROXIMITY) ", tag); + logError(0, "SENSE GAP TEST:................."); + logError(0, "FAIL\n"); + count_fail += 1; + print_frame_short("SS Raw sense frame =", + array1dTo2d_short(ssRawFrame.sense_data, + rows*columns, columns), + rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA + | ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else { + logError(0, "%s SS RAW (PROXIMITY) SENSE", tag); + logError(0, " GAP TEST:.................OK\n"); + } + + kfree(thresholds); + thresholds = NULL; + } else { + logError(0, "%s SS RAW (PROXIMITY) SENSE GAP ", tag); + logError(0, "TEST:.................SKIPPED\n"); + } + + kfree(ssRawFrame.sense_data); + ssRawFrame.sense_data = NULL; + } + + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s SS RAW testes finished!.................OK\n\n", + tag); + return OK; + } + logError(0, "%s SS RAW testes finished!.................", tag); + logError(0, "FAILED fails_count = %d\n\n", count_fail); + return (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); + +ERROR_LIMITS: + kfree(ssRawFrame.force_data); + kfree(ssRawFrame.sense_data); + kfree(thresholds); + return ret; + +} + +int production_test_ss_ix_cx(char *path_limits, int stop_on_fail, + struct TestToDo *todo) +{ + int ret; + int count_fail = 0; + + int *thresholds = NULL; + int trows, tcolumns; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + + struct SelfSenseData ssCompData; + + u8 *adjhor = NULL; + u8 *adjvert = NULL; + + u16 container; + int *ix1_w = NULL; + int *ix2_w = NULL; + u16 *total_ix = NULL; + u16 *total_cx = NULL; + + u16 *total_adjhor = NULL; + u16 *total_adjvert = NULL; + + logError(0, "%s\n", tag); + logError(0, "%s SS IX CX testes are starting...\n", tag); + + //read the SS compensation data + ret = readSelfSenseCompensationData(SS_TOUCH, &ssCompData); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "readSelfSenseCompensationData failed... ", tag); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + //********************** SS FORCE IX *********************************/ + //SS IX1 FORCE TEST + logError(0, "%s SS IX1 FORCE TEST:\n", tag); + if (todo->SelfForceIx1 == 1) { + ret = parseProductionTestLimits(path_limits, + SS_IX1_FORCE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_FORCE_MIN_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + container = (u16) ssCompData.f_ix1; + + //check the limits + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax "); + logError(1, "SS IX1 FORCE TEST failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s SS IX1 FORCE TEST:......OK\n\n", tag); + } + + kfree(thresholds); + thresholds = NULL; + //SS IX2 FORCE TEST + logError(0, "%s SS IX2 FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceIx2 == 1) { + //load the min thresholds + ret = parseProductionTestLimits(path_limits, + SS_IX2_FORCE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX2_FORCE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_IX2_FORCE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits"); + logError(1, "SS_IX2_FORCE_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMap(ssCompData.ix2_fm, + ssCompData.header.force_node, + 1, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS IX2 FORCE failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS IX2 FORCE MIN MAX TEST:.........."); + logError(0, "FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS IX2 FORCE MIN MAX TEST:.....", tag); + logError(0, "OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else { + logError(0, "%s SS IX2 FORCE MIN MAX TEST:...........", tag); + logError(0, "KIPPED\n\n"); + } + + logError(0, "%s SS IX2 FORCE ADJ TEST:\n", tag); + if (todo->SelfForceIx2Adj == 1) { + //SS IX2 FORCE ADJV TEST + logError(0, "%s SS IX2 FORCE ADJVERT TEST:\n", tag); + ret = computeAdjVert(ssCompData.ix2_fm, + ssCompData.header.force_node, + 1, + &adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjVert SS IX2 FORCE ADJV "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS IX2 FORCE ADJV computed!\n", tag); + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_IX2_FORCE_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node - 1 + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX2_FORCE_ADJV_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapAdj(adjvert, + ssCompData.header.force_node - 1, + 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS IX2 FORCE failed... "); + logError(0, "FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS IX2 FORCE ADJV TEST:", tag); + logError(0, ".................OK\n\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjvert); + adjvert = NULL; + + } else { + logError(0, "%s SS IX2 FORCE ADJ TEST:", tag); + logError(0, ".................SKIPPED\n\n"); + } + + //SS TOTAL FORCE IX + logError(0, "%s SS TOTAL IX FORCE TEST:\n", tag); + if (todo->SelfForceIxTotal == 1 || todo->SelfForceIxTotalAdj == 1) { + logError(0, "%s Reading TOTAL IX FORCE Weights...\n", tag); + + //load the IX1 weight + ret = parseProductionTestLimits(path_limits, + SS_IX1_FORCE_W, + &ix1_w, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_FORCE_W failed... ERROR %02X\n", + tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + //load the IX2 weight + ret = parseProductionTestLimits(path_limits, + SS_IX2_FORCE_W, + &ix2_w, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_FORCE_W failed... ERROR %02X\n", + tag, ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s Weights: IX1_W = %d IX2_W = %d\n", + tag, *ix1_w, *ix2_w); + ret = computeTotal(ssCompData.ix2_fm, ssCompData.f_ix1, + ssCompData.header.force_node, + 1, + *ix1_w, + *ix2_w, + &total_ix); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeTotal Ix Force failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + kfree(ix1_w); + ix1_w = NULL; + kfree(ix2_w); + ix2_w = NULL; + + logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceIxTotal == 1) { + //load the min thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_FORCE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_FORCE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_FORCE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_FORCE_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapTotal(total_ix, + ssCompData.header.force_node, + 1, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS TOTAL IX FORCE"); + logError(1, "failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s SS TOTAL IX FORCE MIN MAX ", + tag); + logError(0, "TEST:.................FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL IX FORCE MIN MAX ", + tag); + logError(0, "TEST:.................OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else { + logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:", tag); + logError(0, ".................SKIPPED\n"); + } + + logError(0, "%s SS TOTAL IX FORCE ADJ TEST:\n", tag); + if (todo->SelfForceIxTotalAdj == 1) { + //SS TOTAL IX FORCE ADJV TEST + logError(0, "%s SS TOTAL IX FORCE ADJVERT TEST:\n", + tag); + ret = computeAdjVertTotal(total_ix, + ssCompData.header.force_node, + 1, + &total_adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjVert SS TOTAL IX "); + logError(1, "FORCE ADJV failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS TOTAL IX FORCE ADJV computed!\n", + tag); + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_FORCE_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 + || (trows != ssCompData.header.force_node - 1 + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_FORCE_ADJV_MAP_MAX"); + logError(1, "... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapAdjTotal(total_adjvert, + ssCompData.header.force_node - 1, + 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS TOTAL IX "); + logError(1, "FORCE failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS TOTAL IX FORCE ADJV TEST:", + tag); + logError(0, ".................FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL IX FORCE ADJV TEST:", + tag); + logError(0, ".................OK\n\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjvert); + total_adjvert = NULL; + } else { + logError(0, "%s SS TOTAL IX FORCE ADJ TEST:"); + logError(0, ".................SKIPPED\n"); + } + + kfree(total_ix); + total_ix = NULL; + } else { + logError(0, "%s SS TOTAL IX FORCE TEST:", tag); + logError(0, ".................SKIPPED\n\n"); + } + + + //************** SS SENSE IX *******************/ + //SS IX1 SENSE TEST + logError(0, "%s SS IX1 SENSE TEST:\n", tag); + if (todo->SelfSenseIx1 == 1) { + ret = parseProductionTestLimits(path_limits, + SS_IX1_SENSE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_SENSE_MIN_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (u16) ssCompData.s_ix1; + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); //check the limits + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax SS IX1 SENSE TEST "); + logError(1, "failed... ERROR COUNT = %d\n", ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS IX1 SENSE TEST:..............", tag); + logError(0, "...OK\n\n"); + } + } else { + logError(0, "%s SS IX1 SENSE TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } + + kfree(thresholds); + thresholds = NULL; + //SS IX2 SENSE TEST + logError(0, "%s SS IX2 SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseIx2 == 1) { + ret = parseProductionTestLimits(path_limits, + SS_IX2_SENSE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); //load the min thresholds + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "S_IX2_SENSE_MAP_MIN failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, + SS_IX2_SENSE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); //load the max thresholds + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX2_SENSE_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMap(ssCompData.ix2_sn, + 1, + ssCompData.header.sense_node, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS IX2 SENSE failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS IX2 SENSE MIN MAX TEST:.....", tag); + logError(0, "............FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS IX2 SENSE MIN MAX TEST:", tag); + logError(0, ".................OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else { + logError(0, "%s SS IX2 SENSE MIN MAX TEST:..............", tag); + logError(0, "...SKIPPED\n\n"); + } + + logError(0, "%s SS IX2 SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseIx2Adj == 1) { + //SS IX2 SENSE ADJH TEST + logError(0, "%s SS IX2 SENSE ADJHORIZ TEST:\n", tag); + ret = computeAdjHoriz(ssCompData.ix2_sn, + 1, + ssCompData.header.sense_node, + &adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjHoriz SS IX2 SENSE ADJH "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS IX2 SENSE ADJ HORIZ computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, + SS_IX2_SENSE_ADJH_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); //load the max thresholds + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX2_SENSE_ADJH_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapAdj(adjhor, + 1, + ssCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj SS IX2 SENSE ADJH "); + logError(1, "failed... ERROR COUNT = %d\n", ret); + logError(0, "%s SS IX2 SENSE ADJH TEST:.......", tag); + logError(0, "..........FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS IX2 SENSE ADJH TEST:........", tag); + logError(0, ".........OK\n\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjhor); + adjhor = NULL; + } else { + logError(0, "%s SS IX2 SENSE ADJ TEST:.................", tag); + logError(0, "SKIPPED\n", tag); + } + + //SS TOTAL IX SENSE + logError(0, "%s SS TOTAL IX SENSE TEST:\n", tag); + if (todo->SelfSenseIxTotal == 1 || todo->SelfSenseIxTotalAdj == 1) { + logError(0, "%s Reading TOTAL IX SENSE Weights...\n", tag); + //load the IX1 weight + ret = parseProductionTestLimits(path_limits, + SS_IX1_SENSE_W, + &ix1_w, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_SENSE_W failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load the IX2 weight + ret = parseProductionTestLimits(path_limits, + SS_IX2_SENSE_W, + &ix2_w, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX1_SENSE_W failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + logError(0, "%s Weights: IX1_W = %d IX2_W = %d\n", + tag, *ix1_w, *ix2_w); + + ret = computeTotal(ssCompData.ix2_sn, + ssCompData.s_ix1, + 1, + ssCompData.header.sense_node, + *ix1_w, + *ix2_w, + &total_ix); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeTotal Ix Sense "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + kfree(ix1_w); + ix1_w = NULL; + kfree(ix2_w); + ix2_w = NULL; + + logError(0, "%s SS TOTAL IX SENSE MIN MAX TEST:\n", tag); + //load the min thresholds + if (todo->SelfSenseIxTotal == 1) { + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_SENSE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_SENSE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_SENSE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || + tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_SENSE_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapTotal(total_ix, + 1, + ssCompData.header.sense_node, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS TOTAL IX SENSE"); + logError(1, " failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s SS TOTAL IX SENSE MIN MAX ", + tag); + logError(0, "TEST:.................FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL IX SENSE MIN MAX ", + tag); + logError(0, "TEST:.................OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else { + logError(0, "%s SS TOTAL IX SENSE MIN MAX ", tag); + logError(0, "TEST:.................SKIPPED\n"); + } + + + logError(0, "%s SS TOTAL IX SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseIxTotalAdj == 1) { + //SS TOTAL IX SENSE ADJH TEST + logError(0, "%s SS TOTAL IX SENSE ADJHORIZ TEST:\n", + tag); + ret = computeAdjHorizTotal(total_ix, + 1, + ssCompData.header.sense_node, + &total_adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjHoriz SS TOTAL "); + logError(1, "IXSENSE ADJH failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS TOTAL IX SENSE ADJ HORIZ ", tag); + logError(0, "computed!\n"); + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_IX_SENSE_ADJH_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_IX_SENSE_ADJH_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapAdjTotal(total_adjhor, + 1, + ssCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj SS TOTAL "); + logError(1, "IX SENSE ADJH failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS TOTAL IX SENSE ADJH ", tag); + logError(0, "TEST:.................FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL IX SENSE ADJH ", tag); + logError(0, "TEST:.................OK\n\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjhor); + total_adjhor = NULL; + } else { + logError(0, "%s SS TOTAL IX SENSE ADJ TEST:.....", tag); + logError(0, "............SKIPPED\n"); + } + kfree(total_ix); + total_ix = NULL; + } else { + logError(0, "%s SS TOTAL IX SENSE TEST:............", tag); + logError(0, ".....SKIPPED\n"); + } + + //************************ SS SENSE CX *******************************/ + //SS CX1 FORCE TEST + logError(0, "%s SS CX1 FORCE TEST:\n", tag); + if (todo->SelfForceCx1 == 1) { + + ret = parseProductionTestLimits(path_limits, + SS_CX1_FORCE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX1_FORCE_MIN_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the limits + container = (u16) ssCompData.f_cx1; + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax SS CX1 FORCE TEST "); + logError(1, "failed... ERROR COUNT = %d\n", ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS CX1 FORCE TEST:.............", tag); + logError(0, "....OK\n\n"); + } + kfree(thresholds); + thresholds = NULL; + } else { + logError(0, "%s SS CX1 FORCE TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } + + //SS CX2 FORCE TEST + logError(0, "%s SS CX2 FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceCx2 == 1) { + //load the min thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_FORCE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "SS_CX2_FORCE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_FORCE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX2_FORCE_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMap(ssCompData.cx2_fm, + ssCompData.header.force_node, + 1, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "%checkLimitsMap SS CX2 FORCE "); + logError(1, "%failed... ERROR COUNT = %d\n", ret); + logError(0, "%s SS CX2 FORCE MIN MAX TEST:.....", tag); + logError(0, "............FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS CX2 FORCE MIN MAX TEST:......", tag); + logError(0, "...........OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else { + logError(0, "%s SS CX2 FORCE MIN MAX TEST:..............", tag); + logError(0, "...SKIPPED\n"); + } + + logError(0, "%s SS CX2 FORCE ADJ TEST:\n", tag); + if (todo->SelfForceCx2Adj == 1) { + //SS CX2 FORCE ADJV TEST + logError(0, "%s SS CX2 FORCE ADJVERT TEST:\n", tag); + //comepute the ADJV for CX2 FORCE + ret = computeAdjVert(ssCompData.cx2_fm, + ssCompData.header.force_node, + 1, + &adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjVert SS CX2 FORCE ADJV "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS CX2 FORCE ADJV computed!\n", tag); + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_FORCE_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node - 1 + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX2_FORCE_ADJV_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapAdj(adjvert, + ssCompData.header.force_node - 1, + 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS IX2 FORCE "); + logError(1, "failed... ERROR COUNT = %d\n", ret); + logError(0, "%s SS CX2 FORCE ADJV TEST:......", tag); + logError(0, "...........FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS CX2 FORCE ADJV TEST:.....", tag); + logError(0, "............OK\n\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjvert); + adjvert = NULL; + } else { + logError(0, "%s SS CX2 FORCE ADJ TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } + //SS TOTAL CX FORCE + logError(0, "%s SS TOTAL CX FORCE TEST:\n", tag); + if (todo->SelfForceCxTotal == 1 || todo->SelfForceCxTotalAdj == 1) { + ret = computeTotal(ssCompData.cx2_fm, + ssCompData.f_cx1, + ssCompData.header.force_node, + 1, + CX1_WEIGHT, + CX2_WEIGHT, + &total_cx); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeTotal Cx Force failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + return (ret | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s SS TOTAL CX FORCE MIN MAX TEST:\n", tag); + //load the min thresholds + if (todo->SelfForceCxTotal == 1) { + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_FORCE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_FORCE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_FORCE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != ssCompData.header.force_node + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_FORCE_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapTotal(total_cx, + ssCompData.header.force_node, + 1, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS TOTAL FORCE "); + logError(1, "failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s SS TOTAL FORCE MIN MAX ", tag); + logError(0, "TEST:.................FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL FORCE MIN MAX ", tag); + logError(0, "TEST:.................OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else { + logError(0, "%s SS TOTAL CX FORCE MIN MAX TEST:", tag); + logError(0, ".................SKIPPED\n"); + } + + //SS TOTAL CX FORCE ADJV TEST + logError(0, "%s SS TOTAL CX FORCE ADJ TEST:\n", tag); + if (todo->SelfForceCxTotalAdj == 1) { + logError(0, "%s SS TOTAL CX FORCE ADJVERT ", tag); + logError(0, "TEST:\n"); + + //comepute the ADJV for CX2 FORCE + ret = computeAdjVertTotal(total_cx, + ssCompData.header.force_node, + 1, + &total_adjvert); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjVert SS TOTAL CX FORCE"); + logError(1, " ADJV failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS TOTAL CX FORCE ADJV computed!\n", + tag); + + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_FORCE_ADJV_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); //load the max thresholds + if (ret < 0 + || (trows != ssCompData.header.force_node - 1 + || tcolumns != 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_FORCE_ADJV_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapAdjTotal(total_adjvert, + ssCompData.header.force_node - 1, + 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS TOTAL CX FORCE"); + logError(1, " failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s SS TOTAL CX FORCE ADJV ", tag); + logError(0, "TEST:.................FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL CX FORCE ADJV ", tag); + logError(0, "TEST:.................OK\n\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjvert); + total_adjvert = NULL; + + } else { + logError(0, "%s SS TOTAL CX FORCE ADJ TEST:......", + tag); + logError(0, "..........SKIPPED\n"); + } + kfree(total_cx); + total_cx = NULL; + } else { + logError(0, "%s SS TOTAL CX FORCE TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } + + + //**************** SS SENSE CX **************************************/ + //SS CX1 SENSE TEST + logError(0, "%s SS CX1 SENSE TEST:\n", tag); + if (todo->SelfSenseCx1 == 1) { + ret = parseProductionTestLimits(path_limits, + SS_CX1_SENSE_MIN_MAX, + &thresholds, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX1_SENSE_MIN_MAX failed"); + logError(1, "... ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (u16) ssCompData.s_cx1; + //check the limits + ret = checkLimitsMinMax(&container, + 1, + 1, + thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMinMax SS CX1 SENSE TEST "); + logError(1, "failed... ERROR COUNT = %d\n", ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS CX1 SENSE TEST:..............", tag); + logError(0, "...OK\n\n"); + } + kfree(thresholds); + thresholds = NULL; + } else { + logError(0, "%s SS CX1 SENSE TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } + + + //SS CX2 SENSE TEST + logError(0, "%s SS CX2 SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseCx2 == 1) { + //load the min thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_SENSE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX2_SENSE_MAP_MIN failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_SENSE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_CX2_SENSE_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMap(ssCompData.cx2_sn, + 1, + ssCompData.header.sense_node, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMap SS CX2 SENSE failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS CX2 SENSE MIN MAX TEST:......", tag); + logError(0, "...........FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS CX2 SENSE MIN MAX TEST:", tag); + logError(0, ".................OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else { + logError(0, "%s SS CX2 SENSE MIN MAX TEST:.........", tag); + logError(0, "........SKIPPED\n"); + } + logError(0, "%s SS CX2 SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseCx2Adj == 1) { + //SS CX2 SENSE ADJH TEST + logError(0, "%s SS CX2 SENSE ADJHORIZ TEST:\n", tag); + ret = computeAdjHoriz(ssCompData.cx2_sn, + 1, + ssCompData.header.sense_node, + &adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjHoriz SS CX2 SENSE ADJH "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS CX2 SENSE ADJH computed!\n", tag); + + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_CX2_SENSE_ADJH_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_IX2_SENSE_MAP_MAX failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapAdj(adjhor, + 1, + ssCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj SS CX2 SENSE ADJH "); + logError(1, "failed... ERROR COUNT = %d\n", ret); + logError(0, "%s SS CX2 SENSE ADJH TEST:.........", tag); + logError(0, "........FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS CX2 SENSE ADJH TEST:.........", tag); + logError(0, "........OK\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjhor); + adjhor = NULL; + } else { + logError(0, "%s SS CX2 SENSE ADJ TEST:.................", tag); + logError(0, "SKIPPED\n\n"); + } + + //SS TOTAL CX SENSE + logError(0, "%s SS TOTAL CX SENSE TEST:\n", tag); + if (todo->SelfSenseCxTotal == 1 || todo->SelfSenseCxTotalAdj == 1) { + ret = computeTotal(ssCompData.cx2_sn, + ssCompData.s_cx1, + 1, + ssCompData.header.sense_node, + CX1_WEIGHT, + CX2_WEIGHT, + &total_cx); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeTotal Cx Sense failed... "); + logError(1, "ERROR %02X\n", ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:\n", tag); + //load the min thresholds + if (todo->SelfSenseCxTotal == 1) { + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_SENSE_MAP_MIN, + &thresholds_min, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_SENSE_MAP_MIN "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + //load the max thresholds + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_SENSE_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); + if (ret < 0 || (trows != 1 + || tcolumns != ssCompData.header.sense_node)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_SENSE_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapTotal(total_cx, + 1, + ssCompData.header.sense_node, + thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "heckLimitsMap SS TOTAL CX SENSE "); + logError(1, "failed... ERROR COUNT = %d\n", + ret); + logError(0, "%s SS TOTAL CX SENSE MIN ", tag); + logError(0, "MAX TEST:................."); + logError(0, "FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL CX SENSE MIN ", tag); + logError(0, "MAX TEST:................OK\n\n"); + } + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else { + logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:", tag); + logError(0, ".................SKIPPED\n"); + } + + + //SS TOTAL IX SENSE ADJH TEST + logError(0, "%s SS TOTAL CX SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseCxTotalAdj == 1) { + logError(0, "%s SS TOTAL CX SENSE ADJHORIZ TEST:\n", + tag); + ret = computeAdjHorizTotal(total_cx, + 1, + ssCompData.header.sense_node, + &total_adjhor); + if (ret < 0) { + logError(1, "%s production_test_data: ", tag); + logError(1, "computeAdjHoriz SS TOTAL CX "); + logError(1, "SENSE ADJH failed... "); + logError(1, "ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS TOTAL CX SENSE ADJ HORIZ ", tag); + logError(0, "computed!\n"); + + ret = parseProductionTestLimits(path_limits, + SS_TOTAL_CX_SENSE_ADJH_MAP_MAX, + &thresholds_max, + &trows, + &tcolumns); //load the max thresholds + if (ret < 0 || (trows != 1 || + tcolumns != ssCompData.header.sense_node - 1)) { + logError(1, "%s production_test_data: ", tag); + logError(1, "parseProductionTestLimits "); + logError(1, "SS_TOTAL_CX_SENSE_ADJH_MAP_MAX "); + logError(1, "failed... ERROR %02X\n", + ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + //check the values with thresholds + ret = checkLimitsMapAdjTotal(total_adjhor, + 1, + ssCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, "%s production_test_data: ", tag); + logError(1, "checkLimitsMapAdj SS TOTAL "); + logError(1, "CX SENSE ADJH failed... "); + logError(1, "ERROR COUNT = %d\n", ret); + logError(0, "%s SS TOTAL CX SENSE ADJH ", tag); + logError(0, "TEST:...FAIL\n\n"); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else { + logError(0, "%s SS TOTAL CX SENSE ADJH TEST:", + tag); + logError(0, ".................OK\n\n"); + } + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjhor); + total_adjhor = NULL; + } else { + logError(0, "%s SS TOTAL CX SENSE ADJ TEST:.", tag); + logError(0, "SKIPPED\n"); + } + kfree(total_cx); + total_cx = NULL; + } else + logError(0, "%s SS TOTAL CX SENSE TEST:.....SKIPPED\n", tag); + + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + kfree(ssCompData.ix2_fm); + ssCompData.ix2_fm = NULL; + kfree(ssCompData.ix2_sn); + ssCompData.ix2_sn = NULL; + kfree(ssCompData.cx2_fm); + ssCompData.cx2_fm = NULL; + kfree(ssCompData.cx2_sn); + ssCompData.cx2_sn = NULL; + logError(0, "%s SS IX CX testes finished!........OK\n\n", tag); + ret = OK; + } else { + //print all kind of data in just one row for readability reason + print_frame_u8("SS Init Data Ix2_fm = ", + array1dTo2d_u8(ssCompData.ix2_fm, + ssCompData.header.force_node, + ssCompData.header.force_node), + 1, + ssCompData.header.force_node); + print_frame_u8("SS Init Data Cx2_fm = ", + array1dTo2d_u8(ssCompData.cx2_fm, + ssCompData.header.force_node, + ssCompData.header.force_node), + 1, + ssCompData.header.force_node); + print_frame_u8("SS Init Data Ix2_sn = ", + array1dTo2d_u8(ssCompData.ix2_sn, + ssCompData.header.sense_node, + ssCompData.header.sense_node), + 1, + ssCompData.header.sense_node); + print_frame_u8("SS Init Data Cx2_sn = ", + array1dTo2d_u8(ssCompData.cx2_sn, + ssCompData.header.sense_node, + ssCompData.header.sense_node), + 1, + ssCompData.header.sense_node); + logError(0, "%s SS IX CX testes finished!.................", + tag); + logError(0, "FAILED fails_count = %d\n\n", count_fail); + kfree(thresholds); + kfree(thresholds_min); + kfree(thresholds_max); + kfree(adjhor); + kfree(adjvert); + kfree(ix1_w); + kfree(ix2_w); + kfree(total_ix); + kfree(total_cx); + kfree(total_adjhor); + kfree(total_adjvert); + kfree(ssCompData.ix2_fm); + kfree(ssCompData.ix2_sn); + kfree(ssCompData.cx2_fm); + kfree(ssCompData.cx2_sn); + ret = (ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA); + } + return ret; +ERROR_LIMITS: + kfree(thresholds); + kfree(thresholds_min); + kfree(thresholds_max); + kfree(adjhor); + kfree(adjvert); + kfree(ix1_w); + kfree(ix2_w); + kfree(total_ix); + kfree(total_cx); + kfree(total_adjhor); + kfree(total_adjvert); + kfree(ssCompData.ix2_fm); + kfree(ssCompData.ix2_sn); + kfree(ssCompData.cx2_fm); + kfree(ssCompData.cx2_sn); + return ret; +} + +int production_test_data(char *path_limits, int stop_on_fail, + struct TestToDo *todo) +{ + int res = OK, ret; + + if (todo == NULL) { + logError(0, "%s %s: ", tag, __func__); + logError(0, "No TestToDo specified!! "); + logError(0, "ERROR = %02X\n", + (ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_DATA)); + return (ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_DATA); + } + + logError(0, "%s DATA Production test is starting...\n", tag); + ret = production_test_ms_raw(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(0, "%s %s: ", tag, __func__); + logError(0, "production_test_ms_raw failed... "); + logError(0, "ERROR = %02X\n", ret); + if (stop_on_fail == 1) + goto END; + } + + ret = production_test_ms_cx(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(0, "%s %s: ", tag, __func__); + logError(0, "production_test_ms_cx failed... "); + logError(0, "ERROR = %02X\n", ret); + if (stop_on_fail == 1) + goto END; + } + + ret = production_test_ss_raw(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(0, "%s %s: ", tag, __func__); + logError(0, "production_test_ss_raw failed... "); + logError(0, "ERROR = %02X\n", ret); + if (stop_on_fail == 1) + goto END; + } + + ret = production_test_ss_ix_cx(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(0, "%s %s: ", tag, __func__); + logError(0, "production_test_ss_ix_cx failed... "); + logError(0, "ERROR = %02X\n", ret); + if (stop_on_fail == 1) + goto END; + } + +END: + if (res < OK) + logError(0, "%s DATA Production test failed!\n", tag); + else + logError(0, "%s DATA Production test finished!\n", tag); + return res; +} + + +int save_mp_flag(u32 signature) +{ + int res = -1; + int i; + u8 cmd[6] = {FTS_CMD_WRITE_MP_FLAG, 0x00, 0x00, 0x00, 0x00, 0x00}; + + u32ToU8(signature, &cmd[2]); + + logError(0, "%s Starting Saving Flag with signature = %08X ...\n", + tag, signature); + + for (i = 0; i < SAVE_FLAG_RETRY && res < OK; i++) { + logError(0, "%s Attempt number %d to save mp flag !\n", + tag, i+1); + logError(0, "%s Command write flag sent...\n", tag); + res = fts_writeFwCmd(cmd, 6); + if (res >= OK) + res = save_cx_tuning(); + } + + if (res < OK) { + logError(1, "%s %s: ERROR %08X ...\n", tag, __func__, res); + } else { + logError(0, "%s Saving Flag DONE!\n", tag); + res = OK; + } + return res; +} + +int parseProductionTestLimits(char *path, char *label, + int **data, int *row, int *column) +{ + int find = 0; + char *token = NULL; + int i = 0; + int j = 0; + int z = 0; + + char *line2 = NULL; + char line[800]; + int fd = -1; + char *buf = NULL; + int n, size, pointer = 0, ret = OK; + char *data_file = NULL; +#ifndef LIMITS_H_FILE + const struct firmware *fw = NULL; + struct device *dev = NULL; + + dev = getDev(); + if (dev != NULL) + fd = request_firmware(&fw, path, dev); +#else + fd = 0; +#endif + + if (fd != 0) { + logError(0, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } + +#ifndef LIMITS_H_FILE + size = fw->size; + data_file = (char *)fw->data; + logError(0, "%s Start to reading %s...\n", tag, path); +#else + size = LIMITS_SIZE_NAME; + data_file = (char *)(LIMITS_ARRAY_NAME); +#endif + logError(0, "%s The size of the limits file is %d bytes\n", tag, size); + + while (find == 0) { + //start to look for the wanted label + if (readLine(&data_file[pointer], line, size-pointer, &n) < 0) { + find = -1; + break; + } + pointer += n; + //each header row start with + // *ex. *label, n_row, n_colum + if (line[0] != '*') + continue; + + line2 = kstrdup(line, GFP_KERNEL); + if (line2 == NULL) { + logError(1, "%s %s:kstrdup ERR %02X\n", + tag, __func__, ERROR_ALLOC); + ret = ERROR_ALLOC; + goto END; + } + buf = line2; + line2 += 1; + token = strsep(&line2, ","); + //if the row is the wanted one i + //retrieve rows and columns info + if (strcmp(token, label) == 0) { + find = 1; + token = strsep(&line2, ","); + if (token != NULL) { + ret = kstrtoint(token, 10, row); + if (ret != 0) + return -EINVAL; + logError(0, "%s Row = %d\n", tag, *row); + } else { + logError(1, "%s %s 1:ERROR %02X\n", + tag, __func__, ERROR_FILE_PARSE); + //release_firmware(fw); + //return ERROR_FILE_PARSE; + ret = ERROR_FILE_PARSE; + goto END; + } + token = strsep(&line2, ","); + if (token != NULL) { + ret = kstrtoint(token, 10, column); + if (ret != 0) + return -EINVAL; + logError(0, "%s Column = %d\n", tag, *column); + } else { + logError(1, "%s %s 2: ERROR %02X\n", + tag, __func__, ERROR_FILE_PARSE); + //release_firmware(fw); + //return ERROR_FILE_PARSE; + ret = ERROR_FILE_PARSE; + goto END; + } + kfree(buf); + buf = NULL; + //allocate memory for containing data + *data = (int *)kmalloc_array(((*row) * (*column)), + sizeof(int), GFP_KERNEL); + j = 0; + if (*data == NULL) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_ALLOC); + //release_firmware(fw); + //return ERROR_ALLOC; + ret = ERROR_ALLOC; + goto END; + } + //start to read the data + for (i = 0; i < *row; i++) { + //line = buf; + if (readLine(&data_file[pointer], line, + size-pointer, &n) < 0) { + logError(1, "%s %s : ERROR %02X\n", + tag, __func__, ERROR_FILE_READ); + //release_firmware(fw); + //return ERROR_FILE_READ + ret = ERROR_FILE_READ; + goto END; + } + pointer += n; + line2 = kstrdup(line, GFP_KERNEL); + if (line2 == NULL) { + logError(1, "%s %s: kstrdup ", + tag, __func__); + logError(1, "ERROR %02X\n", + ERROR_ALLOC); + ret = ERROR_ALLOC; + goto END; + } + buf = line2; + token = strsep(&line2, ","); + for (z = 0; + (z < *column) && (token != NULL); z++) { + ret = kstrtoint(token, + 10, + ((*data) + j)); + if (ret != 0) + return -EINVAL; + j++; + token = strsep(&line2, ","); + } + kfree(buf); + buf = NULL; + } + //check that all the data are read + if (j == ((*row) * (*column))) { + logError(0, "%s READ DONE!\n", tag); + //release_firmware(fw); + //return OK; + ret = OK; + goto END; + } + logError(1, "%s %s 3:ERROR %02X\n", + tag, __func__, ERROR_FILE_PARSE); + //release_firmware(fw); + //return ERROR_FILE_PARSE; + ret = ERROR_FILE_PARSE; + goto END; + } + kfree(buf); + buf = NULL; + + } + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_LABEL_NOT_FOUND); + ret = ERROR_LABEL_NOT_FOUND; +END: + kfree(buf); +#ifndef LIMITS_H_FILE + release_firmware(fw); +#endif + return ret; + +} + +int readLine(char *data, char *line, int size, int *n) +{ + int i = 0; + + if (size < 1) + return -EINVAL; + + while (data[i] != '\n' && i < size) { + line[i] = data[i]; + i++; + } + *n = i + 1; + line[i] = '\0'; + + return OK; +} + + diff --git a/st/fts_lib/ftsTest.h b/st/fts_lib/ftsTest.h new file mode 100644 index 0000000000..0e9334d452 --- /dev/null +++ b/st/fts_lib/ftsTest.h @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for MP test ** + * * + ************************************************************************** + ************************************************************************** + */ + +#ifndef __FTS_TEST_H +#define __FTS_TEST_H + +#include "ftsSoftware.h" + +#define LIMITS_FILE "stm_fts_production_limits.csv" + +#define WAIT_FOR_FRESH_FRAMES 100 //ms +#define WAIT_AFTER_SENSEOFF 50 //ms + +#define TIMEOUT_ITO_TEST_RESULT 200 //ms +#define TIMEOUT_INITIALIZATION_TEST_RESULT 5000 //ms + +//LABELS PRODUCTION TEST LIMITS FILE +#define MS_RAW_MIN_MAX "MS_RAW_DATA_MIN_MAX" +#define MS_RAW_GAP "MS_RAW_DATA_GAP" +#define MS_CX1_MIN_MAX "MS_TOUCH_ACTIVE_CX1_MIN_MAX" +#define MS_CX2_MAP_MIN "MS_TOUCH_ACTIVE_CX2_MIN" +#define MS_CX2_MAP_MAX "MS_TOUCH_ACTIVE_CX2_MAX" +#define MS_CX2_ADJH_MAP_MAX "MS_TOUCH_ACTIVE_CX2_ADJ_HORIZONTAL" +#define MS_CX2_ADJV_MAP_MAX "MS_TOUCH_ACTIVE_CX2_ADJ_VERTICAL" +#define MS_TOTAL_CX_MAP_MIN "MS_TOUCH_ACTIVE_TOTAL_CX_MIN" +#define MS_TOTAL_CX_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_MAX" +#define MS_TOTAL_CX_ADJH_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_ADJ_HORIZONTAL" +#define MS_TOTAL_CX_ADJV_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_ADJ_VERTICAL" +#define SS_RAW_FORCE_MIN_MAX "SS_RAW_DATA_FORCE_MIN_MAX" +#define SS_RAW_SENSE_MIN_MAX "SS_RAW_DATA_SENSE_MIN_MAX" +#define SS_RAW_FORCE_GAP "SS_RAW_DATA_FORCE_GAP" +#define SS_RAW_SENSE_GAP "SS_RAW_DATA_SENSE_GAP" +#define SS_IX1_FORCE_MIN_MAX "SS_TOUCH_ACTIVE_IX1_FORCE_MIN_MAX" +#define SS_IX1_SENSE_MIN_MAX "SS_TOUCH_ACTIVE_IX1_SENSE_MIN_MAX" +#define SS_CX1_FORCE_MIN_MAX "SS_TOUCH_ACTIVE_CX1_FORCE_MIN_MAX" +#define SS_CX1_SENSE_MIN_MAX "SS_TOUCH_ACTIVE_CX1_SENSE_MIN_MAX" +#define SS_IX2_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_IX2_FORCE_MIN" +#define SS_IX2_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_IX2_FORCE_MAX" +#define SS_IX2_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_IX2_SENSE_MIN" +#define SS_IX2_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_IX2_SENSE_MAX" +#define SS_IX2_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_IX2_ADJ_VERTICAL" +#define SS_IX2_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_IX2_ADJ_HORIZONTAL" +#define SS_CX2_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_CX2_FORCE_MIN" +#define SS_CX2_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_CX2_FORCE_MAX" +#define SS_CX2_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_CX2_SENSE_MIN" +#define SS_CX2_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_CX2_SENSE_MAX" +#define SS_CX2_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_CX2_ADJ_VERTICAL" +#define SS_CX2_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_CX2_ADJ_HORIZONTAL" + +// TOTAL SS +#define SS_TOTAL_IX_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_IX_FORCE_MIN" +#define SS_TOTAL_IX_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_FORCE_MAX" +#define SS_TOTAL_IX_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_IX_SENSE_MIN" +#define SS_TOTAL_IX_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_SENSE_MAX" +#define SS_TOTAL_IX_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_ADJ_VERTICAL" +#define SS_TOTAL_IX_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_ADJ_HORIZONTAL" +#define SS_TOTAL_CX_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_CX_FORCE_MIN" +#define SS_TOTAL_CX_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_FORCE_MAX" +#define SS_TOTAL_CX_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_CX_SENSE_MIN" +#define SS_TOTAL_CX_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_SENSE_MAX" +#define SS_TOTAL_CX_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_ADJ_VERTICAL" +#define SS_TOTAL_CX_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_ADJ_HORIZONTAL" + +//KEYS +#define MS_KEY_RAW_MIN_MAX "MS_KEY_RAW_DATA_MIN_MAX" +#define MS_KEY_CX1_MIN_MAX "MS_KEY_CX1_MIN_MAX" +#define MS_KEY_CX2_MAP_MIN "MS_KEY_CX2_MIN" +#define MS_KEY_CX2_MAP_MAX "MS_KEY_CX2_MAX" +#define MS_KEY_TOTAL_CX_MAP_MIN "MS_KEY_TOTAL_CX_MIN" +#define MS_KEY_TOTAL_CX_MAP_MAX "MS_KEY_TOTAL_CX_MAX" + +//CONSTANT TOTAL IX +#define SS_IX1_FORCE_W "IX1_FORCE_W" +#define SS_IX2_FORCE_W "IX2_FORCE_W" +#define SS_IX1_SENSE_W "IX1_SENSE_W" +#define SS_IX2_SENSE_W "IX2_SENSE_W" + + +#define SAVE_FLAG_RETRY 3 + + +struct TestToDo { + int MutualRaw; + int MutualRawGap; + int MutualCx1; + int MutualCx2; + int MutualCx2Adj; + int MutualCxTotal; + int MutualCxTotalAdj; + + int MutualKeyRaw; + int MutualKeyCx1; + int MutualKeyCx2; + int MutualKeyCxTotal; + + int SelfForceRaw; + int SelfForceRawGap; + int SelfForceIx1; + int SelfForceIx2; + int SelfForceIx2Adj; + int SelfForceIxTotal; + int SelfForceIxTotalAdj; + int SelfForceCx1; + int SelfForceCx2; + int SelfForceCx2Adj; + int SelfForceCxTotal; + int SelfForceCxTotalAdj; + + int SelfSenseRaw; + int SelfSenseRawGap; + int SelfSenseIx1; + int SelfSenseIx2; + int SelfSenseIx2Adj; + int SelfSenseIxTotal; + int SelfSenseIxTotalAdj; + int SelfSenseCx1; + int SelfSenseCx2; + int SelfSenseCx2Adj; + int SelfSenseCxTotal; + int SelfSenseCxTotalAdj; + +}; + +int computeAdjHoriz(u8 *data, int row, int column, u8 **result); +int computeAdjHorizTotal(u16 *data, int row, int column, u16 **result); +int computeAdjVert(u8 *data, int row, int column, u8 **result); +int computeAdjVertTotal(u16 *data, int row, int column, u16 **result); +int computeTotal(u8 *data, u8 main, int row, int column, int m, + int n, u16 **result); +int checkLimitsMinMax(short *data, int row, int column, int min, int max); +int checkLimitsMap(u8 *data, int row, int column, int *min, int *max); +int checkLimitsMapTotal(u16 *data, int row, int column, int *min, int *max); +int checkLimitsMapAdj(u8 *data, int row, int column, int *max); +int checkLimitsMapAdjTotal(u16 *data, int row, int column, int *max); +int production_test_ito(void); +int production_test_initialization(void); +int ms_compensation_tuning(void); +int ss_compensation_tuning(void); +int lp_timer_calibration(void); +int save_cx_tuning(void); +int production_test_split_initialization(int saveToFlash); +int production_test_main(char *pathThresholds, int stop_on_fail, int saveInit, + struct TestToDo *todo, u32 signature); +int production_test_ms_raw(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_ms_cx(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_ss_raw(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_ss_ix_cx(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_data(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_ms_key_cx(char *path_limits, int stop_on_fail, + struct TestToDo *todo); +int production_test_ms_key_raw(char *path_limits); +int save_mp_flag(u32 signature); +int parseProductionTestLimits(char *path, char *label, int **data, + int *row, int *column); +int readLine(char *data, char *line, int size, int *n); +#endif diff --git a/st/fts_lib/ftsTime.c b/st/fts_lib/ftsTime.c new file mode 100644 index 0000000000..07d1bf5dba --- /dev/null +++ b/st/fts_lib/ftsTime.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Utility for mesuring/handling the time * + * * + ************************************************************************** + *************************************************************************** + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +#include "ftsCrossCompile.h" +#include "ftsTime.h" + + +void startStopWatch(struct StopWatch *w) +{ + ktime_get_ts(&w->start); +} + +void stopStopWatch(struct StopWatch *w) +{ + ktime_get_ts(&w->end); +} + +int elapsedMillisecond(struct StopWatch *w) +{ + int result; + + result = ((w->end.tv_sec - w->start.tv_sec) * 1000) + + (w->end.tv_nsec - w->start.tv_nsec) / 1000000; + return result; +} + +int elapsedNanosecond(struct StopWatch *w) +{ + int result; + + result = ((w->end.tv_sec - w->start.tv_sec) * 1000000000) + + (w->end.tv_nsec - w->start.tv_nsec); + return result; +} + +char *timestamp() +{ + char *result = NULL; + + result = (char *)kmalloc_array(1, sizeof(char), GFP_KERNEL); + if (result == NULL) + return NULL; + result[0] = ' '; + return result; +} + +void stdelay(unsigned long ms) +{ + msleep(ms); +} diff --git a/st/fts_lib/ftsTime.h b/st/fts_lib/ftsTime.h new file mode 100644 index 0000000000..d98dd2a9fc --- /dev/null +++ b/st/fts_lib/ftsTime.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Utility for mesuring/handling the time * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#ifndef __FTS_TIME_H +#define __FTS_TIME_H + +#include + +#include "ftsCrossCompile.h" + +struct StopWatch { + struct timespec start, end; +}; + +void startStopWatch(struct StopWatch *w); +void stopStopWatch(struct StopWatch *w); +int elapsedMillisecond(struct StopWatch *w); +int elapsedNanosecond(struct StopWatch *w); +char *timestamp(void); +void stdelay(unsigned long ms); +#endif diff --git a/st/fts_lib/ftsTool.c b/st/fts_lib/ftsTool.c new file mode 100644 index 0000000000..543682d1e3 --- /dev/null +++ b/st/fts_lib/ftsTool.c @@ -0,0 +1,1348 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2018, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Utility Functions * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ftsCompensation.h" +#include "ftsCrossCompile.h" +#include "ftsError.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTime.h" +#include "ftsFlash.h" +#include "ftsTool.h" +#include "../fts.h" + +static char tag[8] = "[ FTS ]\0"; +static int reset_gpio = GPIO_NOT_DEFINED; +static int system_resetted_up; +static int system_resetted_down; + +int readB2(u16 address, u8 *outBuf, int len) +{ + int remaining = len; + int toRead = 0; + int retry = 0; + int ret; + int event_to_search[3]; + char *temp = NULL; + u8 *init_outBuf = outBuf; + u16 init_addr = address; + u8 readEvent[FIFO_EVENT_SIZE] = {0}; + u8 cmd[4] = { FTS_CMD_REQU_FW_CONF, 0x00, 0x00, (u8)len }; + + if (readEvent == NULL) { + logError(1, "%s %s:ERROR %02X\n", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + u16ToU8_be(address, &cmd[1]); + temp = printHex("Command B2 = ", cmd, 4); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + do { + remaining = len; + ret = fts_writeFwCmd(cmd, 4); + if (ret < 0) { + logError(1, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + return ret; + } + //ask to the FW the data + logError(0, "%s Command to FW sent!\n", tag); + event_to_search[0] = (int)EVENTID_FW_CONFIGURATION; + while (remaining > OK) { + event_to_search[1] = (int)((address & 0xFF00) >> 8); + event_to_search[2] = (int)(address & 0x00FF); + if (remaining > B2_DATA_BYTES) { + toRead = B2_DATA_BYTES; + remaining -= B2_DATA_BYTES; + } else { + toRead = remaining; + remaining = 0; + } + + ret = pollForEvent(event_to_search, 3, + readEvent, GENERAL_TIMEOUT); + if (ret >= OK) { + //start the polling for reading the reply + memcpy(outBuf, &readEvent[3], toRead); + retry = 0; + outBuf += toRead; + } else { + retry += 1; + break; + } + address += B2_DATA_BYTES; + } + logError(0, "%s %s:B2 failed...attempt = %d\n", + tag, __func__, retry); + outBuf = init_outBuf; + address = init_addr; + } while (retry < B2_RETRY && retry != 0); + + if (retry == B2_RETRY) { + logError(1, "%s %s:ERROR %02X\n", tag, __func__, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + logError(0, "%s B2 read %d bytes\n", tag, len); + + return OK; +} + +int readB2U16(u16 address, u8 *outBuf, int byteToRead) +{ + int remaining = byteToRead; + int toRead = 0; + int ret; + + u8 *buff = (u8 *)kmalloc_array((B2_CHUNK + 1), sizeof(u8), GFP_KERNEL); + + if (buff == NULL) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + if (remaining >= B2_CHUNK) { + toRead = B2_CHUNK; + remaining -= B2_CHUNK; + } else { + toRead = remaining; + remaining = 0; + } + + ret = readB2(address, buff, toRead); + if (ret < 0) { + kfree(buff); + return ret; + } + memcpy(outBuf, buff, toRead); + address += toRead; + outBuf += toRead; + } + kfree(buff); + return OK; +} + + +int releaseInformation(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_RELEASE_INFO }; + int event_to_search[1]; + u8 readEvent[FIFO_EVENT_SIZE]; + + event_to_search[0] = (int)EVENTID_RELEASE_INFO; + + logError(0, "%s %s: started... Chip INFO:\n", tag, __func__); + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); + return ret; + } + + ret = pollForEvent(event_to_search, 1, &readEvent[0], + RELEASE_INFO_TIMEOUT); + //start the polling for reading the reply + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); + return ret; + } + + logError(0, "%s %s: Finished! %d\n", tag, __func__, ret); + return OK; +} + +int lockDownInfo(u8 *data, int len) +{ + int ret; + int i = 0, num_event; + u8 cmd[1] = { FTS_CMD_LOCKDOWN_CMD }; + int event_to_search[3] = {EVENTID_LOCKDOWN_INFO, + EVENT_TYPE_LOCKDOWN, 0x00}; + u8 readEvent[FIFO_EVENT_SIZE]; + + logError(0, "%s %s:started...\n", tag, __func__); + if (len <= 0) + return ERROR_OP_NOT_ALLOW; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s %s:ERROR %02X\n", tag, __func__, ret); + return ret; + } + + num_event = (len + 3) / 4; + logError(0, "%s %s:num_event = %d\n", tag, __func__, num_event); + + for (i = 0; i < num_event; i++) { + ret = pollForEvent(event_to_search, 3, + &readEvent[0], GENERAL_TIMEOUT); + //start the polling for reading the reply + if (ret < OK) { + logError(1, "%s %s:ERROR %02X\n", tag, __func__, ret); + return ret; + } + data[i * 4] = readEvent[3]; + data[i * 4 + 1] = readEvent[4]; + data[i * 4 + 2] = readEvent[5]; + data[i * 4 + 3] = readEvent[6]; + event_to_search[2] += 4; + //logError(0, "%02X %02X %02X %02X ", readEvent[3], + //readEvent[4], readEvent[5], readEvent[6]); + } + + logError(0, "%s %s:Finished! %d\n", tag, __func__, ret); + return OK; +} + + +int calculateCRC8(u8 *u8_srcBuff, int size, u8 *crc) +{ + u8 u8_remainder; + u8 bit; + int i = 0; + + u8_remainder = 0x00; + logError(0, "%s %s: Start CRC computing...\n", tag, __func__); + + if (size == 0 || u8_srcBuff == NULL) { + logError(1, "Arguments passed not valid!"); + logError(1, "%s %s:Data pointer = NULL ", tag, __func__); + logError(1, "or size = 0 (%d) ERROR %08X\n", + size, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + // Perform modulo-2 division, a byte at a time. + //Bring the next byte into the remainder. + for (i = 0; i < size; i++) { + //Perform modulo-2 division, a bit at a time. + u8_remainder ^= u8_srcBuff[i]; + //Try to divide the current data bit. + for (bit = 8; bit > 0; --bit) { + if (u8_remainder & (0x1 << 7)) + u8_remainder = (u8_remainder << 1) ^ 0x9B; + else + u8_remainder = (u8_remainder << 1); + } + } //The final remainder is the CRC result. + *crc = u8_remainder; + logError(0, "%s %s: CRC value = %02X\n", tag, __func__, *crc); + return OK; +} + +int writeLockDownInfo(u8 *data, int size) +{ + int ret, i, toWrite, retry = 0, offset = size; + u8 cmd[2 + LOCKDOWN_CODE_WRITE_CHUNK] = {FTS_CMD_LOCKDOWN_FILL, 0x00}; + u8 crc = 0; + int event_to_search[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_LOCKDOWN_WRITE}; + u8 readEvent[FIFO_EVENT_SIZE]; + char *temp = NULL; + + logError(0, "%s %s: Writing Lockdown code into the IC...\n", + tag, __func__); + + ret = fts_disableInterrupt(); + if (ret < OK) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + goto ERROR; + } + if (size > LOCKDOWN_CODE_MAX_SIZE) { + logError(1, "%s %s: Lockdown data to write too big! ", + tag, __func__); + logError(1, "%d>%d ERROR %08X\n", + size, LOCKDOWN_CODE_MAX_SIZE, ret); + ret = (ERROR_OP_NOT_ALLOW | ERROR_LOCKDOWN_CODE); + goto ERROR; + } + + temp = printHex("Lockdown Code = ", data, size); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + + for (retry = 0; retry < LOCKDOWN_CODE_RETRY; retry++) { + logError(0, "%s %s: Filling FW buffer...\n", tag, __func__); + i = 0; + offset = size; + cmd[0] = FTS_CMD_LOCKDOWN_FILL; + while (offset > 0) { + if (offset > LOCKDOWN_CODE_WRITE_CHUNK) + toWrite = LOCKDOWN_CODE_WRITE_CHUNK; + else + toWrite = offset; + memcpy(&cmd[2], &data[i], toWrite); + cmd[1] = i; + + temp = printHex("Commmand = ", cmd, 2 + toWrite); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + ret = fts_writeFwCmd(cmd, 2 + toWrite); + if (ret < OK) { + logError(1, "Unable to write Lockdown data "); + logError(1, "%s %s:Lockdown data at %d ", + tag, __func__, i); + logError(1, "iteration.%08X\n", ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + i += toWrite;//update the offset + offset -= toWrite; + } + logError(0, "%s %s: Compute 8bit CRC...\n", tag, __func__); + ret = calculateCRC8(data, size, &crc); + if (ret < OK) { + logError(1, "%s %s:Unable to compute CRC..ERROR %08X\n", + tag, __func__, ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + cmd[0] = FTS_CMD_LOCKDOWN_WRITE; + cmd[1] = 0x00; + cmd[2] = (u8)size; + cmd[3] = crc; + logError(0, "%s %s: Write Lockdown data...\n", + tag, __func__); + temp = printHex("Commmand = ", cmd, 4); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + ret = fts_writeFwCmd(cmd, 4); + if (ret < OK) { + logError(1, "%s%s:Unable to send Lockdown data ", + tag, __func__); + logError(1, "write command%08X\n", ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + + ret = pollForEvent(event_to_search, + 2, + &readEvent[0], + GENERAL_TIMEOUT); + //start the polling for reading the reply + + if (ret < OK) { + logError(1, "%s%s:Cann't find lockdown code ", + tag, __func__); + logError(1, "%write reply %08X\n", ret); + continue; + } + + if (readEvent[2] != 0x00) { + logError(1, "%s %s:Event check FAIL!%02X != 0x00 ", + tag, __func__, readEvent[2]); + logError(1, "%ERR%08X\n", ERROR_LOCKDOWN_CODE); + ret = ERROR_LOCKDOWN_CODE; + continue; + } else { + logError(0, "%s %s:Lockdown Code write DONE!\n", + tag, __func__); + ret = OK; + break; + } + } + +ERROR: + //ret = fts_enableInterrupt(); + //ensure that the interrupt are always renabled when exit from funct + if (fts_enableInterrupt() < OK) { + logError(1, "%s %s: Error while re-enabling the interrupt!\n", + tag, __func__); + } + return ret; +} + +int rewriteLockDownInfo(u8 *data, int size) +{ + int ret, i, toWrite, retry = 0, offset = size; + u8 cmd[2 + LOCKDOWN_CODE_WRITE_CHUNK] = {FTS_CMD_LOCKDOWN_FILL, 0x00}; + u8 crc = 0; + int event_to_search[2] = {EVENTID_STATUS_UPDATE, + EVENT_TYPE_LOCKDOWN_WRITE}; + u8 readEvent[FIFO_EVENT_SIZE]; + char *temp = NULL; + + logError(0, "%s %s: ReWriting Lockdown code into the IC start ...\n", + tag, __func__); + + ret = fts_disableInterrupt(); + if (ret < OK) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + goto ERROR; + } + if (size > LOCKDOWN_CODE_MAX_SIZE) { + logError(1, "%s %s: Lockdown data to write too big! ", + tag, __func__); + logError(1, "%d>%d ERROR %08X\n", + size, LOCKDOWN_CODE_MAX_SIZE, ret); + ret = (ERROR_OP_NOT_ALLOW | ERROR_LOCKDOWN_CODE); + goto ERROR; + } + + temp = printHex("Lockdown Code = ", data, size); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + + for (retry = 0; retry < LOCKDOWN_CODE_RETRY; retry++) { + logError(0, "%s %s: Filling FW buffer ...\n", tag, __func__); + i = 0; + offset = size; + cmd[0] = FTS_CMD_LOCKDOWN_FILL; + while (offset > 0) { + if (offset > LOCKDOWN_CODE_WRITE_CHUNK) + toWrite = LOCKDOWN_CODE_WRITE_CHUNK; + else + toWrite = offset; + memcpy(&cmd[2], &data[i], toWrite); + cmd[1] = i; + temp = printHex("Commmand = ", cmd, 2 + toWrite); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + ret = fts_writeFwCmd(cmd, 2 + toWrite); + if (ret < OK) { + logError(1, "Unable to rewrite Lockdown data"); + logError(1, "%s %s: at %d iteration ", + tag, __func__, i); + logError(1, "ERROR %08X\n", ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + i += toWrite;//update the offset + offset -= toWrite; + } + logError(0, "%s %s: Compute 8bit CRC...\n", tag, __func__); + ret = calculateCRC8(data, size, &crc); + if (ret < OK) { + logError(1, "%s %s:Unable to compute CRC.. ", + tag, __func__); + logError(1, "ERROR %08X\n", ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + cmd[0] = FTS_CMD_LOCKDOWN_WRITE; + cmd[1] = 0x01; + cmd[2] = (u8)size; + cmd[3] = crc; + + temp = printHex("Commmand = ", cmd, 4); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + logError(0, "%s %s: ReWrite Lockdown data...\n", tag, __func__); + ret = fts_writeFwCmd(cmd, 4); + if (ret < OK) { + logError(1, "Unable to send Lockdown data"); + logError(1, "%s %s:rewrite command... ERROR %08X\n", + tag, __func__, ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + + //start the polling for reading the reply + ret = pollForEvent(event_to_search, 2, + &readEvent[0], GENERAL_TIMEOUT); + if (ret >= OK) { + if (readEvent[2] < 0x00) { + logError(1, "%s %s:Event check FAIL! ", + tag, __func__); + logError(1, "%02X != 0x00 %08X\n", + readEvent[2], ERROR_LOCKDOWN_CODE); + ret = ERROR_LOCKDOWN_CODE; + continue; + } else { + logError(0, "%s %s: Lockdown Code ", + tag, __func__); + logError(0, "rewrite DONE!\n"); + ret = OK; + break; + } + } else { + logError(1, "Can not find lockdown code write "); + logError(1, "reply event!%s %s: ERROR %08X\n", + tag, __func__, ret); + } + } +ERROR: + //ret = fts_enableInterrupt(); + //ensure that the interrupt are always renabled when exit from funct + if (fts_enableInterrupt() < OK) { + logError(1, "%s %s: Error while re-enabling the interrupt!\n", + tag, __func__); + } + return ret; +} + +int readLockDownInfo(u8 *lockData, int *size) +{ + int ret, retry = 0, toRead = 0, byteToRead; + u8 cmd = FTS_CMD_LOCKDOWN_READ; + int event_to_search[3] = {EVENTID_LOCKDOWN_INFO_READ, -1, 0x00}; + u8 readEvent[FIFO_EVENT_SIZE]; + char *temp = NULL; + + lockData = NULL; + logError(0, "%s %s: Reading Lockdown code from the IC...\n", + tag, __func__); + + ret = fts_disableInterrupt(); + if (ret < OK) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + goto ERROR; + } + for (retry = 0; retry < LOCKDOWN_CODE_RETRY; retry++) { + event_to_search[2] = 0x00; + logError(0, "%s %s: Read Lockdown data.(%d attempt)\n", + tag, __func__, retry + 1); + ret = fts_writeFwCmd(&cmd, 1); + + if (ret < OK) { + logError(1, "%s%s:Unable to send Lockdown data ", + tag, __func__); + logError(1, "write CMD %08X\n", ret); + ret = (ret | ERROR_LOCKDOWN_CODE); + continue; + } + + //start the polling for reading the reply + ret = pollForEvent(event_to_search, 3, + &readEvent[0], GENERAL_TIMEOUT); + + if (ret < OK) { + logError(1, "Cann't find first lockdown code read"); + logError(1, "%s %s:reply event! ERROR %08X\n", + tag, __func__, ret); + continue; + } + + byteToRead = readEvent[1]; + *size = byteToRead; + logError(0, "%s %s:Lockdown Code size = %d\n", + tag, __func__, *size); + lockData = (u8 *)kmalloc_array((byteToRead), + sizeof(u8), GFP_KERNEL); + if (lockData == NULL) { + logError(1, "%s %s:Unable to allocate lockData %08X\n", + tag, __func__, ERROR_ALLOC); + ret = (ERROR_ALLOC | ERROR_LOCKDOWN_CODE); + continue; + } + while (byteToRead > 0) { + if ((readEvent[1] - readEvent[2]) + > LOCKDOWN_CODE_READ_CHUNK) { + toRead = LOCKDOWN_CODE_READ_CHUNK; + } else { + toRead = readEvent[1] - readEvent[2]; + } + byteToRead -= toRead; + memcpy(&lockData[readEvent[2]], + &readEvent[3], toRead); + event_to_search[2] += toRead; + if (byteToRead <= 0) + continue; + + ret = pollForEvent(event_to_search, + 3, + &readEvent[0], + GENERAL_TIMEOUT); + + //start polling for reading reply + if (ret < OK) { + logError(1, "Can not find lockdow"); + logError(1, "code read reply event "); + logError(1, "%s%s:offset%02X%08X\n", + tag, __func__, event_to_search[2], ret); + ret = (ERROR_ALLOC | ERROR_LOCKDOWN_CODE); + break; + } + } + if (byteToRead != 0) { + logError(1, "%s %s:Read Lockdown code FAIL! ", + tag, __func__); + logError(1, "ERROR %08X\n", ret); + continue; + } else { + logError(0, "%s %s: Lockdown Code read DONE!\n", + tag, __func__); + ret = OK; + temp = printHex("Lockdown Code = ", lockData, *size); + if (temp != NULL) { + logError(0, "%s %s: %s", tag, __func__, temp); + kfree(temp); + } + break; + } + } +ERROR: + //ret = fts_enableInterrupt(); + //ensure that the interrupt are always + //renabled when exit from funct + if (fts_enableInterrupt() < OK) { + logError(1, "%s %s:Error while re-enabling the interrupt!\n", + tag, __func__); + } + return ret; +} + +char *printHex(char *label, u8 *buff, int count) +{ + int i, offset; + char *result = NULL; + size_t len = 0; + + offset = strlen(label); + len = (offset + 3 * count) + 2; + result = (char *)kmalloc_array(len, sizeof(char), GFP_KERNEL); + if (result != NULL) { + strlcpy(result, label, len); + for (i = 0; i < count; i++) + snprintf(&result[offset + i * 3], 4, "%02X ", buff[i]); + strlcat(result, "\n", len); + } + return result; +} + +int pollForEvent(int *event_to_search, int event_bytes, + u8 *readData, int time_to_wait) +{ + int i, find, retry, count_err; + int time_to_count; + int err_handling = OK; + struct StopWatch clock; + + u8 cmd[1] = { FIFO_CMD_READONE }; + char *temp = NULL; + + find = 0; + retry = 0; + count_err = 0; + time_to_count = time_to_wait / TIMEOUT_RESOLUTION; + + startStopWatch(&clock); + while (find != 1 && retry < time_to_count + && fts_readCmd(cmd, 1, readData, FIFO_EVENT_SIZE) >= 0) { + + if (readData[0] == EVENTID_ERROR_EVENT) { + temp = printHex("ERROR EVENT = ", + readData, FIFO_EVENT_SIZE); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + count_err++; + err_handling = errorHandler(readData, FIFO_EVENT_SIZE); + if ((err_handling & 0xF0FF0000) + == ERROR_HANDLER_STOP_PROC) { + logError(1, "%s %s: forced to be stopped! ", + tag, __func__); + logError(1, "ERROR %08X\n", err_handling); + return err_handling; + } + } else { + if (readData[0] != EVENTID_NO_EVENT) { + temp = printHex("READ EVENT = ", + readData, FIFO_EVENT_SIZE); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + } + if (readData[0] == EVENTID_CONTROL_READY && + event_to_search[0] != EVENTID_CONTROL_READY) { + logError(0, "Unmanned Controller Ready Event!"); + logError(0, "%s %s:Setting reset flags...\n", + tag, __func__); + setSystemResettedUp(1); + setSystemResettedDown(1); + } + } + find = 1; + + for (i = 0; i < event_bytes; i++) { + if (event_to_search[i] != -1 + && (int)readData[i] != event_to_search[i]) { + find = 0; + break; + } + } + + retry++; + msleep(TIMEOUT_RESOLUTION); + } + stopStopWatch(&clock); + if ((retry >= time_to_count) && find != 1) { + logError(0, "%s %s: ERROR %02X\n", + tag, __func__, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + if (find == 1) { + temp = printHex("FOUND EVENT = ", readData, FIFO_EVENT_SIZE); + if (temp != NULL) + logError(0, "%s %s", tag, temp); + kfree(temp); + logError(0, "%s Event found in %d ms (%d iterations)!\n", + tag, elapsedMillisecond(&clock), retry); + logError(0, "Number of errors found = %d\n", count_err); + return count_err; + } + logError(0, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); + return ERROR_I2C_R; +} + +int flushFIFO(void) +{ + u8 cmd = FIFO_CMD_FLUSH; + + if (fts_writeCmd(&cmd, 1) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + + logError(0, "%s FIFO flushed!\n", tag); + return OK; +} + +int fts_disableInterrupt(void) +{ + //disable interrupt + u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, IER_DISABLE }; + + u16ToU8_be(IER_ADDR, &cmd[1]); + + if (fts_writeCmd(cmd, 4) < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + logError(0, "%s Interrupt Disabled!\n", tag); + return OK; +} + + +int fts_enableInterrupt(void) +{ + u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, IER_ENABLE }; + + u16ToU8_be(IER_ADDR, &cmd[1]); + if (fts_writeCmd(cmd, 4) < 0) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_W); + return ERROR_I2C_W; + } + logError(0, "%s Interrupt Enabled!\n", tag); + return OK; +} + +int u8ToU16n(u8 *src, int src_length, u16 *dst) +{ + int i, j; + u16 *buf; + + if (src_length % 2 != 0) + return -EINVAL; + + j = 0; + buf = (u16 *)kmalloc_array((src_length / 2), sizeof(u16), GFP_KERNEL); + if (!buf) { + dst = NULL; + return -EINVAL; + } + dst = buf; + for (i = 0; i < src_length; i += 2) { + dst[j] = ((src[i+1] & 0x00FF) << 8) + (src[i] & 0x00FF); + j++; + } + + return (src_length / 2); +} + +int u8ToU16(u8 *src, u16 *dst) +{ + *dst = (u16)(((src[1] & 0x00FF) << 8) + (src[0] & 0x00FF)); + return 0; +} + +int u8ToU16_le(u8 *src, u16 *dst) +{ + *dst = (u16)(((src[0] & 0x00FF) << 8) + (src[1] & 0x00FF)); + return 0; +} + +int u16ToU8n(u16 *src, int src_length, u8 *dst) +{ + int i, j; + u8 *buf = (u8 *)kmalloc_array(2 * src_length, sizeof(u8), GFP_KERNEL); + + if (!buf) { + dst = NULL; + return -EINVAL; + } + dst = buf; + j = 0; + for (i = 0; i < src_length; i++) { + dst[j] = (u8) (src[i] & 0xFF00) >> 8; + dst[j+1] = (u8) (src[i] & 0x00FF); + j += 2; + } + + return src_length * 2; +} + +int u16ToU8(u16 src, u8 *dst) +{ + dst[0] = (u8)((src & 0xFF00) >> 8); + dst[1] = (u8)(src & 0x00FF); + return 0; +} + +int u16ToU8_be(u16 src, u8 *dst) +{ + dst[0] = (u8)((src & 0xFF00) >> 8); + dst[1] = (u8)(src & 0x00FF); + return 0; +} + +int u16ToU8_le(u16 src, u8 *dst) +{ + dst[1] = (u8)((src & 0xFF00) >> 8); + dst[0] = (u8)(src & 0x00FF); + return 0; +} + +int u8ToU32(u8 *src, u32 *dst) +{ + *dst = (u32)(((src[3] & 0xFF) << 24) + ((src[2] & 0xFF) << 16) + + ((src[1] & 0xFF) << 8) + (src[0] & 0xFF)); + return 0; +} + +int u32ToU8(u32 src, u8 *dst) +{ + dst[3] = (u8)((src & 0xFF000000) >> 24); + dst[2] = (u8)((src & 0x00FF0000) >> 16); + dst[1] = (u8)((src & 0x0000FF00) >> 8); + dst[0] = (u8)(src & 0x000000FF); + return 0; +} + +int attempt_function(int(*code)(void), unsigned long wait_before_retry, + int retry_count) +{ + int result; + int count = 0; + + do { + result = code(); + count++; + msleep(wait_before_retry); + } while (count < retry_count && result < 0); + + if (count == retry_count) + result |= ERROR_TIMEOUT; + + return result; +} + +void setResetGpio(int gpio) +{ + reset_gpio = gpio; + logError(0, "%s %s: reset_gpio = %d\n", tag, __func__, reset_gpio); +} + +int fts_system_reset(void) +{ + u8 readData[FIFO_EVENT_SIZE]; + int event_to_search; + int res = -1; + int i; + u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, SYSTEM_RESET_VALUE }; + + event_to_search = (int)EVENTID_CONTROL_READY; + + u16ToU8_be(SYSTEM_RESET_ADDRESS, &cmd[1]); + logError(0, "%s System resetting...\n", tag); + for (i = 0; i < SYSTEM_RESET_RETRY && res < 0; i++) { + if (reset_gpio == GPIO_NOT_DEFINED) { +#ifndef FTM3_CHIP + res |= fts_warm_boot(); +#endif + res = fts_writeCmd(cmd, 4); + } else { + gpio_set_value(reset_gpio, 0); + msleep(20); + gpio_set_value(reset_gpio, 1); + res = OK; + } + if (res < OK) { + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_I2C_W); + } else { + res = pollForEvent(&event_to_search, 1, + readData, GENERAL_TIMEOUT); + if (res < OK) { + logError(0, "%s %s: ERROR %02X\n", + tag, __func__, res); + } + } + } + if (res < OK) { + logError(1, "%s %s:failed after 3 attempts: ERROR %02X\n", + tag, __func__, (res | ERROR_SYSTEM_RESET_FAIL)); + res = (res | ERROR_SYSTEM_RESET_FAIL); + } else { + logError(0, "%s System reset DONE!\n", tag); + system_resetted_down = 1; + system_resetted_up = 1; + res = OK; + } + return res; +} + +int isSystemResettedDown(void) +{ + return system_resetted_down; +} + +int isSystemResettedUp(void) +{ + return system_resetted_up; +} + +void setSystemResettedDown(int val) +{ + system_resetted_down = val; +} + +void setSystemResettedUp(int val) +{ + system_resetted_up = val; +} + +int senseOn(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_MS_MT_SENSE_ON }; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_SENSE_ON_FAIL); + return (ret|ERROR_SENSE_ON_FAIL); + } + logError(0, "%s %s: SENSE ON\n", tag, __func__); + return OK; +} + +int senseOff(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_MS_MT_SENSE_OFF }; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_SENSE_OFF_FAIL); + return (ret | ERROR_SENSE_OFF_FAIL); + } + logError(0, "%s %s: SENSE OFF\n", tag, __func__); + return OK; +} + +int keyOn(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_MS_KEY_ON }; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_SENSE_ON_FAIL); + return (ret | ERROR_SENSE_ON_FAIL); + } + + logError(0, "%s %s: KEY ON\n", tag, __func__); + return OK; +} + +int keyOff(void) +{ + int ret; + u8 cmd[1] = { FTS_CMD_MS_KEY_OFF }; + + ret = fts_writeFwCmd(cmd, 1); + if (ret < OK) { + logError(1, "%s %s:ERROR %02X\n", + tag, __func__, ERROR_SENSE_OFF_FAIL); + return (ret | ERROR_SENSE_OFF_FAIL); + } + + logError(0, "%s %s: KEY OFF\n", tag, __func__); + return OK; +} + +int cleanUp(int enableTouch) +{ + int res; + + logError(0, "%s %s: system reset...\n", tag, __func__); + res = fts_system_reset(); + if (res < OK) + return res; + + if (enableTouch) { + logError(0, "%s %s:enabling touches...\n", tag, __func__); + res = senseOn(); + if (res < OK) + return res; +#ifdef PHONE_KEY + res = keyOn(); + if (res < OK) + return res; +#endif + logError(0, "%s %s:enabling interrupts...\n", tag, __func__); + res = fts_enableInterrupt(); + if (res < OK) + return res; + } + return OK; +} + +int checkEcho(u8 *cmd, int size) +{ + int ret, i; + int event_to_search[FIFO_EVENT_SIZE + 1]; + u8 readData[FIFO_EVENT_SIZE]; + + if ((ftsInfo.u32_echoEn & 0x00000001) != ECHO_ENABLED) { + logError(0, "%s ECHO Not Enabled!\n", tag); + return OK; + } + if (size < 1) { + logError(1, "%s:Error Size = %d not valid!", tag, size); + logError(1, " or ECHO not Enabled!%08X\n", ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + if ((size + 2) > FIFO_EVENT_SIZE) + size = FIFO_EVENT_SIZE - 2; + //Echo event EC xx xx xx xx xx xx + //fifo_status therefore for command + //with more than 6 bytes will echo only the first 6 + event_to_search[0] = EVENTID_ECHO; + for (i = 1; i <= size; i++) + event_to_search[i] = cmd[i - 1]; + ret = pollForEvent(event_to_search, size + 1, + readData, GENERAL_TIMEOUT); + if (ret < OK) { + logError(1, "%s %s:Echo Event not found! ERROR %02X\n", + tag, __func__, ret); + return (ret | ERROR_CHECK_ECHO_FAIL); + } + + logError(0, "%s ECHO OK!\n", tag); + ret = OK; + return ret; +} + +int featureEnableDisable(int on_off, u32 feature) +{ + int ret; + u8 cmd[5]; + + if (on_off == FEAT_ENABLE) { + cmd[0] = FTS_CMD_FEATURE_ENABLE; + logError(0, "%s %s: Enabling feature %08X ...\n", + tag, __func__, feature); + } else { + cmd[0] = FTS_CMD_FEATURE_DISABLE; + logError(0, "%s %s: Disabling feature %08X ...\n", + tag, __func__, feature); + } + u32ToU8(feature, &cmd[1]); + + //not use writeFwCmd because this function can be + //called also during interrupt enable and should be fast + ret = fts_writeCmd(cmd, 5); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); + return (ret | ERROR_FEATURE_ENABLE_DISABLE); + } + + logError(0, "%s %s: DONE!\n", tag, __func__); + return OK; +} + +int writeNoiseParameters(u8 *noise) +{ + int ret, i; + u8 cmd[2+NOISE_PARAMETERS_SIZE]; + u8 readData[FIFO_EVENT_SIZE]; + int event_to_search[2] = {EVENTID_NOISE_WRITE, NOISE_PARAMETERS}; + + logError(0, "%s %s: Writing noise parameters to the IC ...\n", + tag, __func__); + ret = fts_disableInterrupt(); + if (ret < OK) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + cmd[0] = FTS_CMD_NOISE_WRITE; + cmd[1] = NOISE_PARAMETERS; + logError(0, "%s %s: Noise parameters = ", tag, __func__); + for (i = 0; i < NOISE_PARAMETERS_SIZE; i++) { + cmd[2 + i] = noise[i]; + logError(0, "%02X", cmd[2 + i]); + } + + logError(0, "\n"); + ret = fts_writeCmd(cmd, NOISE_PARAMETERS_SIZE + 2); + //not use writeFwCmd because this function should be fast + if (ret < OK) { + logError(0, "%s %s:impossible write command... ERROR %02X\n", + tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + + ret = pollForEvent(event_to_search, 2, readData, GENERAL_TIMEOUT); + if (ret < OK) { + logError(0, "%s %s: polling FIFO ERROR %02X\n", + tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + + if (readData[2] != 0x00) { + logError(1, "%s %s:Event check FAIL! %02X != 0x00 ERROR%02X\n", + tag, __func__, readData[2], ERROR_NOISE_PARAMETERS); + ret = ERROR_NOISE_PARAMETERS; + goto ERROR; + } + + logError(0, "%s %s:DONE!\n", tag, __func__); + ret = OK; +ERROR: + ret = fts_enableInterrupt(); + //ensure that the interrupt are always renabled when exit from funct + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); + return (ret | ERROR_NOISE_PARAMETERS); + } + return ret; +} + +int readNoiseParameters(u8 *noise) +{ + int ret, i; + u8 cmd[2]; + u8 readData[FIFO_EVENT_SIZE]; + int event_to_search[2] = {EVENTID_NOISE_READ, NOISE_PARAMETERS}; + + logError(0, "%s %s:Reading noise parameters from the IC ...\n", + tag, __func__); + ret = fts_disableInterrupt(); + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + cmd[0] = FTS_CMD_NOISE_READ; + cmd[1] = NOISE_PARAMETERS; + ret = fts_writeCmd(cmd, 2);//not use writeFwCmd should be fast + if (ret < OK) { + logError(0, "%s %s:impossible write command... ERROR %02X\n", + tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + + ret = pollForEvent(event_to_search, 2, readData, GENERAL_TIMEOUT); + if (ret < OK) { + logError(0, "%s %s: polling FIFO ERROR %02X\n", + tag, __func__, ret); + ret = (ret | ERROR_NOISE_PARAMETERS); + goto ERROR; + } + + logError(0, "%s %s: Noise parameters = ", tag, __func__); + for (i = 0; i < NOISE_PARAMETERS_SIZE; i++) { + noise[i] = readData[2 + i]; + logError(0, "%02X ", noise[i]); + } + + logError(0, "\n"); + logError(0, "%s %s: DONE!\n", tag, __func__); + ret = OK; +ERROR: + ret = fts_enableInterrupt(); + //ensure that the interrupt are always renabled when exit from funct + if (ret < OK) { + logError(1, "%s %s: ERROR %02X\n", tag, __func__, ret); + return (ret | ERROR_NOISE_PARAMETERS); + } + return ret; +} + +short **array1dTo2d_short(short *data, int size, int columns) +{ + int i; + int count = size / columns; + short **matrix = (short **)kmalloc_array(count, + sizeof(short *), GFP_KERNEL); + if (matrix != NULL) { + for (i = 0; i < count; i++) { + matrix[i] = (short *)kmalloc_array(columns, + sizeof(short), GFP_KERNEL); + } + + for (i = 0; i < size; i++) + matrix[i / columns][i % columns] = data[i]; + } + + return matrix; +} + +u8 **array1dTo2d_u8(u8 *data, int size, int columns) +{ + int i; + int count = size / columns; + u8 **matrix = (u8 **)kmalloc_array(count, + sizeof(u8 *), GFP_KERNEL); + if (matrix != NULL) { + for (i = 0; i < count; i++) { + matrix[i] = (u8 *)kmalloc_array(columns, + sizeof(u8), GFP_KERNEL); + } + + for (i = 0; i < size; i++) + matrix[i / columns][i % columns] = data[i]; + } + + return matrix; +} + +void print_frame_short(char *label, short **matrix, int row, int column) +{ + int i, j; + + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) + logError(0, "%d", matrix[i][j]); + logError(0, "\n"); + kfree(matrix[i]); + } + kfree(matrix); +} + +void print_frame_u8(char *label, u8 **matrix, int row, int column) +{ + int i, j; + + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) + logError(0, "%d ", matrix[i][j]); + logError(0, "\n"); + kfree(matrix[i]); + } + kfree(matrix); +} + +void print_frame_u32(char *label, u32 **matrix, int row, int column) +{ + int i, j; + + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) + logError(0, "%d ", matrix[i][j]); + logError(0, "\n"); + kfree(matrix[i]); + } + kfree(matrix); +} + +void print_frame_int(char *label, int **matrix, int row, int column) +{ + int i, j; + + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) + logError(0, "%d ", matrix[i][j]); + logError(0, "\n"); + kfree(matrix[i]); + } + kfree(matrix); +} diff --git a/st/fts_lib/ftsTool.h b/st/fts_lib/ftsTool.h new file mode 100644 index 0000000000..350cbd57e1 --- /dev/null +++ b/st/fts_lib/ftsTool.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/** + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Utility Functions * + * * + ************************************************************************** + ************************************************************************** + * + */ + +#ifndef __FTS_TOOL_H +#define __FTS_TOOL_H + +#define GPIO_NOT_DEFINED -1 +#define TIMEOUT_RESOLUTION 10 //ms +#define GENERAL_TIMEOUT (50*TIMEOUT_RESOLUTION) //ms +#define RELEASE_INFO_TIMEOUT (15*TIMEOUT_RESOLUTION) //ms + + +#define FEAT_ENABLE 1 +#define FEAT_DISABLE 0 + +#define SYSTEM_RESET_RETRY 3 + +#define B2_RETRY 2 +//for FTM4 can not be greater than 13 bytes +#define LOCKDOWN_CODE_SIZE 10 + +#define LOCKDOWN_CODE_MAX_SIZE 63 +#define LOCKDOWN_CODE_WRITE_CHUNK 12 +#define LOCKDOWN_CODE_READ_CHUNK 4 +#define LOCKDOWN_CODE_RETRY 2 + +int readB2(u16 address, u8 *outBuf, int len); +int readB2U16(u16 address, u8 *outBuf, int byteToRead); +int releaseInformation(void); +int lockDownInfo(u8 *data, int len); +int calculateCRC8(u8 *u8_srcBuff, int size, u8 *crc); +int writeLockDownInfo(u8 *data, int size); +int rewriteLockDownInfo(u8 *data, int size); +int readLockDownInfo(u8 *lockData, int *size); +char *printHex(char *label, u8 *buff, int count); +int pollForEvent(int *event_to_search, int event_bytes, + u8 *readData, int time_to_wait); +int fts_disableInterrupt(void); +int fts_enableInterrupt(void); +int u8ToU16(u8 *src, u16 *dst); +int u8ToU16_le(u8 *src, u16 *dst); +int u8ToU16n(u8 *src, int src_length, u16 *dst); +int u16ToU8(u16 src, u8 *dst); +int u16ToU8_le(u16 src, u8 *dst); +int u16ToU8_be(u16 src, u8 *dst); +int u16ToU8n(u16 *src, int src_length, u8 *dst); +int u8ToU32(u8 *src, u32 *dst); +int u32ToU8(u32 src, u8 *dst); +int attempt_function(int(*code)(void), unsigned long wait_before_retry, + int retry_count); +void setResetGpio(int gpio); +int fts_system_reset(void); +int isSystemResettedUp(void); +int isSystemResettedDown(void); +void setSystemResettedUp(int val); +void setSystemResettedDown(int val); +int senseOn(void); +int senseOff(void); +int keyOn(void); +int keyOff(void); +int featureEnableDisable(int on_off, u32 feature); +int writeNoiseParameters(u8 *noise); +int readNoiseParameters(u8 *noise); +int checkEcho(u8 *cmd, int size); +void print_frame_short(char *label, short **matrix, int row, int column); +short **array1dTo2d_short(short *data, int size, int columns); +u8 **array1dTo2d_u8(u8 *data, int size, int columns); +void print_frame_u8(char *label, u8 **matrix, int row, int column); +void print_frame_u32(char *label, u32 **matrix, int row, int column); +void print_frame_int(char *label, int **matrix, int row, int column); +int cleanUp(int enableTouch); +int flushFIFO(void); + +#endif diff --git a/synaptics_dsx/synaptics_dsx_active_pen.c b/synaptics_dsx/synaptics_dsx_active_pen.c new file mode 100644 index 0000000000..6cd855043b --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_active_pen.c @@ -0,0 +1,606 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define APEN_PHYS_NAME "synaptics_dsx/active_pen" + +#define ACTIVE_PEN_MAX_PRESSURE_16BIT 65535 +#define ACTIVE_PEN_MAX_PRESSURE_8BIT 255 + +struct synaptics_rmi4_f12_query_8 { + union { + struct { + unsigned char size_of_query9; + struct { + unsigned char data0_is_present:1; + unsigned char data1_is_present:1; + unsigned char data2_is_present:1; + unsigned char data3_is_present:1; + unsigned char data4_is_present:1; + unsigned char data5_is_present:1; + unsigned char data6_is_present:1; + unsigned char data7_is_present:1; + } __packed; + }; + unsigned char data[2]; + }; +}; + +struct apen_data_8b_pressure { + union { + struct { + unsigned char status_pen:1; + unsigned char status_invert:1; + unsigned char status_barrel:1; + unsigned char status_reserved:5; + unsigned char x_lsb; + unsigned char x_msb; + unsigned char y_lsb; + unsigned char y_msb; + unsigned char pressure_msb; + unsigned char battery_state; + unsigned char pen_id_0_7; + unsigned char pen_id_8_15; + unsigned char pen_id_16_23; + unsigned char pen_id_24_31; + } __packed; + unsigned char data[11]; + }; +}; + +struct apen_data { + union { + struct { + unsigned char status_pen:1; + unsigned char status_invert:1; + unsigned char status_barrel:1; + unsigned char status_reserved:5; + unsigned char x_lsb; + unsigned char x_msb; + unsigned char y_lsb; + unsigned char y_msb; + unsigned char pressure_lsb; + unsigned char pressure_msb; + unsigned char battery_state; + unsigned char pen_id_0_7; + unsigned char pen_id_8_15; + unsigned char pen_id_16_23; + unsigned char pen_id_24_31; + } __packed; + unsigned char data[12]; + }; +}; + +struct synaptics_rmi4_apen_handle { + bool apen_present; + unsigned char intr_mask; + unsigned char battery_state; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; + unsigned short apen_data_addr; + unsigned short max_pressure; + unsigned int pen_id; + struct input_dev *apen_dev; + struct apen_data *apen_data; + struct synaptics_rmi4_data *rmi4_data; +}; + +static struct synaptics_rmi4_apen_handle *apen; + +DECLARE_COMPLETION(apen_remove_complete); + +static void apen_lift(void) +{ + input_report_key(apen->apen_dev, BTN_TOUCH, 0); + input_report_key(apen->apen_dev, BTN_TOOL_PEN, 0); + input_report_key(apen->apen_dev, BTN_TOOL_RUBBER, 0); + input_sync(apen->apen_dev); + apen->apen_present = false; +} + +static void apen_report(void) +{ + int retval; + int x; + int y; + int pressure; + static int invert = -1; + struct apen_data_8b_pressure *apen_data_8b; + struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + apen->apen_data_addr, + apen->apen_data->data, + sizeof(apen->apen_data->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read active pen data\n", + __func__); + return; + } + + if (apen->apen_data->status_pen == 0) { + if (apen->apen_present) + apen_lift(); + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: No active pen data\n", + __func__); + + return; + } + + x = (apen->apen_data->x_msb << 8) | (apen->apen_data->x_lsb); + y = (apen->apen_data->y_msb << 8) | (apen->apen_data->y_lsb); + + if ((x == -1) && (y == -1)) { + if (apen->apen_present) + apen_lift(); + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Active pen in range but no valid x & y\n", + __func__); + + return; + } + + if (!apen->apen_present) + invert = -1; + + if (invert != -1 && invert != apen->apen_data->status_invert) + apen_lift(); + + invert = apen->apen_data->status_invert; + + if (apen->max_pressure == ACTIVE_PEN_MAX_PRESSURE_16BIT) { + pressure = (apen->apen_data->pressure_msb << 8) | + apen->apen_data->pressure_lsb; + apen->battery_state = apen->apen_data->battery_state; + apen->pen_id = (apen->apen_data->pen_id_24_31 << 24) | + (apen->apen_data->pen_id_16_23 << 16) | + (apen->apen_data->pen_id_8_15 << 8) | + apen->apen_data->pen_id_0_7; + } else { + apen_data_8b = (struct apen_data_8b_pressure *)apen->apen_data; + pressure = apen_data_8b->pressure_msb; + apen->battery_state = apen_data_8b->battery_state; + apen->pen_id = (apen_data_8b->pen_id_24_31 << 24) | + (apen_data_8b->pen_id_16_23 << 16) | + (apen_data_8b->pen_id_8_15 << 8) | + apen_data_8b->pen_id_0_7; + } + + input_report_key(apen->apen_dev, BTN_TOUCH, pressure > 0 ? 1 : 0); + input_report_key(apen->apen_dev, + apen->apen_data->status_invert > 0 ? + BTN_TOOL_RUBBER : BTN_TOOL_PEN, 1); + input_report_key(apen->apen_dev, + BTN_STYLUS, apen->apen_data->status_barrel > 0 ? + 1 : 0); + input_report_abs(apen->apen_dev, ABS_X, x); + input_report_abs(apen->apen_dev, ABS_Y, y); + input_report_abs(apen->apen_dev, ABS_PRESSURE, pressure); + + input_sync(apen->apen_dev); + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Active pen: status = %d, invert = %d, barrel = %d, x = %d, y = %d, pressure = %d\n", + __func__, + apen->apen_data->status_pen, + apen->apen_data->status_invert, + apen->apen_data->status_barrel, + x, y, pressure); + + apen->apen_present = true; +} + +static void apen_set_params(void) +{ + input_set_abs_params(apen->apen_dev, ABS_X, 0, + apen->rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(apen->apen_dev, ABS_Y, 0, + apen->rmi4_data->sensor_max_y, 0, 0); + input_set_abs_params(apen->apen_dev, ABS_PRESSURE, 0, + apen->max_pressure, 0, 0); +} + +static int apen_pressure(struct synaptics_rmi4_f12_query_8 *query_8) +{ + int retval; + unsigned char ii; + unsigned char data_reg_presence; + unsigned char size_of_query_9; + unsigned char *query_9; + unsigned char *data_desc; + struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data; + + data_reg_presence = query_8->data[1]; + + size_of_query_9 = query_8->size_of_query9; + query_9 = kmalloc(size_of_query_9, GFP_KERNEL); + + retval = synaptics_rmi4_reg_read(rmi4_data, + apen->query_base_addr + 9, + query_9, + size_of_query_9); + if (retval < 0) + goto exit; + + data_desc = query_9; + + for (ii = 0; ii < 6; ii++) { + if (!(data_reg_presence & (1 << ii))) + continue; /* The data register is not present */ + data_desc++; /* Jump over the size entry */ + while (*data_desc & (1 << 7)) + data_desc++; + data_desc++; /* Go to the next descriptor */ + } + + data_desc++; /* Jump over the size entry */ + /* Check for the presence of subpackets 1 and 2 */ + if ((*data_desc & (3 << 1)) == (3 << 1)) + apen->max_pressure = ACTIVE_PEN_MAX_PRESSURE_16BIT; + else + apen->max_pressure = ACTIVE_PEN_MAX_PRESSURE_8BIT; + +exit: + kfree(query_9); + + return retval; +} + +static int apen_reg_init(void) +{ + int retval; + unsigned char data_offset; + unsigned char size_of_query8; + struct synaptics_rmi4_f12_query_8 query_8; + struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + apen->query_base_addr + 7, + &size_of_query8, + sizeof(size_of_query8)); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_read(rmi4_data, + apen->query_base_addr + 8, + query_8.data, + sizeof(query_8.data)); + if (retval < 0) + return retval; + + if ((size_of_query8 >= 2) && (query_8.data6_is_present)) { + data_offset = query_8.data0_is_present + + query_8.data1_is_present + + query_8.data2_is_present + + query_8.data3_is_present + + query_8.data4_is_present + + query_8.data5_is_present; + apen->apen_data_addr = apen->data_base_addr + data_offset; + retval = apen_pressure(&query_8); + if (retval < 0) + return retval; + } else { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Active pen support unavailable\n", + __func__); + retval = -ENODEV; + } + + return retval; +} + +static int apen_scan_pdt(void) +{ + int retval; + unsigned char ii; + unsigned char page; + unsigned char intr_count = 0; + unsigned char intr_off; + unsigned char intr_src; + unsigned short addr; + struct synaptics_rmi4_fn_desc fd; + struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data; + + for (page = 0; page < PAGES_TO_SERVICE; page++) { + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + addr |= (page << 8); + + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + (unsigned char *)&fd, + sizeof(fd)); + if (retval < 0) + return retval; + + addr &= ~(MASK_8BIT << 8); + + if (fd.fn_number) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Found F%02x\n", + __func__, fd.fn_number); + switch (fd.fn_number) { + case SYNAPTICS_RMI4_F12: + goto f12_found; + } + } else { + break; + } + + intr_count += fd.intr_src_count; + } + } + + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F12\n", + __func__); + return -EINVAL; + +f12_found: + apen->query_base_addr = fd.query_base_addr | (page << 8); + apen->control_base_addr = fd.ctrl_base_addr | (page << 8); + apen->data_base_addr = fd.data_base_addr | (page << 8); + apen->command_base_addr = fd.cmd_base_addr | (page << 8); + + retval = apen_reg_init(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to initialize active pen registers\n", + __func__); + return retval; + } + + apen->intr_mask = 0; + intr_src = fd.intr_src_count; + intr_off = intr_count % 8; + for (ii = intr_off; + ii < (intr_src + intr_off); + ii++) { + apen->intr_mask |= 1 << ii; + } + + rmi4_data->intr_mask[0] |= apen->intr_mask; + + addr = rmi4_data->f01_ctrl_base_addr + 1; + + retval = synaptics_rmi4_reg_write(rmi4_data, + addr, + &(rmi4_data->intr_mask[0]), + sizeof(rmi4_data->intr_mask[0])); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set interrupt enable bit\n", + __func__); + return retval; + } + + return 0; +} + +static void synaptics_rmi4_apen_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + if (!apen) + return; + + if (apen->intr_mask & intr_mask) + apen_report(); + + return; +} + +static int synaptics_rmi4_apen_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + + if (apen) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Handle already exists\n", + __func__); + return 0; + } + + apen = kzalloc(sizeof(*apen), GFP_KERNEL); + if (!apen) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for apen\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + apen->apen_data = kzalloc(sizeof(*(apen->apen_data)), GFP_KERNEL); + if (!apen->apen_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for apen_data\n", + __func__); + retval = -ENOMEM; + goto exit_free_apen; + } + + apen->rmi4_data = rmi4_data; + + retval = apen_scan_pdt(); + if (retval < 0) + goto exit_free_apen_data; + + apen->apen_dev = input_allocate_device(); + if (apen->apen_dev == NULL) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate active pen device\n", + __func__); + retval = -ENOMEM; + goto exit_free_apen_data; + } + + apen->apen_dev->name = ACTIVE_PEN_DRIVER_NAME; + apen->apen_dev->phys = APEN_PHYS_NAME; + apen->apen_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT; + apen->apen_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION; + apen->apen_dev->dev.parent = rmi4_data->pdev->dev.parent; + input_set_drvdata(apen->apen_dev, rmi4_data); + + set_bit(EV_KEY, apen->apen_dev->evbit); + set_bit(EV_ABS, apen->apen_dev->evbit); + set_bit(BTN_TOUCH, apen->apen_dev->keybit); + set_bit(BTN_TOOL_PEN, apen->apen_dev->keybit); + set_bit(BTN_TOOL_RUBBER, apen->apen_dev->keybit); + set_bit(BTN_STYLUS, apen->apen_dev->keybit); +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, apen->apen_dev->propbit); +#endif + + apen_set_params(); + + retval = input_register_device(apen->apen_dev); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to register active pen device\n", + __func__); + goto exit_free_input_device; + } + + return 0; + +exit_free_input_device: + input_free_device(apen->apen_dev); + +exit_free_apen_data: + kfree(apen->apen_data); + +exit_free_apen: + kfree(apen); + apen = NULL; + +exit: + return retval; +} + +static void synaptics_rmi4_apen_remove(struct synaptics_rmi4_data *rmi4_data) +{ + if (!apen) + goto exit; + + input_unregister_device(apen->apen_dev); + kfree(apen->apen_data); + kfree(apen); + apen = NULL; + +exit: + complete(&apen_remove_complete); +} + +static void synaptics_rmi4_apen_reset(struct synaptics_rmi4_data *rmi4_data) +{ + if (!apen) { + synaptics_rmi4_apen_init(rmi4_data); + return; + } + + apen_lift(); + + apen_scan_pdt(); +} + +static void synaptics_rmi4_apen_reinit(struct synaptics_rmi4_data *rmi4_data) +{ + if (!apen) + return; + + apen_lift(); +} + +static void synaptics_rmi4_apen_e_suspend(struct synaptics_rmi4_data *rmi4_data) +{ + if (!apen) + return; + + apen_lift(); +} + +static void synaptics_rmi4_apen_suspend(struct synaptics_rmi4_data *rmi4_data) +{ + if (!apen) + return; + + apen_lift(); +} + +static struct synaptics_rmi4_exp_fn active_pen_module = { + .fn_type = RMI_ACTIVE_PEN, + .init = synaptics_rmi4_apen_init, + .remove = synaptics_rmi4_apen_remove, + .reset = synaptics_rmi4_apen_reset, + .reinit = synaptics_rmi4_apen_reinit, + .early_suspend = synaptics_rmi4_apen_e_suspend, + .suspend = synaptics_rmi4_apen_suspend, + .resume = NULL, + .late_resume = NULL, + .attn = synaptics_rmi4_apen_attn, +}; + +static int __init rmi4_active_pen_module_init(void) +{ + synaptics_rmi4_new_function(&active_pen_module, true); + + return 0; +} + +static void __exit rmi4_active_pen_module_exit(void) +{ + synaptics_rmi4_new_function(&active_pen_module, false); + + wait_for_completion(&apen_remove_complete); +} + +module_init(rmi4_active_pen_module_init); +module_exit(rmi4_active_pen_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX Active Pen Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_core.c b/synaptics_dsx/synaptics_dsx_core.c new file mode 100644 index 0000000000..065cdbb7da --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_core.c @@ -0,0 +1,5079 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" +#ifdef KERNEL_ABOVE_2_6_38 +#include +#endif + +#include + +#define INPUT_PHYS_NAME "synaptics_dsx/touch_input" +#define STYLUS_PHYS_NAME "synaptics_dsx/stylus" + +#define VIRTUAL_KEY_MAP_FILE_NAME "virtualkeys." PLATFORM_DRIVER_NAME + +#ifdef KERNEL_ABOVE_2_6_38 +#define TYPE_B_PROTOCOL +#endif + +/* +#define USE_DATA_SERVER +*/ + +#define WAKEUP_GESTURE false + +#define NO_0D_WHILE_2D +#define REPORT_2D_Z +#define REPORT_2D_W +/* +#define REPORT_2D_PRESSURE +*/ + +#define F12_DATA_15_WORKAROUND + +#define IGNORE_FN_INIT_FAILURE +#define FB_READY_RESET +#define FB_READY_WAIT_MS 100 +#define FB_READY_TIMEOUT_S 30 +#ifdef SYNA_TDDI +#define TDDI_LPWG_WAIT_US 10 +#endif +#define RPT_TYPE (1 << 0) +#define RPT_X_LSB (1 << 1) +#define RPT_X_MSB (1 << 2) +#define RPT_Y_LSB (1 << 3) +#define RPT_Y_MSB (1 << 4) +#define RPT_Z (1 << 5) +#define RPT_WX (1 << 6) +#define RPT_WY (1 << 7) +#define RPT_DEFAULT (RPT_TYPE | RPT_X_LSB | RPT_X_MSB | RPT_Y_LSB | RPT_Y_MSB) + +#define REBUILD_WORK_DELAY_MS 500 /* ms */ + +#define EXP_FN_WORK_DELAY_MS 500 /* ms */ +#define MAX_F11_TOUCH_WIDTH 15 +#define MAX_F12_TOUCH_WIDTH 255 + +#define CHECK_STATUS_TIMEOUT_MS 100 + +#define F01_STD_QUERY_LEN 21 +#define F01_BUID_ID_OFFSET 18 + +#define STATUS_NO_ERROR 0x00 +#define STATUS_RESET_OCCURRED 0x01 +#define STATUS_INVALID_CONFIG 0x02 +#define STATUS_DEVICE_FAILURE 0x03 +#define STATUS_CONFIG_CRC_FAILURE 0x04 +#define STATUS_FIRMWARE_CRC_FAILURE 0x05 +#define STATUS_CRC_IN_PROGRESS 0x06 + +#define NORMAL_OPERATION (0 << 0) +#define SENSOR_SLEEP (1 << 0) +#define NO_SLEEP_OFF (0 << 2) +#define NO_SLEEP_ON (1 << 2) +#define CONFIGURED (1 << 7) + +#define F11_CONTINUOUS_MODE 0x00 +#define F11_WAKEUP_GESTURE_MODE 0x04 +#define F12_CONTINUOUS_MODE 0x00 +#define F12_WAKEUP_GESTURE_MODE 0x02 +#define F12_UDG_DETECT 0x0f + +static int synaptics_rmi4_check_status(struct synaptics_rmi4_data *rmi4_data, + bool *was_in_bl_mode); +static int synaptics_rmi4_free_fingers(struct synaptics_rmi4_data *rmi4_data); +static int synaptics_rmi4_reinit_device(struct synaptics_rmi4_data *rmi4_data); +static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data, + bool rebuild); + +#ifdef CONFIG_DRM +static void synaptics_rmi4_dsi_panel_notifier_cb( + enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, + void *client_data); +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND +#ifndef CONFIG_FB +#define USE_EARLYSUSPEND +#endif +#endif + +#ifdef USE_EARLYSUSPEND +static int synaptics_rmi4_early_suspend(struct early_suspend *h); + +static int synaptics_rmi4_late_resume(struct early_suspend *h); +#endif + +static int synaptics_rmi4_suspend(struct device *dev); + +static int synaptics_rmi4_resume(struct device *dev); + +static void synaptics_rmi4_defer_probe(struct work_struct *work); + +static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_suspend_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_wake_gesture_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_wake_gesture_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +#ifdef USE_DATA_SERVER +static ssize_t synaptics_rmi4_synad_pid_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +#endif + +static ssize_t synaptics_rmi4_virtual_key_map_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf); + +struct synaptics_rmi4_f01_device_status { + union { + struct { + unsigned char status_code:4; + unsigned char reserved:2; + unsigned char flash_prog:1; + unsigned char unconfigured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f11_query_0_5 { + union { + struct { + /* query 0 */ + unsigned char f11_query0_b0__2:3; + unsigned char has_query_9:1; + unsigned char has_query_11:1; + unsigned char has_query_12:1; + unsigned char has_query_27:1; + unsigned char has_query_28:1; + + /* query 1 */ + unsigned char num_of_fingers:3; + unsigned char has_rel:1; + unsigned char has_abs:1; + unsigned char has_gestures:1; + unsigned char has_sensitibity_adjust:1; + unsigned char f11_query1_b7:1; + + /* query 2 */ + unsigned char num_of_x_electrodes; + + /* query 3 */ + unsigned char num_of_y_electrodes; + + /* query 4 */ + unsigned char max_electrodes:7; + unsigned char f11_query4_b7:1; + + /* query 5 */ + unsigned char abs_data_size:2; + unsigned char has_anchored_finger:1; + unsigned char has_adj_hyst:1; + unsigned char has_dribble:1; + unsigned char has_bending_correction:1; + unsigned char has_large_object_suppression:1; + unsigned char has_jitter_filter:1; + } __packed; + unsigned char data[6]; + }; +}; + +struct synaptics_rmi4_f11_query_7_8 { + union { + struct { + /* query 7 */ + unsigned char has_single_tap:1; + unsigned char has_tap_and_hold:1; + unsigned char has_double_tap:1; + unsigned char has_early_tap:1; + unsigned char has_flick:1; + unsigned char has_press:1; + unsigned char has_pinch:1; + unsigned char has_chiral_scroll:1; + + /* query 8 */ + unsigned char has_palm_detect:1; + unsigned char has_rotate:1; + unsigned char has_touch_shapes:1; + unsigned char has_scroll_zones:1; + unsigned char individual_scroll_zones:1; + unsigned char has_multi_finger_scroll:1; + unsigned char has_multi_finger_scroll_edge_motion:1; + unsigned char has_multi_finger_scroll_inertia:1; + } __packed; + unsigned char data[2]; + }; +}; + +struct synaptics_rmi4_f11_query_9 { + union { + struct { + unsigned char has_pen:1; + unsigned char has_proximity:1; + unsigned char has_large_object_sensitivity:1; + unsigned char has_suppress_on_large_object_detect:1; + unsigned char has_two_pen_thresholds:1; + unsigned char has_contact_geometry:1; + unsigned char has_pen_hover_discrimination:1; + unsigned char has_pen_hover_and_edge_filters:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f11_query_12 { + union { + struct { + unsigned char has_small_object_detection:1; + unsigned char has_small_object_detection_tuning:1; + unsigned char has_8bit_w:1; + unsigned char has_2d_adjustable_mapping:1; + unsigned char has_general_information_2:1; + unsigned char has_physical_properties:1; + unsigned char has_finger_limit:1; + unsigned char has_linear_cofficient_2:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f11_query_27 { + union { + struct { + unsigned char f11_query27_b0:1; + unsigned char has_pen_position_correction:1; + unsigned char has_pen_jitter_filter_coefficient:1; + unsigned char has_group_decomposition:1; + unsigned char has_wakeup_gesture:1; + unsigned char has_small_finger_correction:1; + unsigned char has_data_37:1; + unsigned char f11_query27_b7:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f11_ctrl_6_9 { + union { + struct { + unsigned char sensor_max_x_pos_7_0; + unsigned char sensor_max_x_pos_11_8:4; + unsigned char f11_ctrl7_b4__7:4; + unsigned char sensor_max_y_pos_7_0; + unsigned char sensor_max_y_pos_11_8:4; + unsigned char f11_ctrl9_b4__7:4; + } __packed; + unsigned char data[4]; + }; +}; + +struct synaptics_rmi4_f11_data_1_5 { + union { + struct { + unsigned char x_position_11_4; + unsigned char y_position_11_4; + unsigned char x_position_3_0:4; + unsigned char y_position_3_0:4; + unsigned char wx:4; + unsigned char wy:4; + unsigned char z; + } __packed; + unsigned char data[5]; + }; +}; + +struct synaptics_rmi4_f12_query_5 { + union { + struct { + unsigned char size_of_query6; + struct { + unsigned char ctrl0_is_present:1; + unsigned char ctrl1_is_present:1; + unsigned char ctrl2_is_present:1; + unsigned char ctrl3_is_present:1; + unsigned char ctrl4_is_present:1; + unsigned char ctrl5_is_present:1; + unsigned char ctrl6_is_present:1; + unsigned char ctrl7_is_present:1; + } __packed; + struct { + unsigned char ctrl8_is_present:1; + unsigned char ctrl9_is_present:1; + unsigned char ctrl10_is_present:1; + unsigned char ctrl11_is_present:1; + unsigned char ctrl12_is_present:1; + unsigned char ctrl13_is_present:1; + unsigned char ctrl14_is_present:1; + unsigned char ctrl15_is_present:1; + } __packed; + struct { + unsigned char ctrl16_is_present:1; + unsigned char ctrl17_is_present:1; + unsigned char ctrl18_is_present:1; + unsigned char ctrl19_is_present:1; + unsigned char ctrl20_is_present:1; + unsigned char ctrl21_is_present:1; + unsigned char ctrl22_is_present:1; + unsigned char ctrl23_is_present:1; + } __packed; + struct { + unsigned char ctrl24_is_present:1; + unsigned char ctrl25_is_present:1; + unsigned char ctrl26_is_present:1; + unsigned char ctrl27_is_present:1; + unsigned char ctrl28_is_present:1; + unsigned char ctrl29_is_present:1; + unsigned char ctrl30_is_present:1; + unsigned char ctrl31_is_present:1; + } __packed; + struct { + unsigned char ctrl32_is_present:1; + unsigned char ctrl33_is_present:1; + unsigned char ctrl34_is_present:1; + unsigned char ctrl35_is_present:1; + unsigned char ctrl36_is_present:1; + unsigned char ctrl37_is_present:1; + unsigned char ctrl38_is_present:1; + unsigned char ctrl39_is_present:1; + } __packed; + struct { + unsigned char ctrl40_is_present:1; + unsigned char ctrl41_is_present:1; + unsigned char ctrl42_is_present:1; + unsigned char ctrl43_is_present:1; + unsigned char ctrl44_is_present:1; + unsigned char ctrl45_is_present:1; + unsigned char ctrl46_is_present:1; + unsigned char ctrl47_is_present:1; + } __packed; + struct { + unsigned char ctrl48_is_present:1; + unsigned char ctrl49_is_present:1; + unsigned char ctrl50_is_present:1; + unsigned char ctrl51_is_present:1; + unsigned char ctrl52_is_present:1; + unsigned char ctrl53_is_present:1; + unsigned char ctrl54_is_present:1; + unsigned char ctrl55_is_present:1; + } __packed; + struct { + unsigned char ctrl56_is_present:1; + unsigned char ctrl57_is_present:1; + unsigned char ctrl58_is_present:1; + unsigned char ctrl59_is_present:1; + unsigned char ctrl60_is_present:1; + unsigned char ctrl61_is_present:1; + unsigned char ctrl62_is_present:1; + unsigned char ctrl63_is_present:1; + } __packed; + }; + unsigned char data[9]; + }; +}; + +struct synaptics_rmi4_f12_query_8 { + union { + struct { + unsigned char size_of_query9; + struct { + unsigned char data0_is_present:1; + unsigned char data1_is_present:1; + unsigned char data2_is_present:1; + unsigned char data3_is_present:1; + unsigned char data4_is_present:1; + unsigned char data5_is_present:1; + unsigned char data6_is_present:1; + unsigned char data7_is_present:1; + } __packed; + struct { + unsigned char data8_is_present:1; + unsigned char data9_is_present:1; + unsigned char data10_is_present:1; + unsigned char data11_is_present:1; + unsigned char data12_is_present:1; + unsigned char data13_is_present:1; + unsigned char data14_is_present:1; + unsigned char data15_is_present:1; + } __packed; + struct { + unsigned char data16_is_present:1; + unsigned char data17_is_present:1; + unsigned char data18_is_present:1; + unsigned char data19_is_present:1; + unsigned char data20_is_present:1; + unsigned char data21_is_present:1; + unsigned char data22_is_present:1; + unsigned char data23_is_present:1; + } __packed; + struct { + unsigned char data24_is_present:1; + unsigned char data25_is_present:1; + unsigned char data26_is_present:1; + unsigned char data27_is_present:1; + unsigned char data28_is_present:1; + unsigned char data29_is_present:1; + unsigned char data30_is_present:1; + unsigned char data31_is_present:1; + } __packed; + }; + unsigned char data[5]; + }; +}; + +struct synaptics_rmi4_f12_ctrl_8 { + union { + struct { + unsigned char max_x_coord_lsb; + unsigned char max_x_coord_msb; + unsigned char max_y_coord_lsb; + unsigned char max_y_coord_msb; + unsigned char rx_pitch_lsb; + unsigned char rx_pitch_msb; + unsigned char tx_pitch_lsb; + unsigned char tx_pitch_msb; + unsigned char low_rx_clip; + unsigned char high_rx_clip; + unsigned char low_tx_clip; + unsigned char high_tx_clip; + unsigned char num_of_rx; + unsigned char num_of_tx; + }; + unsigned char data[14]; + }; +}; + +struct synaptics_rmi4_f12_ctrl_23 { + union { + struct { + unsigned char finger_enable:1; + unsigned char active_stylus_enable:1; + unsigned char palm_enable:1; + unsigned char unclassified_object_enable:1; + unsigned char hovering_finger_enable:1; + unsigned char gloved_finger_enable:1; + unsigned char f12_ctr23_00_b6__7:2; + unsigned char max_reported_objects; + unsigned char f12_ctr23_02_b0:1; + unsigned char report_active_stylus_as_finger:1; + unsigned char report_palm_as_finger:1; + unsigned char report_unclassified_object_as_finger:1; + unsigned char report_hovering_finger_as_finger:1; + unsigned char report_gloved_finger_as_finger:1; + unsigned char report_narrow_object_swipe_as_finger:1; + unsigned char report_handedge_as_finger:1; + unsigned char cover_enable:1; + unsigned char stylus_enable:1; + unsigned char eraser_enable:1; + unsigned char small_object_enable:1; + unsigned char f12_ctr23_03_b4__7:4; + unsigned char report_cover_as_finger:1; + unsigned char report_stylus_as_finger:1; + unsigned char report_eraser_as_finger:1; + unsigned char report_small_object_as_finger:1; + unsigned char f12_ctr23_04_b4__7:4; + }; + unsigned char data[5]; + }; +}; + +struct synaptics_rmi4_f12_ctrl_31 { + union { + struct { + unsigned char max_x_coord_lsb; + unsigned char max_x_coord_msb; + unsigned char max_y_coord_lsb; + unsigned char max_y_coord_msb; + unsigned char rx_pitch_lsb; + unsigned char rx_pitch_msb; + unsigned char rx_clip_low; + unsigned char rx_clip_high; + unsigned char wedge_clip_low; + unsigned char wedge_clip_high; + unsigned char num_of_p; + unsigned char num_of_q; + }; + unsigned char data[12]; + }; +}; + +struct synaptics_rmi4_f12_ctrl_58 { + union { + struct { + unsigned char reporting_format; + unsigned char f12_ctr58_00_reserved; + unsigned char min_force_lsb; + unsigned char min_force_msb; + unsigned char max_force_lsb; + unsigned char max_force_msb; + unsigned char light_press_threshold_lsb; + unsigned char light_press_threshold_msb; + unsigned char light_press_hysteresis_lsb; + unsigned char light_press_hysteresis_msb; + unsigned char hard_press_threshold_lsb; + unsigned char hard_press_threshold_msb; + unsigned char hard_press_hysteresis_lsb; + unsigned char hard_press_hysteresis_msb; + }; + unsigned char data[14]; + }; +}; + +struct synaptics_rmi4_f12_finger_data { + unsigned char object_type_and_status; + unsigned char x_lsb; + unsigned char x_msb; + unsigned char y_lsb; + unsigned char y_msb; +#ifdef REPORT_2D_Z + unsigned char z; +#endif +#ifdef REPORT_2D_W + unsigned char wx; + unsigned char wy; +#endif +}; + +struct synaptics_rmi4_f1a_query { + union { + struct { + unsigned char max_button_count:3; + unsigned char f1a_query0_b3__4:2; + unsigned char has_query4:1; + unsigned char has_query3:1; + unsigned char has_query2:1; + unsigned char has_general_control:1; + unsigned char has_interrupt_enable:1; + unsigned char has_multibutton_select:1; + unsigned char has_tx_rx_map:1; + unsigned char has_perbutton_threshold:1; + unsigned char has_release_threshold:1; + unsigned char has_strongestbtn_hysteresis:1; + unsigned char has_filter_strength:1; + } __packed; + unsigned char data[2]; + }; +}; + +struct synaptics_rmi4_f1a_query_4 { + union { + struct { + unsigned char has_ctrl19:1; + unsigned char f1a_query4_b1__4:4; + unsigned char has_ctrl24:1; + unsigned char f1a_query4_b6__7:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f1a_control_0 { + union { + struct { + unsigned char multibutton_report:2; + unsigned char filter_mode:2; + unsigned char reserved:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f1a_control { + struct synaptics_rmi4_f1a_control_0 general_control; + unsigned char button_int_enable; + unsigned char multi_button; + unsigned char *txrx_map; + unsigned char *button_threshold; + unsigned char button_release_threshold; + unsigned char strongest_button_hysteresis; + unsigned char filter_strength; +}; + +struct synaptics_rmi4_f1a_handle { + int button_bitmask_size; + unsigned char max_count; + unsigned char valid_button_count; + unsigned char *button_data_buffer; + unsigned char *button_map; + struct synaptics_rmi4_f1a_query button_query; + struct synaptics_rmi4_f1a_control button_control; +}; + +struct synaptics_rmi4_exp_fhandler { + struct synaptics_rmi4_exp_fn *exp_fn; + bool insert; + bool remove; + struct list_head link; +}; + +struct synaptics_rmi4_exp_fn_data { + bool initialized; + bool queue_work; + struct mutex mutex; + struct list_head list; + struct delayed_work work; + struct workqueue_struct *workqueue; + struct synaptics_rmi4_data *rmi4_data; +}; + +static struct synaptics_rmi4_exp_fn_data exp_data; + +static struct synaptics_dsx_button_map *vir_button_map; + +#ifdef USE_DATA_SERVER +static pid_t synad_pid; +static struct task_struct *synad_task; +static struct siginfo interrupt_signal; +#endif + +static struct device_attribute attrs[] = { + __ATTR(reset, 0220, + synaptics_rmi4_show_error, + synaptics_rmi4_f01_reset_store), + __ATTR(productinfo, 0444, + synaptics_rmi4_f01_productinfo_show, + synaptics_rmi4_store_error), + __ATTR(buildid, 0444, + synaptics_rmi4_f01_buildid_show, + synaptics_rmi4_store_error), + __ATTR(flashprog, 0444, + synaptics_rmi4_f01_flashprog_show, + synaptics_rmi4_store_error), + __ATTR(0dbutton, 0664, + synaptics_rmi4_0dbutton_show, + synaptics_rmi4_0dbutton_store), + __ATTR(suspend, 0220, + synaptics_rmi4_show_error, + synaptics_rmi4_suspend_store), + __ATTR(wake_gesture, 0664, + synaptics_rmi4_wake_gesture_show, + synaptics_rmi4_wake_gesture_store), +#ifdef USE_DATA_SERVER + __ATTR(synad_pid, 0220, + synaptics_rmi4_show_error, + synaptics_rmi4_synad_pid_store), +#endif +}; + +static struct kobj_attribute virtual_key_map_attr = { + .attr = { + .name = VIRTUAL_KEY_MAP_FILE_NAME, + .mode = 0444, + }, + .show = synaptics_rmi4_virtual_key_map_show, +}; + +static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int reset; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + if (kstrtouint(buf, 10, &reset) != 1) + return -EINVAL; + + if (reset != 1) + return -EINVAL; + + retval = synaptics_rmi4_reset_device(rmi4_data, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue reset command, error = %d\n", + __func__, retval); + return retval; + } + + return count; +} + +static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "0x%02x 0x%02x\n", + (rmi4_data->rmi4_mod_info.product_info[0]), + (rmi4_data->rmi4_mod_info.product_info[1])); +} + +static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + rmi4_data->firmware_id); +} + +static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + struct synaptics_rmi4_f01_device_status device_status; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr, + device_status.data, + sizeof(device_status.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read device status, error = %d\n", + __func__, retval); + return retval; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + device_status.flash_prog); +} + +static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + rmi4_data->button_0d_enabled); +} + +static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + unsigned char ii; + unsigned char intr_enable; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + input = input > 0 ? 1 : 0; + + if (rmi4_data->button_0d_enabled == input) + return count; + + if (list_empty(&rmi->support_fn_list)) + return -ENODEV; + + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) { + ii = fhandler->intr_reg_num; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + + if (input == 1) + intr_enable |= fhandler->intr_mask; + else + intr_enable &= ~fhandler->intr_mask; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + } + } + + rmi4_data->button_0d_enabled = input; + + return count; +} + +static ssize_t synaptics_rmi4_suspend_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input == 1) + synaptics_rmi4_suspend(dev); + else if (input == 0) + synaptics_rmi4_resume(dev); + else + return -EINVAL; + + return count; +} + +static ssize_t synaptics_rmi4_wake_gesture_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + rmi4_data->enable_wakeup_gesture); +} + +static ssize_t synaptics_rmi4_wake_gesture_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + input = input > 0 ? 1 : 0; + + if (rmi4_data->f11_wakeup_gesture || rmi4_data->f12_wakeup_gesture) + rmi4_data->enable_wakeup_gesture = input; + + return count; +} + +#ifdef USE_DATA_SERVER +static ssize_t synaptics_rmi4_synad_pid_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + synad_pid = input; + + if (synad_pid) { + synad_task = pid_task(find_vpid(synad_pid), PIDTYPE_PID); + if (!synad_task) + return -EINVAL; + } + + return count; +} +#endif + +static ssize_t synaptics_rmi4_virtual_key_map_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int ii; + int cnt; + int count = 0; + + for (ii = 0; ii < vir_button_map->nbuttons; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, "0x01:%d:%d:%d:%d:%d\n", + vir_button_map->map[ii * 5 + 0], + vir_button_map->map[ii * 5 + 1], + vir_button_map->map[ii * 5 + 2], + vir_button_map->map[ii * 5 + 3], + vir_button_map->map[ii * 5 + 4]); + buf += cnt; + count += cnt; + } + + return count; +} + +static int synaptics_rmi4_f11_wg(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval; + unsigned char reporting_control; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F11) + break; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base, + &reporting_control, + sizeof(reporting_control)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to change reporting mode\n", + __func__); + return retval; + } + + reporting_control = (reporting_control & ~MASK_3BIT); + if (enable) + reporting_control |= F11_WAKEUP_GESTURE_MODE; + else + reporting_control |= F11_CONTINUOUS_MODE; + + retval = synaptics_rmi4_reg_write(rmi4_data, + fhandler->full_addr.ctrl_base, + &reporting_control, + sizeof(reporting_control)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to change reporting mode\n", + __func__); + return retval; + } + + return retval; +} + +static int synaptics_rmi4_f12_wg(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval; + unsigned char offset; + unsigned char reporting_control[3]; + struct synaptics_rmi4_f12_extra_data *extra_data; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F12) + break; + } + + extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra; + offset = extra_data->ctrl20_offset; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + offset, + reporting_control, + sizeof(reporting_control)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to change reporting mode\n", + __func__); + return retval; + } + + if (enable) + reporting_control[rmi4_data->set_wakeup_gesture] = + F12_WAKEUP_GESTURE_MODE; + else + reporting_control[rmi4_data->set_wakeup_gesture] = + F12_CONTINUOUS_MODE; + + retval = synaptics_rmi4_reg_write(rmi4_data, + fhandler->full_addr.ctrl_base + offset, + reporting_control, + sizeof(reporting_control)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to change reporting mode\n", + __func__); + return retval; + } + + return retval; +} + +static void synaptics_rmi4_wakeup_gesture(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + if (rmi4_data->f11_wakeup_gesture) + synaptics_rmi4_f11_wg(rmi4_data, enable); + else if (rmi4_data->f12_wakeup_gesture) + synaptics_rmi4_f12_wg(rmi4_data, enable); +} + +static int synaptics_rmi4_f11_abs_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; /* number of touch points */ + unsigned char reg_index; + unsigned char finger; + unsigned char fingers_supported; + unsigned char num_of_finger_status_regs; + unsigned char finger_shift; + unsigned char finger_status; + unsigned char finger_status_reg[3]; + unsigned char detected_gestures; + unsigned short data_addr; + unsigned short data_offset; + int x; + int y; + int wx; + int wy; + int temp; + struct synaptics_rmi4_f11_data_1_5 data; + struct synaptics_rmi4_f11_extra_data *extra_data; + + /* + * The number of finger status registers is determined by the + * maximum number of fingers supported - 2 bits per finger. So + * the number of finger status registers to read is: + * register_count = ceil(max_num_of_fingers / 4) + */ + fingers_supported = fhandler->num_of_data_points; + num_of_finger_status_regs = (fingers_supported + 3) / 4; + data_addr = fhandler->full_addr.data_base; + + extra_data = (struct synaptics_rmi4_f11_extra_data *)fhandler->extra; + + if (rmi4_data->suspend && rmi4_data->enable_wakeup_gesture) { + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr + extra_data->data38_offset, + &detected_gestures, + sizeof(detected_gestures)); + if (retval < 0) + return 0; + + if (detected_gestures) { + input_report_key(rmi4_data->input_dev, KEY_WAKEUP, 1); + input_sync(rmi4_data->input_dev); + input_report_key(rmi4_data->input_dev, KEY_WAKEUP, 0); + input_sync(rmi4_data->input_dev); + rmi4_data->suspend = false; + } +/* synaptics_rmi4_wakeup_gesture(rmi4_data, false); */ + return 0; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr, + finger_status_reg, + num_of_finger_status_regs); + if (retval < 0) + return 0; + + mutex_lock(&(rmi4_data->rmi4_report_mutex)); + + for (finger = 0; finger < fingers_supported; finger++) { + reg_index = finger / 4; + finger_shift = (finger % 4) * 2; + finger_status = (finger_status_reg[reg_index] >> finger_shift) + & MASK_2BIT; + + /* + * Each 2-bit finger status field represents the following: + * 00 = finger not present + * 01 = finger present and data accurate + * 10 = finger present but data may be inaccurate + * 11 = reserved + */ +#ifdef TYPE_B_PROTOCOL + input_mt_slot(rmi4_data->input_dev, finger); + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, finger_status); +#endif + + if (finger_status) { + data_offset = data_addr + + num_of_finger_status_regs + + (finger * sizeof(data.data)); + retval = synaptics_rmi4_reg_read(rmi4_data, + data_offset, + data.data, + sizeof(data.data)); + if (retval < 0) { + touch_count = 0; + goto exit; + } + + x = (data.x_position_11_4 << 4) | data.x_position_3_0; + y = (data.y_position_11_4 << 4) | data.y_position_3_0; + wx = data.wx; + wy = data.wy; + + if (rmi4_data->hw_if->board_data->swap_axes) { + temp = x; + x = y; + y = temp; + temp = wx; + wx = wy; + wy = temp; + } + + if (rmi4_data->hw_if->board_data->x_flip) + x = rmi4_data->sensor_max_x - x; + if (rmi4_data->hw_if->board_data->y_flip) + y = rmi4_data->sensor_max_y - y; + + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 1); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 1); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_Y, y); +#ifdef REPORT_2D_W + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, max(wx, wy)); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, min(wx, wy)); +#endif +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Finger %d: status = 0x%02x, x = %d, y = %d, wx = %d, wy = %d\n", + __func__, finger, + finger_status, + x, y, wx, wy); + + touch_count++; + } + } + + if (touch_count == 0) { + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 0); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + } + + input_sync(rmi4_data->input_dev); + +exit: + mutex_unlock(&(rmi4_data->rmi4_report_mutex)); + + return touch_count; +} + +static int synaptics_rmi4_f12_abs_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; /* number of touch points */ + unsigned char index; + unsigned char finger; + unsigned char fingers_to_process; + unsigned char finger_status; + unsigned char size_of_2d_data; + unsigned char gesture_type; + unsigned short data_addr; + int x; + int y; + int wx; + int wy; + int temp; +#if defined(REPORT_2D_PRESSURE) || defined(F51_DISCRETE_FORCE) + int pressure; +#endif +#ifdef REPORT_2D_PRESSURE + unsigned char f_fingers; + unsigned char f_lsb; + unsigned char f_msb; + unsigned char *f_data; +#endif +#ifdef F51_DISCRETE_FORCE + unsigned char force_level; +#endif + struct synaptics_rmi4_f12_extra_data *extra_data; + struct synaptics_rmi4_f12_finger_data *data; + struct synaptics_rmi4_f12_finger_data *finger_data; + static unsigned char finger_presence; + static unsigned char stylus_presence; +#ifdef F12_DATA_15_WORKAROUND + static unsigned char objects_already_present; +#endif + + fingers_to_process = fhandler->num_of_data_points; + data_addr = fhandler->full_addr.data_base; + extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra; + size_of_2d_data = sizeof(struct synaptics_rmi4_f12_finger_data); + + if (rmi4_data->suspend && rmi4_data->enable_wakeup_gesture) { + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr + extra_data->data4_offset, + rmi4_data->gesture_detection, + sizeof(rmi4_data->gesture_detection)); + if (retval < 0) + return 0; + + gesture_type = rmi4_data->gesture_detection[0]; + + if (gesture_type && gesture_type != F12_UDG_DETECT) { + input_report_key(rmi4_data->input_dev, KEY_WAKEUP, 1); + input_sync(rmi4_data->input_dev); + input_report_key(rmi4_data->input_dev, KEY_WAKEUP, 0); + input_sync(rmi4_data->input_dev); + /* synaptics_rmi4_wakeup_gesture(rmi4_data, false); */ + /* rmi4_data->suspend = false; */ + } + + return 0; + } + + /* Determine the total number of fingers to process */ + if (extra_data->data15_size) { + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr + extra_data->data15_offset, + extra_data->data15_data, + extra_data->data15_size); + if (retval < 0) + return 0; + + /* Start checking from the highest bit */ + index = extra_data->data15_size - 1; /* Highest byte */ + finger = (fingers_to_process - 1) % 8; /* Highest bit */ + do { + if (extra_data->data15_data[index] & (1 << finger)) + break; + + if (finger) { + finger--; + } else if (index > 0) { + index--; /* Move to the next lower byte */ + finger = 7; + } + + fingers_to_process--; + } while (fingers_to_process); + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Number of fingers to process = %d\n", + __func__, fingers_to_process); + } + +#ifdef F12_DATA_15_WORKAROUND + fingers_to_process = max(fingers_to_process, objects_already_present); +#endif + + if (!fingers_to_process) { + synaptics_rmi4_free_fingers(rmi4_data); + finger_presence = 0; + stylus_presence = 0; + return 0; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr + extra_data->data1_offset, + (unsigned char *)fhandler->data, + fingers_to_process * size_of_2d_data); + if (retval < 0) + return 0; + + data = (struct synaptics_rmi4_f12_finger_data *)fhandler->data; + +#ifdef REPORT_2D_PRESSURE + if (rmi4_data->report_pressure) { + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr + extra_data->data29_offset, + extra_data->data29_data, + extra_data->data29_size); + if (retval < 0) + return 0; + } +#endif + + mutex_lock(&(rmi4_data->rmi4_report_mutex)); + + for (finger = 0; finger < fingers_to_process; finger++) { + finger_data = data + finger; + finger_status = finger_data->object_type_and_status; + +#ifdef F12_DATA_15_WORKAROUND + objects_already_present = finger + 1; +#endif + + x = (finger_data->x_msb << 8) | (finger_data->x_lsb); + y = (finger_data->y_msb << 8) | (finger_data->y_lsb); +#ifdef REPORT_2D_W + wx = finger_data->wx; + wy = finger_data->wy; +#endif + + if (rmi4_data->hw_if->board_data->swap_axes) { + temp = x; + x = y; + y = temp; + temp = wx; + wx = wy; + wy = temp; + } + + if (rmi4_data->hw_if->board_data->x_flip) + x = rmi4_data->sensor_max_x - x; + if (rmi4_data->hw_if->board_data->y_flip) + y = rmi4_data->sensor_max_y - y; + + switch (finger_status) { + case F12_FINGER_STATUS: + case F12_GLOVED_FINGER_STATUS: + /* Stylus has priority over fingers */ + if (stylus_presence) + break; +#ifdef TYPE_B_PROTOCOL + input_mt_slot(rmi4_data->input_dev, finger); + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, 1); +#endif + + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 1); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 1); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_Y, y); +#ifdef REPORT_2D_W + if (rmi4_data->wedge_sensor) { + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, wx); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, wx); + } else { + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, + max(wx, wy)); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, + min(wx, wy)); + } +#endif +#ifdef REPORT_2D_PRESSURE + if (rmi4_data->report_pressure) { + f_fingers = extra_data->data29_size / 2; + f_data = extra_data->data29_data; + if (finger + 1 > f_fingers) { + pressure = 1; + } else { + f_lsb = finger * 2; + f_msb = finger * 2 + 1; + pressure = (int)f_data[f_lsb] << 0 | + (int)f_data[f_msb] << 8; + } + pressure = pressure > 0 ? pressure : 1; + if (pressure > rmi4_data->force_max) + pressure = rmi4_data->force_max; + input_report_abs(rmi4_data->input_dev, + ABS_MT_PRESSURE, pressure); + } +#elif defined(F51_DISCRETE_FORCE) + if (finger == 0) { + retval = synaptics_rmi4_reg_read(rmi4_data, + FORCE_LEVEL_ADDR, + &force_level, + sizeof(force_level)); + if (retval < 0) + return 0; + pressure = force_level > 0 ? force_level : 1; + } else { + pressure = 1; + } + input_report_abs(rmi4_data->input_dev, + ABS_MT_PRESSURE, pressure); +#endif +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Finger %d: status = 0x%02x, x = %d, y = %d, wx = %d, wy = %d\n", + __func__, finger, + finger_status, + x, y, wx, wy); + + finger_presence = 1; + touch_count++; + break; + case F12_PALM_STATUS: + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Finger %d: x = %d, y = %d, wx = %d, wy = %d\n", + __func__, finger, + x, y, wx, wy); + break; + case F12_STYLUS_STATUS: + case F12_ERASER_STATUS: + if (!rmi4_data->stylus_enable) + break; + /* Stylus has priority over fingers */ + if (finger_presence) { + mutex_unlock(&(rmi4_data->rmi4_report_mutex)); + synaptics_rmi4_free_fingers(rmi4_data); + mutex_lock(&(rmi4_data->rmi4_report_mutex)); + finger_presence = 0; + } + if (stylus_presence) {/* Allow one stylus at a timee */ + if (finger + 1 != stylus_presence) + break; + } + input_report_key(rmi4_data->stylus_dev, + BTN_TOUCH, 1); + if (finger_status == F12_STYLUS_STATUS) { + input_report_key(rmi4_data->stylus_dev, + BTN_TOOL_PEN, 1); + } else { + input_report_key(rmi4_data->stylus_dev, + BTN_TOOL_RUBBER, 1); + } + input_report_abs(rmi4_data->stylus_dev, + ABS_X, x); + input_report_abs(rmi4_data->stylus_dev, + ABS_Y, y); + input_sync(rmi4_data->stylus_dev); + + stylus_presence = finger + 1; + touch_count++; + break; + default: +#ifdef TYPE_B_PROTOCOL + input_mt_slot(rmi4_data->input_dev, finger); + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, 0); +#endif + break; + } + } + + if (touch_count == 0) { + finger_presence = 0; +#ifdef F12_DATA_15_WORKAROUND + objects_already_present = 0; +#endif + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 0); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + + if (rmi4_data->stylus_enable) { + stylus_presence = 0; + input_report_key(rmi4_data->stylus_dev, + BTN_TOUCH, 0); + input_report_key(rmi4_data->stylus_dev, + BTN_TOOL_PEN, 0); + if (rmi4_data->eraser_enable) { + input_report_key(rmi4_data->stylus_dev, + BTN_TOOL_RUBBER, 0); + } + input_sync(rmi4_data->stylus_dev); + } + } + + input_sync(rmi4_data->input_dev); + + mutex_unlock(&(rmi4_data->rmi4_report_mutex)); + + return touch_count; +} + +static int synaptics_rmi4_f1a_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; + unsigned char button; + unsigned char index; + unsigned char shift; + unsigned char status; + unsigned char *data; + unsigned short data_addr = fhandler->full_addr.data_base; + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + static unsigned char do_once = 1; + static bool current_status[MAX_NUMBER_OF_BUTTONS]; +#ifdef NO_0D_WHILE_2D + static bool before_2d_status[MAX_NUMBER_OF_BUTTONS]; + static bool while_2d_status[MAX_NUMBER_OF_BUTTONS]; +#endif + + if (do_once) { + memset(current_status, 0, sizeof(current_status)); +#ifdef NO_0D_WHILE_2D + memset(before_2d_status, 0, sizeof(before_2d_status)); + memset(while_2d_status, 0, sizeof(while_2d_status)); +#endif + do_once = 0; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_addr, + f1a->button_data_buffer, + f1a->button_bitmask_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read button data registers\n", + __func__); + return retval; + } + + data = f1a->button_data_buffer; + + mutex_lock(&(rmi4_data->rmi4_report_mutex)); + + for (button = 0; button < f1a->valid_button_count; button++) { + index = button / 8; + shift = button % 8; + status = ((data[index] >> shift) & MASK_1BIT); + + if (current_status[button] == status) + continue; + else + current_status[button] = status; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Button %d (code %d) ->%d\n", + __func__, button, + f1a->button_map[button], + status); +#ifdef NO_0D_WHILE_2D + if (rmi4_data->fingers_on_2d == false) { + if (status == 1) { + before_2d_status[button] = 1; + } else { + if (while_2d_status[button] == 1) { + while_2d_status[button] = 0; + continue; + } else { + before_2d_status[button] = 0; + } + } + touch_count++; + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + } else { + if (before_2d_status[button] == 1) { + before_2d_status[button] = 0; + touch_count++; + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + } else { + if (status == 1) + while_2d_status[button] = 1; + else + while_2d_status[button] = 0; + } + } +#else + touch_count++; + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); +#endif + } + + if (touch_count) + input_sync(rmi4_data->input_dev); + + mutex_unlock(&(rmi4_data->rmi4_report_mutex)); + + return retval; +} + +static void synaptics_rmi4_report_touch(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + unsigned char touch_count_2d; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Function %02x reporting\n", + __func__, fhandler->fn_number); + + switch (fhandler->fn_number) { + case SYNAPTICS_RMI4_F11: + touch_count_2d = synaptics_rmi4_f11_abs_report(rmi4_data, + fhandler); + + if (touch_count_2d) + rmi4_data->fingers_on_2d = true; + else + rmi4_data->fingers_on_2d = false; + break; + case SYNAPTICS_RMI4_F12: + touch_count_2d = synaptics_rmi4_f12_abs_report(rmi4_data, + fhandler); + + if (touch_count_2d) + rmi4_data->fingers_on_2d = true; + else + rmi4_data->fingers_on_2d = false; + break; + case SYNAPTICS_RMI4_F1A: + synaptics_rmi4_f1a_report(rmi4_data, fhandler); + break; +#ifdef USE_DATA_SERVER + case SYNAPTICS_RMI4_F21: + if (synad_pid) + send_sig_info(SIGIO, &interrupt_signal, synad_task); + break; +#endif + default: + break; + } +} + +static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data, + bool report) +{ + int retval; + unsigned char data[MAX_INTR_REGISTERS + 1]; + unsigned char *intr = &data[1]; + bool was_in_bl_mode; + struct synaptics_rmi4_f01_device_status status; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + /* + * Get interrupt status information from F01 Data1 register to + * determine the source(s) that are flagging the interrupt. + */ + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr, + data, + rmi4_data->num_of_intr_regs + 1); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read interrupt status\n", + __func__); + return retval; + } + + status.data[0] = data[0]; + if (status.status_code == STATUS_CRC_IN_PROGRESS) { + retval = synaptics_rmi4_check_status(rmi4_data, + &was_in_bl_mode); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to check status\n", + __func__); + return retval; + } + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read device status\n", + __func__); + return retval; + } + } + if (status.unconfigured && !status.flash_prog) { + pr_notice("%s: spontaneous reset detected\n", __func__); + retval = synaptics_rmi4_reinit_device(rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to reinit device\n", + __func__); + } + } + + if (!report) + return retval; + + /* + * Traverse the function handler list and service the source(s) + * of the interrupt accordingly. + */ + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + if (fhandler->intr_mask & + intr[fhandler->intr_reg_num]) { + synaptics_rmi4_report_touch(rmi4_data, + fhandler); + } + } + } + } + + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) { + if (!exp_fhandler->insert && + !exp_fhandler->remove && + (exp_fhandler->exp_fn->attn != NULL)) + exp_fhandler->exp_fn->attn(rmi4_data, intr[0]); + } + } + mutex_unlock(&exp_data.mutex); + + return retval; +} + +static irqreturn_t synaptics_rmi4_irq(int irq, void *data) +{ + struct synaptics_rmi4_data *rmi4_data = data; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + if (gpio_get_value(bdata->irq_gpio) != bdata->irq_on_state) + goto exit; + + synaptics_rmi4_sensor_report(rmi4_data, true); + +exit: + return IRQ_HANDLED; +} + +static int synaptics_rmi4_int_enable(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval = 0; + unsigned char ii; + unsigned char zero = 0x00; + unsigned char *intr_mask; + unsigned short intr_addr; + + intr_mask = rmi4_data->intr_mask; + + for (ii = 0; ii < rmi4_data->num_of_intr_regs; ii++) { + if (intr_mask[ii] != 0x00) { + intr_addr = rmi4_data->f01_ctrl_base_addr + 1 + ii; + if (enable) { + retval = synaptics_rmi4_reg_write(rmi4_data, + intr_addr, + &(intr_mask[ii]), + sizeof(intr_mask[ii])); + if (retval < 0) + return retval; + } else { + retval = synaptics_rmi4_reg_write(rmi4_data, + intr_addr, + &zero, + sizeof(zero)); + if (retval < 0) + return retval; + } + } + } + + return retval; +} + +static int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data, + bool enable, bool attn_only) +{ + int retval = 0; + unsigned char data[MAX_INTR_REGISTERS]; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + mutex_lock(&(rmi4_data->rmi4_irq_enable_mutex)); + + if (attn_only) { + retval = synaptics_rmi4_int_enable(rmi4_data, enable); + goto exit; + } + + if (enable) { + if (rmi4_data->irq_enabled) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Interrupt already enabled\n", + __func__); + goto exit; + } + + retval = synaptics_rmi4_int_enable(rmi4_data, false); + if (retval < 0) + goto exit; + + /* Clear interrupts */ + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + data, + rmi4_data->num_of_intr_regs); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read interrupt status\n", + __func__); + goto exit; + } + + retval = request_threaded_irq(rmi4_data->irq, NULL, + synaptics_rmi4_irq, bdata->irq_flags, + PLATFORM_DRIVER_NAME, rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create irq thread\n", + __func__); + goto exit; + } + + retval = synaptics_rmi4_int_enable(rmi4_data, true); + if (retval < 0) + goto exit; + + rmi4_data->irq_enabled = true; + } else { + if (rmi4_data->irq_enabled) { + disable_irq(rmi4_data->irq); + free_irq(rmi4_data->irq, rmi4_data); + rmi4_data->irq_enabled = false; + } + } + +exit: + mutex_unlock(&(rmi4_data->rmi4_irq_enable_mutex)); + + return retval; +} + +static void synaptics_rmi4_set_intr_mask(struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + unsigned char ii; + unsigned char intr_offset; + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < (fd->intr_src_count + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; +} + +static int synaptics_rmi4_f01_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = fd->intr_src_count; + fhandler->data = NULL; + fhandler->extra = NULL; + + synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + + rmi4_data->f01_query_base_addr = fd->query_base_addr; + rmi4_data->f01_ctrl_base_addr = fd->ctrl_base_addr; + rmi4_data->f01_data_base_addr = fd->data_base_addr; + rmi4_data->f01_cmd_base_addr = fd->cmd_base_addr; + + return 0; +} + +static int synaptics_rmi4_f11_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + int temp; + unsigned char offset; + unsigned char fingers_supported; + struct synaptics_rmi4_f11_extra_data *extra_data; + struct synaptics_rmi4_f11_query_0_5 query_0_5; + struct synaptics_rmi4_f11_query_7_8 query_7_8; + struct synaptics_rmi4_f11_query_9 query_9; + struct synaptics_rmi4_f11_query_12 query_12; + struct synaptics_rmi4_f11_query_27 query_27; + struct synaptics_rmi4_f11_ctrl_6_9 control_6_9; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = fd->intr_src_count; + fhandler->extra = kmalloc(sizeof(*extra_data), GFP_KERNEL); + if (!fhandler->extra) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for fhandler->extra\n", + __func__); + return -ENOMEM; + } + extra_data = (struct synaptics_rmi4_f11_extra_data *)fhandler->extra; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base, + query_0_5.data, + sizeof(query_0_5.data)); + if (retval < 0) + return retval; + + /* Maximum number of fingers supported */ + if (query_0_5.num_of_fingers <= 4) + fhandler->num_of_data_points = query_0_5.num_of_fingers + 1; + else if (query_0_5.num_of_fingers == 5) + fhandler->num_of_data_points = 10; + + rmi4_data->num_of_fingers = fhandler->num_of_data_points; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + 6, + control_6_9.data, + sizeof(control_6_9.data)); + if (retval < 0) + return retval; + + /* Maximum x and y */ + rmi4_data->sensor_max_x = control_6_9.sensor_max_x_pos_7_0 | + (control_6_9.sensor_max_x_pos_11_8 << 8); + rmi4_data->sensor_max_y = control_6_9.sensor_max_y_pos_7_0 | + (control_6_9.sensor_max_y_pos_11_8 << 8); + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Function %02x max x = %d max y = %d\n", + __func__, fhandler->fn_number, + rmi4_data->sensor_max_x, + rmi4_data->sensor_max_y); + + rmi4_data->max_touch_width = MAX_F11_TOUCH_WIDTH; + + if (bdata->swap_axes) { + temp = rmi4_data->sensor_max_x; + rmi4_data->sensor_max_x = rmi4_data->sensor_max_y; + rmi4_data->sensor_max_y = temp; + } + + synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + + fhandler->data = NULL; + + offset = sizeof(query_0_5.data); + + /* query 6 */ + if (query_0_5.has_rel) + offset += 1; + + /* queries 7 8 */ + if (query_0_5.has_gestures) { + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + offset, + query_7_8.data, + sizeof(query_7_8.data)); + if (retval < 0) + return retval; + + offset += sizeof(query_7_8.data); + } + + /* query 9 */ + if (query_0_5.has_query_9) { + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + offset, + query_9.data, + sizeof(query_9.data)); + if (retval < 0) + return retval; + + offset += sizeof(query_9.data); + } + + /* query 10 */ + if (query_0_5.has_gestures && query_7_8.has_touch_shapes) + offset += 1; + + /* query 11 */ + if (query_0_5.has_query_11) + offset += 1; + + /* query 12 */ + if (query_0_5.has_query_12) { + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + offset, + query_12.data, + sizeof(query_12.data)); + if (retval < 0) + return retval; + + offset += sizeof(query_12.data); + } + + /* query 13 */ + if (query_0_5.has_jitter_filter) + offset += 1; + + /* query 14 */ + if (query_0_5.has_query_12 && query_12.has_general_information_2) + offset += 1; + + /* queries 15 16 17 18 19 20 21 22 23 24 25 26*/ + if (query_0_5.has_query_12 && query_12.has_physical_properties) + offset += 12; + + /* query 27 */ + if (query_0_5.has_query_27) { + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + offset, + query_27.data, + sizeof(query_27.data)); + if (retval < 0) + return retval; + + rmi4_data->f11_wakeup_gesture = query_27.has_wakeup_gesture; + } + + if (!rmi4_data->f11_wakeup_gesture) + return retval; + + /* data 0 */ + fingers_supported = fhandler->num_of_data_points; + offset = (fingers_supported + 3) / 4; + + /* data 1 2 3 4 5 */ + offset += 5 * fingers_supported; + + /* data 6 7 */ + if (query_0_5.has_rel) + offset += 2 * fingers_supported; + + /* data 8 */ + if (query_0_5.has_gestures && query_7_8.data[0]) + offset += 1; + + /* data 9 */ + if (query_0_5.has_gestures && (query_7_8.data[0] || query_7_8.data[1])) + offset += 1; + + /* data 10 */ + if (query_0_5.has_gestures && + (query_7_8.has_pinch || query_7_8.has_flick)) + offset += 1; + + /* data 11 12 */ + if (query_0_5.has_gestures && + (query_7_8.has_flick || query_7_8.has_rotate)) + offset += 2; + + /* data 13 */ + if (query_0_5.has_gestures && query_7_8.has_touch_shapes) + offset += (fingers_supported + 3) / 4; + + /* data 14 15 */ + if (query_0_5.has_gestures && + (query_7_8.has_scroll_zones || + query_7_8.has_multi_finger_scroll || + query_7_8.has_chiral_scroll)) + offset += 2; + + /* data 16 17 */ + if (query_0_5.has_gestures && + (query_7_8.has_scroll_zones && + query_7_8.individual_scroll_zones)) + offset += 2; + + /* data 18 19 20 21 22 23 24 25 26 27 */ + if (query_0_5.has_query_9 && query_9.has_contact_geometry) + offset += 10 * fingers_supported; + + /* data 28 */ + if (query_0_5.has_bending_correction || + query_0_5.has_large_object_suppression) + offset += 1; + + /* data 29 30 31 */ + if (query_0_5.has_query_9 && query_9.has_pen_hover_discrimination) + offset += 3; + + /* data 32 */ + if (query_0_5.has_query_12 && + query_12.has_small_object_detection_tuning) + offset += 1; + + /* data 33 34 */ + if (query_0_5.has_query_27 && query_27.f11_query27_b0) + offset += 2; + + /* data 35 */ + if (query_0_5.has_query_12 && query_12.has_8bit_w) + offset += fingers_supported; + + /* data 36 */ + if (query_0_5.has_bending_correction) + offset += 1; + + /* data 37 */ + if (query_0_5.has_query_27 && query_27.has_data_37) + offset += 1; + + /* data 38 */ + if (query_0_5.has_query_27 && query_27.has_wakeup_gesture) + extra_data->data38_offset = offset; + + return retval; +} + +static int synaptics_rmi4_f12_set_enables(struct synaptics_rmi4_data *rmi4_data, + unsigned short ctrl28) +{ + int retval; + static unsigned short ctrl_28_address; + + if (ctrl28) + ctrl_28_address = ctrl28; + + retval = synaptics_rmi4_reg_write(rmi4_data, + ctrl_28_address, + &rmi4_data->report_enable, + sizeof(rmi4_data->report_enable)); + if (retval < 0) + return retval; + + return retval; +} + +static int synaptics_rmi4_f12_find_sub(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + unsigned char *presence, unsigned char presence_size, + unsigned char structure_offset, unsigned char reg, + unsigned char sub) +{ + int retval; + unsigned char cnt; + unsigned char regnum; + unsigned char bitnum; + unsigned char p_index; + unsigned char s_index; + unsigned char offset; + unsigned char max_reg; + unsigned char *structure; + + max_reg = (presence_size - 1) * 8 - 1; + + if (reg > max_reg) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Register number (%d) over limit\n", + __func__, reg); + return -EINVAL; + } + + p_index = reg / 8 + 1; + bitnum = reg % 8; + if ((presence[p_index] & (1 << bitnum)) == 0x00) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Register %d is not present\n", + __func__, reg); + return -EINVAL; + } + + structure = kmalloc(presence[0], GFP_KERNEL); + if (!structure) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for structure register\n", + __func__); + return -ENOMEM; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + structure_offset, + structure, + presence[0]); + if (retval < 0) + goto exit; + + s_index = 0; + + for (regnum = 0; regnum < reg; regnum++) { + p_index = regnum / 8 + 1; + bitnum = regnum % 8; + if ((presence[p_index] & (1 << bitnum)) == 0x00) + continue; + + if (structure[s_index] == 0x00) + s_index += 3; + else + s_index++; + + while (structure[s_index] & ~MASK_7BIT) + s_index++; + + s_index++; + } + + cnt = 0; + s_index++; + offset = sub / 7; + bitnum = sub % 7; + + do { + if (cnt == offset) { + if (structure[s_index + cnt] & (1 << bitnum)) + retval = 1; + else + retval = 0; + goto exit; + } + cnt++; + } while (structure[s_index + cnt - 1] & ~MASK_7BIT); + + retval = 0; + +exit: + kfree(structure); + + return retval; +} + +static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval = 0; + int temp; + unsigned char subpacket; + unsigned char ctrl_23_size; + unsigned char size_of_2d_data; + unsigned char size_of_query5; + unsigned char size_of_query8; + unsigned char ctrl_8_offset; + unsigned char ctrl_20_offset; + unsigned char ctrl_23_offset; + unsigned char ctrl_28_offset; + unsigned char ctrl_31_offset; + unsigned char ctrl_58_offset; + unsigned char num_of_fingers; + struct synaptics_rmi4_f12_extra_data *extra_data; + struct synaptics_rmi4_f12_query_5 *query_5 = NULL; + struct synaptics_rmi4_f12_query_8 *query_8 = NULL; + struct synaptics_rmi4_f12_ctrl_8 *ctrl_8 = NULL; + struct synaptics_rmi4_f12_ctrl_23 *ctrl_23 = NULL; + struct synaptics_rmi4_f12_ctrl_31 *ctrl_31 = NULL; + struct synaptics_rmi4_f12_ctrl_58 *ctrl_58 = NULL; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = fd->intr_src_count; + fhandler->extra = kmalloc(sizeof(*extra_data), GFP_KERNEL); + if (!fhandler->extra) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for fhandler->extra\n", + __func__); + return -ENOMEM; + } + extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra; + size_of_2d_data = sizeof(struct synaptics_rmi4_f12_finger_data); + + query_5 = kzalloc(sizeof(*query_5), GFP_KERNEL); + if (!query_5) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for query_5\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + query_8 = kzalloc(sizeof(*query_8), GFP_KERNEL); + if (!query_8) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for query_8\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + ctrl_8 = kzalloc(sizeof(*ctrl_8), GFP_KERNEL); + if (!ctrl_8) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for ctrl_8\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + ctrl_23 = kzalloc(sizeof(*ctrl_23), GFP_KERNEL); + if (!ctrl_23) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for ctrl_23\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + ctrl_31 = kzalloc(sizeof(*ctrl_31), GFP_KERNEL); + if (!ctrl_31) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for ctrl_31\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + ctrl_58 = kzalloc(sizeof(*ctrl_58), GFP_KERNEL); + if (!ctrl_58) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for ctrl_58\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + 4, + &size_of_query5, + sizeof(size_of_query5)); + if (retval < 0) + goto exit; + + if (size_of_query5 > sizeof(query_5->data)) + size_of_query5 = sizeof(query_5->data); + memset(query_5->data, 0x00, sizeof(query_5->data)); + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + 5, + query_5->data, + size_of_query5); + if (retval < 0) + goto exit; + + ctrl_8_offset = query_5->ctrl0_is_present + + query_5->ctrl1_is_present + + query_5->ctrl2_is_present + + query_5->ctrl3_is_present + + query_5->ctrl4_is_present + + query_5->ctrl5_is_present + + query_5->ctrl6_is_present + + query_5->ctrl7_is_present; + + ctrl_20_offset = ctrl_8_offset + + query_5->ctrl8_is_present + + query_5->ctrl9_is_present + + query_5->ctrl10_is_present + + query_5->ctrl11_is_present + + query_5->ctrl12_is_present + + query_5->ctrl13_is_present + + query_5->ctrl14_is_present + + query_5->ctrl15_is_present + + query_5->ctrl16_is_present + + query_5->ctrl17_is_present + + query_5->ctrl18_is_present + + query_5->ctrl19_is_present; + + ctrl_23_offset = ctrl_20_offset + + query_5->ctrl20_is_present + + query_5->ctrl21_is_present + + query_5->ctrl22_is_present; + + ctrl_28_offset = ctrl_23_offset + + query_5->ctrl23_is_present + + query_5->ctrl24_is_present + + query_5->ctrl25_is_present + + query_5->ctrl26_is_present + + query_5->ctrl27_is_present; + + ctrl_31_offset = ctrl_28_offset + + query_5->ctrl28_is_present + + query_5->ctrl29_is_present + + query_5->ctrl30_is_present; + + ctrl_58_offset = ctrl_31_offset + + query_5->ctrl31_is_present + + query_5->ctrl32_is_present + + query_5->ctrl33_is_present + + query_5->ctrl34_is_present + + query_5->ctrl35_is_present + + query_5->ctrl36_is_present + + query_5->ctrl37_is_present + + query_5->ctrl38_is_present + + query_5->ctrl39_is_present + + query_5->ctrl40_is_present + + query_5->ctrl41_is_present + + query_5->ctrl42_is_present + + query_5->ctrl43_is_present + + query_5->ctrl44_is_present + + query_5->ctrl45_is_present + + query_5->ctrl46_is_present + + query_5->ctrl47_is_present + + query_5->ctrl48_is_present + + query_5->ctrl49_is_present + + query_5->ctrl50_is_present + + query_5->ctrl51_is_present + + query_5->ctrl52_is_present + + query_5->ctrl53_is_present + + query_5->ctrl54_is_present + + query_5->ctrl55_is_present + + query_5->ctrl56_is_present + + query_5->ctrl57_is_present; + + ctrl_23_size = 2; + for (subpacket = 2; subpacket <= 4; subpacket++) { + retval = synaptics_rmi4_f12_find_sub(rmi4_data, + fhandler, query_5->data, sizeof(query_5->data), + 6, 23, subpacket); + if (retval == 1) + ctrl_23_size++; + else if (retval < 0) + goto exit; + + } + + retval = synaptics_rmi4_f12_find_sub(rmi4_data, + fhandler, query_5->data, sizeof(query_5->data), + 6, 20, 0); + if (retval == 1) + rmi4_data->set_wakeup_gesture = 2; + else if (retval == 0) + rmi4_data->set_wakeup_gesture = 0; + else if (retval < 0) + goto exit; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + ctrl_23_offset, + ctrl_23->data, + ctrl_23_size); + if (retval < 0) + goto exit; + + /* Maximum number of fingers supported */ + fhandler->num_of_data_points = min_t(unsigned char, + ctrl_23->max_reported_objects, + (unsigned char)F12_FINGERS_TO_SUPPORT); + + num_of_fingers = fhandler->num_of_data_points; + rmi4_data->num_of_fingers = num_of_fingers; + + rmi4_data->stylus_enable = ctrl_23->stylus_enable; + rmi4_data->eraser_enable = ctrl_23->eraser_enable; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + 7, + &size_of_query8, + sizeof(size_of_query8)); + if (retval < 0) + goto exit; + + if (size_of_query8 > sizeof(query_8->data)) + size_of_query8 = sizeof(query_8->data); + memset(query_8->data, 0x00, sizeof(query_8->data)); + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + 8, + query_8->data, + size_of_query8); + if (retval < 0) + goto exit; + + /* Determine the presence of the Data0 register */ + extra_data->data1_offset = query_8->data0_is_present; + + if ((size_of_query8 >= 3) && (query_8->data15_is_present)) { + extra_data->data15_offset = query_8->data0_is_present + + query_8->data1_is_present + + query_8->data2_is_present + + query_8->data3_is_present + + query_8->data4_is_present + + query_8->data5_is_present + + query_8->data6_is_present + + query_8->data7_is_present + + query_8->data8_is_present + + query_8->data9_is_present + + query_8->data10_is_present + + query_8->data11_is_present + + query_8->data12_is_present + + query_8->data13_is_present + + query_8->data14_is_present; + extra_data->data15_size = (num_of_fingers + 7) / 8; + } else { + extra_data->data15_size = 0; + } + +#ifdef REPORT_2D_PRESSURE + if ((size_of_query8 >= 5) && (query_8->data29_is_present)) { + extra_data->data29_offset = query_8->data0_is_present + + query_8->data1_is_present + + query_8->data2_is_present + + query_8->data3_is_present + + query_8->data4_is_present + + query_8->data5_is_present + + query_8->data6_is_present + + query_8->data7_is_present + + query_8->data8_is_present + + query_8->data9_is_present + + query_8->data10_is_present + + query_8->data11_is_present + + query_8->data12_is_present + + query_8->data13_is_present + + query_8->data14_is_present + + query_8->data15_is_present + + query_8->data16_is_present + + query_8->data17_is_present + + query_8->data18_is_present + + query_8->data19_is_present + + query_8->data20_is_present + + query_8->data21_is_present + + query_8->data22_is_present + + query_8->data23_is_present + + query_8->data24_is_present + + query_8->data25_is_present + + query_8->data26_is_present + + query_8->data27_is_present + + query_8->data28_is_present; + extra_data->data29_size = 0; + for (subpacket = 0; subpacket <= num_of_fingers; subpacket++) { + retval = synaptics_rmi4_f12_find_sub(rmi4_data, + fhandler, query_8->data, + sizeof(query_8->data), + 9, 29, subpacket); + if (retval == 1) + extra_data->data29_size += 2; + else if (retval < 0) + goto exit; + } + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + ctrl_58_offset, + ctrl_58->data, + sizeof(ctrl_58->data)); + if (retval < 0) + goto exit; + rmi4_data->force_min = + (int)(ctrl_58->min_force_lsb << 0) | + (int)(ctrl_58->min_force_msb << 8); + rmi4_data->force_max = + (int)(ctrl_58->max_force_lsb << 0) | + (int)(ctrl_58->max_force_msb << 8); + rmi4_data->report_pressure = true; + } else { + extra_data->data29_size = 0; + rmi4_data->report_pressure = false; + } +#endif + + rmi4_data->report_enable = RPT_DEFAULT; +#ifdef REPORT_2D_Z + rmi4_data->report_enable |= RPT_Z; +#endif +#ifdef REPORT_2D_W + rmi4_data->report_enable |= (RPT_WX | RPT_WY); +#endif + + retval = synaptics_rmi4_f12_set_enables(rmi4_data, + fhandler->full_addr.ctrl_base + ctrl_28_offset); + if (retval < 0) + goto exit; + + if (query_5->ctrl8_is_present) { + rmi4_data->wedge_sensor = false; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + ctrl_8_offset, + ctrl_8->data, + sizeof(ctrl_8->data)); + if (retval < 0) + goto exit; + + /* Maximum x and y */ + rmi4_data->sensor_max_x = + ((unsigned int)ctrl_8->max_x_coord_lsb << 0) | + ((unsigned int)ctrl_8->max_x_coord_msb << 8); + rmi4_data->sensor_max_y = + ((unsigned int)ctrl_8->max_y_coord_lsb << 0) | + ((unsigned int)ctrl_8->max_y_coord_msb << 8); + + rmi4_data->max_touch_width = MAX_F12_TOUCH_WIDTH; + } else { + rmi4_data->wedge_sensor = true; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + ctrl_31_offset, + ctrl_31->data, + sizeof(ctrl_31->data)); + if (retval < 0) + goto exit; + + /* Maximum x and y */ + rmi4_data->sensor_max_x = + ((unsigned int)ctrl_31->max_x_coord_lsb << 0) | + ((unsigned int)ctrl_31->max_x_coord_msb << 8); + rmi4_data->sensor_max_y = + ((unsigned int)ctrl_31->max_y_coord_lsb << 0) | + ((unsigned int)ctrl_31->max_y_coord_msb << 8); + + rmi4_data->max_touch_width = MAX_F12_TOUCH_WIDTH; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Function %02x max x = %d max y = %d\n", + __func__, fhandler->fn_number, + rmi4_data->sensor_max_x, + rmi4_data->sensor_max_y); + + if (bdata->swap_axes) { + temp = rmi4_data->sensor_max_x; + rmi4_data->sensor_max_x = rmi4_data->sensor_max_y; + rmi4_data->sensor_max_y = temp; + } + + rmi4_data->f12_wakeup_gesture = query_5->ctrl27_is_present; + if (rmi4_data->f12_wakeup_gesture) { + extra_data->ctrl20_offset = ctrl_20_offset; + extra_data->data4_offset = query_8->data0_is_present + + query_8->data1_is_present + + query_8->data2_is_present + + query_8->data3_is_present; + } + + synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + + /* Allocate memory for finger data storage space */ + fhandler->data_size = num_of_fingers * size_of_2d_data; + fhandler->data = kmalloc(fhandler->data_size, GFP_KERNEL); + if (!fhandler->data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for fhandler->data\n", + __func__); + retval = -ENOMEM; + goto exit; + } + +exit: + kfree(query_5); + kfree(query_8); + kfree(ctrl_8); + kfree(ctrl_23); + kfree(ctrl_31); + kfree(ctrl_58); + + return retval; +} + +static int synaptics_rmi4_f1a_alloc_mem(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + struct synaptics_rmi4_f1a_handle *f1a; + + f1a = kzalloc(sizeof(*f1a), GFP_KERNEL); + if (!f1a) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for function handle\n", + __func__); + return -ENOMEM; + } + + fhandler->data = (void *)f1a; + fhandler->extra = NULL; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base, + f1a->button_query.data, + sizeof(f1a->button_query.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read query registers\n", + __func__); + return retval; + } + + f1a->max_count = f1a->button_query.max_button_count + 1; + + f1a->button_control.txrx_map = kzalloc(f1a->max_count * 2, GFP_KERNEL); + if (!f1a->button_control.txrx_map) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for tx rx mapping\n", + __func__); + return -ENOMEM; + } + + f1a->button_bitmask_size = (f1a->max_count + 7) / 8; + + f1a->button_data_buffer = kcalloc(f1a->button_bitmask_size, + sizeof(*(f1a->button_data_buffer)), GFP_KERNEL); + if (!f1a->button_data_buffer) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for data buffer\n", + __func__); + return -ENOMEM; + } + + f1a->button_map = kcalloc(f1a->max_count, + sizeof(*(f1a->button_map)), GFP_KERNEL); + if (!f1a->button_map) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for button map\n", + __func__); + return -ENOMEM; + } + + return 0; +} + +static int synaptics_rmi4_f1a_button_map(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char ii; + unsigned char offset = 0; + struct synaptics_rmi4_f1a_query_4 query_4; + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + rmi4_data->valid_button_count = f1a->valid_button_count; + + offset = f1a->button_query.has_general_control + + f1a->button_query.has_interrupt_enable + + f1a->button_query.has_multibutton_select; + + if (f1a->button_query.has_tx_rx_map) { + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.ctrl_base + offset, + f1a->button_control.txrx_map, + f1a->max_count * 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read tx rx mapping\n", + __func__); + return retval; + } + + rmi4_data->button_txrx_mapping = f1a->button_control.txrx_map; + } + + if (f1a->button_query.has_query4) { + offset = 2 + f1a->button_query.has_query2 + + f1a->button_query.has_query3; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fhandler->full_addr.query_base + offset, + query_4.data, + sizeof(query_4.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read button features 4\n", + __func__); + return retval; + } + + if (query_4.has_ctrl24) + rmi4_data->external_afe_buttons = true; + else + rmi4_data->external_afe_buttons = false; + } + + if (!bdata->cap_button_map) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: cap_button_map is NULL in board file\n", + __func__); + return -ENODEV; + } else if (!bdata->cap_button_map->map) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Button map is missing in board file\n", + __func__); + return -ENODEV; + } else { + if (bdata->cap_button_map->nbuttons != f1a->max_count) { + f1a->valid_button_count = min(f1a->max_count, + bdata->cap_button_map->nbuttons); + } else { + f1a->valid_button_count = f1a->max_count; + } + + for (ii = 0; ii < f1a->valid_button_count; ii++) + f1a->button_map[ii] = bdata->cap_button_map->map[ii]; + + rmi4_data->valid_button_count = f1a->valid_button_count; + } + + return 0; +} + +static void synaptics_rmi4_f1a_kfree(struct synaptics_rmi4_fn *fhandler) +{ + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + + if (f1a) { + kfree(f1a->button_control.txrx_map); + kfree(f1a->button_data_buffer); + kfree(f1a->button_map); + kfree(f1a); + fhandler->data = NULL; + } +} + +static int synaptics_rmi4_f1a_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = fd->intr_src_count; + + synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + + retval = synaptics_rmi4_f1a_alloc_mem(rmi4_data, fhandler); + if (retval < 0) + goto error_exit; + + retval = synaptics_rmi4_f1a_button_map(rmi4_data, fhandler); + if (retval < 0) + goto error_exit; + + rmi4_data->button_0d_enabled = 1; + + return 0; + +error_exit: + synaptics_rmi4_f1a_kfree(fhandler); + + return retval; +} + +static void synaptics_rmi4_empty_fn_list(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_fn *fhandler_temp; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry_safe(fhandler, + fhandler_temp, + &rmi->support_fn_list, + link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) { + synaptics_rmi4_f1a_kfree(fhandler); + } else { + kfree(fhandler->extra); + kfree(fhandler->data); + } + list_del(&fhandler->link); + kfree(fhandler); + } + } + INIT_LIST_HEAD(&rmi->support_fn_list); +} + +static int synaptics_rmi4_check_status(struct synaptics_rmi4_data *rmi4_data, + bool *was_in_bl_mode) +{ + int retval; + int timeout = CHECK_STATUS_TIMEOUT_MS; + struct synaptics_rmi4_f01_device_status status; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + if (retval < 0) + return retval; + + while (status.status_code == STATUS_CRC_IN_PROGRESS) { + if (timeout > 0) + msleep(20); + else + return -EINVAL; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + if (retval < 0) + return retval; + + timeout -= 20; + } + + if (timeout != CHECK_STATUS_TIMEOUT_MS) + *was_in_bl_mode = true; + + if (status.flash_prog == 1) { + rmi4_data->flash_prog_mode = true; + pr_notice("%s: In flash prog mode, status = 0x%02x\n", + __func__, + status.status_code); + } else { + rmi4_data->flash_prog_mode = false; + } + + return 0; +} + +static int synaptics_rmi4_set_configured(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char device_ctrl; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set configured\n", + __func__); + return retval; + } + + rmi4_data->no_sleep_setting = device_ctrl & NO_SLEEP_ON; + device_ctrl |= CONFIGURED; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set configured\n", + __func__); + } + + return retval; +} + +static int synaptics_rmi4_alloc_fh(struct synaptics_rmi4_fn **fhandler, + struct synaptics_rmi4_fn_desc *rmi_fd, int page_number) +{ + *fhandler = kzalloc(sizeof(**fhandler), GFP_KERNEL); + if (!(*fhandler)) + return -ENOMEM; + + (*fhandler)->full_addr.data_base = + (rmi_fd->data_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.ctrl_base = + (rmi_fd->ctrl_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.cmd_base = + (rmi_fd->cmd_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.query_base = + (rmi_fd->query_base_addr | + (page_number << 8)); + + return 0; +} + +static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char page_number; + unsigned char intr_count; + unsigned char *f01_query; + unsigned short pdt_entry_addr; + bool f01found; + bool f35found; + bool was_in_bl_mode; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + +rescan_pdt: + f01found = false; + f35found = false; + was_in_bl_mode = false; + intr_count = 0; + INIT_LIST_HEAD(&rmi->support_fn_list); + + /* Scan the page description tables of the pages to service */ + for (page_number = 0; page_number < PAGES_TO_SERVICE; page_number++) { + for (pdt_entry_addr = PDT_START; pdt_entry_addr > PDT_END; + pdt_entry_addr -= PDT_ENTRY_SIZE) { + pdt_entry_addr |= (page_number << 8); + + retval = synaptics_rmi4_reg_read(rmi4_data, + pdt_entry_addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + pdt_entry_addr &= ~(MASK_8BIT << 8); + + fhandler = NULL; + + if (rmi_fd.fn_number == 0) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Reached end of PDT\n", + __func__); + break; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: F%02x found (page %d)\n", + __func__, rmi_fd.fn_number, + page_number); + + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F01: + if (rmi_fd.intr_src_count == 0) + break; + + f01found = true; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc for F%d\n", + __func__, + rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f01_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_check_status(rmi4_data, + &was_in_bl_mode); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to check status\n", + __func__); + return retval; + } + + if (was_in_bl_mode) { + kfree(fhandler); + fhandler = NULL; + goto rescan_pdt; + } + + if (rmi4_data->flash_prog_mode) + goto flash_prog_mode; + + break; + case SYNAPTICS_RMI4_F11: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc for F%d\n", + __func__, + rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f11_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) + return retval; + break; + case SYNAPTICS_RMI4_F12: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc for F%d\n", + __func__, + rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f12_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) + return retval; + break; + case SYNAPTICS_RMI4_F1A: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc for F%d\n", + __func__, + rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f1a_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) { +#ifdef IGNORE_FN_INIT_FAILURE + kfree(fhandler); + fhandler = NULL; +#else + return retval; +#endif + } + break; +#ifdef USE_DATA_SERVER + case SYNAPTICS_RMI4_F21: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc for F%d\n", + __func__, + rmi_fd.fn_number); + return retval; + } + + fhandler->fn_number = rmi_fd.fn_number; + fhandler->num_of_data_sources = + rmi_fd.intr_src_count; + + synaptics_rmi4_set_intr_mask(fhandler, &rmi_fd, + intr_count); + break; +#endif + case SYNAPTICS_RMI4_F35: + f35found = true; + break; +#ifdef F51_DISCRETE_FORCE + case SYNAPTICS_RMI4_F51: + rmi4_data->f51_query_base_addr = + rmi_fd.query_base_addr | + (page_number << 8); + break; +#endif + } + + /* Accumulate the interrupt count */ + intr_count += rmi_fd.intr_src_count; + + if (fhandler && rmi_fd.intr_src_count) { + list_add_tail(&fhandler->link, + &rmi->support_fn_list); + } + } + } + + if (!f01found) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F01\n", + __func__); + if (!f35found) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F35\n", + __func__); + return -EINVAL; + } else { + pr_notice("%s: In microbootloader mode\n", + __func__); + return 0; + } + } + +flash_prog_mode: + rmi4_data->num_of_intr_regs = (intr_count + 7) / 8; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Number of interrupt registers = %d\n", + __func__, rmi4_data->num_of_intr_regs); + + f01_query = kmalloc(F01_STD_QUERY_LEN, GFP_KERNEL); + if (!f01_query) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for f01_query\n", + __func__); + return -ENOMEM; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_query_base_addr, + f01_query, + F01_STD_QUERY_LEN); + if (retval < 0) { + kfree(f01_query); + return retval; + } + + /* RMI Version 4.0 currently supported */ + rmi->version_major = 4; + rmi->version_minor = 0; + + rmi->manufacturer_id = f01_query[0]; + rmi->product_props = f01_query[1]; + rmi->product_info[0] = f01_query[2]; + rmi->product_info[1] = f01_query[3]; + retval = secure_memcpy(rmi->product_id_string, + sizeof(rmi->product_id_string), + &f01_query[11], + F01_STD_QUERY_LEN - 11, + PRODUCT_ID_SIZE); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy product ID string\n", + __func__); + } + + kfree(f01_query); + + if (rmi->manufacturer_id != 1) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Non-Synaptics device found, manufacturer ID = %d\n", + __func__, rmi->manufacturer_id); + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_query_base_addr + F01_BUID_ID_OFFSET, + rmi->build_id, + sizeof(rmi->build_id)); + if (retval < 0) + return retval; + + rmi4_data->firmware_id = (unsigned int)rmi->build_id[0] + + (unsigned int)rmi->build_id[1] * 0x100 + + (unsigned int)rmi->build_id[2] * 0x10000; + + memset(rmi4_data->intr_mask, 0x00, sizeof(rmi4_data->intr_mask)); + + /* + * Map out the interrupt bit masks for the interrupt sources + * from the registered function handlers. + */ + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + rmi4_data->intr_mask[fhandler->intr_reg_num] |= + fhandler->intr_mask; + } + } + } + + if (rmi4_data->f11_wakeup_gesture || rmi4_data->f12_wakeup_gesture) + rmi4_data->enable_wakeup_gesture = WAKEUP_GESTURE; + else + rmi4_data->enable_wakeup_gesture = false; + + synaptics_rmi4_set_configured(rmi4_data); + + return 0; +} + +static int synaptics_rmi4_gpio_setup(int gpio, bool config, int dir, int state) +{ + int retval = 0; + unsigned char buf[16]; + + if (config) { + snprintf(buf, sizeof(buf), "dsx_gpio_%u\n", gpio); + + retval = gpio_request(gpio, buf); + if (retval) { + pr_err("%s: Failed to get gpio %d (code: %d)", + __func__, gpio, retval); + return retval; + } + + if (dir == 0) + retval = gpio_direction_input(gpio); + else + retval = gpio_direction_output(gpio, state); + if (retval) { + pr_err("%s: Failed to set gpio %d direction", + __func__, gpio); + return retval; + } + } else { + gpio_free(gpio); + } + + return retval; +} + +static void synaptics_rmi4_set_params(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char ii; + struct synaptics_rmi4_f1a_handle *f1a; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_X, 0, + rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_Y, 0, + rmi4_data->sensor_max_y, 0, 0); +#ifdef REPORT_2D_W + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, 0, + rmi4_data->max_touch_width, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, 0, + rmi4_data->max_touch_width, 0, 0); +#endif + + rmi4_data->input_settings.sensor_max_x = rmi4_data->sensor_max_x; + rmi4_data->input_settings.sensor_max_y = rmi4_data->sensor_max_y; + rmi4_data->input_settings.max_touch_width = rmi4_data->max_touch_width; + +#ifdef REPORT_2D_PRESSURE + if (rmi4_data->report_pressure) { + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_PRESSURE, rmi4_data->force_min, + rmi4_data->force_max, 0, 0); + + rmi4_data->input_settings.force_min = rmi4_data->force_min; + rmi4_data->input_settings.force_max = rmi4_data->force_max; + } +#elif defined(F51_DISCRETE_FORCE) + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_PRESSURE, 0, + FORCE_LEVEL_MAX, 0, 0); +#endif + +#ifdef TYPE_B_PROTOCOL +#ifdef KERNEL_ABOVE_3_6 + input_mt_init_slots(rmi4_data->input_dev, + rmi4_data->num_of_fingers, INPUT_MT_DIRECT); +#else + input_mt_init_slots(rmi4_data->input_dev, + rmi4_data->num_of_fingers); +#endif +#endif + + rmi4_data->input_settings.num_of_fingers = rmi4_data->num_of_fingers; + + f1a = NULL; + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) + f1a = fhandler->data; + } + } + + if (f1a) { + for (ii = 0; ii < f1a->valid_button_count; ii++) { + set_bit(f1a->button_map[ii], + rmi4_data->input_dev->keybit); + input_set_capability(rmi4_data->input_dev, + EV_KEY, f1a->button_map[ii]); + } + + rmi4_data->input_settings.valid_button_count = + f1a->valid_button_count; + } + + if (vir_button_map->nbuttons) { + for (ii = 0; ii < vir_button_map->nbuttons; ii++) { + set_bit(vir_button_map->map[ii * 5], + rmi4_data->input_dev->keybit); + input_set_capability(rmi4_data->input_dev, + EV_KEY, vir_button_map->map[ii * 5]); + } + } + + if (rmi4_data->f11_wakeup_gesture || rmi4_data->f12_wakeup_gesture) { + set_bit(KEY_WAKEUP, rmi4_data->input_dev->keybit); + input_set_capability(rmi4_data->input_dev, EV_KEY, KEY_WAKEUP); + } +} + +static int synaptics_rmi4_set_input_dev(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + rmi4_data->input_dev = input_allocate_device(); + if (rmi4_data->input_dev == NULL) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate input device\n", + __func__); + retval = -ENOMEM; + goto err_input_device; + } + + retval = synaptics_rmi4_query_device(rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to query device\n", + __func__); + goto err_query_device; + } + + rmi4_data->input_dev->name = PLATFORM_DRIVER_NAME; + rmi4_data->input_dev->phys = INPUT_PHYS_NAME; + rmi4_data->input_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT; + rmi4_data->input_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION; + rmi4_data->input_dev->dev.parent = rmi4_data->pdev->dev.parent; + input_set_drvdata(rmi4_data->input_dev, rmi4_data); + + set_bit(EV_SYN, rmi4_data->input_dev->evbit); + set_bit(EV_KEY, rmi4_data->input_dev->evbit); + set_bit(EV_ABS, rmi4_data->input_dev->evbit); + set_bit(BTN_TOUCH, rmi4_data->input_dev->keybit); + set_bit(BTN_TOOL_FINGER, rmi4_data->input_dev->keybit); +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit); +#endif + + if (bdata->max_y_for_2d >= 0) + rmi4_data->sensor_max_y = bdata->max_y_for_2d; + + synaptics_rmi4_set_params(rmi4_data); + + retval = input_register_device(rmi4_data->input_dev); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to register input device\n", + __func__); + goto err_register_input; + } + + rmi4_data->input_settings.stylus_enable = rmi4_data->stylus_enable; + rmi4_data->input_settings.eraser_enable = rmi4_data->eraser_enable; + + if (!rmi4_data->stylus_enable) + return 0; + + rmi4_data->stylus_dev = input_allocate_device(); + if (rmi4_data->stylus_dev == NULL) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate stylus device\n", + __func__); + retval = -ENOMEM; + goto err_stylus_device; + } + + rmi4_data->stylus_dev->name = STYLUS_DRIVER_NAME; + rmi4_data->stylus_dev->phys = STYLUS_PHYS_NAME; + rmi4_data->stylus_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT; + rmi4_data->stylus_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION; + rmi4_data->stylus_dev->dev.parent = rmi4_data->pdev->dev.parent; + input_set_drvdata(rmi4_data->stylus_dev, rmi4_data); + + set_bit(EV_KEY, rmi4_data->stylus_dev->evbit); + set_bit(EV_ABS, rmi4_data->stylus_dev->evbit); + set_bit(BTN_TOUCH, rmi4_data->stylus_dev->keybit); + set_bit(BTN_TOOL_PEN, rmi4_data->stylus_dev->keybit); + if (rmi4_data->eraser_enable) + set_bit(BTN_TOOL_RUBBER, rmi4_data->stylus_dev->keybit); +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, rmi4_data->stylus_dev->propbit); +#endif + + input_set_abs_params(rmi4_data->stylus_dev, ABS_X, 0, + rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(rmi4_data->stylus_dev, ABS_Y, 0, + rmi4_data->sensor_max_y, 0, 0); + + retval = input_register_device(rmi4_data->stylus_dev); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to register stylus device\n", + __func__); + goto err_register_stylus; + } + + return 0; + +err_register_stylus: + rmi4_data->stylus_dev = NULL; + +err_stylus_device: + input_unregister_device(rmi4_data->input_dev); + rmi4_data->input_dev = NULL; + +err_register_input: +err_query_device: + synaptics_rmi4_empty_fn_list(rmi4_data); + input_free_device(rmi4_data->input_dev); + +err_input_device: + return retval; +} + +static int synaptics_rmi4_set_gpio(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + retval = synaptics_rmi4_gpio_setup( + bdata->irq_gpio, + true, 0, 0); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to configure attention GPIO\n", + __func__); + goto err_gpio_irq; + } + + if (bdata->power_gpio >= 0) { + retval = synaptics_rmi4_gpio_setup( + bdata->power_gpio, + true, 1, !bdata->power_on_state); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to configure power GPIO\n", + __func__); + goto err_gpio_power; + } + } + + if (bdata->reset_gpio >= 0) { + retval = synaptics_rmi4_gpio_setup( + bdata->reset_gpio, + true, 1, !bdata->reset_on_state); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to configure reset GPIO\n", + __func__); + goto err_gpio_reset; + } + } + + if (bdata->power_gpio >= 0) { + gpio_set_value(bdata->power_gpio, bdata->power_on_state); + msleep(bdata->power_delay_ms); + } + + if (bdata->reset_gpio >= 0) { + gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + gpio_set_value(bdata->reset_gpio, !bdata->reset_on_state); + msleep(bdata->reset_delay_ms); + } + + return 0; + +err_gpio_reset: + if (bdata->power_gpio >= 0) + synaptics_rmi4_gpio_setup(bdata->power_gpio, false, 0, 0); + +err_gpio_power: + synaptics_rmi4_gpio_setup(bdata->irq_gpio, false, 0, 0); + +err_gpio_irq: + return retval; +} + +static int synaptics_dsx_pinctrl_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + rmi4_data->ts_pinctrl = devm_pinctrl_get((rmi4_data->pdev->dev.parent)); + if (IS_ERR_OR_NULL(rmi4_data->ts_pinctrl)) { + retval = PTR_ERR(rmi4_data->ts_pinctrl); + dev_err(rmi4_data->pdev->dev.parent, + "Target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; + } + + rmi4_data->pinctrl_state_active + = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_active"); + if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_active)) { + retval = PTR_ERR(rmi4_data->pinctrl_state_active); + dev_err(rmi4_data->pdev->dev.parent, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + rmi4_data->pinctrl_state_suspend + = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_suspend"); + if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_suspend)) { + retval = PTR_ERR(rmi4_data->pinctrl_state_suspend); + dev_err(rmi4_data->pdev->dev.parent, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + rmi4_data->pinctrl_state_release + = pinctrl_lookup_state(rmi4_data->ts_pinctrl, "pmx_ts_release"); + if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) { + retval = PTR_ERR(rmi4_data->pinctrl_state_release); + dev_err(rmi4_data->pdev->dev.parent, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(rmi4_data->ts_pinctrl); +err_pinctrl_get: + rmi4_data->ts_pinctrl = NULL; + return retval; +} + +static int synaptics_rmi4_regulator_configure(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval; + + if (enable) { + retval = regulator_set_load(rmi4_data->pwr_reg, + 20000); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set active regulator load avdd\n", + __func__); + return retval; + } + + retval = regulator_set_voltage(rmi4_data->pwr_reg, + 3296000, + 3304000); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set active regulator voltage avdd\n", + __func__); + goto err_avdd_load; + } + + retval = regulator_set_load(rmi4_data->bus_reg, + 62000); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set active regulator load vdd\n", + __func__); + goto err_avdd_load; + } + + retval = regulator_set_voltage(rmi4_data->bus_reg, + 1800000, + 1800000); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set active regulator voltage vdd\n", + __func__); + goto err_vdd_load; + } + + } else { + retval = regulator_set_load(rmi4_data->pwr_reg, 0); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set inactive regulator load avdd\n", + __func__); + return retval; + } + + retval = regulator_set_load(rmi4_data->bus_reg, 0); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set inactive regulator load vdd\n", + __func__); + return retval; + } + + } + return retval; + +err_vdd_load: + regulator_set_load(rmi4_data->bus_reg, 0); +err_avdd_load: + regulator_set_load(rmi4_data->pwr_reg, 0); + return retval; +} + +static int synaptics_rmi4_get_reg(struct synaptics_rmi4_data *rmi4_data, + bool get) +{ + int retval; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + if (!get) { + retval = 0; + goto regulator_put; + } + + if ((bdata->pwr_reg_name != NULL) && (*bdata->pwr_reg_name != 0)) { + rmi4_data->pwr_reg = regulator_get(rmi4_data->pdev->dev.parent, + bdata->pwr_reg_name); + if (IS_ERR(rmi4_data->pwr_reg)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to get power regulator\n", + __func__); + retval = PTR_ERR(rmi4_data->pwr_reg); + goto regulator_put; + } + } + + retval = regulator_set_load(rmi4_data->pwr_reg, + 20000); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set regulator current avdd\n", + __func__); + goto regulator_put; + } + + retval = regulator_set_voltage(rmi4_data->pwr_reg, + 3296000, + 3304000); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set regulator voltage avdd\n", + __func__); + goto regulator_put; + } + + if ((bdata->bus_reg_name != NULL) && (*bdata->bus_reg_name != 0)) { + rmi4_data->bus_reg = regulator_get(rmi4_data->pdev->dev.parent, + bdata->bus_reg_name); + if (IS_ERR(rmi4_data->bus_reg)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to get bus pullup regulator\n", + __func__); + retval = PTR_ERR(rmi4_data->bus_reg); + goto regulator_put; + } + } + + retval = regulator_set_load(rmi4_data->bus_reg, + 62000); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set regulator current vdd\n", + __func__); + goto regulator_put; + } + + retval = regulator_set_voltage(rmi4_data->bus_reg, + 1800000, + 1800000); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set regulator voltage vdd\n", + __func__); + goto regulator_put; + } + + return 0; + +regulator_put: + if (rmi4_data->pwr_reg) { + regulator_put(rmi4_data->pwr_reg); + rmi4_data->pwr_reg = NULL; + } + + if (rmi4_data->bus_reg) { + regulator_put(rmi4_data->bus_reg); + rmi4_data->bus_reg = NULL; + } + + return retval; +} + +static int synaptics_rmi4_enable_reg(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + if (!enable) { + retval = 0; + goto disable_pwr_reg; + } + + if (rmi4_data->bus_reg && rmi4_data->vdd_status == 0) { + retval = regulator_enable(rmi4_data->bus_reg); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to enable bus pullup regulator\n", + __func__); + goto exit; + } + rmi4_data->vdd_status = 1; + } + + if (rmi4_data->pwr_reg && rmi4_data->avdd_status == 0) { + retval = regulator_enable(rmi4_data->pwr_reg); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to enable power regulator\n", + __func__); + goto disable_bus_reg; + } + rmi4_data->avdd_status = 1; + msleep(bdata->power_delay_ms); + } + + return 0; + +disable_pwr_reg: + if (rmi4_data->pwr_reg && rmi4_data->avdd_status == 1) { + regulator_disable(rmi4_data->pwr_reg); + rmi4_data->avdd_status = 0; + } + +disable_bus_reg: + if (rmi4_data->bus_reg && rmi4_data->vdd_status == 1) { + regulator_disable(rmi4_data->bus_reg); + rmi4_data->vdd_status = 0; + } + +exit: + synaptics_rmi4_regulator_configure(rmi4_data, false); + return retval; +} + +static int synaptics_rmi4_free_fingers(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char ii; + + mutex_lock(&(rmi4_data->rmi4_report_mutex)); + +#ifdef TYPE_B_PROTOCOL + for (ii = 0; ii < rmi4_data->num_of_fingers; ii++) { + input_mt_slot(rmi4_data->input_dev, ii); + input_mt_report_slot_state(rmi4_data->input_dev, + MT_TOOL_FINGER, 0); + } +#endif + input_report_key(rmi4_data->input_dev, + BTN_TOUCH, 0); + input_report_key(rmi4_data->input_dev, + BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + input_sync(rmi4_data->input_dev); + + if (rmi4_data->stylus_enable) { + input_report_key(rmi4_data->stylus_dev, + BTN_TOUCH, 0); + input_report_key(rmi4_data->stylus_dev, + BTN_TOOL_PEN, 0); + if (rmi4_data->eraser_enable) { + input_report_key(rmi4_data->stylus_dev, + BTN_TOOL_RUBBER, 0); + } + input_sync(rmi4_data->stylus_dev); + } + + mutex_unlock(&(rmi4_data->rmi4_report_mutex)); + + rmi4_data->fingers_on_2d = false; + + return 0; +} + +static int synaptics_rmi4_sw_reset(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char command = 0x01; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_cmd_base_addr, + &command, + sizeof(command)); + if (retval < 0) + return retval; + + msleep(rmi4_data->hw_if->board_data->reset_delay_ms); + + if (rmi4_data->hw_if->ui_hw_init) { + retval = rmi4_data->hw_if->ui_hw_init(rmi4_data); + if (retval < 0) + return retval; + } + + return 0; +} + +static int synaptics_rmi4_do_rebuild(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_input_settings *settings; + + settings = &(rmi4_data->input_settings); + + if (settings->num_of_fingers != rmi4_data->num_of_fingers) + return 1; + + if (settings->valid_button_count != rmi4_data->valid_button_count) + return 1; + + if (settings->max_touch_width != rmi4_data->max_touch_width) + return 1; + + if (settings->sensor_max_x != rmi4_data->sensor_max_x) + return 1; + + if (settings->sensor_max_y != rmi4_data->sensor_max_y) + return 1; + + if (settings->force_min != rmi4_data->force_min) + return 1; + + if (settings->force_max != rmi4_data->force_max) + return 1; + + if (settings->stylus_enable != rmi4_data->stylus_enable) + return 1; + + if (settings->eraser_enable != rmi4_data->eraser_enable) + return 1; + + return 0; +} + +static void synaptics_rmi4_rebuild_work(struct work_struct *work) +{ + int retval; + unsigned char attr_count; + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct synaptics_rmi4_data *rmi4_data = + container_of(delayed_work, struct synaptics_rmi4_data, + rb_work); + + mutex_lock(&(rmi4_data->rmi4_reset_mutex)); + + mutex_lock(&exp_data.mutex); + + synaptics_rmi4_irq_enable(rmi4_data, false, false); + + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->remove != NULL) + exp_fhandler->exp_fn->remove(rmi4_data); + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + + synaptics_rmi4_free_fingers(rmi4_data); + synaptics_rmi4_empty_fn_list(rmi4_data); + input_unregister_device(rmi4_data->input_dev); + rmi4_data->input_dev = NULL; + if (rmi4_data->stylus_enable) { + input_unregister_device(rmi4_data->stylus_dev); + rmi4_data->stylus_dev = NULL; + } + + retval = synaptics_rmi4_set_input_dev(rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set up input device\n", + __func__); + goto exit; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs attributes\n", + __func__); + goto exit; + } + } + + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->init != NULL) + exp_fhandler->exp_fn->init(rmi4_data); + } + +exit: + synaptics_rmi4_irq_enable(rmi4_data, true, false); + + mutex_unlock(&exp_data.mutex); + + mutex_unlock(&(rmi4_data->rmi4_reset_mutex)); +} + +static int synaptics_rmi4_reinit_device(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + mutex_lock(&(rmi4_data->rmi4_reset_mutex)); + + synaptics_rmi4_free_fingers(rmi4_data); + + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F12) { + synaptics_rmi4_f12_set_enables(rmi4_data, 0); + break; + } + } + } + + retval = synaptics_rmi4_int_enable(rmi4_data, true); + if (retval < 0) + goto exit; + + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->reinit != NULL) + exp_fhandler->exp_fn->reinit(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + synaptics_rmi4_set_configured(rmi4_data); + + retval = 0; + +exit: + mutex_unlock(&(rmi4_data->rmi4_reset_mutex)); + return retval; +} + +static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data, + bool rebuild) +{ + int retval; + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + + mutex_lock(&(rmi4_data->rmi4_reset_mutex)); + + synaptics_rmi4_irq_enable(rmi4_data, false, false); + + retval = synaptics_rmi4_sw_reset(rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue reset command\n", + __func__); + goto exit; + } + + synaptics_rmi4_free_fingers(rmi4_data); + + synaptics_rmi4_empty_fn_list(rmi4_data); + + retval = synaptics_rmi4_query_device(rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to query device\n", + __func__); + goto exit; + } + + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->reset != NULL) + exp_fhandler->exp_fn->reset(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + retval = 0; + +exit: + synaptics_rmi4_irq_enable(rmi4_data, true, false); + + mutex_unlock(&(rmi4_data->rmi4_reset_mutex)); + + if (rebuild && synaptics_rmi4_do_rebuild(rmi4_data)) { + queue_delayed_work(rmi4_data->rb_workqueue, + &rmi4_data->rb_work, + msecs_to_jiffies(REBUILD_WORK_DELAY_MS)); + } + + return retval; +} + +#ifdef FB_READY_RESET +static void synaptics_rmi4_reset_work(struct work_struct *work) +{ + int retval = 0; + unsigned int timeout; + struct synaptics_rmi4_data *rmi4_data = + container_of(work, struct synaptics_rmi4_data, + reset_work); + + timeout = FB_READY_TIMEOUT_S * 1000 / FB_READY_WAIT_MS + 1; + + while (!rmi4_data->fb_ready) { + msleep(FB_READY_WAIT_MS); + timeout--; + if (timeout == 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Timed out waiting for FB ready\n", + __func__); + goto err; + } + } + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + retval = synaptics_rmi4_reset_device(rmi4_data, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue reset command\n", + __func__); + } + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); +err: + + dev_err(rmi4_data->pdev->dev.parent, + "%s: Timed out waiting for FB ready\n", + __func__); + +} +#endif + +static int synaptics_rmi4_sleep_enable(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval; + unsigned char device_ctrl; + unsigned char no_sleep_setting = rmi4_data->no_sleep_setting; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read device control\n", + __func__); + return retval; + } + + device_ctrl = device_ctrl & ~MASK_3BIT; + if (enable) + device_ctrl = device_ctrl | SENSOR_SLEEP; + else + device_ctrl = device_ctrl | no_sleep_setting | NORMAL_OPERATION; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write device control\n", + __func__); + return retval; + } + + rmi4_data->sensor_sleep = enable; + + return retval; +} + +static void synaptics_rmi4_exp_fn_work(struct work_struct *work) +{ + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_exp_fhandler *exp_fhandler_temp; + struct synaptics_rmi4_data *rmi4_data = exp_data.rmi4_data; + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + mutex_lock(&rmi4_data->rmi4_reset_mutex); + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry_safe(exp_fhandler, + exp_fhandler_temp, + &exp_data.list, + link) { + if ((exp_fhandler->exp_fn->init != NULL) && + exp_fhandler->insert) { + exp_fhandler->exp_fn->init(rmi4_data); + exp_fhandler->insert = false; + } else if ((exp_fhandler->exp_fn->remove != NULL) && + exp_fhandler->remove) { + exp_fhandler->exp_fn->remove(rmi4_data); + list_del(&exp_fhandler->link); + kfree(exp_fhandler); + } + } + } + mutex_unlock(&exp_data.mutex); + mutex_unlock(&rmi4_data->rmi4_reset_mutex); + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); +} + +void synaptics_rmi4_new_function(struct synaptics_rmi4_exp_fn *exp_fn, + bool insert) +{ + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + + if (!exp_data.initialized) { + mutex_init(&exp_data.mutex); + INIT_LIST_HEAD(&exp_data.list); + exp_data.initialized = true; + } + + mutex_lock(&exp_data.mutex); + if (insert) { + exp_fhandler = kzalloc(sizeof(*exp_fhandler), GFP_KERNEL); + if (!exp_fhandler) { + pr_err("%s: Failed to alloc mem for expansion function\n", + __func__); + goto exit; + } + exp_fhandler->exp_fn = exp_fn; + exp_fhandler->insert = true; + exp_fhandler->remove = false; + list_add_tail(&exp_fhandler->link, &exp_data.list); + } else if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) { + if (exp_fhandler->exp_fn->fn_type == exp_fn->fn_type) { + exp_fhandler->insert = false; + exp_fhandler->remove = true; + goto exit; + } + } + } + +exit: + mutex_unlock(&exp_data.mutex); + + if (exp_data.queue_work) { + queue_delayed_work(exp_data.workqueue, + &exp_data.work, + msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + } +} +EXPORT_SYMBOL(synaptics_rmi4_new_function); + +#ifdef CONFIG_DRM +static void synaptics_register_for_panel_events( + struct synaptics_rmi4_data *rmi4_data, struct device *dev) +{ + void *cookie; + + cookie = panel_event_notifier_register( + PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, + active_panel, + &synaptics_rmi4_dsi_panel_notifier_cb, + &rmi4_data->fb_notifier); + if (!cookie) { + pr_err("Failed to register for panel events\n"); + return; + } + + pr_debug("Registered for panel notifications on panel: 0x%x\n", + active_panel); + rmi4_data->notifier_cookie = cookie; + +} +#endif + +static int synaptics_rmi4_probe(struct platform_device *pdev) +{ + int retval = 0; + struct synaptics_rmi4_data *rmi4_data; + const struct synaptics_dsx_hw_interface *hw_if; + const struct synaptics_dsx_board_data *bdata; + + hw_if = pdev->dev.platform_data; + if (!hw_if) { + dev_err(&pdev->dev, + "%s: No hardware interface found\n", + __func__); + return -EINVAL; + } + + bdata = hw_if->board_data; + if (!bdata) { + dev_err(&pdev->dev, + "%s: No board data found\n", + __func__); + return -EINVAL; + } + + rmi4_data = kzalloc(sizeof(*rmi4_data), GFP_KERNEL); + if (!rmi4_data) { + dev_err(&pdev->dev, + "%s: Failed to alloc mem for rmi4_data\n", + __func__); + return -ENOMEM; + } + + rmi4_data->pdev = pdev; + rmi4_data->current_page = MASK_8BIT; + rmi4_data->hw_if = hw_if; + rmi4_data->suspend = false; + rmi4_data->irq_enabled = false; + rmi4_data->fingers_on_2d = false; + + rmi4_data->reset_device = synaptics_rmi4_reset_device; + rmi4_data->irq_enable = synaptics_rmi4_irq_enable; + rmi4_data->sleep_enable = synaptics_rmi4_sleep_enable; + rmi4_data->report_touch = synaptics_rmi4_report_touch; + + mutex_init(&(rmi4_data->rmi4_reset_mutex)); + mutex_init(&(rmi4_data->rmi4_report_mutex)); + mutex_init(&(rmi4_data->rmi4_io_ctrl_mutex)); + mutex_init(&(rmi4_data->rmi4_exp_init_mutex)); + mutex_init(&(rmi4_data->rmi4_irq_enable_mutex)); + + platform_set_drvdata(pdev, rmi4_data); + + vir_button_map = bdata->vir_button_map; + + rmi4_data->initialized = false; + +#ifdef CONFIG_DRM + if (active_panel) { + pr_debug("panel detected, registering notifier\n"); + synaptics_register_for_panel_events(rmi4_data, + &pdev->dev); + } else { + pr_debug("panel not detected, freeing data\n"); + retval = -1; + goto err_drm_reg; + } +#endif + rmi4_data->rmi4_probe_wq = create_singlethread_workqueue( + "Synaptics_rmi4_probe_wq"); + if (!rmi4_data->rmi4_probe_wq) { + dev_err(&pdev->dev, + "%s: Failed to create probe workqueue\n", + __func__); + goto err_probe_wq; + } + INIT_WORK(&rmi4_data->rmi4_probe_work, synaptics_rmi4_defer_probe); + queue_work(rmi4_data->rmi4_probe_wq, &rmi4_data->rmi4_probe_work); + + return retval; + +err_probe_wq: +#ifdef CONFIG_DRM + if (active_panel && rmi4_data->notifier_cookie) { + pr_debug("unregistering panel notifications\n"); + panel_event_notifier_unregister( + &rmi4_data->notifier_cookie); + } +#endif +err_drm_reg: + kfree(rmi4_data); + + return retval; +} + +static void synaptics_rmi4_defer_probe(struct work_struct *work) +{ + int retval; + unsigned char attr_count; + struct synaptics_rmi4_data *rmi4_data = container_of(work, + struct synaptics_rmi4_data, rmi4_probe_work); + struct platform_device *pdev; + const struct synaptics_dsx_hw_interface *hw_if; + const struct synaptics_dsx_board_data *bdata; + + pdev = rmi4_data->pdev; + hw_if = rmi4_data->hw_if; + bdata = hw_if->board_data; + + init_completion(&rmi4_data->drm_init_done); + retval = wait_for_completion_interruptible(&rmi4_data->drm_init_done); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Wait for DRM init was interrupted\n", + __func__); + goto err_drm_init_wait; + } + + retval = synaptics_rmi4_get_reg(rmi4_data, true); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to get regulators\n", + __func__); + goto err_get_reg; + } + + retval = synaptics_rmi4_enable_reg(rmi4_data, true); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to enable regulators\n", + __func__); + goto err_enable_reg; + } + + retval = synaptics_rmi4_set_gpio(rmi4_data); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to set up GPIO's\n", + __func__); + goto err_set_gpio; + } + + retval = synaptics_dsx_pinctrl_init(rmi4_data); + if (!retval && rmi4_data->ts_pinctrl) { + /* + * Pinctrl handle is optional. + * If pinctrl handle is found let pins to be + * configured in active state. If not found continue + * further without error. + */ + retval = pinctrl_select_state(rmi4_data->ts_pinctrl, + rmi4_data->pinctrl_state_active); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to select %s pinstate %d\n", + __func__, PINCTRL_STATE_ACTIVE, retval); + } + } + + if (hw_if->ui_hw_init) { + retval = hw_if->ui_hw_init(rmi4_data); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to initialize hardware interface\n", + __func__); + goto err_ui_hw_init; + } + } + + retval = synaptics_rmi4_set_input_dev(rmi4_data); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to set up input device\n", + __func__); + goto err_set_input_dev; + } + +#ifdef USE_EARLYSUSPEND + rmi4_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + rmi4_data->early_suspend.suspend = synaptics_rmi4_early_suspend; + rmi4_data->early_suspend.resume = synaptics_rmi4_late_resume; + register_early_suspend(&rmi4_data->early_suspend); +#endif + + if (!exp_data.initialized) { + mutex_init(&exp_data.mutex); + INIT_LIST_HEAD(&exp_data.list); + exp_data.initialized = true; + } + + rmi4_data->irq = gpio_to_irq(bdata->irq_gpio); + + retval = synaptics_rmi4_irq_enable(rmi4_data, true, false); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to enable attention interrupt\n", + __func__); + goto err_enable_irq; + } + + if (vir_button_map->nbuttons) { + rmi4_data->board_prop_dir = kobject_create_and_add( + "board_properties", NULL); + if (!rmi4_data->board_prop_dir) { + dev_err(&pdev->dev, + "%s: Failed to create board_properties directory\n", + __func__); + goto err_virtual_buttons; + } else { + retval = sysfs_create_file(rmi4_data->board_prop_dir, + &virtual_key_map_attr.attr); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to create virtual key map file\n", + __func__); + goto err_virtual_buttons; + } + } + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto err_sysfs; + } + } + +#ifdef USE_DATA_SERVER + memset(&interrupt_signal, 0, sizeof(interrupt_signal)); + interrupt_signal.si_signo = SIGIO; + interrupt_signal.si_code = SI_USER; +#endif + + rmi4_data->rb_workqueue = + create_singlethread_workqueue("dsx_rebuild_workqueue"); + INIT_DELAYED_WORK(&rmi4_data->rb_work, synaptics_rmi4_rebuild_work); + + exp_data.workqueue = create_singlethread_workqueue("dsx_exp_workqueue"); + INIT_DELAYED_WORK(&exp_data.work, synaptics_rmi4_exp_fn_work); + exp_data.rmi4_data = rmi4_data; + exp_data.queue_work = true; + queue_delayed_work(exp_data.workqueue, + &exp_data.work, + 0); + +#ifdef FB_READY_RESET + rmi4_data->reset_workqueue = + create_singlethread_workqueue("dsx_reset_workqueue"); + INIT_WORK(&rmi4_data->reset_work, synaptics_rmi4_reset_work); + queue_work(rmi4_data->reset_workqueue, &rmi4_data->reset_work); +#endif + rmi4_data->initialized = true; + + return; + +err_sysfs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + +err_virtual_buttons: + if (rmi4_data->board_prop_dir) { + sysfs_remove_file(rmi4_data->board_prop_dir, + &virtual_key_map_attr.attr); + kobject_put(rmi4_data->board_prop_dir); + } + + synaptics_rmi4_irq_enable(rmi4_data, false, false); + +err_enable_irq: +#ifdef USE_EARLYSUSPEND + unregister_early_suspend(&rmi4_data->early_suspend); +#endif + + synaptics_rmi4_empty_fn_list(rmi4_data); + input_unregister_device(rmi4_data->input_dev); + rmi4_data->input_dev = NULL; + if (rmi4_data->stylus_enable) { + input_unregister_device(rmi4_data->stylus_dev); + rmi4_data->stylus_dev = NULL; + } + +err_set_input_dev: + synaptics_rmi4_gpio_setup(bdata->irq_gpio, false, 0, 0); + + if (bdata->reset_gpio >= 0) + synaptics_rmi4_gpio_setup(bdata->reset_gpio, false, 0, 0); + + if (bdata->power_gpio >= 0) + synaptics_rmi4_gpio_setup(bdata->power_gpio, false, 0, 0); + +err_ui_hw_init: +err_set_gpio: + synaptics_rmi4_enable_reg(rmi4_data, false); + synaptics_rmi4_regulator_configure(rmi4_data, false); + + if (rmi4_data->ts_pinctrl) { + if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) { + devm_pinctrl_put(rmi4_data->ts_pinctrl); + rmi4_data->ts_pinctrl = NULL; + } else { + if (pinctrl_select_state( + rmi4_data->ts_pinctrl, + rmi4_data->pinctrl_state_release)) + dev_err(&pdev->dev, + "%s: Failed to select %s pinstate\n", + __func__, PINCTRL_STATE_RELEASE); + } + } + +err_enable_reg: + synaptics_rmi4_get_reg(rmi4_data, false); + +err_get_reg: +err_drm_init_wait: +#ifdef CONFIG_DRM + if (active_panel && rmi4_data->notifier_cookie) { + pr_debug("unregistering panel_events\n"); + panel_event_notifier_unregister(&rmi4_data->notifier_cookie); + } +#endif + cancel_work_sync(&rmi4_data->rmi4_probe_work); + destroy_workqueue(rmi4_data->rmi4_probe_wq); + kfree(rmi4_data); +} + +static int synaptics_rmi4_remove(struct platform_device *pdev) +{ + unsigned char attr_count; + struct synaptics_rmi4_data *rmi4_data = platform_get_drvdata(pdev); + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + +#ifdef FB_READY_RESET + cancel_work_sync(&rmi4_data->reset_work); + flush_workqueue(rmi4_data->reset_workqueue); + destroy_workqueue(rmi4_data->reset_workqueue); +#endif + + cancel_delayed_work_sync(&exp_data.work); + flush_workqueue(exp_data.workqueue); + destroy_workqueue(exp_data.workqueue); + + cancel_delayed_work_sync(&rmi4_data->rb_work); + flush_workqueue(rmi4_data->rb_workqueue); + destroy_workqueue(rmi4_data->rb_workqueue); + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + + if (rmi4_data->board_prop_dir) { + sysfs_remove_file(rmi4_data->board_prop_dir, + &virtual_key_map_attr.attr); + kobject_put(rmi4_data->board_prop_dir); + } + + synaptics_rmi4_irq_enable(rmi4_data, false, false); + +#ifdef CONFIG_DRM + if (active_panel) { + pr_debug("unregistering panel_events\n"); + panel_event_notifier_unregister(&rmi4_data->notifier_cookie); + } +#endif + +#ifdef USE_EARLYSUSPEND + unregister_early_suspend(&rmi4_data->early_suspend); +#endif + + synaptics_rmi4_empty_fn_list(rmi4_data); + input_unregister_device(rmi4_data->input_dev); + rmi4_data->input_dev = NULL; + if (rmi4_data->stylus_enable) { + input_unregister_device(rmi4_data->stylus_dev); + rmi4_data->stylus_dev = NULL; + } + + synaptics_rmi4_gpio_setup(bdata->irq_gpio, false, 0, 0); + + if (bdata->reset_gpio >= 0) + synaptics_rmi4_gpio_setup(bdata->reset_gpio, false, 0, 0); + + if (bdata->power_gpio >= 0) + synaptics_rmi4_gpio_setup(bdata->power_gpio, false, 0, 0); + + if (rmi4_data->ts_pinctrl) { + if (IS_ERR_OR_NULL(rmi4_data->pinctrl_state_release)) { + devm_pinctrl_put(rmi4_data->ts_pinctrl); + rmi4_data->ts_pinctrl = NULL; + } else { + pinctrl_select_state( + rmi4_data->ts_pinctrl, + rmi4_data->pinctrl_state_release); + } + } + + synaptics_rmi4_enable_reg(rmi4_data, false); + synaptics_rmi4_regulator_configure(rmi4_data, false); + synaptics_rmi4_get_reg(rmi4_data, false); + + cancel_work_sync(&rmi4_data->rmi4_probe_work); + destroy_workqueue(rmi4_data->rmi4_probe_wq); + + kfree(rmi4_data); + + return 0; +} + +#ifdef CONFIG_DRM +static void synaptics_rmi4_dsi_panel_notifier_cb( + enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, + void *client_data) +{ + struct synaptics_rmi4_data *rmi4_data = + container_of(client_data, struct synaptics_rmi4_data, + fb_notifier); + + if (!notification) { + pr_err("Invalid notification\n"); + return; + } + pr_debug("Notification type: %d, early trigger:%d\n", + notification->notif_type, + notification->notif_data.early_trigger); + + if (!client_data) { + pr_err("No client data\n"); + return; + } + + switch (notification->notif_type) { + case DRM_PANEL_EVENT_UNBLANK: + if (rmi4_data->initialized) { + synaptics_rmi4_regulator_configure(rmi4_data, true); + synaptics_rmi4_resume(&rmi4_data->pdev->dev); + } else { + complete(&rmi4_data->drm_init_done); + } + rmi4_data->fb_ready = true; + break; + case DRM_PANEL_EVENT_BLANK: + if (rmi4_data->initialized) { + synaptics_rmi4_suspend(&rmi4_data->pdev->dev); + synaptics_rmi4_regulator_configure(rmi4_data, false); + } + rmi4_data->fb_ready = false; + break; + case DRM_PANEL_EVENT_BLANK_LP: + pr_debug("received lp event\n"); + break; + case DRM_PANEL_EVENT_FPS_CHANGE: + pr_debug("received fps change old fps:%d new fps:%d\n", + notification->notif_data.old_fps, + notification->notif_data.new_fps); + break; + default: + pr_debug("notification serviced :%d\n", + notification->notif_type); + break; + } + +} +#endif + +#ifdef USE_EARLYSUSPEND +static int synaptics_rmi4_early_suspend(struct early_suspend *h) +{ + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_data *rmi4_data = + container_of(h, struct synaptics_rmi4_data, + early_suspend); + unsigned char device_ctrl; + + if (rmi4_data->stay_awake) + return retval; + + if (rmi4_data->enable_wakeup_gesture) { + if (rmi4_data->no_sleep_setting) { + synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + device_ctrl = device_ctrl & ~NO_SLEEP_ON; + synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + } + synaptics_rmi4_wakeup_gesture(rmi4_data, true); + enable_irq_wake(rmi4_data->irq); + goto exit; + } + +#ifdef SYNA_TDDI + if (rmi4_data->no_sleep_setting) { + synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + device_ctrl = device_ctrl & ~NO_SLEEP_ON; + synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + } + synaptics_rmi4_wakeup_gesture(rmi4_data, true); + usleep(TDDI_LPWG_WAIT_US); +#endif + synaptics_rmi4_irq_enable(rmi4_data, false, false); + synaptics_rmi4_sleep_enable(rmi4_data, true); + synaptics_rmi4_free_fingers(rmi4_data); + +exit: + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->early_suspend != NULL) + exp_fhandler->exp_fn->early_suspend(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + rmi4_data->suspend = true; + + return retval; +} + +static int synaptics_rmi4_late_resume(struct early_suspend *h) +{ +#ifdef FB_READY_RESET + int retval; +#endif + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_data *rmi4_data = + container_of(h, struct synaptics_rmi4_data, + early_suspend); + + if (rmi4_data->stay_awake) + return retval; + + if (rmi4_data->enable_wakeup_gesture) { + disable_irq_wake(rmi4_data->irq); + goto exit; + } + + rmi4_data->current_page = MASK_8BIT; + + if (rmi4_data->suspend) { + synaptics_rmi4_sleep_enable(rmi4_data, false); + synaptics_rmi4_irq_enable(rmi4_data, true, false); + } + +exit: +#ifdef FB_READY_RESET + if (rmi4_data->suspend) { + retval = synaptics_rmi4_reset_device(rmi4_data, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue reset command\n", + __func__); + } + } +#endif + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->late_resume != NULL) + exp_fhandler->exp_fn->late_resume(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + rmi4_data->suspend = false; + + return retval; +} +#endif + +static int synaptics_rmi4_suspend(struct device *dev) +{ + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + unsigned char device_ctrl; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + if (rmi4_data->stay_awake) + return 0; + + if (rmi4_data->enable_wakeup_gesture) { + if (rmi4_data->no_sleep_setting) { + synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + device_ctrl = device_ctrl & ~NO_SLEEP_ON; + synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + } + synaptics_rmi4_wakeup_gesture(rmi4_data, true); + enable_irq_wake(rmi4_data->irq); + goto exit; + } + + if (!rmi4_data->suspend) { +#ifdef SYNA_TDDI + if (rmi4_data->no_sleep_setting) { + synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + device_ctrl = device_ctrl & ~NO_SLEEP_ON; + synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + } + synaptics_rmi4_wakeup_gesture(rmi4_data, true); + usleep(TDDI_LPWG_WAIT_US); +#endif + synaptics_rmi4_irq_enable(rmi4_data, false, false); + synaptics_rmi4_sleep_enable(rmi4_data, true); + synaptics_rmi4_free_fingers(rmi4_data); + } + + if (bdata->reset_gpio >= 0) { + gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + } + + synaptics_rmi4_enable_reg(rmi4_data, false); + +exit: + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->suspend != NULL) + exp_fhandler->exp_fn->suspend(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + rmi4_data->suspend = true; + + return 0; +} + +static int synaptics_rmi4_resume(struct device *dev) +{ +#ifdef FB_READY_RESET + int retval; +#endif + struct synaptics_rmi4_exp_fhandler *exp_fhandler; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + if (rmi4_data->stay_awake) + return 0; + + if (rmi4_data->enable_wakeup_gesture) { + disable_irq_wake(rmi4_data->irq); + synaptics_rmi4_wakeup_gesture(rmi4_data, false); + goto exit; + } + + synaptics_rmi4_enable_reg(rmi4_data, true); + + if (bdata->reset_gpio >= 0) { + gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + gpio_set_value(bdata->reset_gpio, !bdata->reset_on_state); + msleep(bdata->reset_delay_ms); + } + + + rmi4_data->current_page = MASK_8BIT; + + synaptics_rmi4_sleep_enable(rmi4_data, false); + synaptics_rmi4_irq_enable(rmi4_data, true, false); + +exit: +#ifdef FB_READY_RESET + retval = synaptics_rmi4_reset_device(rmi4_data, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue reset command\n", + __func__); + } +#endif + mutex_lock(&exp_data.mutex); + if (!list_empty(&exp_data.list)) { + list_for_each_entry(exp_fhandler, &exp_data.list, link) + if (exp_fhandler->exp_fn->resume != NULL) + exp_fhandler->exp_fn->resume(rmi4_data); + } + mutex_unlock(&exp_data.mutex); + + rmi4_data->suspend = false; + + return 0; +} + +#ifdef CONFIG_PM +static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = { + .suspend = synaptics_rmi4_suspend, + .resume = synaptics_rmi4_resume, +}; +#endif + +static struct platform_driver synaptics_rmi4_driver = { + .driver = { + .name = PLATFORM_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &synaptics_rmi4_dev_pm_ops, +#endif + }, + .probe = synaptics_rmi4_probe, + .remove = synaptics_rmi4_remove, +}; + +static int __init synaptics_rmi4_init(void) +{ + int retval; + + retval = synaptics_rmi4_bus_init(); + if (retval) + return retval; + + return platform_driver_register(&synaptics_rmi4_driver); +} + +static void __exit synaptics_rmi4_exit(void) +{ + platform_driver_unregister(&synaptics_rmi4_driver); + + synaptics_rmi4_bus_exit(); +} + +module_init(synaptics_rmi4_init); +module_exit(synaptics_rmi4_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX Touch Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_core.h b/synaptics_dsx/synaptics_dsx_core.h new file mode 100644 index 0000000000..409227d6fe --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_core.h @@ -0,0 +1,536 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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. + */ + +#ifndef _SYNAPTICS_DSX_RMI4_H_ +#define _SYNAPTICS_DSX_RMI4_H_ + +#define SYNAPTICS_DS4 (1 << 0) +#define SYNAPTICS_DS5 (1 << 1) +#define SYNAPTICS_DSX_DRIVER_PRODUCT (SYNAPTICS_DS4 | SYNAPTICS_DS5) +#define SYNAPTICS_DSX_DRIVER_VERSION 0x2070 + +#include +#ifdef CONFIG_FB +#include +#include +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)) +#define KERNEL_ABOVE_2_6_38 +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)) +#define KERNEL_ABOVE_3_6 +#endif + +#ifdef KERNEL_ABOVE_2_6_38 +#define sstrtoul(...) kstrtoul(__VA_ARGS__) +#else +#define sstrtoul(...) strict_strtoul(__VA_ARGS__) +#endif + +#define PDT_PROPS (0X00EF) +#define PDT_START (0x00E9) +#define PDT_END (0x00D0) +#define PDT_ENTRY_SIZE (0x0006) +#define PAGES_TO_SERVICE (10) +#define PAGE_SELECT_LEN (2) +#define ADDRESS_LEN (2) + +#define SYNAPTICS_RMI4_F01 (0x01) +#define SYNAPTICS_RMI4_F11 (0x11) +#define SYNAPTICS_RMI4_F12 (0x12) +#define SYNAPTICS_RMI4_F1A (0x1A) +#define SYNAPTICS_RMI4_F21 (0x21) +#define SYNAPTICS_RMI4_F34 (0x34) +#define SYNAPTICS_RMI4_F35 (0x35) +#define SYNAPTICS_RMI4_F38 (0x38) +#define SYNAPTICS_RMI4_F51 (0x51) +#define SYNAPTICS_RMI4_F54 (0x54) +#define SYNAPTICS_RMI4_F55 (0x55) +#define SYNAPTICS_RMI4_FDB (0xDB) + +#define PRODUCT_INFO_SIZE 2 +#define PRODUCT_ID_SIZE 10 +#define BUILD_ID_SIZE 3 + +#define F12_FINGERS_TO_SUPPORT 10 +#define F12_NO_OBJECT_STATUS 0x00 +#define F12_FINGER_STATUS 0x01 +#define F12_ACTIVE_STYLUS_STATUS 0x02 +#define F12_PALM_STATUS 0x03 +#define F12_HOVERING_FINGER_STATUS 0x05 +#define F12_GLOVED_FINGER_STATUS 0x06 +#define F12_NARROW_OBJECT_STATUS 0x07 +#define F12_HAND_EDGE_STATUS 0x08 +#define F12_COVER_STATUS 0x0A +#define F12_STYLUS_STATUS 0x0B +#define F12_ERASER_STATUS 0x0C +#define F12_SMALL_OBJECT_STATUS 0x0D + +#define F12_GESTURE_DETECTION_LEN 5 + +#define MAX_NUMBER_OF_BUTTONS 4 +#define MAX_INTR_REGISTERS 4 + +#define MASK_16BIT 0xFFFF +#define MASK_8BIT 0xFF +#define MASK_7BIT 0x7F +#define MASK_6BIT 0x3F +#define MASK_5BIT 0x1F +#define MASK_4BIT 0x0F +#define MASK_3BIT 0x07 +#define MASK_2BIT 0x03 +#define MASK_1BIT 0x01 + +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" + +enum exp_fn { + RMI_DEV = 0, + RMI_FW_UPDATER, + RMI_TEST_REPORTING, + RMI_PROXIMITY, + RMI_ACTIVE_PEN, + RMI_GESTURE, + RMI_VIDEO, + RMI_DEBUG, + RMI_LAST, +}; + +extern struct drm_panel *active_panel; + +/* + * struct synaptics_rmi4_fn_desc - function descriptor fields in PDT entry + * @query_base_addr: base address for query registers + * @cmd_base_addr: base address for command registers + * @ctrl_base_addr: base address for control registers + * @data_base_addr: base address for data registers + * @intr_src_count: number of interrupt sources + * @fn_version: version of function + * @fn_number: function number + */ +struct synaptics_rmi4_fn_desc { + union { + struct { + unsigned char query_base_addr; + unsigned char cmd_base_addr; + unsigned char ctrl_base_addr; + unsigned char data_base_addr; + unsigned char intr_src_count:3; + unsigned char reserved_1:2; + unsigned char fn_version:2; + unsigned char reserved_2:1; + unsigned char fn_number; + } __packed; + unsigned char data[6]; + }; +}; + +/* + * synaptics_rmi4_fn_full_addr - full 16-bit base addresses + * @query_base: 16-bit base address for query registers + * @cmd_base: 16-bit base address for command registers + * @ctrl_base: 16-bit base address for control registers + * @data_base: 16-bit base address for data registers + */ +struct synaptics_rmi4_fn_full_addr { + unsigned short query_base; + unsigned short cmd_base; + unsigned short ctrl_base; + unsigned short data_base; +}; + +/* + * struct synaptics_rmi4_f11_extra_data - extra data of F$11 + * @data38_offset: offset to F11_2D_DATA38 register + */ +struct synaptics_rmi4_f11_extra_data { + unsigned char data38_offset; +}; + +/* + * struct synaptics_rmi4_f12_extra_data - extra data of F$12 + * @data1_offset: offset to F12_2D_DATA01 register + * @data4_offset: offset to F12_2D_DATA04 register + * @data15_offset: offset to F12_2D_DATA15 register + * @data15_size: size of F12_2D_DATA15 register + * @data15_data: buffer for reading F12_2D_DATA15 register + * @data29_offset: offset to F12_2D_DATA29 register + * @data29_size: size of F12_2D_DATA29 register + * @data29_data: buffer for reading F12_2D_DATA29 register + * @ctrl20_offset: offset to F12_2D_CTRL20 register + */ +struct synaptics_rmi4_f12_extra_data { + unsigned char data1_offset; + unsigned char data4_offset; + unsigned char data15_offset; + unsigned char data15_size; + unsigned char data15_data[(F12_FINGERS_TO_SUPPORT + 7) / 8]; + unsigned char data29_offset; + unsigned char data29_size; + unsigned char data29_data[F12_FINGERS_TO_SUPPORT * 2]; + unsigned char ctrl20_offset; +}; + +/* + * struct synaptics_rmi4_fn - RMI function handler + * @fn_number: function number + * @num_of_data_sources: number of data sources + * @num_of_data_points: maximum number of fingers supported + * @intr_reg_num: index to associated interrupt register + * @intr_mask: interrupt mask + * @full_addr: full 16-bit base addresses of function registers + * @link: linked list for function handlers + * @data_size: size of private data + * @data: pointer to private data + * @extra: pointer to extra data + */ +struct synaptics_rmi4_fn { + unsigned char fn_number; + unsigned char num_of_data_sources; + unsigned char num_of_data_points; + unsigned char intr_reg_num; + unsigned char intr_mask; + struct synaptics_rmi4_fn_full_addr full_addr; + struct list_head link; + int data_size; + void *data; + void *extra; +}; + +/* + * struct synaptics_rmi4_input_settings - current input settings + * @num_of_fingers: maximum number of fingers for 2D touch + * @valid_button_count: number of valid 0D buttons + * @max_touch_width: maximum touch width + * @sensor_max_x: maximum x coordinate for 2D touch + * @sensor_max_y: maximum y coordinate for 2D touch + * @force_min: minimum force value + * @force_max: maximum force value + * @stylus_enable: flag to indicate reporting of stylus data + * @eraser_enable: flag to indicate reporting of eraser data + */ +struct synaptics_rmi4_input_settings { + unsigned char num_of_fingers; + unsigned char valid_button_count; + unsigned char max_touch_width; + int sensor_max_x; + int sensor_max_y; + int force_min; + int force_max; + bool stylus_enable; + bool eraser_enable; +}; + +/* + * struct synaptics_rmi4_device_info - device information + * @version_major: RMI protocol major version number + * @version_minor: RMI protocol minor version number + * @manufacturer_id: manufacturer ID + * @product_props: product properties + * @product_info: product information + * @product_id_string: product ID + * @build_id: firmware build ID + * @support_fn_list: linked list for function handlers + */ +struct synaptics_rmi4_device_info { + unsigned int version_major; + unsigned int version_minor; + unsigned char manufacturer_id; + unsigned char product_props; + unsigned char product_info[PRODUCT_INFO_SIZE]; + unsigned char product_id_string[PRODUCT_ID_SIZE + 1]; + unsigned char build_id[BUILD_ID_SIZE]; + struct list_head support_fn_list; +}; + +/* + * struct synaptics_rmi4_data - RMI4 device instance data + * @pdev: pointer to platform device + * @input_dev: pointer to associated input device + * @stylus_dev: pointer to associated stylus device + * @hw_if: pointer to hardware interface data + * @rmi4_mod_info: device information + * @board_prop_dir: /sys/board_properties directory for virtual key map file + * @pwr_reg: pointer to regulator for power control + * @bus_reg: pointer to regulator for bus pullup control + * @rmi4_reset_mutex: mutex for software reset + * @rmi4_report_mutex: mutex for input event reporting + * @rmi4_io_ctrl_mutex: mutex for communication interface I/O + * @rmi4_exp_init_mutex: mutex for expansion function module initialization + * @rmi4_irq_enable_mutex: mutex for enabling/disabling interrupt + * @rb_work: work for rebuilding input device + * @rb_workqueue: workqueue for rebuilding input device + * @fb_notifier: framebuffer notifier client + * @reset_work: work for issuing reset after display framebuffer ready + * @reset_workqueue: workqueue for issuing reset after display framebuffer ready + * @early_suspend: early suspend power management + * @current_page: current RMI page for register access + * @button_0d_enabled: switch for enabling 0d button support + * @num_of_tx: number of Tx channels for 2D touch + * @num_of_rx: number of Rx channels for 2D touch + * @num_of_fingers: maximum number of fingers for 2D touch + * @max_touch_width: maximum touch width + * @valid_button_count: number of valid 0D buttons + * @report_enable: input data to report for F$12 + * @no_sleep_setting: default setting of NoSleep in F01_RMI_CTRL00 register + * @gesture_detection: detected gesture type and properties + * @intr_mask: interrupt enable mask + * @button_txrx_mapping: Tx Rx mapping of 0D buttons + * @num_of_intr_regs: number of interrupt registers + * @f01_query_base_addr: query base address for f$01 + * @f01_cmd_base_addr: command base address for f$01 + * @f01_ctrl_base_addr: control base address for f$01 + * @f01_data_base_addr: data base address for f$01 + * @f51_query_base_addr: query base address for f$51 + * @firmware_id: firmware build ID + * @irq: attention interrupt + * @sensor_max_x: maximum x coordinate for 2D touch + * @sensor_max_y: maximum y coordinate for 2D touch + * @force_min: minimum force value + * @force_max: maximum force value + * @set_wakeup_gesture: location of set wakeup gesture + * @flash_prog_mode: flag to indicate flash programming mode status + * @irq_enabled: flag to indicate attention interrupt enable status + * @fingers_on_2d: flag to indicate presence of fingers in 2D area + * @suspend: flag to indicate whether in suspend state + * @sensor_sleep: flag to indicate sleep state of sensor + * @stay_awake: flag to indicate whether to stay awake during suspend + * @fb_ready: flag to indicate whether display framebuffer in ready state + * @f11_wakeup_gesture: flag to indicate support for wakeup gestures in F$11 + * @f12_wakeup_gesture: flag to indicate support for wakeup gestures in F$12 + * @enable_wakeup_gesture: flag to indicate usage of wakeup gestures + * @wedge_sensor: flag to indicate use of wedge sensor + * @report_pressure: flag to indicate reporting of pressure data + * @stylus_enable: flag to indicate reporting of stylus data + * @eraser_enable: flag to indicate reporting of eraser data + * @external_afe_buttons: flag to indicate presence of external AFE buttons + * @notifier_cookie: touch notifier for panel events + * @reset_device: pointer to device reset function + * @irq_enable: pointer to interrupt enable function + * @sleep_enable: pointer to sleep enable function + * @report_touch: pointer to touch reporting function + */ +struct synaptics_rmi4_data { + bool initialized; + struct platform_device *pdev; + struct input_dev *input_dev; + struct input_dev *stylus_dev; + const struct synaptics_dsx_hw_interface *hw_if; + struct synaptics_rmi4_device_info rmi4_mod_info; + struct synaptics_rmi4_input_settings input_settings; + struct kobject *board_prop_dir; + struct regulator *pwr_reg; + struct regulator *bus_reg; + struct mutex rmi4_reset_mutex; + struct mutex rmi4_report_mutex; + struct mutex rmi4_io_ctrl_mutex; + struct mutex rmi4_exp_init_mutex; + struct mutex rmi4_irq_enable_mutex; + struct delayed_work rb_work; + struct workqueue_struct *rb_workqueue; + struct work_struct rmi4_probe_work; + struct workqueue_struct *rmi4_probe_wq; + struct completion drm_init_done; + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; + struct notifier_block fb_notifier; + struct work_struct reset_work; + struct workqueue_struct *reset_workqueue; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend early_suspend; +#endif + unsigned char current_page; + unsigned char button_0d_enabled; + unsigned char num_of_tx; + unsigned char num_of_rx; + unsigned char num_of_fingers; + unsigned char max_touch_width; + unsigned char valid_button_count; + unsigned char report_enable; + unsigned char no_sleep_setting; + unsigned char gesture_detection[F12_GESTURE_DETECTION_LEN]; + unsigned char intr_mask[MAX_INTR_REGISTERS]; + unsigned char *button_txrx_mapping; + unsigned short num_of_intr_regs; + unsigned short f01_query_base_addr; + unsigned short f01_cmd_base_addr; + unsigned short f01_ctrl_base_addr; + unsigned short f01_data_base_addr; +#ifdef F51_DISCRETE_FORCE + unsigned short f51_query_base_addr; +#endif + unsigned int firmware_id; + int irq; + int sensor_max_x; + int sensor_max_y; + int force_min; + int force_max; + int set_wakeup_gesture; + int avdd_status; + int vdd_status; + bool flash_prog_mode; + bool irq_enabled; + bool fingers_on_2d; + bool suspend; + bool sensor_sleep; + bool stay_awake; + bool fb_ready; + bool f11_wakeup_gesture; + bool f12_wakeup_gesture; + bool enable_wakeup_gesture; + bool wedge_sensor; + bool report_pressure; + bool stylus_enable; + bool eraser_enable; + bool external_afe_buttons; + void *notifier_cookie; + int (*reset_device)(struct synaptics_rmi4_data *rmi4_data, + bool rebuild); + int (*irq_enable)(struct synaptics_rmi4_data *rmi4_data, bool enable, + bool attn_only); + int (*sleep_enable)(struct synaptics_rmi4_data *rmi4_data, + bool enable); + void (*report_touch)(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler); +}; + +struct synaptics_dsx_bus_access { + unsigned char type; + int (*read)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr, + unsigned char *data, unsigned int length); + int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr, + unsigned char *data, unsigned int length); +}; + +struct synaptics_dsx_hw_interface { + struct synaptics_dsx_board_data *board_data; + const struct synaptics_dsx_bus_access *bus_access; + int (*bl_hw_init)(struct synaptics_rmi4_data *rmi4_data); + int (*ui_hw_init)(struct synaptics_rmi4_data *rmi4_data); +}; + +struct synaptics_rmi4_exp_fn { + enum exp_fn fn_type; + int (*init)(struct synaptics_rmi4_data *rmi4_data); + void (*remove)(struct synaptics_rmi4_data *rmi4_data); + void (*reset)(struct synaptics_rmi4_data *rmi4_data); + void (*reinit)(struct synaptics_rmi4_data *rmi4_data); + void (*early_suspend)(struct synaptics_rmi4_data *rmi4_data); + void (*suspend)(struct synaptics_rmi4_data *rmi4_data); + void (*resume)(struct synaptics_rmi4_data *rmi4_data); + void (*late_resume)(struct synaptics_rmi4_data *rmi4_data); + void (*attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask); +}; + +int synaptics_rmi4_bus_init(void); + +void synaptics_rmi4_bus_exit(void); + +void synaptics_rmi4_new_function(struct synaptics_rmi4_exp_fn *exp_fn_module, + bool insert); + +int synaptics_fw_updater(const unsigned char *fw_data); + +static inline int synaptics_rmi4_reg_read( + struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, + unsigned char *data, + unsigned int len) +{ + return rmi4_data->hw_if->bus_access->read(rmi4_data, addr, data, len); +} + +static inline int synaptics_rmi4_reg_write( + struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, + unsigned char *data, + unsigned int len) +{ + return rmi4_data->hw_if->bus_access->write(rmi4_data, addr, data, len); +} + +static inline ssize_t synaptics_rmi4_show_error(struct device *dev, + struct device_attribute *attr, char *buf) +{ + dev_warn(dev, "%s Attempted to read from write-only attribute %s\n", + __func__, attr->attr.name); + return -EPERM; +} + +static inline ssize_t synaptics_rmi4_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + dev_warn(dev, "%s Attempted to write to read-only attribute %s\n", + __func__, attr->attr.name); + return -EPERM; +} + +static inline int secure_memcpy(unsigned char *dest, unsigned int dest_size, + const unsigned char *src, unsigned int src_size, + unsigned int count) +{ + if (dest == NULL || src == NULL) + return -EINVAL; + + if (count > dest_size || count > src_size) + return -EINVAL; + + memcpy((void *)dest, (const void *)src, count); + + return 0; +} + +static inline void batohs(unsigned short *dest, unsigned char *src) +{ + *dest = src[1] * 0x100 + src[0]; +} + +static inline void hstoba(unsigned char *dest, unsigned short src) +{ + dest[0] = src % 0x100; + dest[1] = src / 0x100; +} + +#endif diff --git a/synaptics_dsx/synaptics_dsx_fw_update.c b/synaptics_dsx/synaptics_dsx_fw_update.c new file mode 100644 index 0000000000..52ca0ac86b --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_fw_update.c @@ -0,0 +1,5797 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define FW_IHEX_NAME "synaptics/startup_fw_update.bin" +#define FW_IMAGE_NAME "synaptics/startup_fw_update.img" + +#define ENABLE_SYS_REFLASH false +#define FORCE_UPDATE false +#define DO_LOCKDOWN false + +#define MAX_IMAGE_NAME_LEN 256 +#define MAX_FIRMWARE_ID_LEN 10 + +#define IMAGE_HEADER_VERSION_05 0x05 +#define IMAGE_HEADER_VERSION_06 0x06 +#define IMAGE_HEADER_VERSION_10 0x10 + +#define IMAGE_AREA_OFFSET 0x100 +#define LOCKDOWN_SIZE 0x50 + +#define MAX_UTILITY_PARAMS 20 + +#define V5V6_BOOTLOADER_ID_OFFSET 0 +#define V5V6_CONFIG_ID_SIZE 4 + +#define V5_PROPERTIES_OFFSET 2 +#define V5_BLOCK_SIZE_OFFSET 3 +#define V5_BLOCK_COUNT_OFFSET 5 +#define V5_BLOCK_NUMBER_OFFSET 0 +#define V5_BLOCK_DATA_OFFSET 2 + +#define V6_PROPERTIES_OFFSET 1 +#define V6_BLOCK_SIZE_OFFSET 2 +#define V6_BLOCK_COUNT_OFFSET 3 +#define V6_PROPERTIES_2_OFFSET 4 +#define V6_GUEST_CODE_BLOCK_COUNT_OFFSET 5 +#define V6_BLOCK_NUMBER_OFFSET 0 +#define V6_BLOCK_DATA_OFFSET 1 +#define V6_FLASH_COMMAND_OFFSET 2 +#define V6_FLASH_STATUS_OFFSET 3 + +#define V7_CONFIG_ID_SIZE 32 + +#define V7_FLASH_STATUS_OFFSET 0 +#define V7_PARTITION_ID_OFFSET 1 +#define V7_BLOCK_NUMBER_OFFSET 2 +#define V7_TRANSFER_LENGTH_OFFSET 3 +#define V7_COMMAND_OFFSET 4 +#define V7_PAYLOAD_OFFSET 5 + +#define V7_PARTITION_SUPPORT_BYTES 4 + +#define F35_ERROR_CODE_OFFSET 0 +#define F35_FLASH_STATUS_OFFSET 5 +#define F35_CHUNK_NUM_LSB_OFFSET 0 +#define F35_CHUNK_NUM_MSB_OFFSET 1 +#define F35_CHUNK_DATA_OFFSET 2 +#define F35_CHUNK_COMMAND_OFFSET 18 + +#define F35_CHUNK_SIZE 16 +#define F35_ERASE_ALL_WAIT_MS 5000 +#define F35_RESET_WAIT_MS 250 + +#define SLEEP_MODE_NORMAL (0x00) +#define SLEEP_MODE_SENSOR_SLEEP (0x01) +#define SLEEP_MODE_RESERVED0 (0x02) +#define SLEEP_MODE_RESERVED1 (0x03) + +#define ENABLE_WAIT_MS (1 * 1000) +#define WRITE_WAIT_MS (3 * 1000) +#define ERASE_WAIT_MS (5 * 1000) + +#define MIN_SLEEP_TIME_US 50 +#define MAX_SLEEP_TIME_US 100 + +#define INT_DISABLE_WAIT_MS 20 +#define ENTER_FLASH_PROG_WAIT_MS 20 +#define READ_CONFIG_WAIT_MS 20 + +static int fwu_do_reflash(void); + +static int fwu_recovery_check_status(void); + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static ssize_t fwu_sysfs_show_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t fwu_sysfs_store_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t fwu_sysfs_do_recovery_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_write_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_read_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_config_area_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_image_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_image_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_block_size_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_utility_parameter_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_guest_code_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_write_guest_code_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +#ifdef SYNA_TDDI +static ssize_t fwu_sysfs_write_lockdown_code_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_read_lockdown_code_show(struct device *dev, + struct device_attribute *attr, char *buf); +#endif + +#endif + +enum f34_version { + F34_V0 = 0, + F34_V1, + F34_V2, +}; + +enum bl_version { + BL_V5 = 5, + BL_V6 = 6, + BL_V7 = 7, + BL_V8 = 8, +}; + +enum flash_area { + NONE = 0, + UI_FIRMWARE, + UI_CONFIG, +}; + +enum update_mode { + NORMAL = 1, + FORCE = 2, + LOCKDOWN = 8, +}; + +enum config_area { + UI_CONFIG_AREA = 0, + PM_CONFIG_AREA, + BL_CONFIG_AREA, + DP_CONFIG_AREA, + FLASH_CONFIG_AREA, +#ifdef SYNA_TDDI + TDDI_FORCE_CONFIG_AREA, + TDDI_LCM_DATA_AREA, + TDDI_OEM_DATA_AREA, +#endif + UPP_AREA, +}; + +enum v7_status { + SUCCESS = 0x00, + DEVICE_NOT_IN_BOOTLOADER_MODE, + INVALID_PARTITION, + INVALID_COMMAND, + INVALID_BLOCK_OFFSET, + INVALID_TRANSFER, + NOT_ERASED, + FLASH_PROGRAMMING_KEY_INCORRECT, + BAD_PARTITION_TABLE, + CHECKSUM_FAILED, + FLASH_HARDWARE_FAILURE = 0x1f, +}; + +enum v7_partition_id { + BOOTLOADER_PARTITION = 0x01, + DEVICE_CONFIG_PARTITION, + FLASH_CONFIG_PARTITION, + MANUFACTURING_BLOCK_PARTITION, + GUEST_SERIALIZATION_PARTITION, + GLOBAL_PARAMETERS_PARTITION, + CORE_CODE_PARTITION, + CORE_CONFIG_PARTITION, + GUEST_CODE_PARTITION, + DISPLAY_CONFIG_PARTITION, + EXTERNAL_TOUCH_AFE_CONFIG_PARTITION, + UTILITY_PARAMETER_PARTITION, +}; + +enum v7_flash_command { + CMD_V7_IDLE = 0x00, + CMD_V7_ENTER_BL, + CMD_V7_READ, + CMD_V7_WRITE, + CMD_V7_ERASE, + CMD_V7_ERASE_AP, + CMD_V7_SENSOR_ID, +}; + +enum v5v6_flash_command { + CMD_V5V6_IDLE = 0x0, + CMD_V5V6_WRITE_FW = 0x2, + CMD_V5V6_ERASE_ALL = 0x3, + CMD_V5V6_WRITE_LOCKDOWN = 0x4, + CMD_V5V6_READ_CONFIG = 0x5, + CMD_V5V6_WRITE_CONFIG = 0x6, + CMD_V5V6_ERASE_UI_CONFIG = 0x7, + CMD_V5V6_ERASE_BL_CONFIG = 0x9, + CMD_V5V6_ERASE_DISP_CONFIG = 0xa, + CMD_V5V6_ERASE_GUEST_CODE = 0xb, + CMD_V5V6_WRITE_GUEST_CODE = 0xc, + CMD_V5V6_ERASE_CHIP = 0x0d, + CMD_V5V6_ENABLE_FLASH_PROG = 0xf, +#ifdef SYNA_TDDI + CMD_V5V6_ERASE_FORCE_CONFIG = 0x11, + CMD_V5V6_READ_FORCE_CONFIG = 0x12, + CMD_V5V6_WRITE_FORCE_CONFIG = 0x13, + CMD_V5V6_ERASE_LOCKDOWN_DATA = 0x1a, + CMD_V5V6_READ_LOCKDOWN_DATA = 0x1b, + CMD_V5V6_WRITE_LOCKDOWN_DATA = 0x1c, + CMD_V5V6_ERASE_LCM_DATA = 0x1d, + CMD_V5V6_ERASE_OEM_DATA = 0x1e, +#endif +}; + +enum flash_command { + CMD_IDLE = 0, + CMD_WRITE_FW, + CMD_WRITE_CONFIG, + CMD_WRITE_LOCKDOWN, + CMD_WRITE_GUEST_CODE, + CMD_WRITE_BOOTLOADER, + CMD_WRITE_UTILITY_PARAM, + CMD_READ_CONFIG, + CMD_ERASE_ALL, + CMD_ERASE_UI_FIRMWARE, + CMD_ERASE_UI_CONFIG, + CMD_ERASE_BL_CONFIG, + CMD_ERASE_DISP_CONFIG, + CMD_ERASE_FLASH_CONFIG, + CMD_ERASE_GUEST_CODE, + CMD_ERASE_BOOTLOADER, + CMD_ERASE_UTILITY_PARAMETER, + CMD_ENABLE_FLASH_PROG, +#ifdef SYNA_TDDI + CMD_ERASE_CHIP, + CMD_ERASE_FORCE_CONFIG, + CMD_READ_FORCE_CONFIG, + CMD_WRITE_FORCE_CONFIG, + CMD_ERASE_LOCKDOWN_DATA, + CMD_READ_LOCKDOWN_DATA, + CMD_WRITE_LOCKDOWN_DATA, + CMD_ERASE_LCM_DATA, + CMD_READ_LCM_DATA, + CMD_WRITE_LCM_DATA, + CMD_ERASE_OEM_DATA, + CMD_READ_OEM_DATA, + CMD_WRITE_OEM_DATA, +#endif +}; + +enum f35_flash_command { + CMD_F35_IDLE = 0x0, + CMD_F35_RESERVED = 0x1, + CMD_F35_WRITE_CHUNK = 0x2, + CMD_F35_ERASE_ALL = 0x3, + CMD_F35_RESET = 0x10, +}; + +enum container_id { + TOP_LEVEL_CONTAINER = 0, + UI_CONTAINER, + UI_CONFIG_CONTAINER, + BL_CONTAINER, + BL_IMAGE_CONTAINER, + BL_CONFIG_CONTAINER, + BL_LOCKDOWN_INFO_CONTAINER, + PERMANENT_CONFIG_CONTAINER, + GUEST_CODE_CONTAINER, + BL_PROTOCOL_DESCRIPTOR_CONTAINER, + UI_PROTOCOL_DESCRIPTOR_CONTAINER, + RMI_SELF_DISCOVERY_CONTAINER, + RMI_PAGE_CONTENT_CONTAINER, + GENERAL_INFORMATION_CONTAINER, + DEVICE_CONFIG_CONTAINER, + FLASH_CONFIG_CONTAINER, + GUEST_SERIALIZATION_CONTAINER, + GLOBAL_PARAMETERS_CONTAINER, + CORE_CODE_CONTAINER, + CORE_CONFIG_CONTAINER, + DISPLAY_CONFIG_CONTAINER, + EXTERNAL_TOUCH_AFE_CONFIG_CONTAINER, + UTILITY_CONTAINER, + UTILITY_PARAMETER_CONTAINER, +}; + +enum utility_parameter_id { + UNUSED = 0, + FORCE_PARAMETER, + ANTI_BENDING_PARAMETER, +}; + +struct pdt_properties { + union { + struct { + unsigned char reserved_1:6; + unsigned char has_bsr:1; + unsigned char reserved_2:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct partition_table { + unsigned char partition_id:5; + unsigned char byte_0_reserved:3; + unsigned char byte_1_reserved; + unsigned char partition_length_7_0; + unsigned char partition_length_15_8; + unsigned char start_physical_address_7_0; + unsigned char start_physical_address_15_8; + unsigned char partition_properties_7_0; + unsigned char partition_properties_15_8; +} __packed; + +struct f01_device_control { + union { + struct { + unsigned char sleep_mode:2; + unsigned char nosleep:1; + unsigned char reserved:2; + unsigned char charger_connected:1; + unsigned char report_rate:1; + unsigned char configured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v7_query_0 { + union { + struct { + unsigned char subpacket_1_size:3; + unsigned char has_config_id:1; + unsigned char f34_query0_b4:1; + unsigned char has_thqa:1; + unsigned char f34_query0_b6__7:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v7_query_1_7 { + union { + struct { + /* query 1 */ + unsigned char bl_minor_revision; + unsigned char bl_major_revision; + + /* query 2 */ + unsigned char bl_fw_id_7_0; + unsigned char bl_fw_id_15_8; + unsigned char bl_fw_id_23_16; + unsigned char bl_fw_id_31_24; + + /* query 3 */ + unsigned char minimum_write_size; + unsigned char block_size_7_0; + unsigned char block_size_15_8; + unsigned char flash_page_size_7_0; + unsigned char flash_page_size_15_8; + + /* query 4 */ + unsigned char adjustable_partition_area_size_7_0; + unsigned char adjustable_partition_area_size_15_8; + + /* query 5 */ + unsigned char flash_config_length_7_0; + unsigned char flash_config_length_15_8; + + /* query 6 */ + unsigned char payload_length_7_0; + unsigned char payload_length_15_8; + + /* query 7 */ + unsigned char f34_query7_b0:1; + unsigned char has_bootloader:1; + unsigned char has_device_config:1; + unsigned char has_flash_config:1; + unsigned char has_manufacturing_block:1; + unsigned char has_guest_serialization:1; + unsigned char has_global_parameters:1; + unsigned char has_core_code:1; + unsigned char has_core_config:1; + unsigned char has_guest_code:1; + unsigned char has_display_config:1; + unsigned char f34_query7_b11__15:5; + unsigned char f34_query7_b16__23; + unsigned char f34_query7_b24__31; + } __packed; + unsigned char data[21]; + }; +}; + +struct f34_v7_data0 { + union { + struct { + unsigned char operation_status:5; + unsigned char device_cfg_status:2; + unsigned char bl_mode:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v7_data_1_5 { + union { + struct { + unsigned char partition_id:5; + unsigned char f34_data1_b5__7:3; + unsigned char block_offset_7_0; + unsigned char block_offset_15_8; + unsigned char transfer_length_7_0; + unsigned char transfer_length_15_8; + unsigned char command; + unsigned char payload_0; + unsigned char payload_1; + } __packed; + unsigned char data[8]; + }; +}; + +struct f34_v5v6_flash_properties { + union { + struct { + unsigned char reg_map:1; + unsigned char unlocked:1; + unsigned char has_config_id:1; + unsigned char has_pm_config:1; + unsigned char has_bl_config:1; + unsigned char has_disp_config:1; + unsigned char has_ctrl1:1; + unsigned char has_query4:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v5v6_flash_properties_2 { + union { + struct { + unsigned char has_guest_code:1; + unsigned char f34_query4_b1:1; + unsigned char has_gesture_config:1; + unsigned char has_force_config:1; + unsigned char has_lockdown_data:1; + unsigned char has_lcm_data:1; + unsigned char has_oem_data:1; + unsigned char f34_query4_b7:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct register_offset { + unsigned char properties; + unsigned char properties_2; + unsigned char block_size; + unsigned char block_count; + unsigned char gc_block_count; + unsigned char flash_status; + unsigned char partition_id; + unsigned char block_number; + unsigned char transfer_length; + unsigned char flash_cmd; + unsigned char payload; +}; + +struct block_count { + unsigned short ui_firmware; + unsigned short ui_config; + unsigned short dp_config; + unsigned short pm_config; + unsigned short fl_config; + unsigned short bl_image; + unsigned short bl_config; + unsigned short utility_param; + unsigned short lockdown; + unsigned short guest_code; +#ifdef SYNA_TDDI + unsigned short tddi_force_config; + unsigned short tddi_lockdown_data; + unsigned short tddi_lcm_data; + unsigned short tddi_oem_data; +#endif + unsigned short total_count; +}; + +struct physical_address { + unsigned short ui_firmware; + unsigned short ui_config; + unsigned short dp_config; + unsigned short pm_config; + unsigned short fl_config; + unsigned short bl_image; + unsigned short bl_config; + unsigned short utility_param; + unsigned short lockdown; + unsigned short guest_code; +}; + +struct container_descriptor { + unsigned char content_checksum[4]; + unsigned char container_id[2]; + unsigned char minor_version; + unsigned char major_version; + unsigned char reserved_08; + unsigned char reserved_09; + unsigned char reserved_0a; + unsigned char reserved_0b; + unsigned char container_option_flags[4]; + unsigned char content_options_length[4]; + unsigned char content_options_address[4]; + unsigned char content_length[4]; + unsigned char content_address[4]; +}; + +struct image_header_10 { + unsigned char checksum[4]; + unsigned char reserved_04; + unsigned char reserved_05; + unsigned char minor_header_version; + unsigned char major_header_version; + unsigned char reserved_08; + unsigned char reserved_09; + unsigned char reserved_0a; + unsigned char reserved_0b; + unsigned char top_level_container_start_addr[4]; +}; + +struct image_header_05_06 { + /* 0x00 - 0x0f */ + unsigned char checksum[4]; + unsigned char reserved_04; + unsigned char reserved_05; + unsigned char options_firmware_id:1; + unsigned char options_bootloader:1; + unsigned char options_guest_code:1; + unsigned char options_tddi:1; + unsigned char options_reserved:4; + unsigned char header_version; + unsigned char firmware_size[4]; + unsigned char config_size[4]; + /* 0x10 - 0x1f */ + unsigned char product_id[PRODUCT_ID_SIZE]; + unsigned char package_id[2]; + unsigned char package_id_revision[2]; + unsigned char product_info[PRODUCT_INFO_SIZE]; + /* 0x20 - 0x2f */ + unsigned char bootloader_addr[4]; + unsigned char bootloader_size[4]; + unsigned char ui_addr[4]; + unsigned char ui_size[4]; + /* 0x30 - 0x3f */ + unsigned char ds_id[16]; + /* 0x40 - 0x4f */ + union { + struct { + unsigned char cstmr_product_id[PRODUCT_ID_SIZE]; + unsigned char reserved_4a_4f[6]; + }; + struct { + unsigned char dsp_cfg_addr[4]; + unsigned char dsp_cfg_size[4]; + unsigned char reserved_48_4f[8]; + }; + }; + /* 0x50 - 0x53 */ + unsigned char firmware_id[4]; +}; + +struct block_data { + unsigned int size; + const unsigned char *data; +}; + +struct image_metadata { + bool contains_firmware_id; + bool contains_bootloader; + bool contains_guest_code; + bool contains_disp_config; + bool contains_perm_config; + bool contains_flash_config; + bool contains_utility_param; + unsigned int firmware_id; + unsigned int checksum; + unsigned int bootloader_size; + unsigned int disp_config_offset; + unsigned char bl_version; + unsigned char product_id[PRODUCT_ID_SIZE + 1]; + unsigned char cstmr_product_id[PRODUCT_ID_SIZE + 1]; + unsigned char utility_param_id[MAX_UTILITY_PARAMS]; + struct block_data bootloader; + struct block_data utility; + struct block_data ui_firmware; + struct block_data ui_config; + struct block_data dp_config; + struct block_data pm_config; + struct block_data fl_config; + struct block_data bl_image; + struct block_data bl_config; + struct block_data utility_param[MAX_UTILITY_PARAMS]; + struct block_data lockdown; + struct block_data guest_code; + struct block_count blkcount; + struct physical_address phyaddr; +}; + +struct synaptics_rmi4_fwu_handle { + enum bl_version bl_version; + bool initialized; + bool in_bl_mode; + bool in_ub_mode; + bool bl_mode_device; + bool force_update; + bool do_lockdown; + bool has_guest_code; +#ifdef SYNA_TDDI + bool has_force_config; + bool has_lockdown_data; + bool has_lcm_data; + bool has_oem_data; +#endif + bool has_utility_param; + bool new_partition_table; + bool incompatible_partition_tables; + bool write_bootloader; + unsigned int data_pos; + unsigned char *ext_data_source; + unsigned char *read_config_buf; + unsigned char intr_mask; + unsigned char command; + unsigned char bootloader_id[2]; + unsigned char config_id[32]; + unsigned char flash_status; + unsigned char partitions; +#ifdef F51_DISCRETE_FORCE + unsigned char *cal_data; + unsigned short cal_data_off; + unsigned short cal_data_size; + unsigned short cal_data_buf_size; + unsigned short cal_packet_data_size; +#endif + unsigned short block_size; + unsigned short config_size; + unsigned short config_area; + unsigned short config_block_count; + unsigned short flash_config_length; + unsigned short payload_length; + unsigned short partition_table_bytes; + unsigned short read_config_buf_size; + const unsigned char *config_data; + const unsigned char *image; + unsigned char *image_name; + unsigned int image_size; + struct image_metadata img; + struct register_offset off; + struct block_count blkcount; + struct physical_address phyaddr; + struct f34_v5v6_flash_properties flash_properties; + struct synaptics_rmi4_fn_desc f34_fd; + struct synaptics_rmi4_fn_desc f35_fd; + struct synaptics_rmi4_data *rmi4_data; + struct workqueue_struct *fwu_workqueue; + struct work_struct fwu_work; +}; + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static struct bin_attribute dev_attr_data = { + .attr = { + .name = "data", + .mode = 0664, + }, + .size = 0, + .read = fwu_sysfs_show_image, + .write = fwu_sysfs_store_image, +}; +#endif + +static struct device_attribute attrs[] = { +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS + __ATTR(dorecovery, 0220, + synaptics_rmi4_show_error, + fwu_sysfs_do_recovery_store), + __ATTR(doreflash, 0220, + synaptics_rmi4_show_error, + fwu_sysfs_do_reflash_store), + __ATTR(writeconfig, 0220, + synaptics_rmi4_show_error, + fwu_sysfs_write_config_store), + __ATTR(readconfig, 0220, + synaptics_rmi4_show_error, + fwu_sysfs_read_config_store), + __ATTR(configarea, 0220, + synaptics_rmi4_show_error, + fwu_sysfs_config_area_store), + __ATTR(imagename, 0220, + synaptics_rmi4_show_error, + fwu_sysfs_image_name_store), + __ATTR(imagesize, 0220, + synaptics_rmi4_show_error, + fwu_sysfs_image_size_store), + __ATTR(blocksize, 0444, + fwu_sysfs_block_size_show, + synaptics_rmi4_store_error), + __ATTR(fwblockcount, 0444, + fwu_sysfs_firmware_block_count_show, + synaptics_rmi4_store_error), + __ATTR(configblockcount, 0444, + fwu_sysfs_configuration_block_count_show, + synaptics_rmi4_store_error), + __ATTR(dispconfigblockcount, 0444, + fwu_sysfs_disp_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(permconfigblockcount, 0444, + fwu_sysfs_perm_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(blconfigblockcount, 0444, + fwu_sysfs_bl_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(uppblockcount, 0444, + fwu_sysfs_utility_parameter_block_count_show, + synaptics_rmi4_store_error), + __ATTR(guestcodeblockcount, 0444, + fwu_sysfs_guest_code_block_count_show, + synaptics_rmi4_store_error), + __ATTR(writeguestcode, 0220, + synaptics_rmi4_show_error, + fwu_sysfs_write_guest_code_store), +#ifdef SYNA_TDDI + __ATTR(lockdowncode, 0664, + fwu_sysfs_read_lockdown_code_show, + fwu_sysfs_write_lockdown_code_store), +#endif +#endif +}; + +static struct synaptics_rmi4_fwu_handle *fwu; + +DECLARE_COMPLETION(fwu_remove_complete); + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +DEFINE_MUTEX(fwu_sysfs_mutex); +#endif + +static void calculate_checksum(unsigned short *data, unsigned long len, + unsigned long *result) +{ + unsigned long temp; + unsigned long sum1 = 0xffff; + unsigned long sum2 = 0xffff; + + *result = 0xffffffff; + + while (len--) { + temp = *data; + sum1 += temp; + sum2 += sum1; + sum1 = (sum1 & 0xffff) + (sum1 >> 16); + sum2 = (sum2 & 0xffff) + (sum2 >> 16); + data++; + } + + *result = sum2 << 16 | sum1; +} + +static void convert_to_little_endian(unsigned char *dest, unsigned long src) +{ + dest[0] = (unsigned char)(src & 0xff); + dest[1] = (unsigned char)((src >> 8) & 0xff); + dest[2] = (unsigned char)((src >> 16) & 0xff); + dest[3] = (unsigned char)((src >> 24) & 0xff); +} + +static unsigned int le_to_uint(const unsigned char *ptr) +{ + return (unsigned int)ptr[0] + + (unsigned int)ptr[1] * 0x100 + + (unsigned int)ptr[2] * 0x10000 + + (unsigned int)ptr[3] * 0x1000000; +} + +#ifdef F51_DISCRETE_FORCE +static int fwu_f51_force_data_init(void) +{ + int retval; + unsigned char query_count; + unsigned char packet_info; + unsigned char offset[2]; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f51_query_base_addr + 7, + offset, + sizeof(offset)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read force data offset\n", + __func__); + return retval; + } + + fwu->cal_data_off = offset[0] | offset[1] << 8; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f51_query_base_addr, + &query_count, + sizeof(query_count)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read number of F51 query registers\n", + __func__); + return retval; + } + + if (query_count >= 10) { + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f51_query_base_addr + 9, + &packet_info, + sizeof(packet_info)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F51 packet register info\n", + __func__); + return retval; + } + + if (packet_info & MASK_1BIT) { + fwu->cal_packet_data_size = packet_info >> 1; + fwu->cal_packet_data_size *= 2; + } else { + fwu->cal_packet_data_size = 0; + } + } else { + fwu->cal_packet_data_size = 0; + } + + fwu->cal_data_size = CAL_DATA_SIZE + fwu->cal_packet_data_size; + if (fwu->cal_data_size > fwu->cal_data_buf_size) { + kfree(fwu->cal_data); + fwu->cal_data_buf_size = fwu->cal_data_size; + fwu->cal_data = kmalloc(fwu->cal_data_buf_size, GFP_KERNEL); + if (!fwu->cal_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for fwu->cal_data\n", + __func__); + fwu->cal_data_buf_size = 0; + return -ENOMEM; + } + } + + return 0; +} +#endif + +static int fwu_allocate_read_config_buf(unsigned int count) +{ + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (count > fwu->read_config_buf_size) { + kfree(fwu->read_config_buf); + fwu->read_config_buf = kzalloc(count, GFP_KERNEL); + if (!fwu->read_config_buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for fwu->read_config_buf\n", + __func__); + fwu->read_config_buf_size = 0; + return -ENOMEM; + } + fwu->read_config_buf_size = count; + } + + return 0; +} + +static void fwu_compare_partition_tables(void) +{ + fwu->incompatible_partition_tables = false; + + if (fwu->phyaddr.bl_image != fwu->img.phyaddr.bl_image) + fwu->incompatible_partition_tables = true; + else if (fwu->phyaddr.lockdown != fwu->img.phyaddr.lockdown) + fwu->incompatible_partition_tables = true; + else if (fwu->phyaddr.bl_config != fwu->img.phyaddr.bl_config) + fwu->incompatible_partition_tables = true; + else if (fwu->phyaddr.utility_param != fwu->img.phyaddr.utility_param) + fwu->incompatible_partition_tables = true; + + if (fwu->bl_version == BL_V7) { + if (fwu->phyaddr.fl_config != fwu->img.phyaddr.fl_config) + fwu->incompatible_partition_tables = true; + } + + fwu->new_partition_table = false; + + if (fwu->phyaddr.ui_firmware != fwu->img.phyaddr.ui_firmware) + fwu->new_partition_table = true; + else if (fwu->phyaddr.ui_config != fwu->img.phyaddr.ui_config) + fwu->new_partition_table = true; + + if (fwu->flash_properties.has_disp_config) { + if (fwu->phyaddr.dp_config != fwu->img.phyaddr.dp_config) + fwu->new_partition_table = true; + } + + if (fwu->has_guest_code) { + if (fwu->phyaddr.guest_code != fwu->img.phyaddr.guest_code) + fwu->new_partition_table = true; + } +} + +static void fwu_parse_partition_table(const unsigned char *partition_table, + struct block_count *blkcount, struct physical_address *phyaddr) +{ + unsigned char ii; + unsigned char index; + unsigned char offset; + unsigned short partition_length; + unsigned short physical_address; + struct partition_table *ptable; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + for (ii = 0; ii < fwu->partitions; ii++) { + index = ii * 8 + 2; + ptable = (struct partition_table *)&partition_table[index]; + partition_length = ptable->partition_length_15_8 << 8 | + ptable->partition_length_7_0; + physical_address = ptable->start_physical_address_15_8 << 8 | + ptable->start_physical_address_7_0; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Partition entry %d:\n", + __func__, ii); + for (offset = 0; offset < 8; offset++) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: 0x%02x\n", + __func__, + partition_table[index + offset]); + } + switch (ptable->partition_id) { + case CORE_CODE_PARTITION: + blkcount->ui_firmware = partition_length; + phyaddr->ui_firmware = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Core code block count: %d\n", + __func__, blkcount->ui_firmware); + blkcount->total_count += partition_length; + break; + case CORE_CONFIG_PARTITION: + blkcount->ui_config = partition_length; + phyaddr->ui_config = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Core config block count: %d\n", + __func__, blkcount->ui_config); + blkcount->total_count += partition_length; + break; + case BOOTLOADER_PARTITION: + blkcount->bl_image = partition_length; + phyaddr->bl_image = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Bootloader block count: %d\n", + __func__, blkcount->bl_image); + blkcount->total_count += partition_length; + break; + case UTILITY_PARAMETER_PARTITION: + blkcount->utility_param = partition_length; + phyaddr->utility_param = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Utility parameter block count: %d\n", + __func__, blkcount->utility_param); + blkcount->total_count += partition_length; + break; + case DISPLAY_CONFIG_PARTITION: + blkcount->dp_config = partition_length; + phyaddr->dp_config = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Display config block count: %d\n", + __func__, blkcount->dp_config); + blkcount->total_count += partition_length; + break; + case FLASH_CONFIG_PARTITION: + blkcount->fl_config = partition_length; + phyaddr->fl_config = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Flash config block count: %d\n", + __func__, blkcount->fl_config); + blkcount->total_count += partition_length; + break; + case GUEST_CODE_PARTITION: + blkcount->guest_code = partition_length; + phyaddr->guest_code = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Guest code block count: %d\n", + __func__, blkcount->guest_code); + blkcount->total_count += partition_length; + break; + case GUEST_SERIALIZATION_PARTITION: + blkcount->pm_config = partition_length; + phyaddr->pm_config = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Guest serialization block count: %d\n", + __func__, blkcount->pm_config); + blkcount->total_count += partition_length; + break; + case GLOBAL_PARAMETERS_PARTITION: + blkcount->bl_config = partition_length; + phyaddr->bl_config = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Global parameters block count: %d\n", + __func__, blkcount->bl_config); + blkcount->total_count += partition_length; + break; + case DEVICE_CONFIG_PARTITION: + blkcount->lockdown = partition_length; + phyaddr->lockdown = physical_address; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Device config block count: %d\n", + __func__, blkcount->lockdown); + blkcount->total_count += partition_length; + break; + }; + } +} + +static void fwu_parse_image_header_10_utility(const unsigned char *image) +{ + unsigned char ii; + unsigned char num_of_containers; + unsigned int addr; + unsigned int container_id; + unsigned int length; + const unsigned char *content; + struct container_descriptor *descriptor; + + num_of_containers = fwu->img.utility.size / 4; + + for (ii = 0; ii < num_of_containers; ii++) { + if (ii >= MAX_UTILITY_PARAMS) + continue; + addr = le_to_uint(fwu->img.utility.data + (ii * 4)); + descriptor = (struct container_descriptor *)(image + addr); + container_id = descriptor->container_id[0] | + descriptor->container_id[1] << 8; + content = image + le_to_uint(descriptor->content_address); + length = le_to_uint(descriptor->content_length); + switch (container_id) { + case UTILITY_PARAMETER_CONTAINER: + fwu->img.utility_param[ii].data = content; + fwu->img.utility_param[ii].size = length; + fwu->img.utility_param_id[ii] = content[0]; + break; + default: + break; + }; + } +} + +static void fwu_parse_image_header_10_bootloader(const unsigned char *image) +{ + unsigned char ii; + unsigned char num_of_containers; + unsigned int addr; + unsigned int container_id; + unsigned int length; + const unsigned char *content; + struct container_descriptor *descriptor; + + num_of_containers = (fwu->img.bootloader.size - 4) / 4; + + for (ii = 1; ii <= num_of_containers; ii++) { + addr = le_to_uint(fwu->img.bootloader.data + (ii * 4)); + descriptor = (struct container_descriptor *)(image + addr); + container_id = descriptor->container_id[0] | + descriptor->container_id[1] << 8; + content = image + le_to_uint(descriptor->content_address); + length = le_to_uint(descriptor->content_length); + switch (container_id) { + case BL_IMAGE_CONTAINER: + fwu->img.bl_image.data = content; + fwu->img.bl_image.size = length; + break; + case BL_CONFIG_CONTAINER: + case GLOBAL_PARAMETERS_CONTAINER: + fwu->img.bl_config.data = content; + fwu->img.bl_config.size = length; + break; + case BL_LOCKDOWN_INFO_CONTAINER: + case DEVICE_CONFIG_CONTAINER: + fwu->img.lockdown.data = content; + fwu->img.lockdown.size = length; + break; + default: + break; + }; + } +} + +static void fwu_parse_image_header_10(void) +{ + unsigned char ii; + unsigned char num_of_containers; + unsigned int addr; + unsigned int offset; + unsigned int container_id; + unsigned int length; + const unsigned char *image; + const unsigned char *content; + struct container_descriptor *descriptor; + struct image_header_10 *header; + + image = fwu->image; + header = (struct image_header_10 *)image; + + fwu->img.checksum = le_to_uint(header->checksum); + + /* address of top level container */ + offset = le_to_uint(header->top_level_container_start_addr); + descriptor = (struct container_descriptor *)(image + offset); + + /* address of top level container content */ + offset = le_to_uint(descriptor->content_address); + num_of_containers = le_to_uint(descriptor->content_length) / 4; + + for (ii = 0; ii < num_of_containers; ii++) { + addr = le_to_uint(image + offset); + offset += 4; + descriptor = (struct container_descriptor *)(image + addr); + container_id = descriptor->container_id[0] | + descriptor->container_id[1] << 8; + content = image + le_to_uint(descriptor->content_address); + length = le_to_uint(descriptor->content_length); + switch (container_id) { + case UI_CONTAINER: + case CORE_CODE_CONTAINER: + fwu->img.ui_firmware.data = content; + fwu->img.ui_firmware.size = length; + break; + case UI_CONFIG_CONTAINER: + case CORE_CONFIG_CONTAINER: + fwu->img.ui_config.data = content; + fwu->img.ui_config.size = length; + break; + case BL_CONTAINER: + fwu->img.bl_version = *content; + fwu->img.bootloader.data = content; + fwu->img.bootloader.size = length; + fwu_parse_image_header_10_bootloader(image); + break; + case UTILITY_CONTAINER: + fwu->img.utility.data = content; + fwu->img.utility.size = length; + fwu_parse_image_header_10_utility(image); + break; + case GUEST_CODE_CONTAINER: + fwu->img.contains_guest_code = true; + fwu->img.guest_code.data = content; + fwu->img.guest_code.size = length; + break; + case DISPLAY_CONFIG_CONTAINER: + fwu->img.contains_disp_config = true; + fwu->img.dp_config.data = content; + fwu->img.dp_config.size = length; + break; + case PERMANENT_CONFIG_CONTAINER: + case GUEST_SERIALIZATION_CONTAINER: + fwu->img.contains_perm_config = true; + fwu->img.pm_config.data = content; + fwu->img.pm_config.size = length; + break; + case FLASH_CONFIG_CONTAINER: + fwu->img.contains_flash_config = true; + fwu->img.fl_config.data = content; + fwu->img.fl_config.size = length; + break; + case GENERAL_INFORMATION_CONTAINER: + fwu->img.contains_firmware_id = true; + fwu->img.firmware_id = le_to_uint(content + 4); + break; + default: + break; + } + } +} + +static void fwu_parse_image_header_05_06(void) +{ + int retval; + const unsigned char *image; + struct image_header_05_06 *header; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + image = fwu->image; + header = (struct image_header_05_06 *)image; + + fwu->img.checksum = le_to_uint(header->checksum); + + fwu->img.bl_version = header->header_version; + + fwu->img.contains_bootloader = header->options_bootloader; + if (fwu->img.contains_bootloader) + fwu->img.bootloader_size = le_to_uint(header->bootloader_size); + + fwu->img.ui_firmware.size = le_to_uint(header->firmware_size); + if (fwu->img.ui_firmware.size) { + fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET; + if (fwu->img.contains_bootloader) + fwu->img.ui_firmware.data += fwu->img.bootloader_size; + } + + if ((fwu->img.bl_version == BL_V6) && header->options_tddi) + fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET; + + fwu->img.ui_config.size = le_to_uint(header->config_size); + if (fwu->img.ui_config.size) { + fwu->img.ui_config.data = fwu->img.ui_firmware.data + + fwu->img.ui_firmware.size; + } + + if (fwu->img.contains_bootloader || header->options_tddi) + fwu->img.contains_disp_config = true; + else + fwu->img.contains_disp_config = false; + + if (fwu->img.contains_disp_config) { + fwu->img.disp_config_offset = le_to_uint(header->dsp_cfg_addr); + fwu->img.dp_config.size = le_to_uint(header->dsp_cfg_size); + fwu->img.dp_config.data = image + fwu->img.disp_config_offset; + } else { + retval = secure_memcpy(fwu->img.cstmr_product_id, + sizeof(fwu->img.cstmr_product_id), + header->cstmr_product_id, + sizeof(header->cstmr_product_id), + PRODUCT_ID_SIZE); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy custom product ID string\n", + __func__); + } + fwu->img.cstmr_product_id[PRODUCT_ID_SIZE] = 0; + } + + fwu->img.contains_firmware_id = header->options_firmware_id; + if (fwu->img.contains_firmware_id) + fwu->img.firmware_id = le_to_uint(header->firmware_id); + + retval = secure_memcpy(fwu->img.product_id, + sizeof(fwu->img.product_id), + header->product_id, + sizeof(header->product_id), + PRODUCT_ID_SIZE); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy product ID string\n", + __func__); + } + fwu->img.product_id[PRODUCT_ID_SIZE] = 0; + + fwu->img.lockdown.size = LOCKDOWN_SIZE; + fwu->img.lockdown.data = image + IMAGE_AREA_OFFSET - LOCKDOWN_SIZE; +} + +static int fwu_parse_image_info(void) +{ + struct image_header_10 *header; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + header = (struct image_header_10 *)fwu->image; + + memset(&fwu->img, 0x00, sizeof(fwu->img)); + + switch (header->major_header_version) { + case IMAGE_HEADER_VERSION_10: + fwu_parse_image_header_10(); + break; + case IMAGE_HEADER_VERSION_05: + case IMAGE_HEADER_VERSION_06: + fwu_parse_image_header_05_06(); + break; + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Unsupported image file format (0x%02x)\n", + __func__, header->major_header_version); + return -EINVAL; + } + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) { + if (!fwu->img.contains_flash_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: No flash config found in firmware image\n", + __func__); + return -EINVAL; + } + + fwu_parse_partition_table(fwu->img.fl_config.data, + &fwu->img.blkcount, &fwu->img.phyaddr); + + if (fwu->img.blkcount.utility_param) + fwu->img.contains_utility_param = true; + + fwu_compare_partition_tables(); + } else { + fwu->new_partition_table = false; + fwu->incompatible_partition_tables = false; + } + + return 0; +} + +static int fwu_read_flash_status(void) +{ + int retval; + unsigned char status; + unsigned char command; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.flash_status, + &status, + sizeof(status)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash status\n", + __func__); + return retval; + } + + fwu->in_bl_mode = status >> 7; + + if (fwu->bl_version == BL_V5) + fwu->flash_status = (status >> 4) & MASK_3BIT; + else if (fwu->bl_version == BL_V6) + fwu->flash_status = status & MASK_3BIT; + else if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + fwu->flash_status = status & MASK_5BIT; + + if (fwu->write_bootloader) + fwu->flash_status = 0x00; + + if (fwu->flash_status != 0x00) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Flash status = %d, command = 0x%02x\n", + __func__, fwu->flash_status, fwu->command); + } + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) { + if (fwu->flash_status == 0x08) + fwu->flash_status = 0x00; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash command\n", + __func__); + return retval; + } + + if (fwu->bl_version == BL_V5) + fwu->command = command & MASK_4BIT; + else if (fwu->bl_version == BL_V6) + fwu->command = command & MASK_6BIT; + else if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + fwu->command = command; + + if (fwu->write_bootloader) + fwu->command = 0x00; + + return 0; +} + +static int fwu_wait_for_idle(int timeout_ms, bool poll) +{ + int count = 0; + int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + do { + usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US); + + count++; + if (poll || (count == timeout_count)) + fwu_read_flash_status(); + + if ((fwu->command == CMD_IDLE) && (fwu->flash_status == 0x00)) + return 0; + } while (count < timeout_count); + + dev_err(rmi4_data->pdev->dev.parent, + "%s: Timed out waiting for idle status\n", + __func__); + + return -ETIMEDOUT; +} + +static int fwu_write_f34_v7_command_single_transaction(unsigned char cmd) +{ + int retval; + unsigned char data_base; + struct f34_v7_data_1_5 data_1_5; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + memset(data_1_5.data, 0x00, sizeof(data_1_5.data)); + + switch (cmd) { + case CMD_ERASE_ALL: + data_1_5.partition_id = CORE_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE_AP; + break; + case CMD_ERASE_UI_FIRMWARE: + data_1_5.partition_id = CORE_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_BL_CONFIG: + data_1_5.partition_id = GLOBAL_PARAMETERS_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_UI_CONFIG: + data_1_5.partition_id = CORE_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_DISP_CONFIG: + data_1_5.partition_id = DISPLAY_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_FLASH_CONFIG: + data_1_5.partition_id = FLASH_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_GUEST_CODE: + data_1_5.partition_id = GUEST_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_BOOTLOADER: + data_1_5.partition_id = BOOTLOADER_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_UTILITY_PARAMETER: + data_1_5.partition_id = UTILITY_PARAMETER_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ENABLE_FLASH_PROG: + data_1_5.partition_id = BOOTLOADER_PARTITION; + data_1_5.command = CMD_V7_ENTER_BL; + break; + }; + + data_1_5.payload_0 = fwu->bootloader_id[0]; + data_1_5.payload_1 = fwu->bootloader_id[1]; + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.partition_id, + data_1_5.data, + sizeof(data_1_5.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write single transaction command\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_v7_command(unsigned char cmd) +{ + int retval; + unsigned char data_base; + unsigned char command; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_WRITE_FW: + case CMD_WRITE_CONFIG: + case CMD_WRITE_LOCKDOWN: + case CMD_WRITE_GUEST_CODE: + case CMD_WRITE_BOOTLOADER: + case CMD_WRITE_UTILITY_PARAM: + command = CMD_V7_WRITE; + break; + case CMD_READ_CONFIG: + command = CMD_V7_READ; + break; + case CMD_ERASE_ALL: + command = CMD_V7_ERASE_AP; + break; + case CMD_ERASE_UI_FIRMWARE: + case CMD_ERASE_BL_CONFIG: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_FLASH_CONFIG: + case CMD_ERASE_GUEST_CODE: + case CMD_ERASE_BOOTLOADER: + case CMD_ERASE_UTILITY_PARAMETER: + command = CMD_V7_ERASE; + break; + case CMD_ENABLE_FLASH_PROG: + command = CMD_V7_ENTER_BL; + break; + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + }; + + fwu->command = command; + + switch (cmd) { + case CMD_ERASE_ALL: + case CMD_ERASE_UI_FIRMWARE: + case CMD_ERASE_BL_CONFIG: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_FLASH_CONFIG: + case CMD_ERASE_GUEST_CODE: + case CMD_ERASE_BOOTLOADER: + case CMD_ERASE_UTILITY_PARAMETER: + case CMD_ENABLE_FLASH_PROG: + retval = fwu_write_f34_v7_command_single_transaction(cmd); + if (retval < 0) + return retval; + else + return 0; + default: + break; + }; + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write flash command\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_v5v6_command(unsigned char cmd) +{ + int retval; + unsigned char data_base; + unsigned char command; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_IDLE: + command = CMD_V5V6_IDLE; + break; + case CMD_WRITE_FW: + command = CMD_V5V6_WRITE_FW; + break; + case CMD_WRITE_CONFIG: + command = CMD_V5V6_WRITE_CONFIG; + break; + case CMD_WRITE_LOCKDOWN: + command = CMD_V5V6_WRITE_LOCKDOWN; + break; + case CMD_WRITE_GUEST_CODE: + command = CMD_V5V6_WRITE_GUEST_CODE; + break; + case CMD_READ_CONFIG: + command = CMD_V5V6_READ_CONFIG; + break; + case CMD_ERASE_ALL: + command = CMD_V5V6_ERASE_ALL; + break; + case CMD_ERASE_UI_CONFIG: + command = CMD_V5V6_ERASE_UI_CONFIG; + break; + case CMD_ERASE_DISP_CONFIG: + command = CMD_V5V6_ERASE_DISP_CONFIG; + break; + case CMD_ERASE_GUEST_CODE: + command = CMD_V5V6_ERASE_GUEST_CODE; + break; + case CMD_ENABLE_FLASH_PROG: + command = CMD_V5V6_ENABLE_FLASH_PROG; + break; +#ifdef SYNA_TDDI + case CMD_ERASE_CHIP: + command = CMD_V5V6_ERASE_CHIP; + break; + case CMD_ERASE_FORCE_CONFIG: + command = CMD_V5V6_ERASE_FORCE_CONFIG; + break; + case CMD_READ_FORCE_CONFIG: + command = CMD_V5V6_READ_FORCE_CONFIG; + break; + case CMD_WRITE_FORCE_CONFIG: + command = CMD_V5V6_WRITE_CONFIG; + break; + case CMD_ERASE_LOCKDOWN_DATA: + command = CMD_V5V6_ERASE_LOCKDOWN_DATA; + break; + case CMD_READ_LOCKDOWN_DATA: + command = CMD_V5V6_READ_LOCKDOWN_DATA; + break; + case CMD_WRITE_LOCKDOWN_DATA: + command = CMD_V5V6_WRITE_LOCKDOWN_DATA; + break; + case CMD_ERASE_LCM_DATA: + command = CMD_V5V6_ERASE_LCM_DATA; + break; + case CMD_ERASE_OEM_DATA: + command = CMD_V5V6_ERASE_OEM_DATA; + break; +#endif + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + } + + switch (cmd) { + case CMD_ERASE_ALL: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_GUEST_CODE: +#ifdef SYNA_TDDI + case CMD_ERASE_CHIP: + case CMD_ERASE_FORCE_CONFIG: + case CMD_ERASE_LOCKDOWN_DATA: + case CMD_ERASE_LCM_DATA: + case CMD_ERASE_OEM_DATA: +#endif + case CMD_ENABLE_FLASH_PROG: + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.payload, + fwu->bootloader_id, + sizeof(fwu->bootloader_id)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write bootloader ID\n", + __func__); + return retval; + } + break; + default: + break; + }; + + fwu->command = command; + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write command 0x%02x\n", + __func__, command); + return retval; + } + + return 0; +} + +static int fwu_write_f34_command(unsigned char cmd) +{ + int retval; + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + retval = fwu_write_f34_v7_command(cmd); + else + retval = fwu_write_f34_v5v6_command(cmd); + + return retval; +} + +static int fwu_write_f34_v7_partition_id(unsigned char cmd) +{ + int retval; + unsigned char data_base; + unsigned char partition; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_WRITE_FW: + partition = CORE_CODE_PARTITION; + break; + case CMD_WRITE_CONFIG: + case CMD_READ_CONFIG: + if (fwu->config_area == UI_CONFIG_AREA) + partition = CORE_CONFIG_PARTITION; + else if (fwu->config_area == DP_CONFIG_AREA) + partition = DISPLAY_CONFIG_PARTITION; + else if (fwu->config_area == PM_CONFIG_AREA) + partition = GUEST_SERIALIZATION_PARTITION; + else if (fwu->config_area == BL_CONFIG_AREA) + partition = GLOBAL_PARAMETERS_PARTITION; + else if (fwu->config_area == FLASH_CONFIG_AREA) + partition = FLASH_CONFIG_PARTITION; + else if (fwu->config_area == UPP_AREA) + partition = UTILITY_PARAMETER_PARTITION; + break; + case CMD_WRITE_LOCKDOWN: + partition = DEVICE_CONFIG_PARTITION; + break; + case CMD_WRITE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case CMD_WRITE_BOOTLOADER: + partition = BOOTLOADER_PARTITION; + break; + case CMD_WRITE_UTILITY_PARAM: + partition = UTILITY_PARAMETER_PARTITION; + break; + case CMD_ERASE_ALL: + partition = CORE_CODE_PARTITION; + break; + case CMD_ERASE_BL_CONFIG: + partition = GLOBAL_PARAMETERS_PARTITION; + break; + case CMD_ERASE_UI_CONFIG: + partition = CORE_CONFIG_PARTITION; + break; + case CMD_ERASE_DISP_CONFIG: + partition = DISPLAY_CONFIG_PARTITION; + break; + case CMD_ERASE_FLASH_CONFIG: + partition = FLASH_CONFIG_PARTITION; + break; + case CMD_ERASE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case CMD_ERASE_BOOTLOADER: + partition = BOOTLOADER_PARTITION; + break; + case CMD_ENABLE_FLASH_PROG: + partition = BOOTLOADER_PARTITION; + break; + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + }; + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.partition_id, + &partition, + sizeof(partition)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write partition ID\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_partition_id(unsigned char cmd) +{ + int retval; + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + retval = fwu_write_f34_v7_partition_id(cmd); + else + retval = 0; + + return retval; +} + +static int fwu_read_f34_v7_partition_table(unsigned char *partition_table) +{ + int retval; + unsigned char data_base; + unsigned char length[2]; + unsigned short block_number = 0; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + fwu->config_area = FLASH_CONFIG_AREA; + + retval = fwu_write_f34_partition_id(CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + length[0] = (unsigned char)(fwu->flash_config_length & MASK_8BIT); + length[1] = (unsigned char)(fwu->flash_config_length >> 8); + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write transfer length\n", + __func__); + return retval; + } + + retval = fwu_write_f34_command(CMD_READ_CONFIG); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write command\n", + __func__); + return retval; + } + + msleep(READ_CONFIG_WAIT_MS); + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, true); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to wait for idle status\n", + __func__); + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_base + fwu->off.payload, + partition_table, + fwu->partition_table_bytes); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read block data\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_read_f34_v7_queries(void) +{ + int retval; + unsigned char ii; + unsigned char query_base; + unsigned char index; + unsigned char offset; + unsigned char *ptable; + struct f34_v7_query_0 query_0; + struct f34_v7_query_1_7 query_1_7; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + query_base = fwu->f34_fd.query_base_addr; + + retval = synaptics_rmi4_reg_read(rmi4_data, + query_base, + query_0.data, + sizeof(query_0.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read query 0\n", + __func__); + return retval; + } + + offset = query_0.subpacket_1_size + 1; + + retval = synaptics_rmi4_reg_read(rmi4_data, + query_base + offset, + query_1_7.data, + sizeof(query_1_7.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read queries 1 to 7\n", + __func__); + return retval; + } + + fwu->bootloader_id[0] = query_1_7.bl_minor_revision; + fwu->bootloader_id[1] = query_1_7.bl_major_revision; + + if (fwu->bootloader_id[1] == BL_V8) + fwu->bl_version = BL_V8; + + fwu->block_size = query_1_7.block_size_15_8 << 8 | + query_1_7.block_size_7_0; + + fwu->flash_config_length = query_1_7.flash_config_length_15_8 << 8 | + query_1_7.flash_config_length_7_0; + + fwu->payload_length = query_1_7.payload_length_15_8 << 8 | + query_1_7.payload_length_7_0; + + fwu->off.flash_status = V7_FLASH_STATUS_OFFSET; + fwu->off.partition_id = V7_PARTITION_ID_OFFSET; + fwu->off.block_number = V7_BLOCK_NUMBER_OFFSET; + fwu->off.transfer_length = V7_TRANSFER_LENGTH_OFFSET; + fwu->off.flash_cmd = V7_COMMAND_OFFSET; + fwu->off.payload = V7_PAYLOAD_OFFSET; + + index = sizeof(query_1_7.data) - V7_PARTITION_SUPPORT_BYTES; + + fwu->partitions = 0; + for (offset = 0; offset < V7_PARTITION_SUPPORT_BYTES; offset++) { + for (ii = 0; ii < 8; ii++) { + if (query_1_7.data[index + offset] & (1 << ii)) + fwu->partitions++; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Supported partitions: 0x%02x\n", + __func__, query_1_7.data[index + offset]); + } + + fwu->partition_table_bytes = fwu->partitions * 8 + 2; + + ptable = kzalloc(fwu->partition_table_bytes, GFP_KERNEL); + if (!ptable) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for partition table\n", + __func__); + return -ENOMEM; + } + + retval = fwu_read_f34_v7_partition_table(ptable); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read partition table\n", + __func__); + kfree(ptable); + return retval; + } + + fwu_parse_partition_table(ptable, &fwu->blkcount, &fwu->phyaddr); + + if (fwu->blkcount.dp_config) + fwu->flash_properties.has_disp_config = 1; + else + fwu->flash_properties.has_disp_config = 0; + + if (fwu->blkcount.pm_config) + fwu->flash_properties.has_pm_config = 1; + else + fwu->flash_properties.has_pm_config = 0; + + if (fwu->blkcount.bl_config) + fwu->flash_properties.has_bl_config = 1; + else + fwu->flash_properties.has_bl_config = 0; + + if (fwu->blkcount.guest_code) + fwu->has_guest_code = 1; + else + fwu->has_guest_code = 0; + + if (fwu->blkcount.utility_param) + fwu->has_utility_param = 1; + else + fwu->has_utility_param = 0; + + kfree(ptable); + + return 0; +} + +static int fwu_read_f34_v5v6_queries(void) +{ + int retval; + unsigned char count; + unsigned char base; + unsigned char offset; + unsigned char buf[10]; + struct f34_v5v6_flash_properties_2 properties_2; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + base = fwu->f34_fd.query_base_addr; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + V5V6_BOOTLOADER_ID_OFFSET, + fwu->bootloader_id, + sizeof(fwu->bootloader_id)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read bootloader ID\n", + __func__); + return retval; + } + + if (fwu->bl_version == BL_V5) { + fwu->off.properties = V5_PROPERTIES_OFFSET; + fwu->off.block_size = V5_BLOCK_SIZE_OFFSET; + fwu->off.block_count = V5_BLOCK_COUNT_OFFSET; + fwu->off.block_number = V5_BLOCK_NUMBER_OFFSET; + fwu->off.payload = V5_BLOCK_DATA_OFFSET; + } else if (fwu->bl_version == BL_V6) { + fwu->off.properties = V6_PROPERTIES_OFFSET; + fwu->off.properties_2 = V6_PROPERTIES_2_OFFSET; + fwu->off.block_size = V6_BLOCK_SIZE_OFFSET; + fwu->off.block_count = V6_BLOCK_COUNT_OFFSET; + fwu->off.gc_block_count = V6_GUEST_CODE_BLOCK_COUNT_OFFSET; + fwu->off.block_number = V6_BLOCK_NUMBER_OFFSET; + fwu->off.payload = V6_BLOCK_DATA_OFFSET; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.block_size, + buf, + 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read block size info\n", + __func__); + return retval; + } + + batohs(&fwu->block_size, &(buf[0])); + + if (fwu->bl_version == BL_V5) { + fwu->off.flash_cmd = fwu->off.payload + fwu->block_size; + fwu->off.flash_status = fwu->off.flash_cmd; + } else if (fwu->bl_version == BL_V6) { + fwu->off.flash_cmd = V6_FLASH_COMMAND_OFFSET; + fwu->off.flash_status = V6_FLASH_STATUS_OFFSET; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.properties, + fwu->flash_properties.data, + sizeof(fwu->flash_properties.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash properties\n", + __func__); + return retval; + } + + count = 4; + + if (fwu->flash_properties.has_pm_config) + count += 2; + + if (fwu->flash_properties.has_bl_config) + count += 2; + + if (fwu->flash_properties.has_disp_config) + count += 2; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.block_count, + buf, + count); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read block count info\n", + __func__); + return retval; + } + + batohs(&fwu->blkcount.ui_firmware, &(buf[0])); + batohs(&fwu->blkcount.ui_config, &(buf[2])); + + count = 4; + + if (fwu->flash_properties.has_pm_config) { + batohs(&fwu->blkcount.pm_config, &(buf[count])); + count += 2; + } + + if (fwu->flash_properties.has_bl_config) { + batohs(&fwu->blkcount.bl_config, &(buf[count])); + count += 2; + } + + if (fwu->flash_properties.has_disp_config) + batohs(&fwu->blkcount.dp_config, &(buf[count])); + + fwu->has_guest_code = false; +#ifdef SYNA_TDDI + fwu->has_force_config = false; + fwu->has_lockdown_data = false; + fwu->has_lcm_data = false; + fwu->has_oem_data = false; +#endif + + if (fwu->flash_properties.has_query4) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.properties_2, + properties_2.data, + sizeof(properties_2.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash properties 2\n", + __func__); + return retval; + } + offset = fwu->off.properties_2 + 1; + count = 0; + if (properties_2.has_guest_code) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + offset + count, + buf, + 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read guest code block count\n", + __func__); + return retval; + } + + batohs(&fwu->blkcount.guest_code, &(buf[0])); + count++; + fwu->has_guest_code = true; + } +#ifdef SYNA_TDDI + if (properties_2.has_force_config) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + offset + count, + buf, + 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read tddi force block count\n", + __func__); + return retval; + } + batohs(&fwu->blkcount.tddi_force_config, &(buf[0])); + count++; + fwu->has_force_config = true; + } + if (properties_2.has_lockdown_data) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + offset + count, + buf, + 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read tddi lockdown block count\n", + __func__); + return retval; + } + batohs(&fwu->blkcount.tddi_lockdown_data, &(buf[0])); + count++; + fwu->has_lockdown_data = true; + } + if (properties_2.has_lcm_data) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + offset + count, + buf, + 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read tddi lcm block count\n", + __func__); + return retval; + } + batohs(&fwu->blkcount.tddi_lcm_data, &(buf[0])); + count++; + fwu->has_lcm_data = true; + } + if (properties_2.has_oem_data) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + offset + count, + buf, + 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read tddi oem block count\n", + __func__); + return retval; + } + batohs(&fwu->blkcount.tddi_oem_data, &(buf[0])); + fwu->has_oem_data = true; + } +#endif + } + + fwu->has_utility_param = false; + + return 0; +} + +static int fwu_read_f34_queries(void) +{ + int retval; + + memset(&fwu->blkcount, 0x00, sizeof(fwu->blkcount)); + memset(&fwu->phyaddr, 0x00, sizeof(fwu->phyaddr)); + + if (fwu->bl_version == BL_V7) + retval = fwu_read_f34_v7_queries(); + else + retval = fwu_read_f34_v5v6_queries(); + + return retval; +} + +static int fwu_write_f34_v7_blocks(unsigned char *block_ptr, + unsigned short block_cnt, unsigned char command) +{ + int retval; + unsigned char data_base; + unsigned char length[2]; + unsigned short transfer; + unsigned short remaining = block_cnt; + unsigned short block_number = 0; + unsigned short left_bytes; + unsigned short write_size; + unsigned short max_write_size; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + retval = fwu_write_f34_partition_id(command); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + do { + if (remaining / fwu->payload_length) + transfer = fwu->payload_length; + else + transfer = remaining; + + length[0] = (unsigned char)(transfer & MASK_8BIT); + length[1] = (unsigned char)(transfer >> 8); + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write transfer length (remaining = %d)\n", + __func__, remaining); + return retval; + } + + retval = fwu_write_f34_command(command); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write command (remaining = %d)\n", + __func__, remaining); + return retval; + } + +#ifdef MAX_WRITE_SIZE + max_write_size = MAX_WRITE_SIZE; + if (max_write_size >= transfer * fwu->block_size) + max_write_size = transfer * fwu->block_size; + else if (max_write_size > fwu->block_size) + max_write_size -= max_write_size % fwu->block_size; + else + max_write_size = fwu->block_size; +#else + max_write_size = transfer * fwu->block_size; +#endif + left_bytes = transfer * fwu->block_size; + + do { + if (left_bytes / max_write_size) + write_size = max_write_size; + else + write_size = left_bytes; + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.payload, + block_ptr, + write_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write block data (remaining = %d)\n", + __func__, remaining); + return retval; + } + + block_ptr += write_size; + left_bytes -= write_size; + } while (left_bytes); + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to wait for idle status (remaining = %d)\n", + __func__, remaining); + return retval; + } + + remaining -= transfer; + } while (remaining); + + return 0; +} + +static int fwu_write_f34_v5v6_blocks(unsigned char *block_ptr, + unsigned short block_cnt, unsigned char command) +{ + int retval; + unsigned char data_base; + unsigned char block_number[] = {0, 0}; + unsigned short blk; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + block_number[1] |= (fwu->config_area << 5); + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.block_number, + block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + for (blk = 0; blk < block_cnt; blk++) { + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.payload, + block_ptr, + fwu->block_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write block data (block %d)\n", + __func__, blk); + return retval; + } + + retval = fwu_write_f34_command(command); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write command for block %d\n", + __func__, blk); + return retval; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to wait for idle status (block %d)\n", + __func__, blk); + return retval; + } + + block_ptr += fwu->block_size; + } + + return 0; +} + +static int fwu_write_f34_blocks(unsigned char *block_ptr, + unsigned short block_cnt, unsigned char cmd) +{ + int retval; + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + retval = fwu_write_f34_v7_blocks(block_ptr, block_cnt, cmd); + else + retval = fwu_write_f34_v5v6_blocks(block_ptr, block_cnt, cmd); + + return retval; +} + +static int fwu_read_f34_v7_blocks(unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char data_base; + unsigned char length[2]; + unsigned short transfer; + unsigned short remaining = block_cnt; + unsigned short block_number = 0; + unsigned short index = 0; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + retval = fwu_write_f34_partition_id(command); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + do { + if (remaining / fwu->payload_length) + transfer = fwu->payload_length; + else + transfer = remaining; + + length[0] = (unsigned char)(transfer & MASK_8BIT); + length[1] = (unsigned char)(transfer >> 8); + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write transfer length (remaining = %d)\n", + __func__, remaining); + return retval; + } + + retval = fwu_write_f34_command(command); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write command (remaining = %d)\n", + __func__, remaining); + return retval; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to wait for idle status (remaining = %d)\n", + __func__, remaining); + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_base + fwu->off.payload, + &fwu->read_config_buf[index], + transfer * fwu->block_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read block data (remaining = %d)\n", + __func__, remaining); + return retval; + } + + index += (transfer * fwu->block_size); + remaining -= transfer; + } while (remaining); + + return 0; +} + +static int fwu_read_f34_v5v6_blocks(unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char data_base; + unsigned char block_number[] = {0, 0}; + unsigned short blk; + unsigned short index = 0; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f34_fd.data_base_addr; + + block_number[1] |= (fwu->config_area << 5); + + retval = synaptics_rmi4_reg_write(rmi4_data, + data_base + fwu->off.block_number, + block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + for (blk = 0; blk < block_cnt; blk++) { + retval = fwu_write_f34_command(command); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write read config command\n", + __func__); + return retval; + } + + retval = fwu_wait_for_idle(WRITE_WAIT_MS, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to wait for idle status\n", + __func__); + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_base + fwu->off.payload, + &fwu->read_config_buf[index], + fwu->block_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read block data (block %d)\n", + __func__, blk); + return retval; + } + + index += fwu->block_size; + } + + return 0; +} + +static int fwu_read_f34_blocks(unsigned short block_cnt, unsigned char cmd) +{ + int retval; + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + retval = fwu_read_f34_v7_blocks(block_cnt, cmd); + else + retval = fwu_read_f34_v5v6_blocks(block_cnt, cmd); + + return retval; +} + +static int fwu_get_image_firmware_id(unsigned int *fw_id) +{ + int retval; + unsigned char index = 0; + char *strptr; + char *firmware_id; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (fwu->img.contains_firmware_id) { + *fw_id = fwu->img.firmware_id; + } else { + strptr = strnstr(fwu->image_name, "PR", MAX_IMAGE_NAME_LEN); + if (!strptr) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: No valid PR number (PRxxxxxxx) found in image file name (%s)\n", + __func__, fwu->image_name); + return -EINVAL; + } + + strptr += 2; + firmware_id = kzalloc(MAX_FIRMWARE_ID_LEN, GFP_KERNEL); + if (!firmware_id) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for firmware_id\n", + __func__); + return -ENOMEM; + } + while (strptr[index] >= '0' && strptr[index] <= '9') { + firmware_id[index] = strptr[index]; + index++; + if (index == MAX_FIRMWARE_ID_LEN - 1) + break; + } + + retval = sstrtoul(firmware_id, 10, (unsigned long *)fw_id); + kfree(firmware_id); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to obtain image firmware ID\n", + __func__); + return -EINVAL; + } + } + + return 0; +} + +static int fwu_get_device_config_id(void) +{ + int retval; + unsigned char config_id_size; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + config_id_size = V7_CONFIG_ID_SIZE; + else + config_id_size = V5V6_CONFIG_ID_SIZE; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.ctrl_base_addr, + fwu->config_id, + config_id_size); + if (retval < 0) + return retval; + + return 0; +} + +static enum flash_area fwu_go_nogo(void) +{ + int retval; + enum flash_area flash_area = NONE; + unsigned char ii; + unsigned char config_id_size; + unsigned int device_fw_id; + unsigned int image_fw_id; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (fwu->force_update) { + flash_area = UI_FIRMWARE; + goto exit; + } + + /* Update both UI and config if device is in bootloader mode */ + if (fwu->bl_mode_device) { + flash_area = UI_FIRMWARE; + goto exit; + } + + /* Get device firmware ID */ + device_fw_id = rmi4_data->firmware_id; + dev_info(rmi4_data->pdev->dev.parent, + "%s: Device firmware ID = %d\n", + __func__, device_fw_id); + + /* Get image firmware ID */ + retval = fwu_get_image_firmware_id(&image_fw_id); + if (retval < 0) { + flash_area = NONE; + goto exit; + } + dev_info(rmi4_data->pdev->dev.parent, + "%s: Image firmware ID = %d\n", + __func__, image_fw_id); + + if (image_fw_id > device_fw_id) { + flash_area = UI_FIRMWARE; + goto exit; + } else if (image_fw_id < device_fw_id) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Image firmware ID older than device firmware ID\n", + __func__); + flash_area = NONE; + goto exit; + } + + /* Get device config ID */ + retval = fwu_get_device_config_id(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read device config ID\n", + __func__); + flash_area = NONE; + goto exit; + } + + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + config_id_size = V7_CONFIG_ID_SIZE; + else + config_id_size = V5V6_CONFIG_ID_SIZE; + + for (ii = 0; ii < config_id_size; ii++) { + if (fwu->img.ui_config.data[ii] > fwu->config_id[ii]) { + flash_area = UI_CONFIG; + goto exit; + } else if (fwu->img.ui_config.data[ii] < fwu->config_id[ii]) { + flash_area = NONE; + goto exit; + } + } + + flash_area = NONE; + +exit: + if (flash_area == NONE) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: No need to do reflash\n", + __func__); + } else { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Updating %s\n", + __func__, + flash_area == UI_FIRMWARE ? + "UI firmware and config" : + "UI config only"); + } + + return flash_area; +} + +static int fwu_scan_pdt(void) +{ + int retval; + unsigned char ii; + unsigned char intr_count = 0; + unsigned char intr_off; + unsigned char intr_src; + unsigned short addr; + bool f01found = false; + bool f34found = false; + bool f35found = false; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + fwu->in_ub_mode = false; + + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + if (rmi_fd.fn_number) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Found F%02x\n", + __func__, rmi_fd.fn_number); + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F01: + f01found = true; + + rmi4_data->f01_query_base_addr = + rmi_fd.query_base_addr; + rmi4_data->f01_ctrl_base_addr = + rmi_fd.ctrl_base_addr; + rmi4_data->f01_data_base_addr = + rmi_fd.data_base_addr; + rmi4_data->f01_cmd_base_addr = + rmi_fd.cmd_base_addr; + break; + case SYNAPTICS_RMI4_F34: + f34found = true; + fwu->f34_fd.query_base_addr = + rmi_fd.query_base_addr; + fwu->f34_fd.ctrl_base_addr = + rmi_fd.ctrl_base_addr; + fwu->f34_fd.data_base_addr = + rmi_fd.data_base_addr; + + switch (rmi_fd.fn_version) { + case F34_V0: + fwu->bl_version = BL_V5; + break; + case F34_V1: + fwu->bl_version = BL_V6; + break; + case F34_V2: + fwu->bl_version = BL_V7; + break; + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Unrecognized F34 version\n", + __func__); + return -EINVAL; + } + + fwu->intr_mask = 0; + intr_src = rmi_fd.intr_src_count; + intr_off = intr_count % 8; + for (ii = intr_off; + ii < (intr_src + intr_off); + ii++) { + fwu->intr_mask |= 1 << ii; + } + break; + case SYNAPTICS_RMI4_F35: + f35found = true; + fwu->f35_fd.query_base_addr = + rmi_fd.query_base_addr; + fwu->f35_fd.ctrl_base_addr = + rmi_fd.ctrl_base_addr; + fwu->f35_fd.data_base_addr = + rmi_fd.data_base_addr; + fwu->f35_fd.cmd_base_addr = + rmi_fd.cmd_base_addr; + break; + } + } else { + break; + } + + intr_count += rmi_fd.intr_src_count; + } + + if (!f01found || !f34found) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find both F01 and F34\n", + __func__); + if (!f35found) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F35\n", + __func__); + return -EINVAL; + } else { + fwu->in_ub_mode = true; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: In microbootloader mode\n", + __func__); + fwu_recovery_check_status(); + return 0; + } + } + + rmi4_data->intr_mask[0] |= fwu->intr_mask; + + addr = rmi4_data->f01_ctrl_base_addr + 1; + + retval = synaptics_rmi4_reg_write(rmi4_data, + addr, + &(rmi4_data->intr_mask[0]), + sizeof(rmi4_data->intr_mask[0])); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set interrupt enable bit\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_enter_flash_prog(void) +{ + int retval; + struct f01_device_control f01_device_control; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_read_flash_status(); + if (retval < 0) + return retval; + + if (fwu->in_bl_mode) + return 0; + + retval = rmi4_data->irq_enable(rmi4_data, false, true); + if (retval < 0) + return retval; + + msleep(INT_DISABLE_WAIT_MS); + + retval = fwu_write_f34_command(CMD_ENABLE_FLASH_PROG); + if (retval < 0) + return retval; + + retval = fwu_wait_for_idle(ENABLE_WAIT_MS, false); + if (retval < 0) + return retval; + + if (!fwu->in_bl_mode) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: BL mode not entered\n", + __func__); + return -EINVAL; + } + + if (rmi4_data->hw_if->bl_hw_init) { + retval = rmi4_data->hw_if->bl_hw_init(rmi4_data); + if (retval < 0) + return retval; + } + + retval = fwu_scan_pdt(); + if (retval < 0) + return retval; + + retval = fwu_read_f34_queries(); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + f01_device_control.data, + sizeof(f01_device_control.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F01 device control\n", + __func__); + return retval; + } + + f01_device_control.nosleep = true; + f01_device_control.sleep_mode = SLEEP_MODE_NORMAL; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + f01_device_control.data, + sizeof(f01_device_control.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write F01 device control\n", + __func__); + return retval; + } + + msleep(ENTER_FLASH_PROG_WAIT_MS); + + return retval; +} + +static int fwu_check_ui_firmware_size(void) +{ + unsigned short block_count; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + block_count = fwu->img.ui_firmware.size / fwu->block_size; + + if (block_count != fwu->blkcount.ui_firmware) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: UI firmware size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_ui_configuration_size(void) +{ + unsigned short block_count; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + block_count = fwu->img.ui_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.ui_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: UI configuration size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_dp_configuration_size(void) +{ + unsigned short block_count; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + block_count = fwu->img.dp_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.dp_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Display configuration size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static int fwu_check_pm_configuration_size(void) +{ + unsigned short block_count; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + block_count = fwu->img.pm_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.pm_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Permanent configuration size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} +#endif + +static int fwu_check_bl_configuration_size(void) +{ + unsigned short block_count; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + block_count = fwu->img.bl_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.bl_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Bootloader configuration size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_guest_code_size(void) +{ + unsigned short block_count; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + block_count = fwu->img.guest_code.size / fwu->block_size; + if (block_count != fwu->blkcount.guest_code) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Guest code size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_erase_configuration(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_UI_CONFIG); + if (retval < 0) + return retval; + break; + case DP_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_DISP_CONFIG); + if (retval < 0) + return retval; + break; + case BL_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_BL_CONFIG); + if (retval < 0) + return retval; + break; + case FLASH_CONFIG_AREA: + retval = fwu_write_f34_command(CMD_ERASE_FLASH_CONFIG); + if (retval < 0) + return retval; + break; + case UPP_AREA: + retval = fwu_write_f34_command(CMD_ERASE_UTILITY_PARAMETER); + if (retval < 0) + return retval; + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid config area\n", + __func__); + return -EINVAL; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Idle status detected\n", + __func__); + + return retval; +} + +static int fwu_erase_bootloader(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_write_f34_command(CMD_ERASE_BOOTLOADER); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Idle status detected\n", + __func__); + + return 0; +} + +#ifdef SYNA_TDDI +static int fwu_erase_lockdown_data(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_write_f34_command(CMD_ERASE_LOCKDOWN_DATA); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Erase command written\n", + __func__); + + msleep(100); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Idle status detected\n", + __func__); + + return 0; +} + +#endif + +static int fwu_erase_guest_code(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_write_f34_command(CMD_ERASE_GUEST_CODE); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Idle status detected\n", + __func__); + + return 0; +} + +static int fwu_erase_all(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (fwu->bl_version == BL_V7) { + retval = fwu_write_f34_command(CMD_ERASE_UI_FIRMWARE); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Idle status detected\n", + __func__); + + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + } else { + retval = fwu_write_f34_command(CMD_ERASE_ALL); + if (retval < 0) + return retval; + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Erase all command written\n", + __func__); + + retval = fwu_wait_for_idle(ERASE_WAIT_MS, false); + if (!(fwu->bl_version == BL_V8 && + fwu->flash_status == BAD_PARTITION_TABLE)) { + if (retval < 0) + return retval; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Idle status detected\n", + __func__); + + if (fwu->bl_version == BL_V8) + return 0; + } + + if (fwu->flash_properties.has_disp_config) { + fwu->config_area = DP_CONFIG_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + } + + if (fwu->has_guest_code) { + retval = fwu_erase_guest_code(); + if (retval < 0) + return retval; + } + + return 0; +} + +static int fwu_write_firmware(void) +{ + unsigned short firmware_block_count; + + firmware_block_count = fwu->img.ui_firmware.size / fwu->block_size; + + return fwu_write_f34_blocks((unsigned char *)fwu->img.ui_firmware.data, + firmware_block_count, CMD_WRITE_FW); +} + +static int fwu_write_bootloader(void) +{ + int retval; + unsigned short bootloader_block_count; + + bootloader_block_count = fwu->img.bl_image.size / fwu->block_size; + + fwu->write_bootloader = true; + retval = fwu_write_f34_blocks((unsigned char *)fwu->img.bl_image.data, + bootloader_block_count, CMD_WRITE_BOOTLOADER); + fwu->write_bootloader = false; + + return retval; +} + +static int fwu_write_utility_parameter(void) +{ + int retval; + unsigned char ii; + unsigned char checksum_array[4]; + unsigned char *pbuf; + unsigned short remaining_size; + unsigned short utility_param_size; + unsigned long checksum; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + utility_param_size = fwu->blkcount.utility_param * fwu->block_size; + retval = fwu_allocate_read_config_buf(utility_param_size); + if (retval < 0) + return retval; + memset(fwu->read_config_buf, 0x00, utility_param_size); + + pbuf = fwu->read_config_buf; + remaining_size = utility_param_size - 4; + + for (ii = 0; ii < MAX_UTILITY_PARAMS; ii++) { + if (fwu->img.utility_param_id[ii] == UNUSED) + continue; + +#ifdef F51_DISCRETE_FORCE + if (fwu->img.utility_param_id[ii] == FORCE_PARAMETER) { + if (fwu->bl_mode_device) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Device in bootloader mode, skipping calibration data restoration\n", + __func__); + goto image_param; + } + retval = secure_memcpy(&(pbuf[4]), + remaining_size - 4, + fwu->cal_data, + fwu->cal_data_buf_size, + fwu->cal_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy force calibration data\n", + __func__); + return retval; + } + pbuf[0] = FORCE_PARAMETER; + pbuf[1] = 0x00; + pbuf[2] = (4 + fwu->cal_data_size) / 2; + pbuf += (fwu->cal_data_size + 4); + remaining_size -= (fwu->cal_data_size + 4); + continue; + } +image_param: +#endif + + retval = secure_memcpy(pbuf, + remaining_size, + fwu->img.utility_param[ii].data, + fwu->img.utility_param[ii].size, + fwu->img.utility_param[ii].size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy utility parameter data\n", + __func__); + return retval; + } + pbuf += fwu->img.utility_param[ii].size; + remaining_size -= fwu->img.utility_param[ii].size; + } + + calculate_checksum((unsigned short *)fwu->read_config_buf, + ((utility_param_size - 4) / 2), + &checksum); + + convert_to_little_endian(checksum_array, checksum); + + fwu->read_config_buf[utility_param_size - 4] = checksum_array[0]; + fwu->read_config_buf[utility_param_size - 3] = checksum_array[1]; + fwu->read_config_buf[utility_param_size - 2] = checksum_array[2]; + fwu->read_config_buf[utility_param_size - 1] = checksum_array[3]; + + retval = fwu_write_f34_blocks((unsigned char *)fwu->read_config_buf, + fwu->blkcount.utility_param, CMD_WRITE_UTILITY_PARAM); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_write_configuration(void) +{ + return fwu_write_f34_blocks((unsigned char *)fwu->config_data, + fwu->config_block_count, CMD_WRITE_CONFIG); +} + +static int fwu_write_ui_configuration(void) +{ + fwu->config_area = UI_CONFIG_AREA; + fwu->config_data = fwu->img.ui_config.data; + fwu->config_size = fwu->img.ui_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + return fwu_write_configuration(); +} + +static int fwu_write_dp_configuration(void) +{ + fwu->config_area = DP_CONFIG_AREA; + fwu->config_data = fwu->img.dp_config.data; + fwu->config_size = fwu->img.dp_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + return fwu_write_configuration(); +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static int fwu_write_pm_configuration(void) +{ + fwu->config_area = PM_CONFIG_AREA; + fwu->config_data = fwu->img.pm_config.data; + fwu->config_size = fwu->img.pm_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + return fwu_write_configuration(); +} + +#ifdef SYNA_TDDI +static int fwu_write_tddi_lockdown_data(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_write_f34_blocks(fwu->read_config_buf, + fwu->blkcount.tddi_lockdown_data, + CMD_WRITE_LOCKDOWN_DATA); + if (retval < 0) + return retval; + rmi4_data->reset_device(rmi4_data, false); + return 0; +} +#endif +#endif + +static int fwu_write_flash_configuration(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + fwu->config_area = FLASH_CONFIG_AREA; + fwu->config_data = fwu->img.fl_config.data; + fwu->config_size = fwu->img.fl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + if (fwu->config_block_count != fwu->blkcount.fl_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Flash configuration size mismatch\n", + __func__); + return -EINVAL; + } + + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + + rmi4_data->reset_device(rmi4_data, false); + + return 0; +} + +static int fwu_write_guest_code(void) +{ + int retval; + unsigned short guest_code_block_count; + + guest_code_block_count = fwu->img.guest_code.size / fwu->block_size; + + retval = fwu_write_f34_blocks((unsigned char *)fwu->img.guest_code.data, + guest_code_block_count, CMD_WRITE_GUEST_CODE); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_write_lockdown(void) +{ + unsigned short lockdown_block_count; + + lockdown_block_count = fwu->img.lockdown.size / fwu->block_size; + + return fwu_write_f34_blocks((unsigned char *)fwu->img.lockdown.data, + lockdown_block_count, CMD_WRITE_LOCKDOWN); +} + +static int fwu_write_partition_table_v8(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + fwu->config_area = FLASH_CONFIG_AREA; + fwu->config_data = fwu->img.fl_config.data; + fwu->config_size = fwu->img.fl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + if (fwu->config_block_count != fwu->blkcount.fl_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Flash configuration size mismatch\n", + __func__); + return -EINVAL; + } + + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + + rmi4_data->reset_device(rmi4_data, false); + + return 0; +} + +static int fwu_write_partition_table_v7(void) +{ + int retval; + unsigned short block_count; + + block_count = fwu->blkcount.bl_config; + fwu->config_area = BL_CONFIG_AREA; + fwu->config_size = fwu->block_size * block_count; + + retval = fwu_allocate_read_config_buf(fwu->config_size); + if (retval < 0) + return retval; + + retval = fwu_read_f34_blocks(block_count, CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + + retval = fwu_write_flash_configuration(); + if (retval < 0) + return retval; + + fwu->config_area = BL_CONFIG_AREA; + fwu->config_data = fwu->read_config_buf; + fwu->config_size = fwu->img.bl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_write_bl_area_v7(void) +{ + int retval; + bool has_utility_param; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + has_utility_param = fwu->has_utility_param; + + if (fwu->has_utility_param) { + fwu->config_area = UPP_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + } + + fwu->config_area = BL_CONFIG_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + + fwu->config_area = FLASH_CONFIG_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + + retval = fwu_erase_bootloader(); + if (retval < 0) + return retval; + + retval = fwu_write_bootloader(); + if (retval < 0) + return retval; + + msleep(rmi4_data->hw_if->board_data->reset_delay_ms); + rmi4_data->reset_device(rmi4_data, false); + + fwu->config_area = FLASH_CONFIG_AREA; + fwu->config_data = fwu->img.fl_config.data; + fwu->config_size = fwu->img.fl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + rmi4_data->reset_device(rmi4_data, false); + + fwu->config_area = BL_CONFIG_AREA; + fwu->config_data = fwu->img.bl_config.data; + fwu->config_size = fwu->img.bl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + + if (fwu->img.contains_utility_param) { + retval = fwu_write_utility_parameter(); + if (retval < 0) + return retval; + } + + return 0; +} + +static int fwu_do_reflash(void) +{ + int retval; + bool do_bl_update = false; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!fwu->new_partition_table) { + retval = fwu_check_ui_firmware_size(); + if (retval < 0) + return retval; + + retval = fwu_check_ui_configuration_size(); + if (retval < 0) + return retval; + + if (fwu->flash_properties.has_disp_config && + fwu->img.contains_disp_config) { + retval = fwu_check_dp_configuration_size(); + if (retval < 0) + return retval; + } + + if (fwu->has_guest_code && fwu->img.contains_guest_code) { + retval = fwu_check_guest_code_size(); + if (retval < 0) + return retval; + } + } else if (fwu->bl_version == BL_V7) { + retval = fwu_check_bl_configuration_size(); + if (retval < 0) + return retval; + } + + if (!fwu->has_utility_param && fwu->img.contains_utility_param) { + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + do_bl_update = true; + } + + if (fwu->has_utility_param && !fwu->img.contains_utility_param) { + if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8) + do_bl_update = true; + } + + if (!do_bl_update && fwu->incompatible_partition_tables) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Incompatible partition tables\n", + __func__); + return -EINVAL; + } else if (!do_bl_update && fwu->new_partition_table) { + if (!fwu->force_update) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Partition table mismatch\n", + __func__); + return -EINVAL; + } + } + + retval = fwu_erase_all(); + if (retval < 0) + return retval; + + if (do_bl_update) { + retval = fwu_write_bl_area_v7(); + if (retval < 0) + return retval; + pr_notice("%s: Bootloader area programmed\n", __func__); + } else if (fwu->bl_version == BL_V7 && fwu->new_partition_table) { + retval = fwu_write_partition_table_v7(); + if (retval < 0) + return retval; + pr_notice("%s: Partition table programmed\n", __func__); + } else if (fwu->bl_version == BL_V8) { + retval = fwu_write_partition_table_v8(); + if (retval < 0) + return retval; + pr_notice("%s: Partition table programmed\n", __func__); + } + + fwu->config_area = UI_CONFIG_AREA; + if (fwu->flash_properties.has_disp_config && + fwu->img.contains_disp_config) { + retval = fwu_write_dp_configuration(); + if (retval < 0) + return retval; + pr_notice("%s: Display configuration programmed\n", __func__); + } + + retval = fwu_write_ui_configuration(); + if (retval < 0) + return retval; + pr_notice("%s: Configuration programmed\n", __func__); + + if (fwu->has_guest_code && fwu->img.contains_guest_code) { + retval = fwu_write_guest_code(); + if (retval < 0) + return retval; + pr_notice("%s: Guest code programmed\n", __func__); + } + + retval = fwu_write_firmware(); + if (retval < 0) + return retval; + pr_notice("%s: Firmware programmed\n", __func__); + + return retval; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static int fwu_do_read_config(void) +{ + int retval; + unsigned short block_count; + unsigned short config_area; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + block_count = fwu->blkcount.ui_config; + break; + case DP_CONFIG_AREA: + if (!fwu->flash_properties.has_disp_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Display configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.dp_config; + break; + case PM_CONFIG_AREA: + if (!fwu->flash_properties.has_pm_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Permanent configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.pm_config; + break; + case BL_CONFIG_AREA: + if (!fwu->flash_properties.has_bl_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Bootloader configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.bl_config; + break; + case UPP_AREA: + if (!fwu->has_utility_param) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Utility parameter not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.utility_param; + break; +#ifdef SYNA_TDDI + case TDDI_FORCE_CONFIG_AREA: + if (!fwu->has_force_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: force configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.tddi_force_config; + break; + case TDDI_OEM_DATA_AREA: + if (!fwu->has_oem_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: oem data not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.tddi_oem_data; + break; + case TDDI_LCM_DATA_AREA: + if (!fwu->has_lcm_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: lcm data not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.tddi_lcm_data; + break; +#endif + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid config area\n", + __func__); + return -EINVAL; + } + + if (block_count == 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid block count\n", + __func__); + return -EINVAL; + } + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + if (fwu->bl_version == BL_V5 || fwu->bl_version == BL_V6) { + config_area = fwu->config_area; + retval = fwu_enter_flash_prog(); + fwu->config_area = config_area; + if (retval < 0) + goto exit; + } + + fwu->config_size = fwu->block_size * block_count; + + retval = fwu_allocate_read_config_buf(fwu->config_size); + if (retval < 0) + goto exit; + + retval = fwu_read_f34_blocks(block_count, CMD_READ_CONFIG); + +exit: + if (fwu->bl_version == BL_V5 || fwu->bl_version == BL_V6) + rmi4_data->reset_device(rmi4_data, false); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + return retval; +} + +#ifdef SYNA_TDDI +static int fwu_do_read_tddi_lockdown_data(void) +{ + int retval = -EINVAL; + unsigned short block_count; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + block_count = fwu->blkcount.tddi_lockdown_data; + fwu->config_size = fwu->block_size * block_count; + + if (fwu->bl_version != BL_V6) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not support lockdown data in bl v.%d\n", + __func__, + fwu->bl_version); + goto exit; + } else if (!fwu->has_lockdown_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not support lockdown data\n", __func__); + goto exit; + } + + kfree(fwu->read_config_buf); + + fwu->read_config_buf = kzalloc(fwu->config_size, GFP_KERNEL); + + if (!fwu->read_config_buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for fwu->read_config_buf\n", + __func__); + fwu->read_config_buf_size = 0; + retval = -ENOMEM; + goto exit; + } + fwu->read_config_buf_size = fwu->config_size; + retval = fwu_read_f34_blocks(block_count, CMD_READ_LOCKDOWN_DATA); +exit: + return retval; +} + +int get_tddi_lockdown_data(unsigned char *lockdown_data, unsigned short leng) +{ + int retval; + + retval = fwu_do_read_tddi_lockdown_data(); + if (retval < 0) + return retval; + memcpy(lockdown_data, fwu->read_config_buf, leng); + return retval; +} + +int set_tddi_lockdown_data(unsigned char *lockdown_data, unsigned short leng) +{ + int retval = -EINVAL; + unsigned long checksum; + unsigned char checksum_array[4]; + unsigned short blk_cnt; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (fwu->bl_version != BL_V6) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not support lockdown data in bl v.%d\n", + __func__, + fwu->bl_version); + goto exit; + } else if (!fwu->has_lockdown_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not support lockdown data\n", __func__); + goto exit; + } + + retval = fwu_enter_flash_prog(); + if (retval < 0) + goto exit; + + retval = fwu_erase_lockdown_data(); + if (retval < 0) + goto exit; + + blk_cnt = fwu->blkcount.tddi_lockdown_data; + + fwu->config_size = fwu->blkcount.tddi_lockdown_data * fwu->block_size; + retval = fwu_allocate_read_config_buf(fwu->config_size); + if (retval < 0) + goto exit; + memset(fwu->read_config_buf, 0x00, fwu->config_size); + retval = secure_memcpy(fwu->read_config_buf, fwu->config_size, + lockdown_data, leng, leng); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy tddi lockdwon data\n", + __func__); + goto exit; + } + + calculate_checksum((unsigned short *)fwu->read_config_buf, + ((fwu->config_size - 4) / 2), + &checksum); + + convert_to_little_endian(checksum_array, checksum); + + fwu->read_config_buf[blk_cnt * fwu->block_size - 4] = checksum_array[0]; + fwu->read_config_buf[blk_cnt * fwu->block_size - 3] = checksum_array[1]; + fwu->read_config_buf[blk_cnt * fwu->block_size - 2] = checksum_array[2]; + fwu->read_config_buf[blk_cnt * fwu->block_size - 1] = checksum_array[3]; + retval = fwu_write_tddi_lockdown_data(); +exit: + return retval; +} +#endif +#endif + +static int fwu_do_lockdown_v7(void) +{ + int retval; + struct f34_v7_data0 status; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_enter_flash_prog(); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.flash_status, + status.data, + sizeof(status.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash status\n", + __func__); + return retval; + } + + if (status.device_cfg_status == 2) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Device already locked down\n", + __func__); + return 0; + } + + retval = fwu_write_lockdown(); + if (retval < 0) + return retval; + + pr_notice("%s: Lockdown programmed\n", __func__); + + return retval; +} + +static int fwu_do_lockdown_v5v6(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; +#ifdef SYNA_TDDI + unsigned char *img_ld; + + img_ld = (unsigned char *)fwu->img.lockdown.data; + if (fwu->has_lockdown_data) { + retval = set_tddi_lockdown_data(img_ld, + LOCKDOWN_SIZE); + if (retval < 0) + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write lockdown data\n", + __func__); + return retval; + } +#endif + + retval = fwu_enter_flash_prog(); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.query_base_addr + fwu->off.properties, + fwu->flash_properties.data, + sizeof(fwu->flash_properties.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash properties\n", + __func__); + return retval; + } + + if (fwu->flash_properties.unlocked == 0) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Device already locked down\n", + __func__); + return 0; + } + + retval = fwu_write_lockdown(); + if (retval < 0) + return retval; + + pr_notice("%s: Lockdown programmed\n", __func__); + + return retval; +} + +#ifdef F51_DISCRETE_FORCE +static int fwu_do_restore_f51_cal_data(void) +{ + int retval; + unsigned char checksum_array[4]; + unsigned short block_count; + unsigned long checksum; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + block_count = fwu->blkcount.ui_config; + fwu->config_size = fwu->block_size * block_count; + fwu->config_area = UI_CONFIG_AREA; + + retval = fwu_allocate_read_config_buf(fwu->config_size); + if (retval < 0) + return retval; + + retval = fwu_read_f34_blocks(block_count, CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = secure_memcpy(&fwu->read_config_buf[fwu->cal_data_off], + fwu->cal_data_size, fwu->cal_data, + fwu->cal_data_buf_size, fwu->cal_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to restore calibration data\n", + __func__); + return retval; + } + + calculate_checksum((unsigned short *)fwu->read_config_buf, + ((fwu->config_size - 4) / 2), + &checksum); + + convert_to_little_endian(checksum_array, checksum); + + fwu->read_config_buf[fwu->config_size - 4] = checksum_array[0]; + fwu->read_config_buf[fwu->config_size - 3] = checksum_array[1]; + fwu->read_config_buf[fwu->config_size - 2] = checksum_array[2]; + fwu->read_config_buf[fwu->config_size - 1] = checksum_array[3]; + + retval = fwu_enter_flash_prog(); + if (retval < 0) + return retval; + + fwu->config_area = UI_CONFIG_AREA; + fwu->config_data = fwu->read_config_buf; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + retval = fwu_erase_configuration(); + if (retval < 0) + return retval; + + retval = fwu_write_configuration(); + if (retval < 0) + return retval; + + return 0; +} +#endif + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static int fwu_start_write_guest_code(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_parse_image_info(); + if (retval < 0) + return -EINVAL; + + if (!fwu->has_guest_code) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Guest code not supported\n", + __func__); + return -EINVAL; + } + + if (!fwu->img.contains_guest_code) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: No guest code in firmware image\n", + __func__); + return -EINVAL; + } + + if (rmi4_data->sensor_sleep) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Sensor sleeping\n", + __func__); + return -ENODEV; + } + + rmi4_data->stay_awake = true; + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of write guest code process\n", __func__); + + retval = fwu_enter_flash_prog(); + if (retval < 0) + goto exit; + + retval = fwu_check_guest_code_size(); + if (retval < 0) + goto exit; + + retval = fwu_erase_guest_code(); + if (retval < 0) + goto exit; + + retval = fwu_write_guest_code(); + if (retval < 0) + goto exit; + + pr_notice("%s: Guest code programmed\n", __func__); + +exit: + rmi4_data->reset_device(rmi4_data, false); + + pr_notice("%s: End of write guest code process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + rmi4_data->stay_awake = false; + + return retval; +} + +static int fwu_start_write_config(void) +{ + int retval; + unsigned short config_area; + unsigned int device_fw_id; + unsigned int image_fw_id; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = fwu_parse_image_info(); + if (retval < 0) + return -EINVAL; + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + device_fw_id = rmi4_data->firmware_id; + retval = fwu_get_image_firmware_id(&image_fw_id); + if (retval < 0) + return retval; + if (device_fw_id != image_fw_id) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Device and image firmware IDs don't match\n", + __func__); + return -EINVAL; + } + retval = fwu_check_ui_configuration_size(); + if (retval < 0) + return retval; + break; + case DP_CONFIG_AREA: + if (!fwu->flash_properties.has_disp_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Display configuration not supported\n", + __func__); + return -EINVAL; + } + if (!fwu->img.contains_disp_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: No display configuration in firmware image\n", + __func__); + return -EINVAL; + } + retval = fwu_check_dp_configuration_size(); + if (retval < 0) + return retval; + break; + case PM_CONFIG_AREA: + if (!fwu->flash_properties.has_pm_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Permanent configuration not supported\n", + __func__); + return -EINVAL; + } + if (!fwu->img.contains_perm_config) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: No permanent configuration in firmware image\n", + __func__); + return -EINVAL; + } + retval = fwu_check_pm_configuration_size(); + if (retval < 0) + return retval; + break; + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Configuration not supported\n", + __func__); + return -EINVAL; + } + + if (rmi4_data->sensor_sleep) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Sensor sleeping\n", + __func__); + return -ENODEV; + } + + rmi4_data->stay_awake = true; + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of write config process\n", __func__); + + config_area = fwu->config_area; + + retval = fwu_enter_flash_prog(); + if (retval < 0) + goto exit; + + fwu->config_area = config_area; + + if (fwu->config_area != PM_CONFIG_AREA) { + retval = fwu_erase_configuration(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to erase config\n", + __func__); + goto exit; + } + } + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + retval = fwu_write_ui_configuration(); + if (retval < 0) + goto exit; + break; + case DP_CONFIG_AREA: + retval = fwu_write_dp_configuration(); + if (retval < 0) + goto exit; + break; + case PM_CONFIG_AREA: + retval = fwu_write_pm_configuration(); + if (retval < 0) + goto exit; + break; + } + + pr_notice("%s: Config written\n", __func__); + +exit: + switch (fwu->config_area) { + case UI_CONFIG_AREA: + rmi4_data->reset_device(rmi4_data, true); + break; + case DP_CONFIG_AREA: + case PM_CONFIG_AREA: + rmi4_data->reset_device(rmi4_data, false); + break; + } + + pr_notice("%s: End of write config process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + rmi4_data->stay_awake = false; + + return retval; +} +#endif + +static int fwu_start_reflash(void) +{ + int retval = 0; + enum flash_area flash_area; + bool do_rebuild = false; + const struct firmware *fw_entry = NULL; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (rmi4_data->sensor_sleep) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Sensor sleeping\n", + __func__); + return -ENODEV; + } + + rmi4_data->stay_awake = true; + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of reflash process\n", __func__); + + if (fwu->image == NULL) { + retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN, + FW_IMAGE_NAME, sizeof(FW_IMAGE_NAME), + sizeof(FW_IMAGE_NAME)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy image file name\n", + __func__); + goto exit; + } + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Requesting firmware image %s\n", + __func__, fwu->image_name); + + retval = request_firmware(&fw_entry, fwu->image_name, + rmi4_data->pdev->dev.parent); + if (retval != 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Firmware image %s not available\n", + __func__, fwu->image_name); + retval = -EINVAL; + goto exit; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Firmware image size = %d\n", + __func__, (unsigned int)fw_entry->size); + + fwu->image = fw_entry->data; + } + + retval = fwu_parse_image_info(); + if (retval < 0) + goto exit; + + if (fwu->blkcount.total_count != fwu->img.blkcount.total_count) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Flash size mismatch\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (fwu->bl_version != fwu->img.bl_version) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Bootloader version mismatch\n", + __func__); + retval = -EINVAL; + goto exit; + } + + retval = fwu_read_flash_status(); + if (retval < 0) + goto exit; + + if (fwu->in_bl_mode) { + fwu->bl_mode_device = true; + dev_info(rmi4_data->pdev->dev.parent, + "%s: Device in bootloader mode\n", + __func__); + } else { + fwu->bl_mode_device = false; + } + + flash_area = fwu_go_nogo(); + + if (flash_area != NONE) { + retval = fwu_enter_flash_prog(); + if (retval < 0) { + rmi4_data->reset_device(rmi4_data, false); + goto exit; + } + } + +#ifdef F51_DISCRETE_FORCE + if (flash_area != NONE && !fwu->bl_mode_device) { + fwu->config_size = fwu->block_size * fwu->blkcount.ui_config; + fwu->config_area = UI_CONFIG_AREA; + + retval = fwu_allocate_read_config_buf(fwu->config_size); + if (retval < 0) { + rmi4_data->reset_device(rmi4_data, false); + goto exit; + } + + retval = fwu_read_f34_blocks(fwu->blkcount.ui_config, + CMD_READ_CONFIG); + if (retval < 0) { + rmi4_data->reset_device(rmi4_data, false); + goto exit; + } + + retval = secure_memcpy(fwu->cal_data, fwu->cal_data_buf_size, + &fwu->read_config_buf[fwu->cal_data_off], + fwu->cal_data_size, fwu->cal_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to save calibration data\n", + __func__); + rmi4_data->reset_device(rmi4_data, false); + goto exit; + } + } +#endif + + switch (flash_area) { + case UI_FIRMWARE: + do_rebuild = true; + retval = fwu_do_reflash(); +#ifdef F51_DISCRETE_FORCE + if (retval < 0) + break; + + if (fwu->has_utility_param || fwu->img.contains_utility_param) + break; + + rmi4_data->reset_device(rmi4_data, false); + + if (fwu->bl_mode_device || fwu->in_bl_mode) { + dev_info(rmi4_data->pdev->dev.parent, + "%s: Device in bootloader mode, skipping calibration data restoration\n", + __func__); + break; + } + + retval = fwu_do_restore_f51_cal_data(); +#endif + break; + case UI_CONFIG: + do_rebuild = true; + retval = fwu_check_ui_configuration_size(); + if (retval < 0) + break; + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_erase_configuration(); + if (retval < 0) + break; + retval = fwu_write_ui_configuration(); +#ifdef F51_DISCRETE_FORCE + if (retval < 0) + break; + + if (fwu->has_utility_param) + break; + + retval = fwu_do_restore_f51_cal_data(); +#endif + break; + case NONE: + default: + break; + } + + if (retval < 0) { + do_rebuild = false; + rmi4_data->reset_device(rmi4_data, false); + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do reflash\n", + __func__); + goto exit; + } + + if (fwu->do_lockdown && (fwu->img.lockdown.data != NULL)) { + switch (fwu->bl_version) { + case BL_V5: + case BL_V6: + retval = fwu_do_lockdown_v5v6(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do lockdown\n", + __func__); + } + rmi4_data->reset_device(rmi4_data, false); + break; + case BL_V7: + case BL_V8: + retval = fwu_do_lockdown_v7(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do lockdown\n", + __func__); + } + rmi4_data->reset_device(rmi4_data, false); + break; + default: + break; + } + } + +exit: + if (fw_entry) + release_firmware(fw_entry); + + if (do_rebuild) + rmi4_data->reset_device(rmi4_data, true); + + pr_notice("%s: End of reflash process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + rmi4_data->stay_awake = false; + + return retval; +} + +static int fwu_recovery_check_status(void) +{ + int retval; + unsigned char data_base; + unsigned char status; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f35_fd.data_base_addr; + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_base + F35_ERROR_CODE_OFFSET, + &status, + 1); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read status\n", + __func__); + return retval; + } + + status = status & MASK_5BIT; + + if (status != 0x00) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Recovery mode status = %d\n", + __func__, status); + return -EINVAL; + } + + return 0; +} + +static int fwu_recovery_erase_completion(void) +{ + int retval; + unsigned char data_base; + unsigned char command; + unsigned char status; + unsigned int timeout = F35_ERASE_ALL_WAIT_MS / 20; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + data_base = fwu->f35_fd.data_base_addr; + + do { + command = 0x01; + retval = synaptics_rmi4_reg_write(rmi4_data, + fwu->f35_fd.cmd_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue command\n", + __func__); + return retval; + } + + do { + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f35_fd.cmd_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read command status\n", + __func__); + return retval; + } + + if ((command & 0x01) == 0x00) + break; + + msleep(20); + timeout--; + } while (timeout > 0); + + if (timeout == 0) + goto exit; + + retval = synaptics_rmi4_reg_read(rmi4_data, + data_base + F35_FLASH_STATUS_OFFSET, + &status, + sizeof(status)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read flash status\n", + __func__); + return retval; + } + + if ((status & 0x01) == 0x00) + break; + + msleep(20); + timeout--; + } while (timeout > 0); + +exit: + if (timeout == 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Timed out waiting for flash erase completion\n", + __func__); + return -ETIMEDOUT; + } + + return 0; +} + +static int fwu_recovery_erase_all(void) +{ + int retval; + unsigned char ctrl_base; + unsigned char command = CMD_F35_ERASE_ALL; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + ctrl_base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(rmi4_data, + ctrl_base + F35_CHUNK_COMMAND_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue erase all command\n", + __func__); + return retval; + } + + if (fwu->f35_fd.cmd_base_addr) { + retval = fwu_recovery_erase_completion(); + if (retval < 0) + return retval; + } else { + msleep(F35_ERASE_ALL_WAIT_MS); + } + + retval = fwu_recovery_check_status(); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_recovery_write_chunk(void) +{ + int retval; + unsigned char ctrl_base; + unsigned char chunk_number[] = {0, 0}; + unsigned char chunk_spare; + unsigned char chunk_size; + unsigned char buf[F35_CHUNK_SIZE + 1]; + unsigned short chunk; + unsigned short chunk_total; + unsigned short bytes_written = 0; + unsigned char *chunk_ptr = (unsigned char *)fwu->image; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + ctrl_base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(rmi4_data, + ctrl_base + F35_CHUNK_NUM_LSB_OFFSET, + chunk_number, + sizeof(chunk_number)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write chunk number\n", + __func__); + return retval; + } + + buf[sizeof(buf) - 1] = CMD_F35_WRITE_CHUNK; + + chunk_total = fwu->image_size / F35_CHUNK_SIZE; + chunk_spare = fwu->image_size % F35_CHUNK_SIZE; + if (chunk_spare) + chunk_total++; + + for (chunk = 0; chunk < chunk_total; chunk++) { + if (chunk_spare && chunk == chunk_total - 1) + chunk_size = chunk_spare; + else + chunk_size = F35_CHUNK_SIZE; + + memset(buf, 0x00, F35_CHUNK_SIZE); + secure_memcpy(buf, sizeof(buf), chunk_ptr, + fwu->image_size - bytes_written, + chunk_size); + + retval = synaptics_rmi4_reg_write(rmi4_data, + ctrl_base + F35_CHUNK_DATA_OFFSET, + buf, + sizeof(buf)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write chunk data (chunk %d)\n", + __func__, chunk); + return retval; + } + chunk_ptr += chunk_size; + bytes_written += chunk_size; + } + + retval = fwu_recovery_check_status(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write chunk data\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_recovery_reset(void) +{ + int retval; + unsigned char ctrl_base; + unsigned char command = CMD_F35_RESET; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + ctrl_base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(rmi4_data, + ctrl_base + F35_CHUNK_COMMAND_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to issue reset command\n", + __func__); + return retval; + } + + msleep(F35_RESET_WAIT_MS); + + return 0; +} + +static int fwu_start_recovery(void) +{ + int retval; + const struct firmware *fw_entry = NULL; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (rmi4_data->sensor_sleep) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Sensor sleeping\n", + __func__); + return -ENODEV; + } + + rmi4_data->stay_awake = true; + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of recovery process\n", __func__); + + if (fwu->image == NULL) { + retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN, + FW_IHEX_NAME, sizeof(FW_IHEX_NAME), + sizeof(FW_IHEX_NAME)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy ihex file name\n", + __func__); + goto exit; + } + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Requesting firmware ihex %s\n", + __func__, fwu->image_name); + + retval = request_firmware(&fw_entry, fwu->image_name, + rmi4_data->pdev->dev.parent); + if (retval != 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Firmware ihex %s not available\n", + __func__, fwu->image_name); + retval = -EINVAL; + goto exit; + } + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Firmware image size = %d\n", + __func__, (unsigned int)fw_entry->size); + + fwu->image = fw_entry->data; + fwu->image_size = fw_entry->size; + } + + retval = rmi4_data->irq_enable(rmi4_data, false, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to disable interrupt\n", + __func__); + goto exit; + } + + retval = fwu_recovery_erase_all(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do erase all in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: External flash erased\n", __func__); + + retval = fwu_recovery_write_chunk(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write chunk data in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: Chunk data programmed\n", __func__); + + retval = fwu_recovery_reset(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to reset device in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: Recovery mode reset issued\n", __func__); + + rmi4_data->reset_device(rmi4_data, true); + + retval = 0; + +exit: + if (fw_entry) + release_firmware(fw_entry); + + pr_notice("%s: End of recovery process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + rmi4_data->stay_awake = false; + + return retval; +} + +int synaptics_fw_updater(const unsigned char *fw_data) +{ + int retval; + + if (!fwu) + return -ENODEV; + + if (!fwu->initialized) + return -ENODEV; + + if (fwu->in_ub_mode) { + fwu->image = NULL; + retval = fwu_start_recovery(); + if (retval < 0) + return retval; + } + + fwu->image = fw_data; + + retval = fwu_start_reflash(); + + fwu->image = NULL; + + return retval; +} +EXPORT_SYMBOL(synaptics_fw_updater); + +#ifdef DO_STARTUP_FW_UPDATE +static void fwu_startup_fw_update_work(struct work_struct *work) +{ + static unsigned char do_once = 1; +#ifdef WAIT_FOR_FB_READY + unsigned int timeout; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; +#endif + + if (!do_once) + return; + do_once = 0; + +#ifdef WAIT_FOR_FB_READY + timeout = FB_READY_TIMEOUT_S * 1000 / FB_READY_WAIT_MS + 1; + + while (!rmi4_data->fb_ready) { + msleep(FB_READY_WAIT_MS); + timeout--; + if (timeout == 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Timed out waiting for FB ready\n", + __func__); + return; + } + } +#endif + + synaptics_fw_updater(NULL); +} +#endif + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS +static ssize_t fwu_sysfs_show_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + if (count < fwu->config_size) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not enough space (%d bytes) in buffer\n", + __func__, (unsigned int)count); + retval = -EINVAL; + goto exit; + } + + retval = secure_memcpy(buf, count, fwu->read_config_buf, + fwu->read_config_buf_size, fwu->config_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy config data\n", + __func__); + goto exit; + } else { + retval = fwu->config_size; + } + +exit: + mutex_unlock(&fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_store_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = secure_memcpy(&fwu->ext_data_source[fwu->data_pos], + fwu->image_size - fwu->data_pos, buf, count, count); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy image data\n", + __func__); + goto exit; + } else { + retval = count; + } + + fwu->data_pos += count; + +exit: + mutex_unlock(&fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_do_recovery_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + if (kstrtouint(buf, 10, &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (!fwu->in_ub_mode) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not in microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) { + retval = -EINVAL; + goto exit; + } else { + fwu->image = fwu->ext_data_source; + } + + retval = fwu_start_recovery(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do recovery\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + mutex_unlock(&fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + if (kstrtouint(buf, 10, &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (fwu->in_ub_mode) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: In microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) { + retval = -EINVAL; + goto exit; + } else { + fwu->image = fwu->ext_data_source; + } + + if (input & LOCKDOWN) { + fwu->do_lockdown = true; + input &= ~LOCKDOWN; + } + + if ((input != NORMAL) && (input != FORCE)) { + retval = -EINVAL; + goto exit; + } + + if (input == FORCE) + fwu->force_update = true; + + retval = synaptics_fw_updater(fwu->image); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do reflash\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + fwu->force_update = FORCE_UPDATE; + fwu->do_lockdown = DO_LOCKDOWN; + mutex_unlock(&fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_write_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + if (kstrtouint(buf, 10, &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (input != 1) { + retval = -EINVAL; + goto exit; + } + + if (fwu->in_ub_mode) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: In microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) { + retval = -EINVAL; + goto exit; + } else { + fwu->image = fwu->ext_data_source; + } + + retval = fwu_start_write_config(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write config\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + mutex_unlock(&fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_read_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + if (fwu->in_ub_mode) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: In microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + retval = fwu_do_read_config(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read config\n", + __func__); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&fwu_sysfs_mutex); + return retval; +} + +static ssize_t fwu_sysfs_config_area_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long config_area; + + retval = sstrtoul(buf, 10, &config_area); + if (retval) + return retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + fwu->config_area = config_area; + + mutex_unlock(&fwu_sysfs_mutex); + + return count; +} + +static ssize_t fwu_sysfs_image_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN, + buf, count, count); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy image file name\n", + __func__); + } else { + retval = count; + } + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_image_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long size; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + retval = sstrtoul(buf, 10, &size); + if (retval) + return retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + fwu->image_size = size; + fwu->data_pos = 0; + + kfree(fwu->ext_data_source); + fwu->ext_data_source = kzalloc(fwu->image_size, GFP_KERNEL); + if (!fwu->ext_data_source) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for image data\n", + __func__); + retval = -ENOMEM; + } + retval = count; + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_block_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->block_size); + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.ui_firmware); + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.ui_config); + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.dp_config); + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.pm_config); + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.bl_config); + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_utility_parameter_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.utility_param); + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_guest_code_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + retval = snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.guest_code); + + mutex_unlock(&fwu_sysfs_mutex); + + return retval; +} + +static ssize_t fwu_sysfs_write_guest_code_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + if (kstrtouint(buf, 10, &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (input != 1) { + retval = -EINVAL; + goto exit; + } + + if (fwu->in_ub_mode) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: In microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) { + retval = -EINVAL; + goto exit; + } else { + fwu->image = fwu->ext_data_source; + } + + retval = fwu_start_write_guest_code(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write guest code\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + mutex_unlock(&fwu_sysfs_mutex); + return retval; +} + +#ifdef SYNA_TDDI +static ssize_t fwu_sysfs_read_lockdown_code_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned short lockdown_data_size; + unsigned char *lockdown_data; + char ld_val[2]; + int retval = 0; + int i = 0; + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + lockdown_data_size = fwu->blkcount.tddi_lockdown_data * fwu->block_size; + lockdown_data = kzalloc(lockdown_data_size, GFP_KERNEL); + if (!lockdown_data) { + mutex_unlock(&fwu_sysfs_mutex); + return -ENOMEM; + } + + if (get_tddi_lockdown_data(lockdown_data, lockdown_data_size) < 0) { + kfree(lockdown_data); + mutex_unlock(&fwu_sysfs_mutex); + return -EINVAL; + } + + for (i = 0; i < lockdown_data_size; i++) { + retval += snprintf(ld_val, PAGE_SIZE, "%02x", + *(lockdown_data + i)); + strlcat(buf, ld_val, lockdown_data_size); + } + *(buf + retval) = '\n'; + kfree(lockdown_data); + mutex_unlock(&fwu_sysfs_mutex); + return retval + 1; +} + +static ssize_t fwu_sysfs_write_lockdown_code_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned short lockdown_data_size = (count - 1) / 2; + unsigned char *lockdown_data; + unsigned char temp[2]; + int ld_val; + int i = 0; + + for (i = 0; i < (count - 1); i++) { + if (((*buf >= '0') && (*buf <= '9')) || + (('a' < *buf) && (*buf > 'f')) || + (('A' < *buf) && (*buf > 'F'))) + continue; + else + return -EINVAL; + } + + if (count % 2 != 1) + return -EINVAL; + + lockdown_data = kzalloc(lockdown_data_size, GFP_KERNEL); + if (!lockdown_data) + return -ENOMEM; + + for (i = 0; i < lockdown_data_size; i++) { + memcpy(temp, (buf + 2 * i), sizeof(temp)); + if (kstrtoint(temp, 16, &ld_val) == 1) + *(lockdown_data + i) = ld_val & 0xff; + } + + if (!mutex_trylock(&fwu_sysfs_mutex)) + return -EBUSY; + + if (set_tddi_lockdown_data(lockdown_data, lockdown_data_size) < 0) { + kfree(lockdown_data); + mutex_unlock(&fwu_sysfs_mutex); + return -EINVAL; + } + kfree(lockdown_data); + mutex_unlock(&fwu_sysfs_mutex); + return count; +} +#endif +#endif +static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + if (!fwu) + return; + + if (fwu->intr_mask & intr_mask) + fwu_read_flash_status(); + + return; +} + +static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char attr_count; + struct pdt_properties pdt_props; + + if (fwu) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Handle already exists\n", + __func__); + return 0; + } + + fwu = kzalloc(sizeof(*fwu), GFP_KERNEL); + if (!fwu) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for fwu\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + fwu->image_name = kzalloc(MAX_IMAGE_NAME_LEN, GFP_KERNEL); + if (!fwu->image_name) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for image name\n", + __func__); + retval = -ENOMEM; + goto exit_free_fwu; + } + + fwu->rmi4_data = rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + PDT_PROPS, + pdt_props.data, + sizeof(pdt_props.data)); + if (retval < 0) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Failed to read PDT properties, assuming 0x00\n", + __func__); + } else if (pdt_props.has_bsr) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Reflash for LTS not currently supported\n", + __func__); + retval = -ENODEV; + goto exit_free_mem; + } + + retval = fwu_scan_pdt(); + if (retval < 0) + goto exit_free_mem; + + if (!fwu->in_ub_mode) { + retval = fwu_read_f34_queries(); + if (retval < 0) + goto exit_free_mem; + + retval = fwu_get_device_config_id(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read device config ID\n", + __func__); + goto exit_free_mem; + } + } + + fwu->force_update = FORCE_UPDATE; + fwu->do_lockdown = DO_LOCKDOWN; + fwu->initialized = true; + +#ifdef DO_STARTUP_FW_UPDATE + fwu->fwu_workqueue = create_singlethread_workqueue("fwu_workqueue"); + INIT_WORK(&fwu->fwu_work, fwu_startup_fw_update_work); + queue_work(fwu->fwu_workqueue, + &fwu->fwu_work); +#endif + +#ifdef F51_DISCRETE_FORCE + fwu_read_flash_status(); + if (!fwu->in_bl_mode) { + retval = fwu_f51_force_data_init(); + if (retval < 0) + goto exit_free_mem; + } +#endif + + if (ENABLE_SYS_REFLASH == false) + return 0; + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS + retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj, + &dev_attr_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs bin file\n", + __func__); + goto exit_free_mem; + } +#endif + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto exit_remove_attrs; + } + } + + return 0; + +exit_remove_attrs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS + sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data); +#endif + +exit_free_mem: + kfree(fwu->image_name); + +exit_free_fwu: + kfree(fwu); + fwu = NULL; + +exit: + return retval; +} + +static void synaptics_rmi4_fwu_remove(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char attr_count; + + if (!fwu) + goto exit; + +#ifdef DO_STARTUP_FW_UPDATE + cancel_work_sync(&fwu->fwu_work); + flush_workqueue(fwu->fwu_workqueue); + destroy_workqueue(fwu->fwu_workqueue); +#endif + +#ifdef F51_DISCRETE_FORCE + kfree(fwu->cal_data); +#endif + kfree(fwu->read_config_buf); + kfree(fwu->image_name); + kfree(fwu); + fwu = NULL; + + if (ENABLE_SYS_REFLASH == false) + goto exit; + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS + sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data); +#endif + +exit: + complete(&fwu_remove_complete); +} + +static void synaptics_rmi4_fwu_reset(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + + if (!fwu) { + synaptics_rmi4_fwu_init(rmi4_data); + return; + } + + retval = fwu_scan_pdt(); + if (retval < 0) + return; + + if (!fwu->in_ub_mode) + fwu_read_f34_queries(); + +#ifdef F51_DISCRETE_FORCE + fwu_read_flash_status(); + if (!fwu->in_bl_mode) + fwu_f51_force_data_init(); +#endif + + return; +} + +static struct synaptics_rmi4_exp_fn fwu_module = { + .fn_type = RMI_FW_UPDATER, + .init = synaptics_rmi4_fwu_init, + .remove = synaptics_rmi4_fwu_remove, + .reset = synaptics_rmi4_fwu_reset, + .reinit = NULL, + .early_suspend = NULL, + .suspend = NULL, + .resume = NULL, + .late_resume = NULL, + .attn = synaptics_rmi4_fwu_attn, +}; + +static int __init rmi4_fw_update_module_init(void) +{ + synaptics_rmi4_new_function(&fwu_module, true); + + return 0; +} + +static void __exit rmi4_fw_update_module_exit(void) +{ + synaptics_rmi4_new_function(&fwu_module, false); + + wait_for_completion(&fwu_remove_complete); +} + +module_init(rmi4_fw_update_module_init); +module_exit(rmi4_fw_update_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX FW Update Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_gesture.c b/synaptics_dsx/synaptics_dsx_gesture.c new file mode 100644 index 0000000000..64f960efb1 --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_gesture.c @@ -0,0 +1,2291 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define GESTURE_PHYS_NAME "synaptics_dsx/gesture" + +#define TUNING_SYSFS_DIR_NAME "tuning" + +#define STORE_GESTURES +#ifdef STORE_GESTURES +#define GESTURES_TO_STORE 10 +#endif + +#define CTRL23_FINGER_REPORT_ENABLE_BIT 0 +#define CTRL27_UDG_ENABLE_BIT 4 +#define WAKEUP_GESTURE_MODE 0x02 + +static ssize_t udg_sysfs_engine_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_detection_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_detection_score_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_detection_index_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_registration_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_registration_begin_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_registration_status_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_template_size_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_template_max_index_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_template_detection_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_template_index_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_template_valid_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_template_valid_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_template_clear_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_trace_size_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_template_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t udg_sysfs_template_data_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t udg_sysfs_trace_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t udg_sysfs_template_displacement_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_template_displacement_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_rotation_invariance_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_rotation_invariance_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_scale_invariance_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_scale_invariance_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_threshold_factor_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_threshold_factor_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_match_metric_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_match_metric_threshold_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t udg_sysfs_max_inter_stroke_time_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t udg_sysfs_max_inter_stroke_time_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static int udg_read_tuning_params(void); + +static int udg_write_tuning_params(void); + +static int udg_detection_enable(bool enable); + +static int udg_engine_enable(bool enable); + +static int udg_set_index(unsigned char index); + +#ifdef STORE_GESTURES +static int udg_read_valid_data(void); +static int udg_write_valid_data(void); +static int udg_read_template_data(unsigned char index); +static int udg_write_template_data(void); +#endif + +enum gesture_type { + DETECTION = 0x0f, + REGISTRATION = 0x10, +}; + +struct udg_tuning { + union { + struct { + unsigned char maximum_number_of_templates; + unsigned char template_size; + unsigned char template_disp_lsb; + unsigned char template_disp_msb; + unsigned char rotation_inv_lsb; + unsigned char rotation_inv_msb; + unsigned char scale_inv_lsb; + unsigned char scale_inv_msb; + unsigned char thres_factor_lsb; + unsigned char thres_factor_msb; + unsigned char metric_thres_lsb; + unsigned char metric_thres_msb; + unsigned char inter_stroke_lsb; + unsigned char inter_stroke_msb; + } __packed; + unsigned char data[14]; + }; +}; + +struct udg_addr { + unsigned short data_4; + unsigned short ctrl_18; + unsigned short ctrl_20; + unsigned short ctrl_23; + unsigned short ctrl_27; + unsigned short ctrl_41; + unsigned short trace_x; + unsigned short trace_y; + unsigned short trace_segment; + unsigned short template_helper; + unsigned short template_data; + unsigned short template_flags; +}; + +struct synaptics_rmi4_f12_query_0 { + union { + struct { + struct { + unsigned char has_register_descriptors:1; + unsigned char has_closed_cover:1; + unsigned char has_fast_glove_detect:1; + unsigned char has_dribble:1; + unsigned char has_4p4_jitter_filter_strength:1; + unsigned char f12_query0_s0_b5__7:3; + } __packed; + struct { + unsigned char max_num_templates:4; + unsigned char f12_query0_s1_b4__7:4; + unsigned char template_size_lsb; + unsigned char template_size_msb; + } __packed; + }; + unsigned char data[4]; + }; +}; + +struct synaptics_rmi4_f12_query_5 { + union { + struct { + unsigned char size_of_query6; + struct { + unsigned char ctrl0_is_present:1; + unsigned char ctrl1_is_present:1; + unsigned char ctrl2_is_present:1; + unsigned char ctrl3_is_present:1; + unsigned char ctrl4_is_present:1; + unsigned char ctrl5_is_present:1; + unsigned char ctrl6_is_present:1; + unsigned char ctrl7_is_present:1; + } __packed; + struct { + unsigned char ctrl8_is_present:1; + unsigned char ctrl9_is_present:1; + unsigned char ctrl10_is_present:1; + unsigned char ctrl11_is_present:1; + unsigned char ctrl12_is_present:1; + unsigned char ctrl13_is_present:1; + unsigned char ctrl14_is_present:1; + unsigned char ctrl15_is_present:1; + } __packed; + struct { + unsigned char ctrl16_is_present:1; + unsigned char ctrl17_is_present:1; + unsigned char ctrl18_is_present:1; + unsigned char ctrl19_is_present:1; + unsigned char ctrl20_is_present:1; + unsigned char ctrl21_is_present:1; + unsigned char ctrl22_is_present:1; + unsigned char ctrl23_is_present:1; + } __packed; + struct { + unsigned char ctrl24_is_present:1; + unsigned char ctrl25_is_present:1; + unsigned char ctrl26_is_present:1; + unsigned char ctrl27_is_present:1; + unsigned char ctrl28_is_present:1; + unsigned char ctrl29_is_present:1; + unsigned char ctrl30_is_present:1; + unsigned char ctrl31_is_present:1; + } __packed; + struct { + unsigned char ctrl32_is_present:1; + unsigned char ctrl33_is_present:1; + unsigned char ctrl34_is_present:1; + unsigned char ctrl35_is_present:1; + unsigned char ctrl36_is_present:1; + unsigned char ctrl37_is_present:1; + unsigned char ctrl38_is_present:1; + unsigned char ctrl39_is_present:1; + } __packed; + struct { + unsigned char ctrl40_is_present:1; + unsigned char ctrl41_is_present:1; + unsigned char ctrl42_is_present:1; + unsigned char ctrl43_is_present:1; + unsigned char ctrl44_is_present:1; + unsigned char ctrl45_is_present:1; + unsigned char ctrl46_is_present:1; + unsigned char ctrl47_is_present:1; + } __packed; + }; + unsigned char data[7]; + }; +}; + +struct synaptics_rmi4_f12_query_8 { + union { + struct { + unsigned char size_of_query9; + struct { + unsigned char data0_is_present:1; + unsigned char data1_is_present:1; + unsigned char data2_is_present:1; + unsigned char data3_is_present:1; + unsigned char data4_is_present:1; + unsigned char data5_is_present:1; + unsigned char data6_is_present:1; + unsigned char data7_is_present:1; + } __packed; + struct { + unsigned char data8_is_present:1; + unsigned char data9_is_present:1; + unsigned char data10_is_present:1; + unsigned char data11_is_present:1; + unsigned char data12_is_present:1; + unsigned char data13_is_present:1; + unsigned char data14_is_present:1; + unsigned char data15_is_present:1; + } __packed; + struct { + unsigned char data16_is_present:1; + unsigned char data17_is_present:1; + unsigned char data18_is_present:1; + unsigned char data19_is_present:1; + unsigned char data20_is_present:1; + unsigned char data21_is_present:1; + unsigned char data22_is_present:1; + unsigned char data23_is_present:1; + } __packed; + }; + unsigned char data[4]; + }; +}; + +struct synaptics_rmi4_f12_control_41 { + union { + struct { + unsigned char enable_registration:1; + unsigned char template_index:4; + unsigned char begin:1; + unsigned char f12_ctrl41_b6__7:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_udg_handle { + atomic_t attn_event; + unsigned char intr_mask; + unsigned char report_flags; + unsigned char object_type_enable1; + unsigned char object_type_enable2; + unsigned char trace_size; + unsigned char template_index; + unsigned char max_num_templates; + unsigned char detection_score; + unsigned char detection_index; + unsigned char detection_status; + unsigned char registration_status; + unsigned char *ctrl_buf; + unsigned char *trace_data_buf; + unsigned char *template_data_buf; +#ifdef STORE_GESTURES + unsigned char gestures_to_store; + unsigned char *storage_buf; + unsigned char valid_buf[2]; +#endif + unsigned short trace_data_buf_size; + unsigned short template_size; + unsigned short template_data_size; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; + unsigned short ctrl_18_sub10_off; + unsigned short ctrl_20_sub1_off; + unsigned short ctrl_23_sub3_off; + unsigned short ctrl_27_sub5_off; + struct input_dev *udg_dev; + struct kobject *tuning_dir; + struct udg_addr addr; + struct udg_tuning tuning; + struct synaptics_rmi4_data *rmi4_data; +}; + +static struct device_attribute attrs[] = { + __ATTR(engine_enable, 0220, + synaptics_rmi4_show_error, + udg_sysfs_engine_enable_store), + __ATTR(detection_enable, 0220, + synaptics_rmi4_show_error, + udg_sysfs_detection_enable_store), + __ATTR(detection_score, 0444, + udg_sysfs_detection_score_show, + synaptics_rmi4_store_error), + __ATTR(detection_index, 0444, + udg_sysfs_detection_index_show, + synaptics_rmi4_store_error), + __ATTR(registration_enable, 0220, + synaptics_rmi4_show_error, + udg_sysfs_registration_enable_store), + __ATTR(registration_begin, 0220, + synaptics_rmi4_show_error, + udg_sysfs_registration_begin_store), + __ATTR(registration_status, 0444, + udg_sysfs_registration_status_show, + synaptics_rmi4_store_error), + __ATTR(template_size, 0444, + udg_sysfs_template_size_show, + synaptics_rmi4_store_error), + __ATTR(template_max_index, 0444, + udg_sysfs_template_max_index_show, + synaptics_rmi4_store_error), + __ATTR(template_detection, 0444, + udg_sysfs_template_detection_show, + synaptics_rmi4_store_error), + __ATTR(template_index, 0220, + synaptics_rmi4_show_error, + udg_sysfs_template_index_store), + __ATTR(template_valid, 0664, + udg_sysfs_template_valid_show, + udg_sysfs_template_valid_store), + __ATTR(template_clear, 0220, + synaptics_rmi4_show_error, + udg_sysfs_template_clear_store), + __ATTR(trace_size, 0444, + udg_sysfs_trace_size_show, + synaptics_rmi4_store_error), +}; + +static struct bin_attribute template_data = { + .attr = { + .name = "template_data", + .mode = 0664, + }, + .size = 0, + .read = udg_sysfs_template_data_show, + .write = udg_sysfs_template_data_store, +}; + +static struct bin_attribute trace_data = { + .attr = { + .name = "trace_data", + .mode = 0444, + }, + .size = 0, + .read = udg_sysfs_trace_data_show, + .write = NULL, +}; + +static struct device_attribute params[] = { + __ATTR(template_displacement, 0664, + udg_sysfs_template_displacement_show, + udg_sysfs_template_displacement_store), + __ATTR(rotation_invariance, 0664, + udg_sysfs_rotation_invariance_show, + udg_sysfs_rotation_invariance_store), + __ATTR(scale_invariance, 0664, + udg_sysfs_scale_invariance_show, + udg_sysfs_scale_invariance_store), + __ATTR(threshold_factor, 0664, + udg_sysfs_threshold_factor_show, + udg_sysfs_threshold_factor_store), + __ATTR(match_metric_threshold, 0664, + udg_sysfs_match_metric_threshold_show, + udg_sysfs_match_metric_threshold_store), + __ATTR(max_inter_stroke_time, 0664, + udg_sysfs_max_inter_stroke_time_show, + udg_sysfs_max_inter_stroke_time_store), +}; + +static struct synaptics_rmi4_udg_handle *udg; + +static unsigned char ctrl_18_sub_size[] = {10, 10, 10, 2, 3, 4, 3, 3, 1, 1}; +static unsigned char ctrl_20_sub_size[] = {2}; +static unsigned char ctrl_23_sub_size[] = {1, 1, 1}; +static unsigned char ctrl_27_sub_size[] = {1, 5, 2, 1, 7}; + +DECLARE_COMPLETION(udg_remove_complete); + +static ssize_t udg_sysfs_engine_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + bool enable; + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input == 1) + enable = true; + else if (input == 0) + enable = false; + else + return -EINVAL; + + retval = udg_engine_enable(enable); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_detection_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + bool enable; + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input == 1) + enable = true; + else if (input == 0) + enable = false; + else + return -EINVAL; + + udg->detection_status = 0; + + retval = udg_detection_enable(enable); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_detection_score_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", udg->detection_score); +} + +static ssize_t udg_sysfs_detection_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", udg->detection_index); +} + +static ssize_t udg_sysfs_registration_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + bool enable; + unsigned int input; + struct synaptics_rmi4_f12_control_41 control_41; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input == 1) + enable = true; + else if (input == 0) + enable = false; + else + return -EINVAL; + + if (enable) { + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_23, + udg->ctrl_buf, + udg->ctrl_23_sub3_off + 1); + if (retval < 0) + return retval; + + udg->ctrl_buf[0] = 0; + udg->ctrl_buf[0] |= (1 << CTRL23_FINGER_REPORT_ENABLE_BIT); + if (udg->ctrl_23_sub3_off) + udg->ctrl_buf[udg->ctrl_23_sub3_off] = 0; + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_23, + udg->ctrl_buf, + udg->ctrl_23_sub3_off + 1); + if (retval < 0) + return retval; + } else { + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_23, + udg->ctrl_buf, + udg->ctrl_23_sub3_off + 1); + if (retval < 0) + return retval; + + udg->ctrl_buf[0] = udg->object_type_enable1; + if (udg->ctrl_23_sub3_off) { + udg->ctrl_buf[udg->ctrl_23_sub3_off] = + udg->object_type_enable2; + } + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_23, + udg->ctrl_buf, + udg->ctrl_23_sub3_off + 1); + if (retval < 0) + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_41, + control_41.data, + sizeof(control_41.data)); + if (retval < 0) + return retval; + + control_41.enable_registration = enable ? 1 : 0; + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_41, + control_41.data, + sizeof(control_41.data)); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_registration_begin_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + bool begin; + unsigned int input; + struct synaptics_rmi4_f12_control_41 control_41; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input == 1) + begin = true; + else if (input == 0) + begin = false; + else + return -EINVAL; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_41, + control_41.data, + sizeof(control_41.data)); + if (retval < 0) + return retval; + + control_41.begin = begin ? 1 : 0; + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_41, + control_41.data, + sizeof(control_41.data)); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_registration_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%02x\n", udg->registration_status); +} + +static ssize_t udg_sysfs_template_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", udg->template_size); +} + +static ssize_t udg_sysfs_template_max_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", udg->max_num_templates - 1); +} + +static ssize_t udg_sysfs_template_detection_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + int attn_event; + unsigned char detection_status; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + attn_event = atomic_read(&udg->attn_event); + atomic_set(&udg->attn_event, 0); + + if (attn_event == 0) + return snprintf(buf, PAGE_SIZE, "0\n"); + + if (udg->detection_status == 0) { + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.data_4, + rmi4_data->gesture_detection, + sizeof(rmi4_data->gesture_detection)); + if (retval < 0) + return retval; + + udg->detection_status = rmi4_data->gesture_detection[0]; + } + + detection_status = udg->detection_status; + udg->detection_status = 0; + + switch (detection_status) { + case DETECTION: + udg->detection_score = rmi4_data->gesture_detection[1]; + udg->detection_index = rmi4_data->gesture_detection[4]; + udg->trace_size = rmi4_data->gesture_detection[3]; + break; + case REGISTRATION: + udg->registration_status = rmi4_data->gesture_detection[1]; + udg->trace_size = rmi4_data->gesture_detection[3]; + break; + default: + return snprintf(buf, PAGE_SIZE, "0\n"); + } + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", detection_status); +} + +static ssize_t udg_sysfs_template_index_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long index; + + retval = sstrtoul(buf, 10, &index); + if (retval) + return retval; + + retval = udg_set_index((unsigned char)index); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_template_valid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned char valid; + unsigned char offset; + unsigned char byte_num; + unsigned char template_flags[2]; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + byte_num = udg->template_index / 8; + offset = udg->template_index % 8; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.template_flags, + template_flags, + sizeof(template_flags)); + if (retval < 0) + return retval; + + valid = (template_flags[byte_num] & (1 << offset)) >> offset; + + return snprintf(buf, PAGE_SIZE, "%u\n", valid); +} + +static ssize_t udg_sysfs_template_valid_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long valid; + unsigned char offset; + unsigned char byte_num; + unsigned char template_flags[2]; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + retval = sstrtoul(buf, 10, &valid); + if (retval) + return retval; + + if (valid > 0) + valid = 1; + + byte_num = udg->template_index / 8; + offset = udg->template_index % 8; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.template_flags, + template_flags, + sizeof(template_flags)); + if (retval < 0) + return retval; + + if (valid) + template_flags[byte_num] |= (1 << offset); + else + template_flags[byte_num] &= ~(1 << offset); + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.template_flags, + template_flags, + sizeof(template_flags)); + if (retval < 0) + return retval; + +#ifdef STORE_GESTURES + udg_read_valid_data(); +#endif + + return count; +} + +static ssize_t udg_sysfs_template_clear_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + const char cmd[] = {'0', 0}; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + memset(udg->template_data_buf, 0x00, udg->template_data_size); + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.template_data, + udg->template_data_buf, + udg->template_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to clear template data\n", + __func__); + return retval; + } + + retval = udg_sysfs_template_valid_store(dev, attr, cmd, 1); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to clear valid bit\n", + __func__); + return retval; + } + +#ifdef STORE_GESTURES + udg_read_template_data(udg->template_index); + udg_read_valid_data(); +#endif + + return count; +} + +static ssize_t udg_sysfs_trace_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", udg->trace_size); +} + +static ssize_t udg_sysfs_trace_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned short index = 0; + unsigned short trace_data_size; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + trace_data_size = udg->trace_size * 5; + + if (trace_data_size == 0) + return -EINVAL; + + if (count < trace_data_size) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not enough space (%d bytes) in buffer\n", + __func__, (unsigned int)count); + return -EINVAL; + } + + if (udg->trace_data_buf_size < trace_data_size) { + if (udg->trace_data_buf_size) + kfree(udg->trace_data_buf); + udg->trace_data_buf = kzalloc(trace_data_size, GFP_KERNEL); + if (!udg->trace_data_buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for trace data buffer\n", + __func__); + udg->trace_data_buf_size = 0; + return -ENOMEM; + } + udg->trace_data_buf_size = trace_data_size; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.trace_x, + &udg->trace_data_buf[index], + udg->trace_size * 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read trace X data\n", + __func__); + return retval; + } + index += udg->trace_size * 2; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.trace_y, + &udg->trace_data_buf[index], + udg->trace_size * 2); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read trace Y data\n", + __func__); + return retval; + } + index += udg->trace_size * 2; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.trace_segment, + &udg->trace_data_buf[index], + udg->trace_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read trace segment data\n", + __func__); + return retval; + } + + retval = secure_memcpy(buf, count, udg->trace_data_buf, + udg->trace_data_buf_size, trace_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy trace data\n", + __func__); + return retval; + } + + return trace_data_size; +} + +static ssize_t udg_sysfs_template_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + if (count < udg->template_data_size) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Not enough space (%d bytes) in buffer\n", + __func__, (unsigned int)count); + return -EINVAL; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.template_data, + udg->template_data_buf, + udg->template_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read template data\n", + __func__); + return retval; + } + + retval = secure_memcpy(buf, count, udg->template_data_buf, + udg->template_data_size, udg->template_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy template data\n", + __func__); + return retval; + } + +#ifdef STORE_GESTURES + udg_read_template_data(udg->template_index); + udg_read_valid_data(); +#endif + + return udg->template_data_size; +} + +static ssize_t udg_sysfs_template_data_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + retval = secure_memcpy(udg->template_data_buf, udg->template_data_size, + buf, count, count); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy template data\n", + __func__); + return retval; + } + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.template_data, + udg->template_data_buf, + count); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write template data\n", + __func__); + return retval; + } + +#ifdef STORE_GESTURES + udg_read_template_data(udg->template_index); + udg_read_valid_data(); +#endif + + return count; +} + +static ssize_t udg_sysfs_template_displacement_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned short template_displacement; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + template_displacement = + ((unsigned short)udg->tuning.template_disp_lsb << 0) | + ((unsigned short)udg->tuning.template_disp_msb << 8); + + return snprintf(buf, PAGE_SIZE, "%u\n", template_displacement); +} + +static ssize_t udg_sysfs_template_displacement_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long input; + + retval = sstrtoul(buf, 10, &input); + if (retval) + return retval; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + udg->tuning.template_disp_lsb = (unsigned char)(input >> 0); + udg->tuning.template_disp_msb = (unsigned char)(input >> 8); + + retval = udg_write_tuning_params(); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_rotation_invariance_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned short rotation_invariance; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + rotation_invariance = + ((unsigned short)udg->tuning.rotation_inv_lsb << 0) | + ((unsigned short)udg->tuning.rotation_inv_msb << 8); + + return snprintf(buf, PAGE_SIZE, "%u\n", rotation_invariance); +} + +static ssize_t udg_sysfs_rotation_invariance_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long input; + + retval = sstrtoul(buf, 10, &input); + if (retval) + return retval; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + udg->tuning.rotation_inv_lsb = (unsigned char)(input >> 0); + udg->tuning.rotation_inv_msb = (unsigned char)(input >> 8); + + retval = udg_write_tuning_params(); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_scale_invariance_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned short scale_invariance; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + scale_invariance = + ((unsigned short)udg->tuning.scale_inv_lsb << 0) | + ((unsigned short)udg->tuning.scale_inv_msb << 8); + + return snprintf(buf, PAGE_SIZE, "%u\n", scale_invariance); +} + +static ssize_t udg_sysfs_scale_invariance_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long input; + + retval = sstrtoul(buf, 10, &input); + if (retval) + return retval; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + udg->tuning.scale_inv_lsb = (unsigned char)(input >> 0); + udg->tuning.scale_inv_msb = (unsigned char)(input >> 8); + + retval = udg_write_tuning_params(); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_threshold_factor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned short threshold_factor; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + threshold_factor = + ((unsigned short)udg->tuning.thres_factor_lsb << 0) | + ((unsigned short)udg->tuning.thres_factor_msb << 8); + + return snprintf(buf, PAGE_SIZE, "%u\n", threshold_factor); +} + +static ssize_t udg_sysfs_threshold_factor_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long input; + + retval = sstrtoul(buf, 10, &input); + if (retval) + return retval; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + udg->tuning.thres_factor_lsb = (unsigned char)(input >> 0); + udg->tuning.thres_factor_msb = (unsigned char)(input >> 8); + + retval = udg_write_tuning_params(); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_match_metric_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned short match_metric_threshold; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + match_metric_threshold = + ((unsigned short)udg->tuning.metric_thres_lsb << 0) | + ((unsigned short)udg->tuning.metric_thres_msb << 8); + + return snprintf(buf, PAGE_SIZE, "%u\n", match_metric_threshold); +} + +static ssize_t udg_sysfs_match_metric_threshold_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long input; + + retval = sstrtoul(buf, 10, &input); + if (retval) + return retval; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + udg->tuning.metric_thres_lsb = (unsigned char)(input >> 0); + udg->tuning.metric_thres_msb = (unsigned char)(input >> 8); + + retval = udg_write_tuning_params(); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t udg_sysfs_max_inter_stroke_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned short max_inter_stroke_time; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + max_inter_stroke_time = + ((unsigned short)udg->tuning.inter_stroke_lsb << 0) | + ((unsigned short)udg->tuning.inter_stroke_msb << 8); + + return snprintf(buf, PAGE_SIZE, "%u\n", max_inter_stroke_time); +} + +static ssize_t udg_sysfs_max_inter_stroke_time_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long input; + + retval = sstrtoul(buf, 10, &input); + if (retval) + return retval; + + retval = udg_read_tuning_params(); + if (retval < 0) + return retval; + + udg->tuning.inter_stroke_lsb = (unsigned char)(input >> 0); + udg->tuning.inter_stroke_msb = (unsigned char)(input >> 8); + + retval = udg_write_tuning_params(); + if (retval < 0) + return retval; + + return count; +} + +static int udg_ctrl_subpacket(unsigned char ctrlreg, + unsigned char subpacket, + struct synaptics_rmi4_f12_query_5 *query_5) +{ + int retval; + unsigned char cnt; + unsigned char regnum; + unsigned char bitnum; + unsigned char q5_index; + unsigned char q6_index; + unsigned char offset; + unsigned char max_ctrlreg; + unsigned char *query_6; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + max_ctrlreg = (sizeof(query_5->data) - 1) * 8 - 1; + + if (ctrlreg > max_ctrlreg) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Control register number (%d) over limit\n", + __func__, ctrlreg); + return -EINVAL; + } + + q5_index = ctrlreg / 8 + 1; + bitnum = ctrlreg % 8; + if ((query_5->data[q5_index] & (1 << bitnum)) == 0x00) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Control %d is not present\n", + __func__, ctrlreg); + return -EINVAL; + } + + query_6 = kmalloc(query_5->size_of_query6, GFP_KERNEL); + if (!query_6) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for query 6\n", + __func__); + return -ENOMEM; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->query_base_addr + 6, + query_6, + query_5->size_of_query6); + if (retval < 0) + goto exit; + + q6_index = 0; + + for (regnum = 0; regnum < ctrlreg; regnum++) { + q5_index = regnum / 8 + 1; + bitnum = regnum % 8; + if ((query_5->data[q5_index] & (1 << bitnum)) == 0x00) + continue; + + if (query_6[q6_index] == 0x00) + q6_index += 3; + else + q6_index++; + + while (query_6[q6_index] & ~MASK_7BIT) + q6_index++; + + q6_index++; + } + + cnt = 0; + q6_index++; + offset = subpacket / 7; + bitnum = subpacket % 7; + + do { + if (cnt == offset) { + if (query_6[q6_index + cnt] & (1 << bitnum)) + retval = 1; + else + retval = 0; + goto exit; + } + cnt++; + } while (query_6[q6_index + cnt - 1] & ~MASK_7BIT); + + retval = 0; + +exit: + kfree(query_6); + + return retval; +} + +static int udg_read_tuning_params(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_18, + udg->ctrl_buf, + udg->ctrl_18_sub10_off + sizeof(struct udg_tuning)); + if (retval < 0) + return retval; + + secure_memcpy(udg->tuning.data, + sizeof(udg->tuning.data), + (unsigned char *)&udg->ctrl_buf[udg->ctrl_18_sub10_off], + sizeof(struct udg_tuning), + sizeof(struct udg_tuning)); + + return 0; +} + +static int udg_write_tuning_params(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + secure_memcpy((unsigned char *)&udg->ctrl_buf[udg->ctrl_18_sub10_off], + sizeof(struct udg_tuning), + udg->tuning.data, + sizeof(udg->tuning.data), + sizeof(struct udg_tuning)); + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_18, + udg->ctrl_buf, + udg->ctrl_18_sub10_off + sizeof(struct udg_tuning)); + if (retval < 0) + return retval; + + return 0; +} + +static int udg_detection_enable(bool enable) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_20, + udg->ctrl_buf, + udg->ctrl_20_sub1_off + 1); + if (retval < 0) + return retval; + + if (enable) + udg->ctrl_buf[udg->ctrl_20_sub1_off] = WAKEUP_GESTURE_MODE; + else + udg->ctrl_buf[udg->ctrl_20_sub1_off] = udg->report_flags; + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_20, + udg->ctrl_buf, + udg->ctrl_20_sub1_off + 1); + if (retval < 0) + return retval; + + return 0; +} + +static int udg_engine_enable(bool enable) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + if (enable) { + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_27, + udg->ctrl_buf, + udg->ctrl_27_sub5_off + 1); + if (retval < 0) + return retval; + + udg->ctrl_buf[udg->ctrl_27_sub5_off] |= + (1 << CTRL27_UDG_ENABLE_BIT); + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_27, + udg->ctrl_buf, + udg->ctrl_27_sub5_off + 1); + if (retval < 0) + return retval; + } else { + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_27, + udg->ctrl_buf, + udg->ctrl_27_sub5_off + 1); + if (retval < 0) + return retval; + + udg->ctrl_buf[udg->ctrl_27_sub5_off] &= + ~(1 << CTRL27_UDG_ENABLE_BIT); + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_27, + udg->ctrl_buf, + udg->ctrl_27_sub5_off + 1); + if (retval < 0) + return retval; + } + + return 0; +} + +static void udg_report(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + atomic_set(&udg->attn_event, 1); + + if (rmi4_data->suspend) { + if (rmi4_data->gesture_detection[0] == 0) { + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.data_4, + rmi4_data->gesture_detection, + sizeof(rmi4_data->gesture_detection)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read gesture detection\n", + __func__); + return; + } + } + + udg->detection_status = rmi4_data->gesture_detection[0]; + rmi4_data->gesture_detection[0] = 0; + + if (udg->detection_status == DETECTION) { + input_report_key(udg->udg_dev, KEY_WAKEUP, 1); + input_sync(udg->udg_dev); + input_report_key(udg->udg_dev, KEY_WAKEUP, 0); + input_sync(udg->udg_dev); + rmi4_data->suspend = false; + } + } + + return; +} + +static int udg_set_index(unsigned char index) +{ + int retval; + struct synaptics_rmi4_f12_control_41 control_41; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + if (index >= udg->max_num_templates) + return -EINVAL; + + udg->template_index = index; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_41, + control_41.data, + sizeof(control_41.data)); + if (retval < 0) + return retval; + + control_41.template_index = udg->template_index; + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.ctrl_41, + control_41.data, + sizeof(control_41.data)); + if (retval < 0) + return retval; + + return 0; +} + +#ifdef STORE_GESTURES +static int udg_read_valid_data(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.template_flags, + udg->valid_buf, + sizeof(udg->valid_buf)); + if (retval < 0) + return retval; + + return 0; +} + +static int udg_write_valid_data(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.template_flags, + udg->valid_buf, + sizeof(udg->valid_buf)); + if (retval < 0) + return retval; + + return 0; +} + +static int udg_read_template_data(unsigned char index) +{ + int retval; + unsigned char *storage; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + udg_set_index(index); + storage = &(udg->storage_buf[index * udg->template_data_size]); + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.template_data, + storage, + udg->template_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read template data\n", + __func__); + return retval; + } + + return 0; +} + +static int udg_write_template_data(void) +{ + int retval; + unsigned char ii; + unsigned char *storage; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + for (ii = 0; ii < udg->gestures_to_store; ii++) { + udg_set_index(ii); + storage = &(udg->storage_buf[ii * udg->template_data_size]); + + retval = synaptics_rmi4_reg_write(rmi4_data, + udg->addr.template_data, + storage, + udg->template_data_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write template data\n", + __func__); + return retval; + } + } + + return 0; +} +#endif + +static int udg_reg_init(void) +{ + int retval; + unsigned char ii; + unsigned char data_offset; + unsigned char size_of_query; + unsigned char ctrl_18_offset; + unsigned char ctrl_20_offset; + unsigned char ctrl_23_offset; + unsigned char ctrl_27_offset; + unsigned char ctrl_41_offset; + struct synaptics_rmi4_f12_query_0 query_0; + struct synaptics_rmi4_f12_query_5 query_5; + struct synaptics_rmi4_f12_query_8 query_8; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->query_base_addr + 7, + &size_of_query, + sizeof(size_of_query)); + if (retval < 0) + return retval; + + if (size_of_query < 4) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: User defined gesture support unavailable (missing data registers)\n", + __func__); + retval = -ENODEV; + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->query_base_addr + 8, + query_8.data, + sizeof(query_8.data)); + if (retval < 0) + return retval; + + if ((query_8.data16_is_present) && + (query_8.data17_is_present) && + (query_8.data18_is_present) && + (query_8.data19_is_present) && + (query_8.data20_is_present) && + (query_8.data21_is_present)) { + data_offset = query_8.data0_is_present + + query_8.data1_is_present + + query_8.data2_is_present + + query_8.data3_is_present; + udg->addr.data_4 = udg->data_base_addr + data_offset; + data_offset = data_offset + + query_8.data4_is_present + + query_8.data5_is_present + + query_8.data6_is_present + + query_8.data7_is_present + + query_8.data8_is_present + + query_8.data9_is_present + + query_8.data10_is_present + + query_8.data11_is_present + + query_8.data12_is_present + + query_8.data13_is_present + + query_8.data14_is_present + + query_8.data15_is_present; + udg->addr.trace_x = udg->data_base_addr + data_offset; + udg->addr.trace_y = udg->addr.trace_x + 1; + udg->addr.trace_segment = udg->addr.trace_y + 1; + udg->addr.template_helper = udg->addr.trace_segment + 1; + udg->addr.template_data = udg->addr.template_helper + 1; + udg->addr.template_flags = udg->addr.template_data + 1; + } else { + dev_err(rmi4_data->pdev->dev.parent, + "%s: User defined gesture support unavailable (missing data registers)\n", + __func__); + retval = -ENODEV; + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->query_base_addr + 4, + &size_of_query, + sizeof(size_of_query)); + if (retval < 0) + return retval; + + if (size_of_query < 7) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: User defined gesture support unavailable (missing control registers)\n", + __func__); + retval = -ENODEV; + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->query_base_addr + 5, + query_5.data, + sizeof(query_5.data)); + if (retval < 0) + return retval; + + ctrl_18_offset = query_5.ctrl0_is_present + + query_5.ctrl1_is_present + + query_5.ctrl2_is_present + + query_5.ctrl3_is_present + + query_5.ctrl4_is_present + + query_5.ctrl5_is_present + + query_5.ctrl6_is_present + + query_5.ctrl7_is_present + + query_5.ctrl8_is_present + + query_5.ctrl9_is_present + + query_5.ctrl10_is_present + + query_5.ctrl11_is_present + + query_5.ctrl12_is_present + + query_5.ctrl13_is_present + + query_5.ctrl14_is_present + + query_5.ctrl15_is_present + + query_5.ctrl16_is_present + + query_5.ctrl17_is_present; + + ctrl_20_offset = ctrl_18_offset + + query_5.ctrl18_is_present + + query_5.ctrl19_is_present; + + ctrl_23_offset = ctrl_20_offset + + query_5.ctrl20_is_present + + query_5.ctrl21_is_present + + query_5.ctrl22_is_present; + + ctrl_27_offset = ctrl_23_offset+ + query_5.ctrl23_is_present + + query_5.ctrl24_is_present + + query_5.ctrl25_is_present + + query_5.ctrl26_is_present; + + ctrl_41_offset = ctrl_27_offset+ + query_5.ctrl27_is_present + + query_5.ctrl28_is_present + + query_5.ctrl29_is_present + + query_5.ctrl30_is_present + + query_5.ctrl31_is_present + + query_5.ctrl32_is_present + + query_5.ctrl33_is_present + + query_5.ctrl34_is_present + + query_5.ctrl35_is_present + + query_5.ctrl36_is_present + + query_5.ctrl37_is_present + + query_5.ctrl38_is_present + + query_5.ctrl39_is_present + + query_5.ctrl40_is_present; + + udg->addr.ctrl_18 = udg->control_base_addr + ctrl_18_offset; + udg->addr.ctrl_20 = udg->control_base_addr + ctrl_20_offset; + udg->addr.ctrl_23 = udg->control_base_addr + ctrl_23_offset; + udg->addr.ctrl_27 = udg->control_base_addr + ctrl_27_offset; + udg->addr.ctrl_41 = udg->control_base_addr + ctrl_41_offset; + + udg->ctrl_18_sub10_off = 0; + for (ii = 0; ii < 10; ii++) { + retval = udg_ctrl_subpacket(18, ii, &query_5); + if (retval == 1) + udg->ctrl_18_sub10_off += ctrl_18_sub_size[ii]; + else if (retval < 0) + return retval; + } + + udg->ctrl_20_sub1_off = 0; + for (ii = 0; ii < 1; ii++) { + retval = udg_ctrl_subpacket(20, ii, &query_5); + if (retval == 1) + udg->ctrl_20_sub1_off += ctrl_20_sub_size[ii]; + else if (retval < 0) + return retval; + } + + udg->ctrl_23_sub3_off = 0; + for (ii = 0; ii < 3; ii++) { + retval = udg_ctrl_subpacket(23, ii, &query_5); + if (retval == 1) + udg->ctrl_23_sub3_off += ctrl_23_sub_size[ii]; + else if (retval < 0) + return retval; + } + + retval = udg_ctrl_subpacket(23, 3, &query_5); + if (retval == 0) + udg->ctrl_23_sub3_off = 0; + else if (retval < 0) + return retval; + + udg->ctrl_27_sub5_off = 0; + for (ii = 0; ii < 5; ii++) { + retval = udg_ctrl_subpacket(27, ii, &query_5); + if (retval == 1) + udg->ctrl_27_sub5_off += ctrl_27_sub_size[ii]; + else if (retval < 0) + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->query_base_addr + 0, + query_0.data, + sizeof(query_0.data)); + if (retval < 0) + return retval; + + udg->max_num_templates = query_0.max_num_templates; + udg->template_size = + ((unsigned short)query_0.template_size_lsb << 0) | + ((unsigned short)query_0.template_size_msb << 8); + udg->template_data_size = udg->template_size * 4 * 2 + 4 + 1; + +#ifdef STORE_GESTURES + udg->gestures_to_store = udg->max_num_templates; + if (GESTURES_TO_STORE < udg->gestures_to_store) + udg->gestures_to_store = GESTURES_TO_STORE; +#endif + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_20, + udg->ctrl_buf, + udg->ctrl_20_sub1_off + 1); + if (retval < 0) + return retval; + + udg->report_flags = udg->ctrl_buf[udg->ctrl_20_sub1_off]; + + retval = synaptics_rmi4_reg_read(rmi4_data, + udg->addr.ctrl_23, + udg->ctrl_buf, + udg->ctrl_23_sub3_off + 1); + if (retval < 0) + return retval; + + udg->object_type_enable1 = udg->ctrl_buf[0]; + if (udg->ctrl_23_sub3_off) + udg->object_type_enable2 = udg->ctrl_buf[udg->ctrl_23_sub3_off]; + + return retval; +} + +static int udg_scan_pdt(void) +{ + int retval; + unsigned char ii; + unsigned char page; + unsigned char intr_count = 0; + unsigned char intr_off; + unsigned char intr_src; + unsigned short addr; + struct synaptics_rmi4_fn_desc fd; + struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; + + for (page = 0; page < PAGES_TO_SERVICE; page++) { + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + addr |= (page << 8); + + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + (unsigned char *)&fd, + sizeof(fd)); + if (retval < 0) + return retval; + + addr &= ~(MASK_8BIT << 8); + + if (fd.fn_number) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Found F%02x\n", + __func__, fd.fn_number); + switch (fd.fn_number) { + case SYNAPTICS_RMI4_F12: + goto f12_found; + break; + } + } else { + break; + } + + intr_count += fd.intr_src_count; + } + } + + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F12\n", + __func__); + return -EINVAL; + +f12_found: + udg->query_base_addr = fd.query_base_addr | (page << 8); + udg->control_base_addr = fd.ctrl_base_addr | (page << 8); + udg->data_base_addr = fd.data_base_addr | (page << 8); + udg->command_base_addr = fd.cmd_base_addr | (page << 8); + + retval = udg_reg_init(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to initialize user defined gesture registers\n", + __func__); + return retval; + } + + udg->intr_mask = 0; + intr_src = fd.intr_src_count; + intr_off = intr_count % 8; + for (ii = intr_off; + ii < (intr_src + intr_off); + ii++) { + udg->intr_mask |= 1 << ii; + } + + rmi4_data->intr_mask[0] |= udg->intr_mask; + + addr = rmi4_data->f01_ctrl_base_addr + 1; + + retval = synaptics_rmi4_reg_write(rmi4_data, + addr, + &rmi4_data->intr_mask[0], + sizeof(rmi4_data->intr_mask[0])); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set interrupt enable bit\n", + __func__); + return retval; + } + + return 0; +} + +static void synaptics_rmi4_udg_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + if (!udg) + return; + + if (udg->intr_mask & intr_mask) + udg_report(); + + return; +} + +static int synaptics_rmi4_udg_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char ii; + unsigned char size; + unsigned char attr_count; + unsigned char param_count; + + if (udg) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Handle already exists\n", + __func__); + return 0; + } + + udg = kzalloc(sizeof(*udg), GFP_KERNEL); + if (!udg) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for udg\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + size = 0; + for (ii = 0; ii < sizeof(ctrl_18_sub_size); ii++) + size += ctrl_18_sub_size[ii]; + size += sizeof(struct udg_tuning); + udg->ctrl_buf = kzalloc(size, GFP_KERNEL); + if (!udg->ctrl_buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for ctrl_buf\n", + __func__); + retval = -ENOMEM; + goto exit_free_udg; + } + + udg->rmi4_data = rmi4_data; + + retval = udg_scan_pdt(); + if (retval < 0) + goto exit_free_ctrl_buf; + + udg->template_data_buf = kzalloc(udg->template_data_size, GFP_KERNEL); + if (!udg->template_data_buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for template_data_buf\n", + __func__); + retval = -ENOMEM; + goto exit_free_ctrl_buf; + } + +#ifdef STORE_GESTURES + udg->storage_buf = kzalloc( + udg->template_data_size * udg->gestures_to_store, + GFP_KERNEL); + if (!udg->storage_buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for storage_buf\n", + __func__); + kfree(udg->template_data_buf); + retval = -ENOMEM; + goto exit_free_ctrl_buf; + } +#endif + + udg->udg_dev = input_allocate_device(); + if (udg->udg_dev == NULL) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate gesture device\n", + __func__); + retval = -ENOMEM; + goto exit_free_template_data_buf; + } + + udg->udg_dev->name = GESTURE_DRIVER_NAME; + udg->udg_dev->phys = GESTURE_PHYS_NAME; + udg->udg_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT; + udg->udg_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION; + udg->udg_dev->dev.parent = rmi4_data->pdev->dev.parent; + input_set_drvdata(udg->udg_dev, rmi4_data); + + set_bit(EV_KEY, udg->udg_dev->evbit); + set_bit(KEY_WAKEUP, udg->udg_dev->keybit); + input_set_capability(udg->udg_dev, EV_KEY, KEY_WAKEUP); + + retval = input_register_device(udg->udg_dev); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to register gesture device\n", + __func__); + input_free_device(udg->udg_dev); + goto exit_free_template_data_buf; + } + + udg->tuning_dir = kobject_create_and_add(TUNING_SYSFS_DIR_NAME, + &udg->udg_dev->dev.kobj); + if (!udg->tuning_dir) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create tuning sysfs directory\n", + __func__); + goto exit_unregister_input_device; + } + + retval = sysfs_create_bin_file(&udg->udg_dev->dev.kobj, &template_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create template data bin file\n", + __func__); + goto exit_remove_sysfs_directory; + } + + retval = sysfs_create_bin_file(&udg->udg_dev->dev.kobj, &trace_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create trace data bin file\n", + __func__); + goto exit_remove_bin_file; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&udg->udg_dev->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto exit_remove_attrs; + } + } + + for (param_count = 0; param_count < ARRAY_SIZE(params); param_count++) { + retval = sysfs_create_file(udg->tuning_dir, + ¶ms[param_count].attr); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create tuning parameters\n", + __func__); + retval = -ENODEV; + goto exit_remove_params; + } + } + + retval = udg_engine_enable(true); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to enable gesture engine\n", + __func__); + goto exit_remove_params; + } + + return 0; + +exit_remove_params: + for (param_count--; param_count >= 0; param_count--) { + sysfs_remove_file(udg->tuning_dir, + ¶ms[param_count].attr); + } + +exit_remove_attrs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&udg->udg_dev->dev.kobj, + &attrs[attr_count].attr); + } + + sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &trace_data); + +exit_remove_bin_file: + sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &template_data); + +exit_remove_sysfs_directory: + kobject_put(udg->tuning_dir); + +exit_unregister_input_device: + input_unregister_device(udg->udg_dev); + +exit_free_template_data_buf: +#ifdef STORE_GESTURES + kfree(udg->storage_buf); +#endif + kfree(udg->template_data_buf); + +exit_free_ctrl_buf: + kfree(udg->ctrl_buf); + +exit_free_udg: + kfree(udg); + udg = NULL; + +exit: + return retval; +} + +static void synaptics_rmi4_udg_remove(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char count; + + if (!udg) + goto exit; + + for (count = 0; count < ARRAY_SIZE(params); count++) { + sysfs_remove_file(udg->tuning_dir, + ¶ms[count].attr); + } + + for (count = 0; count < ARRAY_SIZE(attrs); count++) { + sysfs_remove_file(&udg->udg_dev->dev.kobj, + &attrs[count].attr); + } + + sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &trace_data); + sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &template_data); + kobject_put(udg->tuning_dir); + + input_unregister_device(udg->udg_dev); +#ifdef STORE_GESTURES + kfree(udg->storage_buf); +#endif + kfree(udg->template_data_buf); + kfree(udg->trace_data_buf); + kfree(udg->ctrl_buf); + kfree(udg); + udg = NULL; + +exit: + complete(&udg_remove_complete); +} + +static void synaptics_rmi4_udg_reset(struct synaptics_rmi4_data *rmi4_data) +{ + if (!udg) { + synaptics_rmi4_udg_init(rmi4_data); + return; + } + + udg_scan_pdt(); + udg_engine_enable(true); +#ifdef STORE_GESTURES + udg_write_template_data(); + udg_write_valid_data(); +#endif +} + +static void synaptics_rmi4_udg_reinit(struct synaptics_rmi4_data *rmi4_data) +{ + if (!udg) + return; + + udg_engine_enable(true); +#ifdef STORE_GESTURES + udg_write_template_data(); + udg_write_valid_data(); +#endif +} + +static void synaptics_rmi4_udg_e_suspend(struct synaptics_rmi4_data *rmi4_data) +{ + if (!udg) + return; + + rmi4_data->sleep_enable(rmi4_data, false); + rmi4_data->irq_enable(rmi4_data, true, false); + enable_irq_wake(rmi4_data->irq); + + udg_engine_enable(true); + udg_detection_enable(true); +} + +static void synaptics_rmi4_udg_suspend(struct synaptics_rmi4_data *rmi4_data) +{ + if (!udg) + return; + + rmi4_data->sleep_enable(rmi4_data, false); + rmi4_data->irq_enable(rmi4_data, true, false); + enable_irq_wake(rmi4_data->irq); + + udg_engine_enable(true); + udg_detection_enable(true); +} + +static void synaptics_rmi4_udg_resume(struct synaptics_rmi4_data *rmi4_data) +{ + if (!udg) + return; + + disable_irq_wake(rmi4_data->irq); + udg_detection_enable(false); +} + +static void synaptics_rmi4_udg_l_resume(struct synaptics_rmi4_data *rmi4_data) +{ + if (!udg) + return; + + disable_irq_wake(rmi4_data->irq); + udg_detection_enable(false); +} + +static struct synaptics_rmi4_exp_fn gesture_module = { + .fn_type = RMI_GESTURE, + .init = synaptics_rmi4_udg_init, + .remove = synaptics_rmi4_udg_remove, + .reset = synaptics_rmi4_udg_reset, + .reinit = synaptics_rmi4_udg_reinit, + .early_suspend = synaptics_rmi4_udg_e_suspend, + .suspend = synaptics_rmi4_udg_suspend, + .resume = synaptics_rmi4_udg_resume, + .late_resume = synaptics_rmi4_udg_l_resume, + .attn = synaptics_rmi4_udg_attn, +}; + +static int __init rmi4_gesture_module_init(void) +{ + synaptics_rmi4_new_function(&gesture_module, true); + + return 0; +} + +static void __exit rmi4_gesture_module_exit(void) +{ + synaptics_rmi4_new_function(&gesture_module, false); + + wait_for_completion(&udg_remove_complete); +} + +module_init(rmi4_gesture_module_init); +module_exit(rmi4_gesture_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX User Defined Gesture Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_i2c.c b/synaptics_dsx/synaptics_dsx_i2c.c new file mode 100644 index 0000000000..bc37323c1c --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_i2c.c @@ -0,0 +1,672 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" +#include "linux/moduleparam.h" + +#define SYN_I2C_RETRY_TIMES 10 +#define rd_msgs 1 + +#ifdef CONFIG_DRM +#include +struct drm_panel *active_panel; +#endif + +static unsigned char *wr_buf; + +static struct synaptics_dsx_hw_interface hw_if; + +static struct platform_device *synaptics_dsx_i2c_device; + + +#ifdef CONFIG_OF +static int parse_dt(struct device *dev, struct synaptics_dsx_board_data *bdata) +{ + int retval; + u32 value; + const char *name; + struct property *prop; + struct device_node *np = dev->of_node; + + bdata->irq_gpio = of_get_named_gpio_flags(np, + "synaptics,irq-gpio", 0, + (enum of_gpio_flags *)&bdata->irq_flags); + + retval = of_property_read_u32(np, "synaptics,irq-on-state", + &value); + if (retval < 0) + bdata->irq_on_state = 0; + else + bdata->irq_on_state = value; + + retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name); + if (retval < 0) + bdata->pwr_reg_name = NULL; + else + bdata->pwr_reg_name = name; + + retval = of_property_read_string(np, "synaptics,bus-reg-name", &name); + if (retval < 0) + bdata->bus_reg_name = NULL; + else + bdata->bus_reg_name = name; + + prop = of_find_property(np, "synaptics,power-gpio", NULL); + if (prop && prop->length) { + bdata->power_gpio = of_get_named_gpio_flags(np, + "synaptics,power-gpio", 0, NULL); + retval = of_property_read_u32(np, "synaptics,power-on-state", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,power-on-state property\n", + __func__); + return retval; + } + bdata->power_on_state = value; + } else { + bdata->power_gpio = -1; + } + + prop = of_find_property(np, "synaptics,power-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-delay-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,power-delay-ms property\n", + __func__); + return retval; + } + bdata->power_delay_ms = value; + } else { + bdata->power_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-gpio", NULL); + if (prop && prop->length) { + bdata->reset_gpio = of_get_named_gpio_flags(np, + "synaptics,reset-gpio", 0, NULL); + retval = of_property_read_u32(np, "synaptics,reset-on-state", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-on-state property\n", + __func__); + return retval; + } + bdata->reset_on_state = value; + retval = of_property_read_u32(np, "synaptics,reset-active-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-active-ms property\n", + __func__); + return retval; + } + bdata->reset_active_ms = value; + } else { + bdata->reset_gpio = -1; + } + + prop = of_find_property(np, "synaptics,reset-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-delay-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-delay-ms property\n", + __func__); + return retval; + } + bdata->reset_delay_ms = value; + } else { + bdata->reset_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,max-y-for-2d", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,max-y-for-2d", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,max-y-for-2d property\n", + __func__); + return retval; + } + bdata->max_y_for_2d = value; + } else { + bdata->max_y_for_2d = -1; + } + + bdata->swap_axes = of_property_read_bool(np, "synaptics,swap-axes"); + bdata->x_flip = of_property_read_bool(np, "synaptics,x-flip"); + bdata->y_flip = of_property_read_bool(np, "synaptics,y-flip"); + + prop = of_find_property(np, "synaptics,ub-i2c-addr", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,ub-i2c-addr", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,ub-i2c-addr property\n", + __func__); + return retval; + } + bdata->ub_i2c_addr = (unsigned short)value; + } else { + bdata->ub_i2c_addr = -1; + } + + prop = of_find_property(np, "synaptics,cap-button-codes", NULL); + if (prop && prop->length) { + bdata->cap_button_map->map = devm_kzalloc(dev, + prop->length, + GFP_KERNEL); + if (!bdata->cap_button_map->map) + return -ENOMEM; + bdata->cap_button_map->nbuttons = prop->length / sizeof(u32); + retval = of_property_read_u32_array(np, + "synaptics,cap-button-codes", + bdata->cap_button_map->map, + bdata->cap_button_map->nbuttons); + if (retval < 0) { + bdata->cap_button_map->nbuttons = 0; + bdata->cap_button_map->map = NULL; + } + } else { + bdata->cap_button_map->nbuttons = 0; + bdata->cap_button_map->map = NULL; + } + + prop = of_find_property(np, "synaptics,vir-button-codes", NULL); + if (prop && prop->length) { + bdata->vir_button_map->map = devm_kzalloc(dev, + prop->length, + GFP_KERNEL); + if (!bdata->vir_button_map->map) + return -ENOMEM; + bdata->vir_button_map->nbuttons = prop->length / sizeof(u32); + bdata->vir_button_map->nbuttons /= 5; + retval = of_property_read_u32_array(np, + "synaptics,vir-button-codes", + bdata->vir_button_map->map, + bdata->vir_button_map->nbuttons * 5); + if (retval < 0) { + bdata->vir_button_map->nbuttons = 0; + bdata->vir_button_map->map = NULL; + } + } else { + bdata->vir_button_map->nbuttons = 0; + bdata->vir_button_map->map = NULL; + } + + return 0; +} +#endif + +static int synaptics_rmi4_i2c_alloc_buf(struct synaptics_rmi4_data *rmi4_data, + unsigned int count) +{ + static unsigned int buf_size; + + if (count > buf_size) { + if (buf_size) + kfree(wr_buf); + wr_buf = kzalloc(count, GFP_KERNEL); + if (!wr_buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for buffer\n", + __func__); + buf_size = 0; + return -ENOMEM; + } + buf_size = count; + } + + return 0; +} + +static void synaptics_rmi4_i2c_check_addr(struct synaptics_rmi4_data *rmi4_data, + struct i2c_client *i2c) +{ + if (hw_if.board_data->ub_i2c_addr == -1) + return; + + if (hw_if.board_data->i2c_addr == i2c->addr) + hw_if.board_data->i2c_addr = hw_if.board_data->ub_i2c_addr; + else + hw_if.board_data->i2c_addr = i2c->addr; +} + +static int synaptics_rmi4_i2c_set_page(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr) +{ + int retval = 0; + unsigned char retry; + unsigned char buf[PAGE_SELECT_LEN]; + unsigned char page; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + struct i2c_msg msg[2]; + + msg[0].addr = hw_if.board_data->i2c_addr; + msg[0].flags = 0; + msg[0].len = PAGE_SELECT_LEN; + msg[0].buf = buf; + + page = ((addr >> 8) & MASK_8BIT); + buf[0] = MASK_8BIT; + buf[1] = page; + + if (page != rmi4_data->current_page) { + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(i2c->adapter, &msg[0], 1) == 1) { + rmi4_data->current_page = page; + retval = PAGE_SELECT_LEN; + break; + } + dev_err(rmi4_data->pdev->dev.parent, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + + if (retry == SYN_I2C_RETRY_TIMES / 2) { + synaptics_rmi4_i2c_check_addr(rmi4_data, i2c); + msg[0].addr = hw_if.board_data->i2c_addr; + } + } + } else { + retval = PAGE_SELECT_LEN; + } + + return retval; +} + +static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval = 0; + unsigned char retry; + unsigned char buf; + unsigned char index = 0; + unsigned char xfer_msgs; + unsigned char remaining_msgs; + unsigned short i2c_addr; + unsigned short data_offset = 0; + unsigned int remaining_length = length; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + struct i2c_adapter *adap = i2c->adapter; + struct i2c_msg msg[rd_msgs + 1]; + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) { + retval = -EIO; + goto exit; + } + + msg[0].addr = hw_if.board_data->i2c_addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &buf; + msg[rd_msgs].addr = hw_if.board_data->i2c_addr; + msg[rd_msgs].flags = I2C_M_RD; + msg[rd_msgs].len = (unsigned short)remaining_length; + msg[rd_msgs].buf = &data[data_offset]; + + buf = addr & MASK_8BIT; + + remaining_msgs = rd_msgs + 1; + + while (remaining_msgs) { + xfer_msgs = remaining_msgs; + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + retval = i2c_transfer(adap, &msg[index], xfer_msgs); + if (retval == xfer_msgs) + break; + + dev_err(rmi4_data->pdev->dev.parent, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + + if (retry == SYN_I2C_RETRY_TIMES / 2) { + synaptics_rmi4_i2c_check_addr(rmi4_data, i2c); + i2c_addr = hw_if.board_data->i2c_addr; + msg[0].addr = i2c_addr; + msg[rd_msgs].addr = i2c_addr; + } + } + + if (retry == SYN_I2C_RETRY_TIMES) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: I2C read over retry limit\n", + __func__); + retval = -EIO; + goto exit; + } + + remaining_msgs -= xfer_msgs; + index += xfer_msgs; + } + + retval = length; + +exit: + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + return retval; +} + +static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned char retry; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + struct i2c_msg msg[2]; + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + retval = synaptics_rmi4_i2c_alloc_buf(rmi4_data, length + 1); + if (retval < 0) + goto exit; + + retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) { + retval = -EIO; + goto exit; + } + + msg[0].addr = hw_if.board_data->i2c_addr; + msg[0].flags = 0; + msg[0].len = (unsigned short)(length + 1); + msg[0].buf = wr_buf; + + wr_buf[0] = addr & MASK_8BIT; + retval = secure_memcpy(&wr_buf[1], length, &data[0], length, length); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy data\n", + __func__); + goto exit; + } + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(i2c->adapter, &msg[0], 1) == 1) { + retval = length; + break; + } + dev_err(rmi4_data->pdev->dev.parent, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + + if (retry == SYN_I2C_RETRY_TIMES / 2) { + synaptics_rmi4_i2c_check_addr(rmi4_data, i2c); + msg[0].addr = hw_if.board_data->i2c_addr; + } + } + + if (retry == SYN_I2C_RETRY_TIMES) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: I2C write over retry limit\n", + __func__); + retval = -EIO; + } + +exit: + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + return retval; +} + +#ifdef CONFIG_DRM +static int check_dt(struct device_node *np) +{ + int i; + int count; + struct device_node *node; + struct drm_panel *panel; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) + return 0; + + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + panel = of_drm_find_panel(node); + of_node_put(node); + if (!IS_ERR(panel)) { + active_panel = panel; + return 0; + } + } + + return PTR_ERR(panel); +} +#endif + +static int check_default_tp(struct device_node *dt, const char *prop) +{ + const char *active_tp; + const char *compatible; + char *start; + int ret; + + ret = of_property_read_string(dt->parent, prop, &active_tp); + if (ret) { + pr_err(" %s:fail to read %s %d\n", __func__, prop, ret); + return -ENODEV; + } + + ret = of_property_read_string(dt, "compatible", &compatible); + if (ret < 0) { + pr_err(" %s:fail to read %s %d\n", __func__, "compatible", ret); + return -ENODEV; + } + + start = strnstr(active_tp, compatible, strlen(active_tp)); + if (start == NULL) { + pr_err(" %s:no match compatible, %s, %s\n", + __func__, compatible, active_tp); + ret = -ENODEV; + } + + return ret; +} + +static struct synaptics_dsx_bus_access bus_access = { + .type = BUS_I2C, + .read = synaptics_rmi4_i2c_read, + .write = synaptics_rmi4_i2c_write, +}; + +static void synaptics_rmi4_i2c_dev_release(struct device *dev) +{ + kfree(synaptics_dsx_i2c_device); +} + +static int synaptics_rmi4_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int retval; + struct device_node *dp = client->dev.of_node; + +#ifdef CONFIG_DRM + retval = check_dt(dp); + if (retval == -EPROBE_DEFER) + return retval; + + if (retval) { + if (!check_default_tp(dp, "qcom,i2c-touch-active")) + retval = -EPROBE_DEFER; + else + retval = -ENODEV; + + return retval; + } +#endif + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, + "%s: SMBus byte data commands not supported by host\n", + __func__); + return -EIO; + } + + synaptics_dsx_i2c_device = kzalloc( + sizeof(struct platform_device), + GFP_KERNEL); + if (!synaptics_dsx_i2c_device) { + dev_err(&client->dev, + "%s: Failed to allocate memory for synaptics_dsx_i2c_device\n", + __func__); + return -ENOMEM; + } + +#ifdef CONFIG_OF + if (client->dev.of_node) { + hw_if.board_data = devm_kzalloc(&client->dev, + sizeof(struct synaptics_dsx_board_data), + GFP_KERNEL); + if (!hw_if.board_data) { + dev_err(&client->dev, + "%s: Failed to allocate memory for board data\n", + __func__); + return -ENOMEM; + } + hw_if.board_data->cap_button_map = devm_kzalloc(&client->dev, + sizeof(struct synaptics_dsx_button_map), + GFP_KERNEL); + if (!hw_if.board_data->cap_button_map) { + dev_err(&client->dev, + "%s: Failed to allocate memory for 0D button map\n", + __func__); + return -ENOMEM; + } + hw_if.board_data->vir_button_map = devm_kzalloc(&client->dev, + sizeof(struct synaptics_dsx_button_map), + GFP_KERNEL); + if (!hw_if.board_data->vir_button_map) { + dev_err(&client->dev, + "%s: Failed to allocate memory for virtual button map\n", + __func__); + return -ENOMEM; + } + parse_dt(&client->dev, hw_if.board_data); + } +#else + hw_if.board_data = client->dev.platform_data; +#endif + + hw_if.bus_access = &bus_access; + hw_if.board_data->i2c_addr = client->addr; + + synaptics_dsx_i2c_device->name = PLATFORM_DRIVER_NAME; + synaptics_dsx_i2c_device->id = 0; + synaptics_dsx_i2c_device->num_resources = 0; + synaptics_dsx_i2c_device->dev.parent = &client->dev; + synaptics_dsx_i2c_device->dev.platform_data = &hw_if; + synaptics_dsx_i2c_device->dev.release = synaptics_rmi4_i2c_dev_release; + + retval = platform_device_register(synaptics_dsx_i2c_device); + if (retval) { + dev_err(&client->dev, + "%s: Failed to register platform device\n", + __func__); + return -ENODEV; + } + + return 0; +} + +static int synaptics_rmi4_i2c_remove(struct i2c_client *client) +{ + platform_device_unregister(synaptics_dsx_i2c_device); + + return 0; +} + +static const struct i2c_device_id synaptics_rmi4_id_table[] = { + {I2C_DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table); + +#ifdef CONFIG_OF +static const struct of_device_id synaptics_rmi4_of_match_table[] = { + { + .compatible = "synaptics,dsx-i2c", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, synaptics_rmi4_of_match_table); +#else +#define synaptics_rmi4_of_match_table NULL +#endif + +static struct i2c_driver synaptics_rmi4_i2c_driver = { + .driver = { + .name = I2C_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = synaptics_rmi4_of_match_table, + }, + .probe = synaptics_rmi4_i2c_probe, + .remove = synaptics_rmi4_i2c_remove, + .id_table = synaptics_rmi4_id_table, +}; + +int synaptics_rmi4_bus_init(void) +{ + return i2c_add_driver(&synaptics_rmi4_i2c_driver); +} +EXPORT_SYMBOL(synaptics_rmi4_bus_init); + +void synaptics_rmi4_bus_exit(void) +{ + kfree(wr_buf); + + i2c_del_driver(&synaptics_rmi4_i2c_driver); +} +EXPORT_SYMBOL(synaptics_rmi4_bus_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX I2C Bus Support Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_proximity.c b/synaptics_dsx/synaptics_dsx_proximity.c new file mode 100644 index 0000000000..9bc18fa8c2 --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_proximity.c @@ -0,0 +1,673 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define PROX_PHYS_NAME "synaptics_dsx/proximity" + +#define HOVER_Z_MAX (255) + +#define HOVERING_FINGER_EN (1 << 4) + +static ssize_t synaptics_rmi4_hover_finger_en_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_hover_finger_en_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static struct device_attribute attrs[] = { + __ATTR(hover_finger_en, 0664, + synaptics_rmi4_hover_finger_en_show, + synaptics_rmi4_hover_finger_en_store), +}; + +struct synaptics_rmi4_f12_query_5 { + union { + struct { + unsigned char size_of_query6; + struct { + unsigned char ctrl0_is_present:1; + unsigned char ctrl1_is_present:1; + unsigned char ctrl2_is_present:1; + unsigned char ctrl3_is_present:1; + unsigned char ctrl4_is_present:1; + unsigned char ctrl5_is_present:1; + unsigned char ctrl6_is_present:1; + unsigned char ctrl7_is_present:1; + } __packed; + struct { + unsigned char ctrl8_is_present:1; + unsigned char ctrl9_is_present:1; + unsigned char ctrl10_is_present:1; + unsigned char ctrl11_is_present:1; + unsigned char ctrl12_is_present:1; + unsigned char ctrl13_is_present:1; + unsigned char ctrl14_is_present:1; + unsigned char ctrl15_is_present:1; + } __packed; + struct { + unsigned char ctrl16_is_present:1; + unsigned char ctrl17_is_present:1; + unsigned char ctrl18_is_present:1; + unsigned char ctrl19_is_present:1; + unsigned char ctrl20_is_present:1; + unsigned char ctrl21_is_present:1; + unsigned char ctrl22_is_present:1; + unsigned char ctrl23_is_present:1; + } __packed; + }; + unsigned char data[4]; + }; +}; + +struct synaptics_rmi4_f12_query_8 { + union { + struct { + unsigned char size_of_query9; + struct { + unsigned char data0_is_present:1; + unsigned char data1_is_present:1; + unsigned char data2_is_present:1; + unsigned char data3_is_present:1; + unsigned char data4_is_present:1; + unsigned char data5_is_present:1; + unsigned char data6_is_present:1; + unsigned char data7_is_present:1; + } __packed; + }; + unsigned char data[2]; + }; +}; + +struct prox_finger_data { + union { + struct { + unsigned char object_type_and_status; + unsigned char x_lsb; + unsigned char x_msb; + unsigned char y_lsb; + unsigned char y_msb; + unsigned char z; + } __packed; + unsigned char proximity_data[6]; + }; +}; + +struct synaptics_rmi4_prox_handle { + bool hover_finger_present; + bool hover_finger_en; + unsigned char intr_mask; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; + unsigned short hover_finger_en_addr; + unsigned short hover_finger_data_addr; + struct input_dev *prox_dev; + struct prox_finger_data *finger_data; + struct synaptics_rmi4_data *rmi4_data; +}; + +static struct synaptics_rmi4_prox_handle *prox; + +DECLARE_COMPLETION(prox_remove_complete); + +static void prox_hover_finger_lift(void) +{ + input_report_key(prox->prox_dev, BTN_TOUCH, 0); + input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 0); + input_sync(prox->prox_dev); + prox->hover_finger_present = false; +} + +static void prox_hover_finger_report(void) +{ + int retval; + int x; + int y; + int z; + struct prox_finger_data *data; + struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data; + + data = prox->finger_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + prox->hover_finger_data_addr, + data->proximity_data, + sizeof(data->proximity_data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read hovering finger data\n", + __func__); + return; + } + + if (data->object_type_and_status != F12_HOVERING_FINGER_STATUS) { + if (prox->hover_finger_present) + prox_hover_finger_lift(); + + return; + } + + x = (data->x_msb << 8) | (data->x_lsb); + y = (data->y_msb << 8) | (data->y_lsb); + z = HOVER_Z_MAX - data->z; + + input_report_key(prox->prox_dev, BTN_TOUCH, 0); + input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 1); + input_report_abs(prox->prox_dev, ABS_X, x); + input_report_abs(prox->prox_dev, ABS_Y, y); + input_report_abs(prox->prox_dev, ABS_DISTANCE, z); + + input_sync(prox->prox_dev); + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: x = %d y = %d z = %d\n", + __func__, x, y, z); + + prox->hover_finger_present = true; +} + +static int prox_set_hover_finger_en(void) +{ + int retval; + unsigned char object_report_enable; + struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + prox->hover_finger_en_addr, + &object_report_enable, + sizeof(object_report_enable)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read from object report enable register\n", + __func__); + return retval; + } + + if (prox->hover_finger_en) + object_report_enable |= HOVERING_FINGER_EN; + else + object_report_enable &= ~HOVERING_FINGER_EN; + + retval = synaptics_rmi4_reg_write(rmi4_data, + prox->hover_finger_en_addr, + &object_report_enable, + sizeof(object_report_enable)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write to object report enable register\n", + __func__); + return retval; + } + + return 0; +} + +static void prox_set_params(void) +{ + input_set_abs_params(prox->prox_dev, ABS_X, 0, + prox->rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(prox->prox_dev, ABS_Y, 0, + prox->rmi4_data->sensor_max_y, 0, 0); + input_set_abs_params(prox->prox_dev, ABS_DISTANCE, 0, + HOVER_Z_MAX, 0, 0); +} + +static int prox_reg_init(void) +{ + int retval; + unsigned char ctrl_23_offset; + unsigned char data_1_offset; + struct synaptics_rmi4_f12_query_5 query_5; + struct synaptics_rmi4_f12_query_8 query_8; + struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + prox->query_base_addr + 5, + query_5.data, + sizeof(query_5.data)); + if (retval < 0) + return retval; + + ctrl_23_offset = query_5.ctrl0_is_present + + query_5.ctrl1_is_present + + query_5.ctrl2_is_present + + query_5.ctrl3_is_present + + query_5.ctrl4_is_present + + query_5.ctrl5_is_present + + query_5.ctrl6_is_present + + query_5.ctrl7_is_present + + query_5.ctrl8_is_present + + query_5.ctrl9_is_present + + query_5.ctrl10_is_present + + query_5.ctrl11_is_present + + query_5.ctrl12_is_present + + query_5.ctrl13_is_present + + query_5.ctrl14_is_present + + query_5.ctrl15_is_present + + query_5.ctrl16_is_present + + query_5.ctrl17_is_present + + query_5.ctrl18_is_present + + query_5.ctrl19_is_present + + query_5.ctrl20_is_present + + query_5.ctrl21_is_present + + query_5.ctrl22_is_present; + + prox->hover_finger_en_addr = prox->control_base_addr + ctrl_23_offset; + + retval = synaptics_rmi4_reg_read(rmi4_data, + prox->query_base_addr + 8, + query_8.data, + sizeof(query_8.data)); + if (retval < 0) + return retval; + + data_1_offset = query_8.data0_is_present; + prox->hover_finger_data_addr = prox->data_base_addr + data_1_offset; + + return retval; +} + +static int prox_scan_pdt(void) +{ + int retval; + unsigned char ii; + unsigned char page; + unsigned char intr_count = 0; + unsigned char intr_off; + unsigned char intr_src; + unsigned short addr; + struct synaptics_rmi4_fn_desc fd; + struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data; + + for (page = 0; page < PAGES_TO_SERVICE; page++) { + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + addr |= (page << 8); + + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + (unsigned char *)&fd, + sizeof(fd)); + if (retval < 0) + return retval; + + addr &= ~(MASK_8BIT << 8); + + if (fd.fn_number) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Found F%02x\n", + __func__, fd.fn_number); + switch (fd.fn_number) { + case SYNAPTICS_RMI4_F12: + goto f12_found; + break; + } + } else { + break; + } + + intr_count += fd.intr_src_count; + } + } + + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F12\n", + __func__); + return -EINVAL; + +f12_found: + prox->query_base_addr = fd.query_base_addr | (page << 8); + prox->control_base_addr = fd.ctrl_base_addr | (page << 8); + prox->data_base_addr = fd.data_base_addr | (page << 8); + prox->command_base_addr = fd.cmd_base_addr | (page << 8); + + retval = prox_reg_init(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to initialize proximity registers\n", + __func__); + return retval; + } + + prox->intr_mask = 0; + intr_src = fd.intr_src_count; + intr_off = intr_count % 8; + for (ii = intr_off; + ii < (intr_src + intr_off); + ii++) { + prox->intr_mask |= 1 << ii; + } + + rmi4_data->intr_mask[0] |= prox->intr_mask; + + addr = rmi4_data->f01_ctrl_base_addr + 1; + + retval = synaptics_rmi4_reg_write(rmi4_data, + addr, + &(rmi4_data->intr_mask[0]), + sizeof(rmi4_data->intr_mask[0])); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set interrupt enable bit\n", + __func__); + return retval; + } + + return 0; +} + +static ssize_t synaptics_rmi4_hover_finger_en_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (!prox) + return -ENODEV; + + return snprintf(buf, PAGE_SIZE, "%u\n", + prox->hover_finger_en); +} + +static ssize_t synaptics_rmi4_hover_finger_en_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data; + + if (!prox) + return -ENODEV; + + if (kstrtouint(buf, 16, &input) != 1) + return -EINVAL; + + if (input == 1) + prox->hover_finger_en = true; + else if (input == 0) + prox->hover_finger_en = false; + else + return -EINVAL; + + retval = prox_set_hover_finger_en(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to change hovering finger enable setting\n", + __func__); + return retval; + } + + return count; +} + +int synaptics_rmi4_prox_hover_finger_en(bool enable) +{ + int retval; + + if (!prox) + return -ENODEV; + + prox->hover_finger_en = enable; + + retval = prox_set_hover_finger_en(); + if (retval < 0) + return retval; + + return 0; +} +EXPORT_SYMBOL(synaptics_rmi4_prox_hover_finger_en); + +static void synaptics_rmi4_prox_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + if (!prox) + return; + + if (prox->intr_mask & intr_mask) + prox_hover_finger_report(); +} + +static int synaptics_rmi4_prox_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char attr_count; + + if (prox) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Handle already exists\n", + __func__); + return 0; + } + + prox = kzalloc(sizeof(*prox), GFP_KERNEL); + if (!prox) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for prox\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + prox->finger_data = kzalloc(sizeof(*(prox->finger_data)), GFP_KERNEL); + if (!prox->finger_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for finger_data\n", + __func__); + retval = -ENOMEM; + goto exit_free_prox; + } + + prox->rmi4_data = rmi4_data; + + retval = prox_scan_pdt(); + if (retval < 0) + goto exit_free_finger_data; + + prox->hover_finger_en = true; + + retval = prox_set_hover_finger_en(); + if (retval < 0) + return retval; + + prox->prox_dev = input_allocate_device(); + if (prox->prox_dev == NULL) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate proximity device\n", + __func__); + retval = -ENOMEM; + goto exit_free_finger_data; + } + + prox->prox_dev->name = PROXIMITY_DRIVER_NAME; + prox->prox_dev->phys = PROX_PHYS_NAME; + prox->prox_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT; + prox->prox_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION; + prox->prox_dev->dev.parent = rmi4_data->pdev->dev.parent; + input_set_drvdata(prox->prox_dev, rmi4_data); + + set_bit(EV_KEY, prox->prox_dev->evbit); + set_bit(EV_ABS, prox->prox_dev->evbit); + set_bit(BTN_TOUCH, prox->prox_dev->keybit); + set_bit(BTN_TOOL_FINGER, prox->prox_dev->keybit); +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, prox->prox_dev->propbit); +#endif + + prox_set_params(); + + retval = input_register_device(prox->prox_dev); + if (retval) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to register proximity device\n", + __func__); + goto exit_free_input_device; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs attributes\n", + __func__); + goto exit_free_sysfs; + } + } + + return 0; + +exit_free_sysfs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + + input_unregister_device(prox->prox_dev); + prox->prox_dev = NULL; + +exit_free_input_device: + if (prox->prox_dev) + input_free_device(prox->prox_dev); + +exit_free_finger_data: + kfree(prox->finger_data); + +exit_free_prox: + kfree(prox); + prox = NULL; + +exit: + return retval; +} + +static void synaptics_rmi4_prox_remove(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char attr_count; + + if (!prox) + goto exit; + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, + &attrs[attr_count].attr); + } + + input_unregister_device(prox->prox_dev); + kfree(prox->finger_data); + kfree(prox); + prox = NULL; + +exit: + complete(&prox_remove_complete); +} + +static void synaptics_rmi4_prox_reset(struct synaptics_rmi4_data *rmi4_data) +{ + if (!prox) { + synaptics_rmi4_prox_init(rmi4_data); + return; + } + + prox_hover_finger_lift(); + + prox_scan_pdt(); + + prox_set_hover_finger_en(); +} + +static void synaptics_rmi4_prox_reinit(struct synaptics_rmi4_data *rmi4_data) +{ + if (!prox) + return; + + prox_hover_finger_lift(); + + prox_set_hover_finger_en(); +} + +static void synaptics_rmi4_prox_e_suspend(struct synaptics_rmi4_data *rmi4_data) +{ + if (!prox) + return; + + prox_hover_finger_lift(); +} + +static void synaptics_rmi4_prox_suspend(struct synaptics_rmi4_data *rmi4_data) +{ + if (!prox) + return; + + prox_hover_finger_lift(); +} + +static struct synaptics_rmi4_exp_fn proximity_module = { + .fn_type = RMI_PROXIMITY, + .init = synaptics_rmi4_prox_init, + .remove = synaptics_rmi4_prox_remove, + .reset = synaptics_rmi4_prox_reset, + .reinit = synaptics_rmi4_prox_reinit, + .early_suspend = synaptics_rmi4_prox_e_suspend, + .suspend = synaptics_rmi4_prox_suspend, + .resume = NULL, + .late_resume = NULL, + .attn = synaptics_rmi4_prox_attn, +}; + +static int __init rmi4_proximity_module_init(void) +{ + synaptics_rmi4_new_function(&proximity_module, true); + + return 0; +} + +static void __exit rmi4_proximity_module_exit(void) +{ + synaptics_rmi4_new_function(&proximity_module, false); + + wait_for_completion(&prox_remove_complete); +} + +module_init(rmi4_proximity_module_init); +module_exit(rmi4_proximity_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX Proximity Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_rmi_dev.c b/synaptics_dsx/synaptics_dsx_rmi_dev.c new file mode 100644 index 0000000000..ca366d0dbc --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_rmi_dev.c @@ -0,0 +1,1075 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define CHAR_DEVICE_NAME "rmi" +#define DEVICE_CLASS_NAME "rmidev" +#define SYSFS_FOLDER_NAME "rmidev" +#define DEV_NUMBER 1 +#define REG_ADDR_LIMIT 0xFFFF + +#define RMIDEV_MAJOR_NUM 0 + +static ssize_t rmidev_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t rmidev_sysfs_data_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t rmidev_sysfs_open_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_release_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_attn_state_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmidev_sysfs_pid_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmidev_sysfs_pid_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_term_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_intr_mask_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmidev_sysfs_intr_mask_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_concurrent_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t rmidev_sysfs_concurrent_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +struct rmidev_handle { + dev_t dev_no; + pid_t pid; + unsigned char intr_mask; + unsigned char *tmpbuf; + unsigned int tmpbuf_size; + struct device dev; + struct synaptics_rmi4_data *rmi4_data; + struct kobject *sysfs_dir; + struct kernel_siginfo interrupt_signal; + struct kernel_siginfo terminate_signal; + struct task_struct *task; + void *data; + bool concurrent; +}; + +struct rmidev_data { + int ref_count; + struct cdev main_dev; + struct class *device_class; + struct mutex file_mutex; + struct rmidev_handle *rmi_dev; +}; + +static struct bin_attribute attr_data = { + .attr = { + .name = "data", + .mode = 0664, + }, + .size = 0, + .read = rmidev_sysfs_data_show, + .write = rmidev_sysfs_data_store, +}; + +static struct device_attribute attrs[] = { + __ATTR(open, 0220, + synaptics_rmi4_show_error, + rmidev_sysfs_open_store), + __ATTR(release, 0220, + synaptics_rmi4_show_error, + rmidev_sysfs_release_store), + __ATTR(attn_state, 0444, + rmidev_sysfs_attn_state_show, + synaptics_rmi4_store_error), + __ATTR(pid, 0664, + rmidev_sysfs_pid_show, + rmidev_sysfs_pid_store), + __ATTR(term, 0220, + synaptics_rmi4_show_error, + rmidev_sysfs_term_store), + __ATTR(intr_mask, 0664, + rmidev_sysfs_intr_mask_show, + rmidev_sysfs_intr_mask_store), + __ATTR(concurrent, 0664, + rmidev_sysfs_concurrent_show, + rmidev_sysfs_concurrent_store), +}; + +static int rmidev_major_num = RMIDEV_MAJOR_NUM; + +static struct class *rmidev_device_class; + +static struct rmidev_handle *rmidev; + +DECLARE_COMPLETION(rmidev_remove_complete); + +static irqreturn_t rmidev_sysfs_irq(int irq, void *data) +{ + struct synaptics_rmi4_data *rmi4_data = data; + + sysfs_notify(&rmi4_data->input_dev->dev.kobj, + SYSFS_FOLDER_NAME, "attn_state"); + + return IRQ_HANDLED; +} + +static int rmidev_sysfs_irq_enable(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval = 0; + unsigned char intr_status[MAX_INTR_REGISTERS]; + unsigned long irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT; + + mutex_lock(&(rmi4_data->rmi4_irq_enable_mutex)); + + if (enable) { + if (rmi4_data->irq_enabled) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Interrupt already enabled\n", + __func__); + goto exit; + } + + /* Clear interrupts first */ + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + intr_status, + rmi4_data->num_of_intr_regs); + if (retval < 0) + goto exit; + + retval = request_threaded_irq(rmi4_data->irq, NULL, + rmidev_sysfs_irq, irq_flags, + PLATFORM_DRIVER_NAME, rmi4_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create irq thread\n", + __func__); + goto exit; + } + + rmi4_data->irq_enabled = true; + } else { + if (rmi4_data->irq_enabled) { + disable_irq(rmi4_data->irq); + free_irq(rmi4_data->irq, rmi4_data); + rmi4_data->irq_enabled = false; + } + } + +exit: + mutex_unlock(&(rmi4_data->rmi4_irq_enable_mutex)); + + return retval; +} + +static ssize_t rmidev_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned char intr_status = 0; + unsigned int length = (unsigned int)count; + unsigned short address = (unsigned short)pos; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + + rmi = &(rmi4_data->rmi4_mod_info); + + if (length > (REG_ADDR_LIMIT - address)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Out of register map limit\n", + __func__); + return -EINVAL; + } + + if (length) { + retval = synaptics_rmi4_reg_read(rmi4_data, + address, + (unsigned char *)buf, + length); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read data\n", + __func__); + return retval; + } + } else { + return -EINVAL; + } + + if (!rmidev->concurrent) + goto exit; + + if (address != rmi4_data->f01_data_base_addr) + goto exit; + + if (length <= 1) + goto exit; + + intr_status = buf[1]; + + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + if (fhandler->intr_mask & intr_status) { + rmi4_data->report_touch(rmi4_data, + fhandler); + } + } + } + } + +exit: + return length; +} + +static ssize_t rmidev_sysfs_data_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int length = (unsigned int)count; + unsigned short address = (unsigned short)pos; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + + if (length > (REG_ADDR_LIMIT - address)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Out of register map limit\n", + __func__); + return -EINVAL; + } + + if (length) { + retval = synaptics_rmi4_reg_write(rmi4_data, + address, + (unsigned char *)buf, + length); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write data\n", + __func__); + return retval; + } + } else { + return -EINVAL; + } + + return length; +} + +static ssize_t rmidev_sysfs_open_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + if (rmi4_data->sensor_sleep) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Sensor sleeping\n", + __func__); + return -ENODEV; + } + + rmi4_data->stay_awake = true; + + rmi4_data->irq_enable(rmi4_data, false, false); + rmidev_sysfs_irq_enable(rmi4_data, true); + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Attention interrupt disabled\n", + __func__); + + return count; +} + +static ssize_t rmidev_sysfs_release_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + rmidev_sysfs_irq_enable(rmi4_data, false); + + rmi4_data->reset_device(rmi4_data, false); + + rmi4_data->stay_awake = false; + + return count; +} + +static ssize_t rmidev_sysfs_attn_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int attn_state; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + attn_state = gpio_get_value(bdata->irq_gpio); + + return snprintf(buf, PAGE_SIZE, "%u\n", attn_state); +} + +static ssize_t rmidev_sysfs_pid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", rmidev->pid); +} + +static ssize_t rmidev_sysfs_pid_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + rmidev->pid = input; + + if (rmidev->pid) { + rmidev->task = pid_task(find_vpid(rmidev->pid), PIDTYPE_PID); + if (!rmidev->task) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to locate PID of data logging tool\n", + __func__); + return -EINVAL; + } + } + + return count; +} + +static ssize_t rmidev_sysfs_term_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + if (rmidev->pid) + send_sig_info(SIGTERM, &rmidev->terminate_signal, rmidev->task); + + return count; +} + +static ssize_t rmidev_sysfs_intr_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%02x\n", rmidev->intr_mask); +} + +static ssize_t rmidev_sysfs_intr_mask_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + rmidev->intr_mask = (unsigned char)input; + + return count; +} + +static ssize_t rmidev_sysfs_concurrent_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", rmidev->concurrent); +} + +static ssize_t rmidev_sysfs_concurrent_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + rmidev->concurrent = input > 0 ? true : false; + + return count; +} + +static int rmidev_allocate_buffer(int count) +{ + if (count + 1 > rmidev->tmpbuf_size) { + if (rmidev->tmpbuf_size) + kfree(rmidev->tmpbuf); + rmidev->tmpbuf = kzalloc(count + 1, GFP_KERNEL); + if (!rmidev->tmpbuf) { + dev_err(rmidev->rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for buffer\n", + __func__); + rmidev->tmpbuf_size = 0; + return -ENOMEM; + } + rmidev->tmpbuf_size = count + 1; + } + + return 0; +} + +/* + * rmidev_llseek - set register address to access for RMI device + * + * @filp: pointer to file structure + * @off: + * if whence == SEEK_SET, + * off: 16-bit RMI register address + * if whence == SEEK_CUR, + * off: offset from current position + * if whence == SEEK_END, + * off: offset from end position (0xFFFF) + * @whence: SEEK_SET, SEEK_CUR, or SEEK_END + */ +static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence) +{ + loff_t newpos; + struct rmidev_data *dev_data = filp->private_data; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + mutex_lock(&(dev_data->file_mutex)); + + switch (whence) { + case SEEK_SET: + newpos = off; + break; + case SEEK_CUR: + newpos = filp->f_pos + off; + break; + case SEEK_END: + newpos = REG_ADDR_LIMIT + off; + break; + default: + newpos = -EINVAL; + goto clean_up; + } + + if (newpos < 0 || newpos > REG_ADDR_LIMIT) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: New position 0x%04x is invalid\n", + __func__, (unsigned int)newpos); + newpos = -EINVAL; + goto clean_up; + } + + filp->f_pos = newpos; + +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + + return newpos; +} + +/* + * rmidev_read: read register data from RMI device + * + * @filp: pointer to file structure + * @buf: pointer to user space buffer + * @count: number of bytes to read + * @f_pos: starting RMI register address + */ +static ssize_t rmidev_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + ssize_t retval; + unsigned char intr_status = 0; + unsigned short address; + struct rmidev_data *dev_data = filp->private_data; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + + rmi = &(rmi4_data->rmi4_mod_info); + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + mutex_lock(&(dev_data->file_mutex)); + + if (*f_pos > REG_ADDR_LIMIT) { + retval = -EFAULT; + goto clean_up; + } + + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + + if (count == 0) { + retval = 0; + goto clean_up; + } + address = (unsigned short)(*f_pos); + + rmidev_allocate_buffer(count); + + retval = synaptics_rmi4_reg_read(rmidev->rmi4_data, + *f_pos, + rmidev->tmpbuf, + count); + if (retval < 0) + goto clean_up; + + if (copy_to_user(buf, rmidev->tmpbuf, count)) + retval = -EFAULT; + else + *f_pos += retval; + + if (!rmidev->concurrent) + goto clean_up; + + if (address != rmi4_data->f01_data_base_addr) + goto clean_up; + + if (count <= 1) + goto clean_up; + + intr_status = rmidev->tmpbuf[1]; + + if (!list_empty(&rmi->support_fn_list)) { + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + if (fhandler->intr_mask & intr_status) { + rmi4_data->report_touch(rmi4_data, + fhandler); + } + } + } + } + +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +/* + * rmidev_write: write register data to RMI device + * + * @filp: pointer to file structure + * @buf: pointer to user space buffer + * @count: number of bytes to write + * @f_pos: starting RMI register address + */ +static ssize_t rmidev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + ssize_t retval; + struct rmidev_data *dev_data = filp->private_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + mutex_lock(&(dev_data->file_mutex)); + + if (*f_pos > REG_ADDR_LIMIT) { + retval = -EFAULT; + goto unlock; + } + + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + + if (count == 0) { + retval = 0; + goto unlock; + } + rmidev_allocate_buffer(count); + + if (copy_from_user(rmidev->tmpbuf, buf, count)) { + retval = -EFAULT; + goto unlock; + } + + retval = synaptics_rmi4_reg_write(rmidev->rmi4_data, + *f_pos, + rmidev->tmpbuf, + count); + if (retval >= 0) + *f_pos += retval; + +unlock: + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +static int rmidev_open(struct inode *inp, struct file *filp) +{ + int retval = 0; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + struct rmidev_data *dev_data = + container_of(inp->i_cdev, struct rmidev_data, main_dev); + + if (!dev_data) + return -EACCES; + + if (rmi4_data->sensor_sleep) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Sensor sleeping\n", + __func__); + return -ENODEV; + } + + rmi4_data->stay_awake = true; + + filp->private_data = dev_data; + + mutex_lock(&(dev_data->file_mutex)); + + rmi4_data->irq_enable(rmi4_data, false, false); + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Attention interrupt disabled\n", + __func__); + + if (dev_data->ref_count < 1) + dev_data->ref_count++; + else + retval = -EACCES; + + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +static int rmidev_release(struct inode *inp, struct file *filp) +{ + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + struct rmidev_data *dev_data = + container_of(inp->i_cdev, struct rmidev_data, main_dev); + + if (!dev_data) + return -EACCES; + + mutex_lock(&(dev_data->file_mutex)); + + dev_data->ref_count--; + if (dev_data->ref_count < 0) + dev_data->ref_count = 0; + + rmi4_data->reset_device(rmi4_data, false); + + rmi4_data->stay_awake = false; + + mutex_unlock(&(dev_data->file_mutex)); + + return 0; +} + +static const struct file_operations rmidev_fops = { + .owner = THIS_MODULE, + .llseek = rmidev_llseek, + .read = rmidev_read, + .write = rmidev_write, + .open = rmidev_open, + .release = rmidev_release, +}; + +static void rmidev_device_cleanup(struct rmidev_data *dev_data) +{ + dev_t devno; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + + if (dev_data) { + devno = dev_data->main_dev.dev; + + if (dev_data->device_class) + device_destroy(dev_data->device_class, devno); + + cdev_del(&dev_data->main_dev); + + unregister_chrdev_region(devno, 1); + + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: rmidev device removed\n", + __func__); + } +} + +static char *rmi_char_devnode(struct device *dev, umode_t *mode) +{ + if (!mode) + return NULL; + + *mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + + return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev)); +} + +static int rmidev_create_device_class(void) +{ + if (rmidev_device_class != NULL) + return 0; + + rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME); + + if (IS_ERR(rmidev_device_class)) { + pr_err("%s: Failed to create /dev/%s\n", + __func__, CHAR_DEVICE_NAME); + return -ENODEV; + } + + rmidev_device_class->devnode = rmi_char_devnode; + + return 0; +} + +static void rmidev_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + if (!rmidev) + return; + + if (rmidev->pid && (rmidev->intr_mask & intr_mask)) + send_sig_info(SIGIO, &rmidev->interrupt_signal, rmidev->task); + + return; +} + +static int rmidev_init_device(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + dev_t dev_no; + unsigned char attr_count; + struct rmidev_data *dev_data; + struct device *device_ptr; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + if (rmidev) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Handle already exists\n", + __func__); + return 0; + } + + rmidev = kzalloc(sizeof(*rmidev), GFP_KERNEL); + if (!rmidev) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for rmidev\n", + __func__); + retval = -ENOMEM; + goto err_rmidev; + } + + rmidev->rmi4_data = rmi4_data; + + memset(&rmidev->interrupt_signal, 0, sizeof(rmidev->interrupt_signal)); + rmidev->interrupt_signal.si_signo = SIGIO; + rmidev->interrupt_signal.si_code = SI_USER; + + memset(&rmidev->terminate_signal, 0, sizeof(rmidev->terminate_signal)); + rmidev->terminate_signal.si_signo = SIGTERM; + rmidev->terminate_signal.si_code = SI_USER; + + retval = rmidev_create_device_class(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create device class\n", + __func__); + goto err_device_class; + } + + if (rmidev_major_num) { + dev_no = MKDEV(rmidev_major_num, DEV_NUMBER); + retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME); + } else { + retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to allocate char device region\n", + __func__); + goto err_device_region; + } + + rmidev_major_num = MAJOR(dev_no); + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Major number of rmidev = %d\n", + __func__, rmidev_major_num); + } + + dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); + if (!dev_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for dev_data\n", + __func__); + retval = -ENOMEM; + goto err_dev_data; + } + + mutex_init(&dev_data->file_mutex); + dev_data->rmi_dev = rmidev; + rmidev->data = dev_data; + + cdev_init(&dev_data->main_dev, &rmidev_fops); + + retval = cdev_add(&dev_data->main_dev, dev_no, 1); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to add rmi char device\n", + __func__); + goto err_char_device; + } + + dev_set_name(&rmidev->dev, "rmidev%d", MINOR(dev_no)); + dev_data->device_class = rmidev_device_class; + + device_ptr = device_create(dev_data->device_class, NULL, dev_no, + NULL, CHAR_DEVICE_NAME"%d", MINOR(dev_no)); + if (IS_ERR(device_ptr)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create rmi char device\n", + __func__); + retval = -ENODEV; + goto err_char_device; + } + + retval = gpio_export(bdata->irq_gpio, false); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to export attention gpio\n", + __func__); + } else { + retval = gpio_export_link(&(rmi4_data->input_dev->dev), + "attn", bdata->irq_gpio); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s Failed to create gpio symlink\n", + __func__); + } else { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Exported attention gpio %d\n", + __func__, bdata->irq_gpio); + } + } + + rmidev->sysfs_dir = kobject_create_and_add(SYSFS_FOLDER_NAME, + &rmi4_data->input_dev->dev.kobj); + if (!rmidev->sysfs_dir) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs directory\n", + __func__); + retval = -ENODEV; + goto err_sysfs_dir; + } + + retval = sysfs_create_bin_file(rmidev->sysfs_dir, + &attr_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs bin file\n", + __func__); + goto err_sysfs_bin; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(rmidev->sysfs_dir, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto err_sysfs_attrs; + } + } + + return 0; + +err_sysfs_attrs: + for (attr_count--; attr_count >= 0; attr_count--) + sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr); + + sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data); + +err_sysfs_bin: + kobject_put(rmidev->sysfs_dir); + +err_sysfs_dir: + sysfs_remove_link(&(rmi4_data->input_dev->dev.kobj), "attn"); + gpio_unexport(bdata->irq_gpio); + +err_char_device: + rmidev_device_cleanup(dev_data); + kfree(dev_data); + +err_dev_data: + unregister_chrdev_region(dev_no, 1); + +err_device_region: + if (rmidev_device_class != NULL) { + class_destroy(rmidev_device_class); + rmidev_device_class = NULL; + } + +err_device_class: + kfree(rmidev); + rmidev = NULL; + +err_rmidev: + return retval; +} + +static void rmidev_remove_device(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char attr_count; + struct rmidev_data *dev_data; + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + if (!rmidev) + goto exit; + + rmidev_major_num = RMIDEV_MAJOR_NUM; + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) + sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr); + + sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data); + + kobject_put(rmidev->sysfs_dir); + + sysfs_remove_link(&(rmi4_data->input_dev->dev.kobj), "attn"); + gpio_unexport(bdata->irq_gpio); + + dev_data = rmidev->data; + if (dev_data) { + rmidev_device_cleanup(dev_data); + kfree(dev_data); + } + + unregister_chrdev_region(rmidev->dev_no, 1); + + if (rmidev_device_class != NULL) { + class_destroy(rmidev_device_class); + rmidev_device_class = NULL; + } + + kfree(rmidev->tmpbuf); + + kfree(rmidev); + rmidev = NULL; + +exit: + complete(&rmidev_remove_complete); +} + +static struct synaptics_rmi4_exp_fn rmidev_module = { + .fn_type = RMI_DEV, + .init = rmidev_init_device, + .remove = rmidev_remove_device, + .reset = NULL, + .reinit = NULL, + .early_suspend = NULL, + .suspend = NULL, + .resume = NULL, + .late_resume = NULL, + .attn = rmidev_attn, +}; + +static int __init rmidev_module_init(void) +{ + synaptics_rmi4_new_function(&rmidev_module, true); + + return 0; +} + +static void __exit rmidev_module_exit(void) +{ + synaptics_rmi4_new_function(&rmidev_module, false); + + wait_for_completion(&rmidev_remove_complete); +} + +module_init(rmidev_module_init); +module_exit(rmidev_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX RMI Dev Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_rmi_hid_i2c.c b/synaptics_dsx/synaptics_dsx_rmi_hid_i2c.c new file mode 100644 index 0000000000..135dd81e13 --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_rmi_hid_i2c.c @@ -0,0 +1,989 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define SYN_I2C_RETRY_TIMES 10 + +#define REPORT_ID_GET_BLOB 0x07 +#define REPORT_ID_WRITE 0x09 +#define REPORT_ID_READ_ADDRESS 0x0a +#define REPORT_ID_READ_DATA 0x0b +#define REPORT_ID_SET_RMI_MODE 0x0f + +#define PREFIX_USAGE_PAGE_1BYTE 0x05 +#define PREFIX_USAGE_PAGE_2BYTES 0x06 +#define PREFIX_USAGE 0x09 +#define PREFIX_REPORT_ID 0x85 +#define PREFIX_REPORT_COUNT_1BYTE 0x95 +#define PREFIX_REPORT_COUNT_2BYTES 0x96 + +#define USAGE_GET_BLOB 0xc5 +#define USAGE_WRITE 0x02 +#define USAGE_READ_ADDRESS 0x03 +#define USAGE_READ_DATA 0x04 +#define USAGE_SET_MODE 0x06 + +#define FEATURE_REPORT_TYPE 0x03 + +#define VENDOR_DEFINED_PAGE 0xff00 + +#define BLOB_REPORT_SIZE 256 + +#define RESET_COMMAND 0x01 +#define GET_REPORT_COMMAND 0x02 +#define SET_REPORT_COMMAND 0x03 +#define SET_POWER_COMMAND 0x08 + +#define FINGER_MODE 0x00 +#define RMI_MODE 0x02 + +struct hid_report_info { + unsigned char get_blob_id; + unsigned char write_id; + unsigned char read_addr_id; + unsigned char read_data_id; + unsigned char set_mode_id; + unsigned int blob_size; +}; + +static struct hid_report_info hid_report; + +struct hid_device_descriptor { + unsigned short device_descriptor_length; + unsigned short format_version; + unsigned short report_descriptor_length; + unsigned short report_descriptor_index; + unsigned short input_register_index; + unsigned short input_report_max_length; + unsigned short output_register_index; + unsigned short output_report_max_length; + unsigned short command_register_index; + unsigned short data_register_index; + unsigned short vendor_id; + unsigned short product_id; + unsigned short version_id; + unsigned int reserved; +}; + +static struct hid_device_descriptor hid_dd; + +struct i2c_rw_buffer { + unsigned char *read; + unsigned char *write; + unsigned int read_size; + unsigned int write_size; +}; + +static struct i2c_rw_buffer buffer; + +#ifdef CONFIG_OF +static int parse_dt(struct device *dev, struct synaptics_dsx_board_data *bdata) +{ + int retval; + u32 value; + const char *name; + struct property *prop; + struct device_node *np = dev->of_node; + + bdata->irq_gpio = of_get_named_gpio_flags(np, + "synaptics,irq-gpio", 0, + (enum of_gpio_flags *)&bdata->irq_flags); + + retval = of_property_read_u32(np, "synaptics,irq-on-state", + &value); + if (retval < 0) + bdata->irq_on_state = 0; + else + bdata->irq_on_state = value; + + retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name); + if (retval < 0) + bdata->pwr_reg_name = NULL; + else + bdata->pwr_reg_name = name; + + retval = of_property_read_string(np, "synaptics,bus-reg-name", &name); + if (retval < 0) + bdata->bus_reg_name = NULL; + else + bdata->bus_reg_name = name; + + prop = of_find_property(np, "synaptics,power-gpio", NULL); + if (prop && prop->length) { + bdata->power_gpio = of_get_named_gpio_flags(np, + "synaptics,power-gpio", 0, NULL); + retval = of_property_read_u32(np, "synaptics,power-on-state", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,power-on-state property\n", + __func__); + return retval; + } + bdata->power_on_state = value; + } else { + bdata->power_gpio = -1; + } + + prop = of_find_property(np, "synaptics,power-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-delay-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,power-delay-ms property\n", + __func__); + return retval; + } + bdata->power_delay_ms = value; + } else { + bdata->power_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-gpio", NULL); + if (prop && prop->length) { + bdata->reset_gpio = of_get_named_gpio_flags(np, + "synaptics,reset-gpio", 0, NULL); + retval = of_property_read_u32(np, "synaptics,reset-on-state", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-on-state property\n", + __func__); + return retval; + } + bdata->reset_on_state = value; + retval = of_property_read_u32(np, "synaptics,reset-active-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-active-ms property\n", + __func__); + return retval; + } + bdata->reset_active_ms = value; + } else { + bdata->reset_gpio = -1; + } + + prop = of_find_property(np, "synaptics,reset-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-delay-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-delay-ms property\n", + __func__); + return retval; + } + bdata->reset_delay_ms = value; + } else { + bdata->reset_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,dev-dscrptr-addr", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,dev-dscrptr-addr", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,dev-dscrptr-addr property\n", + __func__); + return retval; + } + bdata->device_descriptor_addr = (unsigned short)value; + } else { + bdata->device_descriptor_addr = 0; + } + + prop = of_find_property(np, "synaptics,max-y-for-2d", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,max-y-for-2d", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,max-y-for-2d property\n", + __func__); + return retval; + } + bdata->max_y_for_2d = value; + } else { + bdata->max_y_for_2d = -1; + } + + prop = of_find_property(np, "synaptics,swap-axes", NULL); + bdata->swap_axes = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,x-flip", NULL); + bdata->x_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,y-flip", NULL); + bdata->y_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,ub-i2c-addr", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,ub-i2c-addr", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,ub-i2c-addr property\n", + __func__); + return retval; + } + bdata->ub_i2c_addr = (unsigned short)value; + } else { + bdata->ub_i2c_addr = -1; + } + + prop = of_find_property(np, "synaptics,cap-button-codes", NULL); + if (prop && prop->length) { + bdata->cap_button_map->map = devm_kzalloc(dev, + prop->length, + GFP_KERNEL); + if (!bdata->cap_button_map->map) + return -ENOMEM; + bdata->cap_button_map->nbuttons = prop->length / sizeof(u32); + retval = of_property_read_u32_array(np, + "synaptics,cap-button-codes", + bdata->cap_button_map->map, + bdata->cap_button_map->nbuttons); + if (retval < 0) { + bdata->cap_button_map->nbuttons = 0; + bdata->cap_button_map->map = NULL; + } + } else { + bdata->cap_button_map->nbuttons = 0; + bdata->cap_button_map->map = NULL; + } + + prop = of_find_property(np, "synaptics,vir-button-codes", NULL); + if (prop && prop->length) { + bdata->vir_button_map->map = devm_kzalloc(dev, + prop->length, + GFP_KERNEL); + if (!bdata->vir_button_map->map) + return -ENOMEM; + bdata->vir_button_map->nbuttons = prop->length / sizeof(u32); + bdata->vir_button_map->nbuttons /= 5; + retval = of_property_read_u32_array(np, + "synaptics,vir-button-codes", + bdata->vir_button_map->map, + bdata->vir_button_map->nbuttons * 5); + if (retval < 0) { + bdata->vir_button_map->nbuttons = 0; + bdata->vir_button_map->map = NULL; + } + } else { + bdata->vir_button_map->nbuttons = 0; + bdata->vir_button_map->map = NULL; + } + + return 0; +} +#endif + +static int do_i2c_transfer(struct i2c_client *client, struct i2c_msg *msg) +{ + unsigned char retry; + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) + break; + dev_err(&client->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + } + + if (retry == SYN_I2C_RETRY_TIMES) { + dev_err(&client->dev, + "%s: I2C transfer over retry limit\n", + __func__); + return -EIO; + } + + return 0; +} + +static int check_buffer(unsigned char **buffer, unsigned int *buffer_size, + unsigned int length) +{ + if (*buffer_size < length) { + if (*buffer_size) + kfree(*buffer); + *buffer = kzalloc(length, GFP_KERNEL); + if (!(*buffer)) + return -ENOMEM; + *buffer_size = length; + } + + return 0; +} + +static int generic_read(struct i2c_client *client, unsigned short length) +{ + int retval; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + } + }; + + check_buffer(&buffer.read, &buffer.read_size, length); + msg[0].buf = buffer.read; + + retval = do_i2c_transfer(client, msg); + + return retval; +} + +static int generic_write(struct i2c_client *client, unsigned short length) +{ + int retval; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length, + .buf = buffer.write, + } + }; + + retval = do_i2c_transfer(client, msg); + + return retval; +} + +static void traverse_report_descriptor(unsigned int *index) +{ + unsigned char size; + unsigned char *buf = buffer.read; + + size = buf[*index] & MASK_2BIT; + switch (size) { + case 0: /* 0 bytes */ + *index += 1; + break; + case 1: /* 1 byte */ + *index += 2; + break; + case 2: /* 2 bytes */ + *index += 3; + break; + case 3: /* 4 bytes */ + *index += 5; + break; + default: + break; + } +} + +static void find_blob_size(unsigned int index) +{ + unsigned int ii = index; + unsigned char *buf = buffer.read; + + while (ii < hid_dd.report_descriptor_length) { + if (buf[ii] == PREFIX_REPORT_COUNT_1BYTE) { + hid_report.blob_size = buf[ii + 1]; + return; + } else if (buf[ii] == PREFIX_REPORT_COUNT_2BYTES) { + hid_report.blob_size = buf[ii + 1] | (buf[ii + 2] << 8); + return; + } + traverse_report_descriptor(&ii); + } +} + +static void find_reports(unsigned int index) +{ + unsigned int ii = index; + unsigned char *buf = buffer.read; + static unsigned int report_id_index; + static unsigned char report_id; + static unsigned short usage_page; + + if (buf[ii] == PREFIX_REPORT_ID) { + report_id = buf[ii + 1]; + report_id_index = ii; + return; + } + + if (buf[ii] == PREFIX_USAGE_PAGE_1BYTE) { + usage_page = buf[ii + 1]; + return; + } else if (buf[ii] == PREFIX_USAGE_PAGE_2BYTES) { + usage_page = buf[ii + 1] | (buf[ii + 2] << 8); + return; + } + + if ((usage_page == VENDOR_DEFINED_PAGE) && (buf[ii] == PREFIX_USAGE)) { + switch (buf[ii + 1]) { + case USAGE_GET_BLOB: + hid_report.get_blob_id = report_id; + find_blob_size(report_id_index); + break; + case USAGE_WRITE: + hid_report.write_id = report_id; + break; + case USAGE_READ_ADDRESS: + hid_report.read_addr_id = report_id; + break; + case USAGE_READ_DATA: + hid_report.read_data_id = report_id; + break; + case USAGE_SET_MODE: + hid_report.set_mode_id = report_id; + break; + default: + break; + } + } +} + +static int parse_report_descriptor(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned int ii = 0; + unsigned char *buf; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + + buffer.write[0] = hid_dd.report_descriptor_index & MASK_8BIT; + buffer.write[1] = hid_dd.report_descriptor_index >> 8; + retval = generic_write(i2c, 2); + if (retval < 0) + return retval; + retval = generic_read(i2c, hid_dd.report_descriptor_length); + if (retval < 0) + return retval; + + buf = buffer.read; + + hid_report.get_blob_id = REPORT_ID_GET_BLOB; + hid_report.write_id = REPORT_ID_WRITE; + hid_report.read_addr_id = REPORT_ID_READ_ADDRESS; + hid_report.read_data_id = REPORT_ID_READ_DATA; + hid_report.set_mode_id = REPORT_ID_SET_RMI_MODE; + hid_report.blob_size = BLOB_REPORT_SIZE; + + while (ii < hid_dd.report_descriptor_length) { + find_reports(ii); + traverse_report_descriptor(&ii); + } + + return 0; +} + +static int switch_to_rmi(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + check_buffer(&buffer.write, &buffer.write_size, 11); + + /* set rmi mode */ + buffer.write[0] = hid_dd.command_register_index & MASK_8BIT; + buffer.write[1] = hid_dd.command_register_index >> 8; + buffer.write[2] = (FEATURE_REPORT_TYPE << 4) | hid_report.set_mode_id; + buffer.write[3] = SET_REPORT_COMMAND; + buffer.write[4] = hid_report.set_mode_id; + buffer.write[5] = hid_dd.data_register_index & MASK_8BIT; + buffer.write[6] = hid_dd.data_register_index >> 8; + buffer.write[7] = 0x04; + buffer.write[8] = 0x00; + buffer.write[9] = hid_report.set_mode_id; + buffer.write[10] = RMI_MODE; + + retval = generic_write(i2c, 11); + + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + return retval; +} + +static int check_report_mode(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned short report_size; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + check_buffer(&buffer.write, &buffer.write_size, 7); + + buffer.write[0] = hid_dd.command_register_index & MASK_8BIT; + buffer.write[1] = hid_dd.command_register_index >> 8; + buffer.write[2] = (FEATURE_REPORT_TYPE << 4) | hid_report.set_mode_id; + buffer.write[3] = GET_REPORT_COMMAND; + buffer.write[4] = hid_report.set_mode_id; + buffer.write[5] = hid_dd.data_register_index & MASK_8BIT; + buffer.write[6] = hid_dd.data_register_index >> 8; + + retval = generic_write(i2c, 7); + if (retval < 0) + goto exit; + + retval = generic_read(i2c, 2); + if (retval < 0) + goto exit; + + report_size = (buffer.read[1] << 8) | buffer.read[0]; + + retval = generic_write(i2c, 7); + if (retval < 0) + goto exit; + + retval = generic_read(i2c, report_size); + if (retval < 0) + goto exit; + + retval = buffer.read[3]; + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Report mode = %d\n", + __func__, retval); + +exit: + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + return retval; +} + +static int hid_i2c_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + check_buffer(&buffer.write, &buffer.write_size, 6); + + /* read device descriptor */ + buffer.write[0] = bdata->device_descriptor_addr & MASK_8BIT; + buffer.write[1] = bdata->device_descriptor_addr >> 8; + retval = generic_write(i2c, 2); + if (retval < 0) + goto exit; + retval = generic_read(i2c, sizeof(hid_dd)); + if (retval < 0) + goto exit; + retval = secure_memcpy((unsigned char *)&hid_dd, + sizeof(struct hid_device_descriptor), + buffer.read, + buffer.read_size, + sizeof(hid_dd)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy device descriptor data\n", + __func__); + goto exit; + } + + retval = parse_report_descriptor(rmi4_data); + if (retval < 0) + goto exit; + + /* set power */ + buffer.write[0] = hid_dd.command_register_index & MASK_8BIT; + buffer.write[1] = hid_dd.command_register_index >> 8; + buffer.write[2] = 0x00; + buffer.write[3] = SET_POWER_COMMAND; + retval = generic_write(i2c, 4); + if (retval < 0) + goto exit; + + /* reset */ + buffer.write[0] = hid_dd.command_register_index & MASK_8BIT; + buffer.write[1] = hid_dd.command_register_index >> 8; + buffer.write[2] = 0x00; + buffer.write[3] = RESET_COMMAND; + retval = generic_write(i2c, 4); + if (retval < 0) + goto exit; + + while (gpio_get_value(bdata->irq_gpio)) + msleep(20); + + retval = generic_read(i2c, hid_dd.input_report_max_length); + if (retval < 0) + goto exit; + + /* get blob */ + buffer.write[0] = hid_dd.command_register_index & MASK_8BIT; + buffer.write[1] = hid_dd.command_register_index >> 8; + buffer.write[2] = (FEATURE_REPORT_TYPE << 4) | hid_report.get_blob_id; + buffer.write[3] = 0x02; + buffer.write[4] = hid_dd.data_register_index & MASK_8BIT; + buffer.write[5] = hid_dd.data_register_index >> 8; + + retval = generic_write(i2c, 6); + if (retval < 0) + goto exit; + + msleep(20); + + retval = generic_read(i2c, hid_report.blob_size + 3); + if (retval < 0) + goto exit; + +exit: + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to initialize HID/I2C interface\n", + __func__); + return retval; + } + + retval = switch_to_rmi(rmi4_data); + + return retval; +} + +static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned char retry; + unsigned char recover = 1; + unsigned short report_length; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + struct i2c_msg msg[] = { + { + .addr = i2c->addr, + .flags = 0, + .len = hid_dd.output_report_max_length + 2, + }, + { + .addr = i2c->addr, + .flags = I2C_M_RD, + .len = (unsigned short)(length + 4), + }, + }; + +recover: + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + check_buffer(&buffer.write, &buffer.write_size, + hid_dd.output_report_max_length + 2); + msg[0].buf = buffer.write; + buffer.write[0] = hid_dd.output_register_index & MASK_8BIT; + buffer.write[1] = hid_dd.output_register_index >> 8; + buffer.write[2] = hid_dd.output_report_max_length & MASK_8BIT; + buffer.write[3] = hid_dd.output_report_max_length >> 8; + buffer.write[4] = hid_report.read_addr_id; + buffer.write[5] = 0x00; + buffer.write[6] = addr & MASK_8BIT; + buffer.write[7] = addr >> 8; + buffer.write[8] = (unsigned char)length; + buffer.write[9] = (unsigned char)(length >> 8); + + check_buffer(&buffer.read, &buffer.read_size, length + 4); + msg[1].buf = buffer.read; + + retval = do_i2c_transfer(i2c, &msg[0]); + if (retval != 0) + goto exit; + + retry = 0; + do { + retval = do_i2c_transfer(i2c, &msg[1]); + if (retval == 0) + retval = length; + else + goto exit; + + report_length = (buffer.read[1] << 8) | buffer.read[0]; + if (report_length == hid_dd.input_report_max_length) { + retval = secure_memcpy(&data[0], length, + &buffer.read[4], buffer.read_size - 4, + length); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy data\n", + __func__); + } else { + retval = length; + } + goto exit; + } + + msleep(20); + retry++; + } while (retry < SYN_I2C_RETRY_TIMES); + + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to receive read report\n", + __func__); + retval = -EIO; + +exit: + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + if ((retval != length) && (recover == 1)) { + recover = 0; + if (check_report_mode(rmi4_data) != RMI_MODE) { + retval = hid_i2c_init(rmi4_data); + if (retval == 0) + goto recover; + } + } + + return retval; +} + +static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned char recover = 1; + unsigned int msg_length; + struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent); + struct i2c_msg msg[] = { + { + .addr = i2c->addr, + .flags = 0, + } + }; + + if ((length + 10) < (hid_dd.output_report_max_length + 2)) + msg_length = hid_dd.output_report_max_length + 2; + else + msg_length = length + 10; + +recover: + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + check_buffer(&buffer.write, &buffer.write_size, msg_length); + msg[0].len = (unsigned short)msg_length; + msg[0].buf = buffer.write; + buffer.write[0] = hid_dd.output_register_index & MASK_8BIT; + buffer.write[1] = hid_dd.output_register_index >> 8; + buffer.write[2] = hid_dd.output_report_max_length & MASK_8BIT; + buffer.write[3] = hid_dd.output_report_max_length >> 8; + buffer.write[4] = hid_report.write_id; + buffer.write[5] = 0x00; + buffer.write[6] = addr & MASK_8BIT; + buffer.write[7] = addr >> 8; + buffer.write[8] = (unsigned char)length; + buffer.write[9] = (unsigned char)(length >> 8); + retval = secure_memcpy(&buffer.write[10], buffer.write_size - 10, + &data[0], length, length); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy data\n", + __func__); + } else { + retval = do_i2c_transfer(i2c, msg); + if (retval == 0) + retval = length; + } + + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + if ((retval != length) && (recover == 1)) { + recover = 0; + if (check_report_mode(rmi4_data) != RMI_MODE) { + retval = hid_i2c_init(rmi4_data); + if (retval == 0) + goto recover; + } + } + + return retval; +} + +static struct synaptics_dsx_bus_access bus_access = { + .type = BUS_I2C, + .read = synaptics_rmi4_i2c_read, + .write = synaptics_rmi4_i2c_write, +}; + +static struct synaptics_dsx_hw_interface hw_if; + +static struct platform_device *synaptics_dsx_i2c_device; + +static void synaptics_rmi4_i2c_dev_release(struct device *dev) +{ + kfree(synaptics_dsx_i2c_device); +} + +static int synaptics_rmi4_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int retval; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, + "%s: SMBus byte data commands not supported by host\n", + __func__); + return -EIO; + } + + synaptics_dsx_i2c_device = kzalloc( + sizeof(struct platform_device), + GFP_KERNEL); + if (!synaptics_dsx_i2c_device) { + dev_err(&client->dev, + "%s: Failed to allocate memory for synaptics_dsx_i2c_device\n", + __func__); + return -ENOMEM; + } + +#ifdef CONFIG_OF + if (client->dev.of_node) { + hw_if.board_data = devm_kzalloc(&client->dev, + sizeof(struct synaptics_dsx_board_data), + GFP_KERNEL); + if (!hw_if.board_data) { + dev_err(&client->dev, + "%s: Failed to allocate memory for board data\n", + __func__); + return -ENOMEM; + } + hw_if.board_data->cap_button_map = devm_kzalloc(&client->dev, + sizeof(struct synaptics_dsx_button_map), + GFP_KERNEL); + if (!hw_if.board_data->cap_button_map) { + dev_err(&client->dev, + "%s: Failed to allocate memory for 0D button map\n", + __func__); + return -ENOMEM; + } + hw_if.board_data->vir_button_map = devm_kzalloc(&client->dev, + sizeof(struct synaptics_dsx_button_map), + GFP_KERNEL); + if (!hw_if.board_data->vir_button_map) { + dev_err(&client->dev, + "%s: Failed to allocate memory for virtual button map\n", + __func__); + return -ENOMEM; + } + parse_dt(&client->dev, hw_if.board_data); + } +#else + hw_if.board_data = client->dev.platform_data; +#endif + + hw_if.bus_access = &bus_access; + hw_if.bl_hw_init = switch_to_rmi; + hw_if.ui_hw_init = hid_i2c_init; + + synaptics_dsx_i2c_device->name = PLATFORM_DRIVER_NAME; + synaptics_dsx_i2c_device->id = 0; + synaptics_dsx_i2c_device->num_resources = 0; + synaptics_dsx_i2c_device->dev.parent = &client->dev; + synaptics_dsx_i2c_device->dev.platform_data = &hw_if; + synaptics_dsx_i2c_device->dev.release = synaptics_rmi4_i2c_dev_release; + + retval = platform_device_register(synaptics_dsx_i2c_device); + if (retval) { + dev_err(&client->dev, + "%s: Failed to register platform device\n", + __func__); + return -ENODEV; + } + + return 0; +} + +static int synaptics_rmi4_i2c_remove(struct i2c_client *client) +{ + if (buffer.read_size) + kfree(buffer.read); + + if (buffer.write_size) + kfree(buffer.write); + + platform_device_unregister(synaptics_dsx_i2c_device); + + return 0; +} + +static const struct i2c_device_id synaptics_rmi4_id_table[] = { + {I2C_DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table); + +#ifdef CONFIG_OF +static const struct of_device_id synaptics_rmi4_of_match_table[] = { + { + .compatible = "synaptics,dsx-rmi-hid-i2c", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, synaptics_rmi4_of_match_table); +#else +#define synaptics_rmi4_of_match_table NULL +#endif + +static struct i2c_driver synaptics_rmi4_i2c_driver = { + .driver = { + .name = I2C_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = synaptics_rmi4_of_match_table, + }, + .probe = synaptics_rmi4_i2c_probe, + .remove = synaptics_rmi4_i2c_remove, + .id_table = synaptics_rmi4_id_table, +}; + +int synaptics_rmi4_bus_init(void) +{ + return i2c_add_driver(&synaptics_rmi4_i2c_driver); +} +EXPORT_SYMBOL(synaptics_rmi4_bus_init); + +void synaptics_rmi4_bus_exit(void) +{ + i2c_del_driver(&synaptics_rmi4_i2c_driver); +} +EXPORT_SYMBOL(synaptics_rmi4_bus_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX I2C Bus Support Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_spi.c b/synaptics_dsx/synaptics_dsx_spi.c new file mode 100644 index 0000000000..7e957813dc --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_spi.c @@ -0,0 +1,698 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define SPI_READ 0x80 +#define SPI_WRITE 0x00 + +static unsigned char *buf; + +static struct spi_transfer *xfer; + +#ifdef CONFIG_OF +static int parse_dt(struct device *dev, struct synaptics_dsx_board_data *bdata) +{ + int retval; + u32 value; + const char *name; + struct property *prop; + struct device_node *np = dev->of_node; + + bdata->irq_gpio = of_get_named_gpio_flags(np, + "synaptics,irq-gpio", 0, + (enum of_gpio_flags *)&bdata->irq_flags); + + retval = of_property_read_u32(np, "synaptics,irq-on-state", + &value); + if (retval < 0) + bdata->irq_on_state = 0; + else + bdata->irq_on_state = value; + + retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name); + if (retval < 0) + bdata->pwr_reg_name = NULL; + else + bdata->pwr_reg_name = name; + + retval = of_property_read_string(np, "synaptics,bus-reg-name", &name); + if (retval < 0) + bdata->bus_reg_name = NULL; + else + bdata->bus_reg_name = name; + + prop = of_find_property(np, "synaptics,power-gpio", NULL); + if (prop && prop->length) { + bdata->power_gpio = of_get_named_gpio_flags(np, + "synaptics,power-gpio", 0, NULL); + retval = of_property_read_u32(np, "synaptics,power-on-state", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,power-on-state property\n", + __func__); + return retval; + } + bdata->power_on_state = value; + } else { + bdata->power_gpio = -1; + } + + prop = of_find_property(np, "synaptics,power-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-delay-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,power-delay-ms property\n", + __func__); + return retval; + } + bdata->power_delay_ms = value; + } else { + bdata->power_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-gpio", NULL); + if (prop && prop->length) { + bdata->reset_gpio = of_get_named_gpio_flags(np, + "synaptics,reset-gpio", 0, NULL); + retval = of_property_read_u32(np, "synaptics,reset-on-state", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-on-state property\n", + __func__); + return retval; + } + bdata->reset_on_state = value; + retval = of_property_read_u32(np, "synaptics,reset-active-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-active-ms property\n", + __func__); + return retval; + } + bdata->reset_active_ms = value; + } else { + bdata->reset_gpio = -1; + } + + prop = of_find_property(np, "synaptics,reset-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-delay-ms", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,reset-delay-ms property\n", + __func__); + return retval; + } + bdata->reset_delay_ms = value; + } else { + bdata->reset_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,byte-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,byte-delay-us", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,byte-delay-us property\n", + __func__); + return retval; + } + bdata->byte_delay_us = value; + } else { + bdata->byte_delay_us = 0; + } + + prop = of_find_property(np, "synaptics,block-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,block-delay-us", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,block-delay-us property\n", + __func__); + return retval; + } + bdata->block_delay_us = value; + } else { + bdata->block_delay_us = 0; + } + + prop = of_find_property(np, "synaptics,address-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,address-delay-us", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,address-delay-us property\n", + __func__); + return retval; + } + bdata->addr_delay_us = value; + } else { + bdata->addr_delay_us = 0; + } + + prop = of_find_property(np, "synaptics,max-y-for-2d", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,max-y-for-2d", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,max-y-for-2d property\n", + __func__); + return retval; + } + bdata->max_y_for_2d = value; + } else { + bdata->max_y_for_2d = -1; + } + + prop = of_find_property(np, "synaptics,swap-axes", NULL); + bdata->swap_axes = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,x-flip", NULL); + bdata->x_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,y-flip", NULL); + bdata->y_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,ub-i2c-addr", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,ub-i2c-addr", + &value); + if (retval < 0) { + dev_err(dev, "%s: Unable to read synaptics,ub-i2c-addr property\n", + __func__); + return retval; + } + bdata->ub_i2c_addr = (unsigned short)value; + } else { + bdata->ub_i2c_addr = -1; + } + + prop = of_find_property(np, "synaptics,cap-button-codes", NULL); + if (prop && prop->length) { + bdata->cap_button_map->map = devm_kzalloc(dev, + prop->length, + GFP_KERNEL); + if (!bdata->cap_button_map->map) + return -ENOMEM; + bdata->cap_button_map->nbuttons = prop->length / sizeof(u32); + retval = of_property_read_u32_array(np, + "synaptics,cap-button-codes", + bdata->cap_button_map->map, + bdata->cap_button_map->nbuttons); + if (retval < 0) { + bdata->cap_button_map->nbuttons = 0; + bdata->cap_button_map->map = NULL; + } + } else { + bdata->cap_button_map->nbuttons = 0; + bdata->cap_button_map->map = NULL; + } + + prop = of_find_property(np, "synaptics,vir-button-codes", NULL); + if (prop && prop->length) { + bdata->vir_button_map->map = devm_kzalloc(dev, + prop->length, + GFP_KERNEL); + if (!bdata->vir_button_map->map) + return -ENOMEM; + bdata->vir_button_map->nbuttons = prop->length / sizeof(u32); + bdata->vir_button_map->nbuttons /= 5; + retval = of_property_read_u32_array(np, + "synaptics,vir-button-codes", + bdata->vir_button_map->map, + bdata->vir_button_map->nbuttons * 5); + if (retval < 0) { + bdata->vir_button_map->nbuttons = 0; + bdata->vir_button_map->map = NULL; + } + } else { + bdata->vir_button_map->nbuttons = 0; + bdata->vir_button_map->map = NULL; + } + + return 0; +} +#endif + +static int synaptics_rmi4_spi_alloc_buf(struct synaptics_rmi4_data *rmi4_data, + unsigned int size, unsigned int count) +{ + static unsigned int buf_size; + static unsigned int xfer_count; + + if (size > buf_size) { + if (buf_size) + kfree(buf); + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for buf\n", + __func__); + buf_size = 0; + return -ENOMEM; + } + buf_size = size; + } + + if (count > xfer_count) { + if (xfer_count) + kfree(xfer); + xfer = kcalloc(count, sizeof(struct spi_transfer), GFP_KERNEL); + if (!xfer) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for xfer\n", + __func__); + xfer_count = 0; + return -ENOMEM; + } + xfer_count = count; + } else { + memset(xfer, 0, count * sizeof(struct spi_transfer)); + } + + return 0; +} + +static int synaptics_rmi4_spi_set_page(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr) +{ + int retval; + unsigned int index; + unsigned int byte_count = PAGE_SELECT_LEN + 1; + unsigned char page; + struct spi_message msg; + struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent); + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + page = ((addr >> 8) & MASK_8BIT); + if ((page >> 7) == (rmi4_data->current_page >> 7)) + return PAGE_SELECT_LEN; + + spi_message_init(&msg); + + retval = synaptics_rmi4_spi_alloc_buf(rmi4_data, byte_count, + byte_count); + if (retval < 0) + return retval; + + buf[0] = SPI_WRITE; + buf[1] = MASK_8BIT; + buf[2] = page; + + if (bdata->byte_delay_us == 0) { + xfer[0].len = byte_count; + xfer[0].tx_buf = &buf[0]; + if (bdata->block_delay_us) + xfer[0].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[0], &msg); + } else { + for (index = 0; index < byte_count; index++) { + xfer[index].len = 1; + xfer[index].tx_buf = &buf[index]; + if (index == 1) + xfer[index].delay_usecs = bdata->addr_delay_us; + else + xfer[index].delay_usecs = bdata->byte_delay_us; + spi_message_add_tail(&xfer[index], &msg); + } + if (bdata->block_delay_us) + xfer[index - 1].delay_usecs = bdata->block_delay_us; + } + + retval = spi_sync(spi, &msg); + if (retval == 0) { + rmi4_data->current_page = page; + retval = PAGE_SELECT_LEN; + } else { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to complete SPI transfer, error = %d\n", + __func__, retval); + } + + return retval; +} + +static int synaptics_rmi4_spi_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned int index; + unsigned int byte_count = length + ADDRESS_LEN; + unsigned char txbuf[ADDRESS_LEN]; + struct spi_message msg; + struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent); + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + spi_message_init(&msg); + + txbuf[0] = (addr >> 8) | SPI_READ; + txbuf[1] = addr & MASK_8BIT; + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + retval = synaptics_rmi4_spi_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) { + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + return -EIO; + } + + if (bdata->byte_delay_us == 0) { + retval = synaptics_rmi4_spi_alloc_buf(rmi4_data, length, + 2); + } else { + retval = synaptics_rmi4_spi_alloc_buf(rmi4_data, length, + byte_count); + } + if (retval < 0) { + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + return retval; + } + + if (bdata->byte_delay_us == 0) { + xfer[0].len = ADDRESS_LEN; + xfer[0].tx_buf = &txbuf[0]; + spi_message_add_tail(&xfer[0], &msg); + xfer[1].len = length; + xfer[1].rx_buf = &buf[0]; + if (bdata->block_delay_us) + xfer[1].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[1], &msg); + } else { + for (index = 0; index < byte_count; index++) { + xfer[index].len = 1; + if (index < ADDRESS_LEN) + xfer[index].tx_buf = &txbuf[index]; + else + xfer[index].rx_buf = &buf[index - ADDRESS_LEN]; + if (index == 1) + xfer[index].delay_usecs = bdata->addr_delay_us; + else + xfer[index].delay_usecs = bdata->byte_delay_us; + spi_message_add_tail(&xfer[index], &msg); + } + if (bdata->block_delay_us) + xfer[index - 1].delay_usecs = bdata->block_delay_us; + } + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = secure_memcpy(data, length, buf, length, length); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy data\n", + __func__); + } else { + retval = length; + } + } else { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to complete SPI transfer, error = %d\n", + __func__, retval); + } + + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + return retval; +} + +static int synaptics_rmi4_spi_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned int index; + unsigned int byte_count = length + ADDRESS_LEN; + struct spi_message msg; + struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent); + const struct synaptics_dsx_board_data *bdata = + rmi4_data->hw_if->board_data; + + spi_message_init(&msg); + + mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex); + + retval = synaptics_rmi4_spi_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) { + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + return -EIO; + } + + if (bdata->byte_delay_us == 0) { + retval = synaptics_rmi4_spi_alloc_buf(rmi4_data, byte_count, + 1); + } else { + retval = synaptics_rmi4_spi_alloc_buf(rmi4_data, byte_count, + byte_count); + } + if (retval < 0) { + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + return retval; + } + + buf[0] = (addr >> 8) & ~SPI_READ; + buf[1] = addr & MASK_8BIT; + retval = secure_memcpy(&buf[ADDRESS_LEN], + byte_count - ADDRESS_LEN, data, length, length); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy data\n", + __func__); + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + return retval; + } + + if (bdata->byte_delay_us == 0) { + xfer[0].len = byte_count; + xfer[0].tx_buf = &buf[0]; + if (bdata->block_delay_us) + xfer[0].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(xfer, &msg); + } else { + for (index = 0; index < byte_count; index++) { + xfer[index].len = 1; + xfer[index].tx_buf = &buf[index]; + if (index == 1) + xfer[index].delay_usecs = bdata->addr_delay_us; + else + xfer[index].delay_usecs = bdata->byte_delay_us; + spi_message_add_tail(&xfer[index], &msg); + } + if (bdata->block_delay_us) + xfer[index - 1].delay_usecs = bdata->block_delay_us; + } + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = length; + } else { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to complete SPI transfer, error = %d\n", + __func__, retval); + } + + mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex); + + return retval; +} + +static struct synaptics_dsx_bus_access bus_access = { + .type = BUS_SPI, + .read = synaptics_rmi4_spi_read, + .write = synaptics_rmi4_spi_write, +}; + +static struct synaptics_dsx_hw_interface hw_if; + +static struct platform_device *synaptics_dsx_spi_device; + +static void synaptics_rmi4_spi_dev_release(struct device *dev) +{ + kfree(synaptics_dsx_spi_device); +} + +static int synaptics_rmi4_spi_probe(struct spi_device *spi) +{ + int retval; + + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { + dev_err(&spi->dev, + "%s: Full duplex not supported by host\n", + __func__); + return -EIO; + } + + synaptics_dsx_spi_device = kzalloc( + sizeof(struct platform_device), + GFP_KERNEL); + if (!synaptics_dsx_spi_device) { + dev_err(&spi->dev, + "%s: Failed to allocate memory for synaptics_dsx_spi_device\n", + __func__); + return -ENOMEM; + } + +#ifdef CONFIG_OF + if (spi->dev.of_node) { + hw_if.board_data = devm_kzalloc(&spi->dev, + sizeof(struct synaptics_dsx_board_data), + GFP_KERNEL); + if (!hw_if.board_data) { + dev_err(&spi->dev, + "%s: Failed to allocate memory for board data\n", + __func__); + return -ENOMEM; + } + hw_if.board_data->cap_button_map = devm_kzalloc(&spi->dev, + sizeof(struct synaptics_dsx_button_map), + GFP_KERNEL); + if (!hw_if.board_data->cap_button_map) { + dev_err(&spi->dev, + "%s: Failed to allocate memory for 0D button map\n", + __func__); + return -ENOMEM; + } + hw_if.board_data->vir_button_map = devm_kzalloc(&spi->dev, + sizeof(struct synaptics_dsx_button_map), + GFP_KERNEL); + if (!hw_if.board_data->vir_button_map) { + dev_err(&spi->dev, + "%s: Failed to allocate memory for virtual button map\n", + __func__); + return -ENOMEM; + } + parse_dt(&spi->dev, hw_if.board_data); + } +#else + hw_if.board_data = spi->dev.platform_data; +#endif + + hw_if.bus_access = &bus_access; + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_3; + + retval = spi_setup(spi); + if (retval < 0) { + dev_err(&spi->dev, + "%s: Failed to perform SPI setup\n", + __func__); + return retval; + } + + synaptics_dsx_spi_device->name = PLATFORM_DRIVER_NAME; + synaptics_dsx_spi_device->id = 0; + synaptics_dsx_spi_device->num_resources = 0; + synaptics_dsx_spi_device->dev.parent = &spi->dev; + synaptics_dsx_spi_device->dev.platform_data = &hw_if; + synaptics_dsx_spi_device->dev.release = synaptics_rmi4_spi_dev_release; + + retval = platform_device_register(synaptics_dsx_spi_device); + if (retval) { + dev_err(&spi->dev, + "%s: Failed to register platform device\n", + __func__); + return -ENODEV; + } + + return 0; +} + +static int synaptics_rmi4_spi_remove(struct spi_device *spi) +{ + platform_device_unregister(synaptics_dsx_spi_device); + + return 0; +} + +static const struct spi_device_id synaptics_rmi4_id_table[] = { + {SPI_DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(spi, synaptics_rmi4_id_table); + +#ifdef CONFIG_OF +static const struct of_device_id synaptics_rmi4_of_match_table[] = { + { + .compatible = "synaptics,dsx-spi", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, synaptics_rmi4_of_match_table); +#else +#define synaptics_rmi4_of_match_table NULL +#endif + +static struct spi_driver synaptics_rmi4_spi_driver = { + .driver = { + .name = SPI_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = synaptics_rmi4_of_match_table, + }, + .probe = synaptics_rmi4_spi_probe, + .remove = synaptics_rmi4_spi_remove, + .id_table = synaptics_rmi4_id_table, +}; + +int synaptics_rmi4_bus_init(void) +{ + return spi_register_driver(&synaptics_rmi4_spi_driver); +} +EXPORT_SYMBOL(synaptics_rmi4_bus_init); + +void synaptics_rmi4_bus_exit(void) +{ + kfree(buf); + + kfree(xfer); + + spi_unregister_driver(&synaptics_rmi4_spi_driver); +} +EXPORT_SYMBOL(synaptics_rmi4_bus_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX SPI Bus Support Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_test_reporting.c b/synaptics_dsx/synaptics_dsx_test_reporting.c new file mode 100644 index 0000000000..b4c4cc72be --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_test_reporting.c @@ -0,0 +1,5324 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define SYSFS_FOLDER_NAME "f54" + +#define GET_REPORT_TIMEOUT_S 3 +#define CALIBRATION_TIMEOUT_S 10 +#define COMMAND_TIMEOUT_100MS 20 + +#define NO_SLEEP_OFF (0 << 2) +#define NO_SLEEP_ON (1 << 2) + +#define STATUS_IDLE 0 +#define STATUS_BUSY 1 +#define STATUS_ERROR 2 + +#define REPORT_INDEX_OFFSET 1 +#define REPORT_DATA_OFFSET 3 + +#define SENSOR_RX_MAPPING_OFFSET 1 +#define SENSOR_TX_MAPPING_OFFSET 2 + +#define COMMAND_GET_REPORT 1 +#define COMMAND_FORCE_CAL 2 +#define COMMAND_FORCE_UPDATE 4 + +#define CONTROL_NO_AUTO_CAL 1 + +#define CONTROL_0_SIZE 1 +#define CONTROL_1_SIZE 1 +#define CONTROL_2_SIZE 2 +#define CONTROL_3_SIZE 1 +#define CONTROL_4_6_SIZE 3 +#define CONTROL_7_SIZE 1 +#define CONTROL_8_9_SIZE 3 +#define CONTROL_10_SIZE 1 +#define CONTROL_11_SIZE 2 +#define CONTROL_12_13_SIZE 2 +#define CONTROL_14_SIZE 1 +#define CONTROL_15_SIZE 1 +#define CONTROL_16_SIZE 1 +#define CONTROL_17_SIZE 1 +#define CONTROL_18_SIZE 1 +#define CONTROL_19_SIZE 1 +#define CONTROL_20_SIZE 1 +#define CONTROL_21_SIZE 2 +#define CONTROL_22_26_SIZE 7 +#define CONTROL_27_SIZE 1 +#define CONTROL_28_SIZE 2 +#define CONTROL_29_SIZE 1 +#define CONTROL_30_SIZE 1 +#define CONTROL_31_SIZE 1 +#define CONTROL_32_35_SIZE 8 +#define CONTROL_36_SIZE 1 +#define CONTROL_37_SIZE 1 +#define CONTROL_38_SIZE 1 +#define CONTROL_39_SIZE 1 +#define CONTROL_40_SIZE 1 +#define CONTROL_41_SIZE 1 +#define CONTROL_42_SIZE 2 +#define CONTROL_43_54_SIZE 13 +#define CONTROL_55_56_SIZE 2 +#define CONTROL_57_SIZE 1 +#define CONTROL_58_SIZE 1 +#define CONTROL_59_SIZE 2 +#define CONTROL_60_62_SIZE 3 +#define CONTROL_63_SIZE 1 +#define CONTROL_64_67_SIZE 4 +#define CONTROL_68_73_SIZE 8 +#define CONTROL_70_73_SIZE 6 +#define CONTROL_74_SIZE 2 +#define CONTROL_75_SIZE 1 +#define CONTROL_76_SIZE 1 +#define CONTROL_77_78_SIZE 2 +#define CONTROL_79_83_SIZE 5 +#define CONTROL_84_85_SIZE 2 +#define CONTROL_86_SIZE 1 +#define CONTROL_87_SIZE 1 +#define CONTROL_88_SIZE 1 +#define CONTROL_89_SIZE 1 +#define CONTROL_90_SIZE 1 +#define CONTROL_91_SIZE 1 +#define CONTROL_92_SIZE 1 +#define CONTROL_93_SIZE 1 +#define CONTROL_94_SIZE 1 +#define CONTROL_95_SIZE 1 +#define CONTROL_96_SIZE 1 +#define CONTROL_97_SIZE 1 +#define CONTROL_98_SIZE 1 +#define CONTROL_99_SIZE 1 +#define CONTROL_100_SIZE 1 +#define CONTROL_101_SIZE 1 +#define CONTROL_102_SIZE 1 +#define CONTROL_103_SIZE 1 +#define CONTROL_104_SIZE 1 +#define CONTROL_105_SIZE 1 +#define CONTROL_106_SIZE 1 +#define CONTROL_107_SIZE 1 +#define CONTROL_108_SIZE 1 +#define CONTROL_109_SIZE 1 +#define CONTROL_110_SIZE 1 +#define CONTROL_111_SIZE 1 +#define CONTROL_112_SIZE 1 +#define CONTROL_113_SIZE 1 +#define CONTROL_114_SIZE 1 +#define CONTROL_115_SIZE 1 +#define CONTROL_116_SIZE 1 +#define CONTROL_117_SIZE 1 +#define CONTROL_118_SIZE 1 +#define CONTROL_119_SIZE 1 +#define CONTROL_120_SIZE 1 +#define CONTROL_121_SIZE 1 +#define CONTROL_122_SIZE 1 +#define CONTROL_123_SIZE 1 +#define CONTROL_124_SIZE 1 +#define CONTROL_125_SIZE 1 +#define CONTROL_126_SIZE 1 +#define CONTROL_127_SIZE 1 +#define CONTROL_128_SIZE 1 +#define CONTROL_129_SIZE 1 +#define CONTROL_130_SIZE 1 +#define CONTROL_131_SIZE 1 +#define CONTROL_132_SIZE 1 +#define CONTROL_133_SIZE 1 +#define CONTROL_134_SIZE 1 +#define CONTROL_135_SIZE 1 +#define CONTROL_136_SIZE 1 +#define CONTROL_137_SIZE 1 +#define CONTROL_138_SIZE 1 +#define CONTROL_139_SIZE 1 +#define CONTROL_140_SIZE 1 +#define CONTROL_141_SIZE 1 +#define CONTROL_142_SIZE 1 +#define CONTROL_143_SIZE 1 +#define CONTROL_144_SIZE 1 +#define CONTROL_145_SIZE 1 +#define CONTROL_146_SIZE 1 +#define CONTROL_147_SIZE 1 +#define CONTROL_148_SIZE 1 +#define CONTROL_149_SIZE 1 +#define CONTROL_150_SIZE 1 +#define CONTROL_151_SIZE 1 +#define CONTROL_152_SIZE 1 +#define CONTROL_153_SIZE 1 +#define CONTROL_154_SIZE 1 +#define CONTROL_155_SIZE 1 +#define CONTROL_156_SIZE 1 +#define CONTROL_157_158_SIZE 2 +#define CONTROL_163_SIZE 1 +#define CONTROL_165_SIZE 1 +#define CONTROL_166_SIZE 1 +#define CONTROL_167_SIZE 1 +#define CONTROL_168_SIZE 1 +#define CONTROL_169_SIZE 1 +#define CONTROL_171_SIZE 1 +#define CONTROL_172_SIZE 1 +#define CONTROL_173_SIZE 1 +#define CONTROL_174_SIZE 1 +#define CONTROL_175_SIZE 1 +#define CONTROL_176_SIZE 1 +#define CONTROL_177_178_SIZE 2 +#define CONTROL_179_SIZE 1 +#define CONTROL_182_SIZE 1 +#define CONTROL_183_SIZE 1 +#define CONTROL_185_SIZE 1 +#define CONTROL_186_SIZE 1 +#define CONTROL_187_SIZE 1 +#define CONTROL_188_SIZE 1 + +#define HIGH_RESISTANCE_DATA_SIZE 6 +#define FULL_RAW_CAP_MIN_MAX_DATA_SIZE 4 +#define TRX_OPEN_SHORT_DATA_SIZE 7 + +#define attrify(propname) (&dev_attr_##propname.attr) + +#define show_prototype(propname)\ +static ssize_t propname##_show(\ + struct device *dev,\ + struct device_attribute *attr,\ + char *buf);\ +\ +static struct device_attribute dev_attr_##propname =\ + __ATTR_RO(propname) + +#define store_prototype(propname)\ +static ssize_t propname##_store(\ + struct device *dev,\ + struct device_attribute *attr,\ + const char *buf, size_t count);\ +\ +static struct device_attribute dev_attr_##propname =\ + __ATTR_WO(propname) + +#define show_store_prototype(propname)\ +static ssize_t propname##_show(\ + struct device *dev,\ + struct device_attribute *attr,\ + char *buf);\ +\ +static ssize_t propname##_store(\ + struct device *dev,\ + struct device_attribute *attr,\ + const char *buf, size_t count);\ +\ +static struct device_attribute dev_attr_##propname =\ + __ATTR_RW(propname) + +#define disable_cbc(ctrl_num)\ +do {\ + retval = synaptics_rmi4_reg_read(rmi4_data,\ + f54->control.ctrl_num->address,\ + f54->control.ctrl_num->data,\ + sizeof(f54->control.ctrl_num->data));\ + if (retval < 0) {\ + dev_err(rmi4_data->pdev->dev.parent,\ + "%s: Failed to disable CBC (" #ctrl_num ")\n",\ + __func__);\ + return retval;\ + } \ + f54->control.ctrl_num->cbc_tx_carrier_selection = 0;\ + retval = synaptics_rmi4_reg_write(rmi4_data,\ + f54->control.ctrl_num->address,\ + f54->control.ctrl_num->data,\ + sizeof(f54->control.ctrl_num->data));\ + if (retval < 0) {\ + dev_err(rmi4_data->pdev->dev.parent,\ + "%s: Failed to disable CBC (" #ctrl_num ")\n",\ + __func__);\ + return retval;\ + } \ +} while (0) + +enum f54_report_types { + F54_8BIT_IMAGE = 1, + F54_16BIT_IMAGE = 2, + F54_RAW_16BIT_IMAGE = 3, + F54_HIGH_RESISTANCE = 4, + F54_TX_TO_TX_SHORTS = 5, + F54_RX_TO_RX_SHORTS_1 = 7, + F54_TRUE_BASELINE = 9, + F54_FULL_RAW_CAP_MIN_MAX = 13, + F54_RX_OPENS_1 = 14, + F54_TX_OPENS = 15, + F54_TX_TO_GND_SHORTS = 16, + F54_RX_TO_RX_SHORTS_2 = 17, + F54_RX_OPENS_2 = 18, + F54_FULL_RAW_CAP = 19, + F54_FULL_RAW_CAP_NO_RX_COUPLING = 20, + F54_SENSOR_SPEED = 22, + F54_ADC_RANGE = 23, + F54_TRX_OPENS = 24, + F54_TRX_TO_GND_SHORTS = 25, + F54_TRX_SHORTS = 26, + F54_ABS_RAW_CAP = 38, + F54_ABS_DELTA_CAP = 40, + F54_ABS_HYBRID_DELTA_CAP = 59, + F54_ABS_HYBRID_RAW_CAP = 63, + F54_AMP_FULL_RAW_CAP = 78, + F54_AMP_RAW_ADC = 83, + F54_FULL_RAW_CAP_TDDI = 92, + INVALID_REPORT_TYPE = -1, +}; + +enum f54_afe_cal { + F54_AFE_CAL, + F54_AFE_IS_CAL, +}; + +struct f54_query { + union { + struct { + /* query 0 */ + unsigned char num_of_rx_electrodes; + + /* query 1 */ + unsigned char num_of_tx_electrodes; + + /* query 2 */ + unsigned char f54_query2_b0__1:2; + unsigned char has_baseline:1; + unsigned char has_image8:1; + unsigned char f54_query2_b4__5:2; + unsigned char has_image16:1; + unsigned char f54_query2_b7:1; + + /* queries 3.0 and 3.1 */ + unsigned short clock_rate; + + /* query 4 */ + unsigned char touch_controller_family; + + /* query 5 */ + unsigned char has_pixel_touch_threshold_adjustment:1; + unsigned char f54_query5_b1__7:7; + + /* query 6 */ + unsigned char has_sensor_assignment:1; + unsigned char has_interference_metric:1; + unsigned char has_sense_frequency_control:1; + unsigned char has_firmware_noise_mitigation:1; + unsigned char has_ctrl11:1; + unsigned char has_two_byte_report_rate:1; + unsigned char has_one_byte_report_rate:1; + unsigned char has_relaxation_control:1; + + /* query 7 */ + unsigned char curve_compensation_mode:2; + unsigned char f54_query7_b2__7:6; + + /* query 8 */ + unsigned char f54_query8_b0:1; + unsigned char has_iir_filter:1; + unsigned char has_cmn_removal:1; + unsigned char has_cmn_maximum:1; + unsigned char has_touch_hysteresis:1; + unsigned char has_edge_compensation:1; + unsigned char has_per_frequency_noise_control:1; + unsigned char has_enhanced_stretch:1; + + /* query 9 */ + unsigned char has_force_fast_relaxation:1; + unsigned char has_multi_metric_state_machine:1; + unsigned char has_signal_clarity:1; + unsigned char has_variance_metric:1; + unsigned char has_0d_relaxation_control:1; + unsigned char has_0d_acquisition_control:1; + unsigned char has_status:1; + unsigned char has_slew_metric:1; + + /* query 10 */ + unsigned char has_h_blank:1; + unsigned char has_v_blank:1; + unsigned char has_long_h_blank:1; + unsigned char has_startup_fast_relaxation:1; + unsigned char has_esd_control:1; + unsigned char has_noise_mitigation2:1; + unsigned char has_noise_state:1; + unsigned char has_energy_ratio_relaxation:1; + + /* query 11 */ + unsigned char has_excessive_noise_reporting:1; + unsigned char has_slew_option:1; + unsigned char has_two_overhead_bursts:1; + unsigned char has_query13:1; + unsigned char has_one_overhead_burst:1; + unsigned char f54_query11_b5:1; + unsigned char has_ctrl88:1; + unsigned char has_query15:1; + + /* query 12 */ + unsigned char number_of_sensing_frequencies:4; + unsigned char f54_query12_b4__7:4; + } __packed; + unsigned char data[14]; + }; +}; + +struct f54_query_13 { + union { + struct { + unsigned char has_ctrl86:1; + unsigned char has_ctrl87:1; + unsigned char has_ctrl87_sub0:1; + unsigned char has_ctrl87_sub1:1; + unsigned char has_ctrl87_sub2:1; + unsigned char has_cidim:1; + unsigned char has_noise_mitigation_enhancement:1; + unsigned char has_rail_im:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_15 { + union { + struct { + unsigned char has_ctrl90:1; + unsigned char has_transmit_strength:1; + unsigned char has_ctrl87_sub3:1; + unsigned char has_query16:1; + unsigned char has_query20:1; + unsigned char has_query21:1; + unsigned char has_query22:1; + unsigned char has_query25:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_16 { + union { + struct { + unsigned char has_query17:1; + unsigned char has_data17:1; + unsigned char has_ctrl92:1; + unsigned char has_ctrl93:1; + unsigned char has_ctrl94_query18:1; + unsigned char has_ctrl95_query19:1; + unsigned char has_ctrl99:1; + unsigned char has_ctrl100:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_21 { + union { + struct { + unsigned char has_abs_rx:1; + unsigned char has_abs_tx:1; + unsigned char has_ctrl91:1; + unsigned char has_ctrl96:1; + unsigned char has_ctrl97:1; + unsigned char has_ctrl98:1; + unsigned char has_data19:1; + unsigned char has_query24_data18:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_22 { + union { + struct { + unsigned char has_packed_image:1; + unsigned char has_ctrl101:1; + unsigned char has_dynamic_sense_display_ratio:1; + unsigned char has_query23:1; + unsigned char has_ctrl103_query26:1; + unsigned char has_ctrl104:1; + unsigned char has_ctrl105:1; + unsigned char has_query28:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_23 { + union { + struct { + unsigned char has_ctrl102:1; + unsigned char has_ctrl102_sub1:1; + unsigned char has_ctrl102_sub2:1; + unsigned char has_ctrl102_sub4:1; + unsigned char has_ctrl102_sub5:1; + unsigned char has_ctrl102_sub9:1; + unsigned char has_ctrl102_sub10:1; + unsigned char has_ctrl102_sub11:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_25 { + union { + struct { + unsigned char has_ctrl106:1; + unsigned char has_ctrl102_sub12:1; + unsigned char has_ctrl107:1; + unsigned char has_ctrl108:1; + unsigned char has_ctrl109:1; + unsigned char has_data20:1; + unsigned char f54_query25_b6:1; + unsigned char has_query27:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_27 { + union { + struct { + unsigned char has_ctrl110:1; + unsigned char has_data21:1; + unsigned char has_ctrl111:1; + unsigned char has_ctrl112:1; + unsigned char has_ctrl113:1; + unsigned char has_data22:1; + unsigned char has_ctrl114:1; + unsigned char has_query29:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_29 { + union { + struct { + unsigned char has_ctrl115:1; + unsigned char has_ground_ring_options:1; + unsigned char has_lost_bursts_tuning:1; + unsigned char has_aux_exvcom2_select:1; + unsigned char has_ctrl116:1; + unsigned char has_data23:1; + unsigned char has_ctrl117:1; + unsigned char has_query30:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_30 { + union { + struct { + unsigned char has_ctrl118:1; + unsigned char has_ctrl119:1; + unsigned char has_ctrl120:1; + unsigned char has_ctrl121:1; + unsigned char has_ctrl122_query31:1; + unsigned char has_ctrl123:1; + unsigned char has_ctrl124:1; + unsigned char has_query32:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_32 { + union { + struct { + unsigned char has_ctrl125:1; + unsigned char has_ctrl126:1; + unsigned char has_ctrl127:1; + unsigned char has_abs_charge_pump_disable:1; + unsigned char has_query33:1; + unsigned char has_data24:1; + unsigned char has_query34:1; + unsigned char has_query35:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_33 { + union { + struct { + unsigned char has_ctrl128:1; + unsigned char has_ctrl129:1; + unsigned char has_ctrl130:1; + unsigned char has_ctrl131:1; + unsigned char has_ctrl132:1; + unsigned char has_ctrl133:1; + unsigned char has_ctrl134:1; + unsigned char has_query36:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_35 { + union { + struct { + unsigned char has_data25:1; + unsigned char has_ctrl135:1; + unsigned char has_ctrl136:1; + unsigned char has_ctrl137:1; + unsigned char has_ctrl138:1; + unsigned char has_ctrl139:1; + unsigned char has_data26:1; + unsigned char has_ctrl140:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_36 { + union { + struct { + unsigned char has_ctrl141:1; + unsigned char has_ctrl142:1; + unsigned char has_query37:1; + unsigned char has_ctrl143:1; + unsigned char has_ctrl144:1; + unsigned char has_ctrl145:1; + unsigned char has_ctrl146:1; + unsigned char has_query38:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_38 { + union { + struct { + unsigned char has_ctrl147:1; + unsigned char has_ctrl148:1; + unsigned char has_ctrl149:1; + unsigned char has_ctrl150:1; + unsigned char has_ctrl151:1; + unsigned char has_ctrl152:1; + unsigned char has_ctrl153:1; + unsigned char has_query39:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_39 { + union { + struct { + unsigned char has_ctrl154:1; + unsigned char has_ctrl155:1; + unsigned char has_ctrl156:1; + unsigned char has_ctrl160:1; + unsigned char has_ctrl157_ctrl158:1; + unsigned char f54_query39_b5__6:2; + unsigned char has_query40:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_40 { + union { + struct { + unsigned char has_ctrl169:1; + unsigned char has_ctrl163_query41:1; + unsigned char f54_query40_b2:1; + unsigned char has_ctrl165_query42:1; + unsigned char has_ctrl166:1; + unsigned char has_ctrl167:1; + unsigned char has_ctrl168:1; + unsigned char has_query43:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_43 { + union { + struct { + unsigned char f54_query43_b0__1:2; + unsigned char has_ctrl171:1; + unsigned char has_ctrl172_query44_query45:1; + unsigned char has_ctrl173:1; + unsigned char has_ctrl174:1; + unsigned char has_ctrl175:1; + unsigned char has_query46:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_46 { + union { + struct { + unsigned char has_ctrl176:1; + unsigned char has_ctrl177_ctrl178:1; + unsigned char has_ctrl179:1; + unsigned char f54_query46_b3:1; + unsigned char has_data27:1; + unsigned char has_data28:1; + unsigned char f54_query46_b6:1; + unsigned char has_query47:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_47 { + union { + struct { + unsigned char f54_query47_b0:1; + unsigned char has_ctrl182:1; + unsigned char has_ctrl183:1; + unsigned char f54_query47_b3:1; + unsigned char has_ctrl185:1; + unsigned char has_ctrl186:1; + unsigned char has_ctrl187:1; + unsigned char has_query49:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_49 { + union { + struct { + unsigned char f54_query49_b0__1:2; + unsigned char has_ctrl188:1; + unsigned char has_data31:1; + unsigned char f54_query49_b4__6:3; + unsigned char has_query50:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_50 { + union { + struct { + unsigned char f54_query50_b0__6:7; + unsigned char has_query51:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_51 { + union { + struct { + unsigned char f54_query51_b0__4:5; + unsigned char has_query53_query54_ctrl198:1; + unsigned char has_ctrl199:1; + unsigned char has_query55:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_55 { + union { + struct { + unsigned char has_query56:1; + unsigned char has_data33_data34:1; + unsigned char has_alt_report_rate:1; + unsigned char has_ctrl200:1; + unsigned char has_ctrl201_ctrl202:1; + unsigned char has_ctrl203:1; + unsigned char has_ctrl204:1; + unsigned char has_query57:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_57 { + union { + struct { + unsigned char has_ctrl205:1; + unsigned char has_ctrl206:1; + unsigned char has_usb_bulk_read:1; + unsigned char has_ctrl207:1; + unsigned char has_ctrl208:1; + unsigned char has_ctrl209:1; + unsigned char has_ctrl210:1; + unsigned char has_query58:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_58 { + union { + struct { + unsigned char has_query59:1; + unsigned char has_query60:1; + unsigned char has_ctrl211:1; + unsigned char has_ctrl212:1; + unsigned char has_hybrid_abs_tx_axis_filtering:1; + unsigned char has_hybrid_abs_tx_interpolation:1; + unsigned char has_ctrl213:1; + unsigned char has_query61:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_61 { + union { + struct { + unsigned char has_ctrl214:1; + unsigned char has_ctrl215_query62_query63:1; + unsigned char f54_query_61_b2:1; + unsigned char has_ctrl216:1; + unsigned char has_ctrl217:1; + unsigned char has_misc_host_ctrl:1; + unsigned char hybrid_abs_buttons:1; + unsigned char has_query64:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_64 { + union { + struct { + unsigned char has_ctrl101_sub1:1; + unsigned char has_ctrl220:1; + unsigned char has_ctrl221:1; + unsigned char has_ctrl222:1; + unsigned char has_ctrl219_sub1:1; + unsigned char has_ctrl103_sub3:1; + unsigned char has_ctrl224_ctrl226_ctrl227:1; + unsigned char has_query65:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_65 { + union { + struct { + unsigned char f54_query_65_b0__1:2; + unsigned char has_ctrl101_sub2:1; + unsigned char f54_query_65_b3__4:2; + unsigned char has_query66_ctrl231:1; + unsigned char has_ctrl232:1; + unsigned char has_query67:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_67 { + union { + struct { + unsigned char has_abs_doze_spatial_filter_en:1; + unsigned char has_abs_doze_avg_filter_enhancement_en:1; + unsigned char has_single_display_pulse:1; + unsigned char f54_query_67_b3__4:2; + unsigned char has_ctrl235_ctrl236:1; + unsigned char f54_query_67_b6:1; + unsigned char has_query68:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_68 { + union { + struct { + unsigned char f54_query_68_b0:1; + unsigned char has_ctrl238:1; + unsigned char has_ctrl238_sub1:1; + unsigned char has_ctrl238_sub2:1; + unsigned char has_ctrl239:1; + unsigned char has_freq_filter_bw_ext:1; + unsigned char is_tddi_hic:1; + unsigned char has_query69:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query_69 { + union { + struct { + unsigned char has_ctrl240_sub0:1; + unsigned char has_ctrl240_sub1_sub2:1; + unsigned char has_ctrl240_sub3:1; + unsigned char has_ctrl240_sub4:1; + unsigned char f54_query_69_b4__7:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_data_31 { + union { + struct { + unsigned char is_calibration_crc:1; + unsigned char calibration_crc:1; + unsigned char short_test_row_number:5; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_7 { + union { + struct { + unsigned char cbc_cap:3; + unsigned char cbc_polarity:1; + unsigned char cbc_tx_carrier_selection:1; + unsigned char f54_ctrl7_b5__7:3; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_41 { + union { + struct { + unsigned char no_signal_clarity:1; + unsigned char f54_ctrl41_b1__7:7; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_57 { + union { + struct { + unsigned char cbc_cap:3; + unsigned char cbc_polarity:1; + unsigned char cbc_tx_carrier_selection:1; + unsigned char f54_ctrl57_b5__7:3; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_86 { + union { + struct { + unsigned char enable_high_noise_state:1; + unsigned char dynamic_sense_display_ratio:2; + unsigned char f54_ctrl86_b3__7:5; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_88 { + union { + struct { + unsigned char tx_low_reference_polarity:1; + unsigned char tx_high_reference_polarity:1; + unsigned char abs_low_reference_polarity:1; + unsigned char abs_polarity:1; + unsigned char cbc_polarity:1; + unsigned char cbc_tx_carrier_selection:1; + unsigned char charge_pump_enable:1; + unsigned char cbc_abs_auto_servo:1; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_110 { + union { + struct { + unsigned char active_stylus_rx_feedback_cap; + unsigned char active_stylus_rx_feedback_cap_reference; + unsigned char active_stylus_low_reference; + unsigned char active_stylus_high_reference; + unsigned char active_stylus_gain_control; + unsigned char active_stylus_gain_control_reference; + unsigned char active_stylus_timing_mode; + unsigned char active_stylus_discovery_bursts; + unsigned char active_stylus_detection_bursts; + unsigned char active_stylus_discovery_noise_multiplier; + unsigned char active_stylus_detection_envelope_min; + unsigned char active_stylus_detection_envelope_max; + unsigned char active_stylus_lose_count; + } __packed; + struct { + unsigned char data[13]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_149 { + union { + struct { + unsigned char trans_cbc_global_cap_enable:1; + unsigned char f54_ctrl149_b1__7:7; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_188 { + union { + struct { + unsigned char start_calibration:1; + unsigned char start_is_calibration:1; + unsigned char frequency:2; + unsigned char start_production_test:1; + unsigned char short_test_calibration:1; + unsigned char f54_ctrl188_b7:1; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control { + struct f54_control_7 *reg_7; + struct f54_control_41 *reg_41; + struct f54_control_57 *reg_57; + struct f54_control_86 *reg_86; + struct f54_control_88 *reg_88; + struct f54_control_110 *reg_110; + struct f54_control_149 *reg_149; + struct f54_control_188 *reg_188; +}; + +struct synaptics_rmi4_f54_handle { + bool no_auto_cal; + bool skip_preparation; + unsigned char status; + unsigned char intr_mask; + unsigned char intr_reg_num; + unsigned char tx_assigned; + unsigned char rx_assigned; + unsigned char *report_data; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; + unsigned short fifoindex; + unsigned int report_size; + unsigned int data_buffer_size; + unsigned int data_pos; + enum f54_report_types report_type; + struct f54_query query; + struct f54_query_13 query_13; + struct f54_query_15 query_15; + struct f54_query_16 query_16; + struct f54_query_21 query_21; + struct f54_query_22 query_22; + struct f54_query_23 query_23; + struct f54_query_25 query_25; + struct f54_query_27 query_27; + struct f54_query_29 query_29; + struct f54_query_30 query_30; + struct f54_query_32 query_32; + struct f54_query_33 query_33; + struct f54_query_35 query_35; + struct f54_query_36 query_36; + struct f54_query_38 query_38; + struct f54_query_39 query_39; + struct f54_query_40 query_40; + struct f54_query_43 query_43; + struct f54_query_46 query_46; + struct f54_query_47 query_47; + struct f54_query_49 query_49; + struct f54_query_50 query_50; + struct f54_query_51 query_51; + struct f54_query_55 query_55; + struct f54_query_57 query_57; + struct f54_query_58 query_58; + struct f54_query_61 query_61; + struct f54_query_64 query_64; + struct f54_query_65 query_65; + struct f54_query_67 query_67; + struct f54_query_68 query_68; + struct f54_query_69 query_69; + struct f54_data_31 data_31; + struct f54_control control; + struct mutex status_mutex; + struct kobject *sysfs_dir; + struct hrtimer watchdog; + struct work_struct timeout_work; + struct work_struct test_report_work; + struct workqueue_struct *test_report_workqueue; + struct synaptics_rmi4_data *rmi4_data; +}; + +struct f55_query { + union { + struct { + /* query 0 */ + unsigned char num_of_rx_electrodes; + + /* query 1 */ + unsigned char num_of_tx_electrodes; + + /* query 2 */ + unsigned char has_sensor_assignment:1; + unsigned char has_edge_compensation:1; + unsigned char curve_compensation_mode:2; + unsigned char has_ctrl6:1; + unsigned char has_alternate_transmitter_assignment:1; + unsigned char has_single_layer_multi_touch:1; + unsigned char has_query5:1; + } __packed; + unsigned char data[3]; + }; +}; + +struct f55_query_3 { + union { + struct { + unsigned char has_ctrl8:1; + unsigned char has_ctrl9:1; + unsigned char has_oncell_pattern_support:1; + unsigned char has_data0:1; + unsigned char has_single_wide_pattern_support:1; + unsigned char has_mirrored_tx_pattern_support:1; + unsigned char has_discrete_pattern_support:1; + unsigned char has_query9:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_query_5 { + union { + struct { + unsigned char has_corner_compensation:1; + unsigned char has_ctrl12:1; + unsigned char has_trx_configuration:1; + unsigned char has_ctrl13:1; + unsigned char f55_query5_b4:1; + unsigned char has_ctrl14:1; + unsigned char has_basis_function:1; + unsigned char has_query17:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_query_17 { + union { + struct { + unsigned char f55_query17_b0:1; + unsigned char has_ctrl16:1; + unsigned char has_ctrl18_ctrl19:1; + unsigned char has_ctrl17:1; + unsigned char has_ctrl20:1; + unsigned char has_ctrl21:1; + unsigned char has_ctrl22:1; + unsigned char has_query18:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_query_18 { + union { + struct { + unsigned char has_ctrl23:1; + unsigned char has_ctrl24:1; + unsigned char has_query19:1; + unsigned char has_ctrl25:1; + unsigned char has_ctrl26:1; + unsigned char has_ctrl27_query20:1; + unsigned char has_ctrl28_query21:1; + unsigned char has_query22:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_query_22 { + union { + struct { + unsigned char has_ctrl29:1; + unsigned char has_query23:1; + unsigned char has_guard_disable:1; + unsigned char has_ctrl30:1; + unsigned char has_ctrl31:1; + unsigned char has_ctrl32:1; + unsigned char has_query24_through_query27:1; + unsigned char has_query28:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_query_23 { + union { + struct { + unsigned char amp_sensor_enabled:1; + unsigned char image_transposed:1; + unsigned char first_column_at_left_side:1; + unsigned char size_of_column2mux:5; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_query_28 { + union { + struct { + unsigned char f55_query28_b0__4:5; + unsigned char has_ctrl37:1; + unsigned char has_query29:1; + unsigned char has_query30:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_query_30 { + union { + struct { + unsigned char has_ctrl38:1; + unsigned char has_query31_query32:1; + unsigned char has_ctrl39:1; + unsigned char has_ctrl40:1; + unsigned char has_ctrl41:1; + unsigned char has_ctrl42:1; + unsigned char has_ctrl43_ctrl44:1; + unsigned char has_query33:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_query_33 { + union { + struct { + unsigned char has_extended_amp_pad:1; + unsigned char has_extended_amp_btn:1; + unsigned char has_ctrl45_ctrl46:1; + unsigned char f55_query33_b3:1; + unsigned char has_ctrl47_sub0_sub1:1; + unsigned char f55_query33_b5__7:3; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_control_43 { + union { + struct { + unsigned char swap_sensor_side:1; + unsigned char f55_ctrl43_b1__7:7; + unsigned char afe_l_mux_size:4; + unsigned char afe_r_mux_size:4; + } __packed; + unsigned char data[2]; + }; +}; + +struct synaptics_rmi4_f55_handle { + bool amp_sensor; + bool extended_amp; + bool has_force; + unsigned char size_of_column2mux; + unsigned char afe_mux_offset; + unsigned char force_tx_offset; + unsigned char force_rx_offset; + unsigned char *tx_assignment; + unsigned char *rx_assignment; + unsigned char *force_tx_assignment; + unsigned char *force_rx_assignment; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; + struct f55_query query; + struct f55_query_3 query_3; + struct f55_query_5 query_5; + struct f55_query_17 query_17; + struct f55_query_18 query_18; + struct f55_query_22 query_22; + struct f55_query_23 query_23; + struct f55_query_28 query_28; + struct f55_query_30 query_30; + struct f55_query_33 query_33; +}; + +struct f21_query_2 { + union { + struct { + unsigned char size_of_query3; + struct { + unsigned char query0_is_present:1; + unsigned char query1_is_present:1; + unsigned char query2_is_present:1; + unsigned char query3_is_present:1; + unsigned char query4_is_present:1; + unsigned char query5_is_present:1; + unsigned char query6_is_present:1; + unsigned char query7_is_present:1; + } __packed; + struct { + unsigned char query8_is_present:1; + unsigned char query9_is_present:1; + unsigned char query10_is_present:1; + unsigned char query11_is_present:1; + unsigned char query12_is_present:1; + unsigned char query13_is_present:1; + unsigned char query14_is_present:1; + unsigned char query15_is_present:1; + } __packed; + }; + unsigned char data[3]; + }; +}; + +struct f21_query_5 { + union { + struct { + unsigned char size_of_query6; + struct { + unsigned char ctrl0_is_present:1; + unsigned char ctrl1_is_present:1; + unsigned char ctrl2_is_present:1; + unsigned char ctrl3_is_present:1; + unsigned char ctrl4_is_present:1; + unsigned char ctrl5_is_present:1; + unsigned char ctrl6_is_present:1; + unsigned char ctrl7_is_present:1; + } __packed; + struct { + unsigned char ctrl8_is_present:1; + unsigned char ctrl9_is_present:1; + unsigned char ctrl10_is_present:1; + unsigned char ctrl11_is_present:1; + unsigned char ctrl12_is_present:1; + unsigned char ctrl13_is_present:1; + unsigned char ctrl14_is_present:1; + unsigned char ctrl15_is_present:1; + } __packed; + struct { + unsigned char ctrl16_is_present:1; + unsigned char ctrl17_is_present:1; + unsigned char ctrl18_is_present:1; + unsigned char ctrl19_is_present:1; + unsigned char ctrl20_is_present:1; + unsigned char ctrl21_is_present:1; + unsigned char ctrl22_is_present:1; + unsigned char ctrl23_is_present:1; + } __packed; + }; + unsigned char data[4]; + }; +}; + +struct f21_query_11 { + union { + struct { + unsigned char has_high_resolution_force:1; + unsigned char has_force_sensing_txrx_mapping:1; + unsigned char f21_query11_00_b2__7:6; + unsigned char f21_query11_00_reserved; + unsigned char max_number_of_force_sensors; + unsigned char max_number_of_force_txs; + unsigned char max_number_of_force_rxs; + unsigned char f21_query11_01_reserved; + } __packed; + unsigned char data[6]; + }; +}; + +struct synaptics_rmi4_f21_handle { + bool has_force; + unsigned char tx_assigned; + unsigned char rx_assigned; + unsigned char max_num_of_tx; + unsigned char max_num_of_rx; + unsigned char max_num_of_txrx; + unsigned char *force_txrx_assignment; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; +}; + +show_prototype(num_of_mapped_tx); +show_prototype(num_of_mapped_rx); +show_prototype(tx_mapping); +show_prototype(rx_mapping); +show_prototype(num_of_mapped_force_tx); +show_prototype(num_of_mapped_force_rx); +show_prototype(force_tx_mapping); +show_prototype(force_rx_mapping); +show_prototype(report_size); +show_prototype(status); + +store_prototype(do_preparation); +store_prototype(force_cal); +store_prototype(get_report); +store_prototype(resume_touch); +store_prototype(do_afe_calibration); + +show_store_prototype(report_type); +show_store_prototype(fifoindex); +show_store_prototype(no_auto_cal); +show_store_prototype(read_report); + +static struct attribute *attrs[] = { + attrify(num_of_mapped_tx), + attrify(num_of_mapped_rx), + attrify(tx_mapping), + attrify(rx_mapping), + attrify(num_of_mapped_force_tx), + attrify(num_of_mapped_force_rx), + attrify(force_tx_mapping), + attrify(force_rx_mapping), + attrify(report_size), + attrify(status), + attrify(do_preparation), + attrify(force_cal), + attrify(get_report), + attrify(resume_touch), + attrify(do_afe_calibration), + attrify(report_type), + attrify(fifoindex), + attrify(no_auto_cal), + attrify(read_report), + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = attrs, +}; + +static ssize_t test_sysfs_data_read(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static struct bin_attribute test_report_data = { + .attr = { + .name = "report_data", + .mode = 0444, + }, + .size = 0, + .read = test_sysfs_data_read, +}; + +static struct synaptics_rmi4_f54_handle *f54; +static struct synaptics_rmi4_f55_handle *f55; +static struct synaptics_rmi4_f21_handle *f21; + +DECLARE_COMPLETION(test_remove_complete); + +static bool test_report_type_valid(enum f54_report_types report_type) +{ + switch (report_type) { + case F54_8BIT_IMAGE: + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_HIGH_RESISTANCE: + case F54_TX_TO_TX_SHORTS: + case F54_RX_TO_RX_SHORTS_1: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP_MIN_MAX: + case F54_RX_OPENS_1: + case F54_TX_OPENS: + case F54_TX_TO_GND_SHORTS: + case F54_RX_TO_RX_SHORTS_2: + case F54_RX_OPENS_2: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_NO_RX_COUPLING: + case F54_SENSOR_SPEED: + case F54_ADC_RANGE: + case F54_TRX_OPENS: + case F54_TRX_TO_GND_SHORTS: + case F54_TRX_SHORTS: + case F54_ABS_RAW_CAP: + case F54_ABS_DELTA_CAP: + case F54_ABS_HYBRID_DELTA_CAP: + case F54_ABS_HYBRID_RAW_CAP: + case F54_AMP_FULL_RAW_CAP: + case F54_AMP_RAW_ADC: + case F54_FULL_RAW_CAP_TDDI: + return true; + default: + f54->report_type = INVALID_REPORT_TYPE; + f54->report_size = 0; + return false; + } +} + +static void test_set_report_size(void) +{ + int retval; + unsigned char tx = f54->tx_assigned; + unsigned char rx = f54->rx_assigned; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + switch (f54->report_type) { + case F54_8BIT_IMAGE: + f54->report_size = tx * rx; + break; + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_NO_RX_COUPLING: + case F54_SENSOR_SPEED: + case F54_AMP_FULL_RAW_CAP: + case F54_AMP_RAW_ADC: + case F54_FULL_RAW_CAP_TDDI: + f54->report_size = 2 * tx * rx; + break; + case F54_HIGH_RESISTANCE: + f54->report_size = HIGH_RESISTANCE_DATA_SIZE; + break; + case F54_TX_TO_TX_SHORTS: + case F54_TX_OPENS: + case F54_TX_TO_GND_SHORTS: + f54->report_size = (tx + 7) / 8; + break; + case F54_RX_TO_RX_SHORTS_1: + case F54_RX_OPENS_1: + if (rx < tx) + f54->report_size = 2 * rx * rx; + else + f54->report_size = 2 * tx * rx; + break; + case F54_FULL_RAW_CAP_MIN_MAX: + f54->report_size = FULL_RAW_CAP_MIN_MAX_DATA_SIZE; + break; + case F54_RX_TO_RX_SHORTS_2: + case F54_RX_OPENS_2: + if (rx <= tx) + f54->report_size = 0; + else + f54->report_size = 2 * rx * (rx - tx); + break; + case F54_ADC_RANGE: + if (f54->query.has_signal_clarity) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->control.reg_41->address, + f54->control.reg_41->data, + sizeof(f54->control.reg_41->data)); + if (retval < 0) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Failed to read control reg_41\n", + __func__); + f54->report_size = 0; + break; + } + if (!f54->control.reg_41->no_signal_clarity) { + if (tx % 4) + tx += 4 - (tx % 4); + } + } + f54->report_size = 2 * tx * rx; + break; + case F54_TRX_OPENS: + case F54_TRX_TO_GND_SHORTS: + case F54_TRX_SHORTS: + f54->report_size = TRX_OPEN_SHORT_DATA_SIZE; + break; + case F54_ABS_RAW_CAP: + case F54_ABS_DELTA_CAP: + case F54_ABS_HYBRID_DELTA_CAP: + case F54_ABS_HYBRID_RAW_CAP: + tx += f21->tx_assigned; + rx += f21->rx_assigned; + f54->report_size = 4 * (tx + rx); + break; + default: + f54->report_size = 0; + } +} + +static int test_set_interrupt(bool set) +{ + int retval; + unsigned char ii; + unsigned char zero = 0x00; + unsigned char *intr_mask; + unsigned short f01_ctrl_reg; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + intr_mask = rmi4_data->intr_mask; + f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + f54->intr_reg_num; + + if (!set) { + retval = synaptics_rmi4_reg_write(rmi4_data, + f01_ctrl_reg, + &zero, + sizeof(zero)); + if (retval < 0) + return retval; + } + + for (ii = 0; ii < rmi4_data->num_of_intr_regs; ii++) { + if (intr_mask[ii] != 0x00) { + f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + ii; + if (set) { + retval = synaptics_rmi4_reg_write(rmi4_data, + f01_ctrl_reg, + &zero, + sizeof(zero)); + if (retval < 0) + return retval; + } else { + retval = synaptics_rmi4_reg_write(rmi4_data, + f01_ctrl_reg, + &(intr_mask[ii]), + sizeof(intr_mask[ii])); + if (retval < 0) + return retval; + } + } + } + + f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + f54->intr_reg_num; + + if (set) { + retval = synaptics_rmi4_reg_write(rmi4_data, + f01_ctrl_reg, + &f54->intr_mask, + 1); + if (retval < 0) + return retval; + } + + return 0; +} + +static int test_wait_for_command_completion(void) +{ + int retval; + unsigned char value; + unsigned char timeout_count; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + timeout_count = 0; + do { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->command_base_addr, + &value, + sizeof(value)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read command register\n", + __func__); + return retval; + } + + if (value == 0x00) + break; + + msleep(100); + timeout_count++; + } while (timeout_count < COMMAND_TIMEOUT_100MS); + + if (timeout_count == COMMAND_TIMEOUT_100MS) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Timed out waiting for command completion\n", + __func__); + return -ETIMEDOUT; + } + + return 0; +} + +static int test_do_command(unsigned char command) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write command\n", + __func__); + return retval; + } + + retval = test_wait_for_command_completion(); + if (retval < 0) + return retval; + + return 0; +} + +static int test_do_preparation(void) +{ + int retval; + unsigned char value; + unsigned char zero = 0x00; + unsigned char device_ctrl; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set no sleep\n", + __func__); + return retval; + } + + device_ctrl |= NO_SLEEP_ON; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set no sleep\n", + __func__); + return retval; + } + + if (f54->skip_preparation) + return 0; + + switch (f54->report_type) { + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_SENSOR_SPEED: + case F54_ADC_RANGE: + case F54_ABS_RAW_CAP: + case F54_ABS_DELTA_CAP: + case F54_ABS_HYBRID_DELTA_CAP: + case F54_ABS_HYBRID_RAW_CAP: + case F54_FULL_RAW_CAP_TDDI: + break; + case F54_AMP_RAW_ADC: + if (f54->query_49.has_ctrl188) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->control.reg_188->address, + f54->control.reg_188->data, + sizeof(f54->control.reg_188->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set start production test\n", + __func__); + return retval; + } + f54->control.reg_188->start_production_test = 1; + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->control.reg_188->address, + f54->control.reg_188->data, + sizeof(f54->control.reg_188->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set start production test\n", + __func__); + return retval; + } + } + break; + default: + if (f54->query.touch_controller_family == 1) + disable_cbc(reg_7); + else if (f54->query.has_ctrl88) + disable_cbc(reg_88); + + if (f54->query.has_0d_acquisition_control) + disable_cbc(reg_57); + + if ((f54->query.has_query15) && + (f54->query_15.has_query25) && + (f54->query_25.has_query27) && + (f54->query_27.has_query29) && + (f54->query_29.has_query30) && + (f54->query_30.has_query32) && + (f54->query_32.has_query33) && + (f54->query_33.has_query36) && + (f54->query_36.has_query38) && + (f54->query_38.has_ctrl149)) { + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->control.reg_149->address, + &zero, + sizeof(f54->control.reg_149->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to disable global CBC\n", + __func__); + return retval; + } + } + + if (f54->query.has_signal_clarity) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->control.reg_41->address, + &value, + sizeof(f54->control.reg_41->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to disable signal clarity\n", + __func__); + return retval; + } + value |= 0x01; + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->control.reg_41->address, + &value, + sizeof(f54->control.reg_41->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to disable signal clarity\n", + __func__); + return retval; + } + } + + retval = test_do_command(COMMAND_FORCE_UPDATE); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do force update\n", + __func__); + return retval; + } + + retval = test_do_command(COMMAND_FORCE_CAL); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do force cal\n", + __func__); + return retval; + } + } + + return 0; +} + +static int test_do_afe_calibration(enum f54_afe_cal mode) +{ + int retval; + unsigned char timeout = CALIBRATION_TIMEOUT_S; + unsigned char timeout_count = 0; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->control.reg_188->address, + f54->control.reg_188->data, + sizeof(f54->control.reg_188->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to start calibration\n", + __func__); + return retval; + } + + if (mode == F54_AFE_CAL) + f54->control.reg_188->start_calibration = 1; + else if (mode == F54_AFE_IS_CAL) + f54->control.reg_188->start_is_calibration = 1; + + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->control.reg_188->address, + f54->control.reg_188->data, + sizeof(f54->control.reg_188->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to start calibration\n", + __func__); + return retval; + } + + do { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->control.reg_188->address, + f54->control.reg_188->data, + sizeof(f54->control.reg_188->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to complete calibration\n", + __func__); + return retval; + } + + if (mode == F54_AFE_CAL) { + if (!f54->control.reg_188->start_calibration) + break; + } else if (mode == F54_AFE_IS_CAL) { + if (!f54->control.reg_188->start_is_calibration) + break; + } + + if (timeout_count == timeout) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Timed out waiting for calibration completion\n", + __func__); + return -EBUSY; + } + + timeout_count++; + msleep(1000); + } while (true); + + /* check CRC */ + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->data_31.address, + f54->data_31.data, + sizeof(f54->data_31.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read calibration CRC\n", + __func__); + return retval; + } + + if (mode == F54_AFE_CAL) { + if (f54->data_31.calibration_crc == 0) + return 0; + } else if (mode == F54_AFE_IS_CAL) { + if (f54->data_31.is_calibration_crc == 0) + return 0; + } + + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read calibration CRC\n", + __func__); + + return -EINVAL; +} + +static int test_check_for_idle_status(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + switch (f54->status) { + case STATUS_IDLE: + retval = 0; + break; + case STATUS_BUSY: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Status busy\n", + __func__); + retval = -EINVAL; + break; + case STATUS_ERROR: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Status error\n", + __func__); + retval = -EINVAL; + break; + default: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid status (%d)\n", + __func__, f54->status); + retval = -EINVAL; + } + + return retval; +} + +static void test_timeout_work(struct work_struct *work) +{ + int retval; + unsigned char command; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->status_mutex); + + if (f54->status == STATUS_BUSY) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read command register\n", + __func__); + } else if (command & COMMAND_GET_REPORT) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Report type not supported by FW\n", + __func__); + } else { + queue_work(f54->test_report_workqueue, + &f54->test_report_work); + goto exit; + } + f54->status = STATUS_ERROR; + f54->report_size = 0; + } + +exit: + mutex_unlock(&f54->status_mutex); +} + +static enum hrtimer_restart test_get_report_timeout(struct hrtimer *timer) +{ + schedule_work(&(f54->timeout_work)); + + return HRTIMER_NORESTART; +} + +static ssize_t num_of_mapped_tx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->tx_assigned); +} + +static ssize_t num_of_mapped_rx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->rx_assigned); +} + +static ssize_t tx_mapping_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int cnt; + int count = 0; + unsigned char ii; + unsigned char tx_num; + unsigned char tx_electrodes; + + if (!f55) + return -EINVAL; + + tx_electrodes = f55->query.num_of_tx_electrodes; + + for (ii = 0; ii < tx_electrodes; ii++) { + tx_num = f55->tx_assignment[ii]; + if (tx_num == 0xff) + cnt = snprintf(buf, PAGE_SIZE - count, "xx "); + else + cnt = snprintf(buf, PAGE_SIZE - count, "%02u ", tx_num); + buf += cnt; + count += cnt; + } + + snprintf(buf, PAGE_SIZE - count, "\n"); + count++; + + return count; +} + +static ssize_t rx_mapping_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int cnt; + int count = 0; + unsigned char ii; + unsigned char rx_num; + unsigned char rx_electrodes; + + if (!f55) + return -EINVAL; + + rx_electrodes = f55->query.num_of_rx_electrodes; + + for (ii = 0; ii < rx_electrodes; ii++) { + rx_num = f55->rx_assignment[ii]; + if (rx_num == 0xff) + cnt = snprintf(buf, PAGE_SIZE - count, "xx "); + else + cnt = snprintf(buf, PAGE_SIZE - count, "%02u ", rx_num); + buf += cnt; + count += cnt; + } + + snprintf(buf, PAGE_SIZE - count, "\n"); + count++; + + return count; +} + +static ssize_t num_of_mapped_force_tx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f21->tx_assigned); +} + +static ssize_t num_of_mapped_force_rx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f21->rx_assigned); +} + +static ssize_t force_tx_mapping_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int cnt; + int count = 0; + unsigned char ii; + unsigned char tx_num; + unsigned char tx_electrodes; + + if ((!f55 || !f55->has_force) && (!f21 || !f21->has_force)) + return -EINVAL; + + if (f55->has_force) { + tx_electrodes = f55->query.num_of_tx_electrodes; + + for (ii = 0; ii < tx_electrodes; ii++) { + tx_num = f55->force_tx_assignment[ii]; + if (tx_num == 0xff) { + cnt = snprintf(buf, PAGE_SIZE - count, "xx "); + } else { + cnt = snprintf(buf, PAGE_SIZE - count, "%02u ", + tx_num); + } + buf += cnt; + count += cnt; + } + } else if (f21->has_force) { + tx_electrodes = f21->max_num_of_tx; + + for (ii = 0; ii < tx_electrodes; ii++) { + tx_num = f21->force_txrx_assignment[ii]; + if (tx_num == 0xff) { + cnt = snprintf(buf, PAGE_SIZE - count, "xx "); + } else { + cnt = snprintf(buf, PAGE_SIZE - count, "%02u ", + tx_num); + } + buf += cnt; + count += cnt; + } + } + + snprintf(buf, PAGE_SIZE - count, "\n"); + count++; + + return count; +} + +static ssize_t force_rx_mapping_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int cnt; + int count = 0; + unsigned char ii; + unsigned char offset; + unsigned char rx_num; + unsigned char rx_electrodes; + + if ((!f55 || !f55->has_force) && (!f21 || !f21->has_force)) + return -EINVAL; + + if (f55->has_force) { + rx_electrodes = f55->query.num_of_rx_electrodes; + + for (ii = 0; ii < rx_electrodes; ii++) { + rx_num = f55->force_rx_assignment[ii]; + if (rx_num == 0xff) + cnt = snprintf(buf, PAGE_SIZE - count, "xx "); + else + cnt = snprintf(buf, PAGE_SIZE - count, "%02u ", + rx_num); + buf += cnt; + count += cnt; + } + } else if (f21->has_force) { + offset = f21->max_num_of_tx; + rx_electrodes = f21->max_num_of_rx; + + for (ii = offset; ii < (rx_electrodes + offset); ii++) { + rx_num = f21->force_txrx_assignment[ii]; + if (rx_num == 0xff) + cnt = snprintf(buf, PAGE_SIZE - count, "xx "); + else + cnt = snprintf(buf, PAGE_SIZE - count, "%02u ", + rx_num); + buf += cnt; + count += cnt; + } + } + + snprintf(buf, PAGE_SIZE - count, "\n"); + count++; + + return count; +} + +static ssize_t report_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->report_size); +} + +static ssize_t status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + + mutex_lock(&f54->status_mutex); + + retval = snprintf(buf, PAGE_SIZE, "%u\n", f54->status); + + mutex_unlock(&f54->status_mutex); + + return retval; +} + +static ssize_t do_preparation_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return -EINVAL; + + mutex_lock(&f54->status_mutex); + + retval = test_check_for_idle_status(); + if (retval < 0) + goto exit; + + retval = test_do_preparation(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do preparation\n", + __func__); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&f54->status_mutex); + + return retval; +} + +static ssize_t force_cal_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return -EINVAL; + + mutex_lock(&f54->status_mutex); + + retval = test_check_for_idle_status(); + if (retval < 0) + goto exit; + + retval = test_do_command(COMMAND_FORCE_CAL); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to do force cal\n", + __func__); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&f54->status_mutex); + + return retval; +} + +static ssize_t get_report_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned char command; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return -EINVAL; + + mutex_lock(&f54->status_mutex); + + retval = test_check_for_idle_status(); + if (retval < 0) + goto exit; + + if (!test_report_type_valid(f54->report_type)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Invalid report type\n", + __func__); + retval = -EINVAL; + goto exit; + } + + test_set_interrupt(true); + + command = (unsigned char)COMMAND_GET_REPORT; + + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write get report command\n", + __func__); + goto exit; + } + + f54->status = STATUS_BUSY; + f54->report_size = 0; + f54->data_pos = 0; + + hrtimer_start(&f54->watchdog, + ktime_set(GET_REPORT_TIMEOUT_S, 0), + HRTIMER_MODE_REL); + + retval = count; + +exit: + mutex_unlock(&f54->status_mutex); + + return retval; +} + +static ssize_t resume_touch_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned char device_ctrl; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return -EINVAL; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to restore no sleep setting\n", + __func__); + return retval; + } + + device_ctrl = device_ctrl & ~NO_SLEEP_ON; + device_ctrl |= rmi4_data->no_sleep_setting; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &device_ctrl, + sizeof(device_ctrl)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to restore no sleep setting\n", + __func__); + return retval; + } + + test_set_interrupt(false); + + if (f54->skip_preparation) + return count; + + switch (f54->report_type) { + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_SENSOR_SPEED: + case F54_ADC_RANGE: + case F54_ABS_RAW_CAP: + case F54_ABS_DELTA_CAP: + case F54_ABS_HYBRID_DELTA_CAP: + case F54_ABS_HYBRID_RAW_CAP: + case F54_FULL_RAW_CAP_TDDI: + break; + case F54_AMP_RAW_ADC: + if (f54->query_49.has_ctrl188) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->control.reg_188->address, + f54->control.reg_188->data, + sizeof(f54->control.reg_188->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set start production test\n", + __func__); + return retval; + } + f54->control.reg_188->start_production_test = 0; + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->control.reg_188->address, + f54->control.reg_188->data, + sizeof(f54->control.reg_188->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set start production test\n", + __func__); + return retval; + } + } + break; + default: + rmi4_data->reset_device(rmi4_data, false); + } + + return count; +} + +static ssize_t do_afe_calibration_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (!f54->query_49.has_ctrl188) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: F54_ANALOG_Ctrl188 not found\n", + __func__); + return -EINVAL; + } + + if (setting == 0 || setting == 1) + retval = test_do_afe_calibration((enum f54_afe_cal)setting); + else + return -EINVAL; + + if (retval) + return retval; + else + return count; +} + +static ssize_t report_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->report_type); +} + +static ssize_t report_type_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned char data; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + mutex_lock(&f54->status_mutex); + + retval = test_check_for_idle_status(); + if (retval < 0) + goto exit; + + if (!test_report_type_valid((enum f54_report_types)setting)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Report type not supported by driver\n", + __func__); + retval = -EINVAL; + goto exit; + } + + f54->report_type = (enum f54_report_types)setting; + data = (unsigned char)setting; + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->data_base_addr, + &data, + sizeof(data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write report type\n", + __func__); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&f54->status_mutex); + + return retval; +} + +static ssize_t fifoindex_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned char data[2]; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->data_base_addr + REPORT_INDEX_OFFSET, + data, + sizeof(data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read report index\n", + __func__); + return retval; + } + + batohs(&f54->fifoindex, data); + + return snprintf(buf, PAGE_SIZE, "%u\n", f54->fifoindex); +} + +static ssize_t fifoindex_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned char data[2]; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + f54->fifoindex = setting; + + hstoba(data, (unsigned short)setting); + + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->data_base_addr + REPORT_INDEX_OFFSET, + data, + sizeof(data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write report index\n", + __func__); + return retval; + } + + return count; +} + +static ssize_t no_auto_cal_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->no_auto_cal); +} + +static ssize_t no_auto_cal_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned char data; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting > 1) + return -EINVAL; + + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->control_base_addr, + &data, + sizeof(data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read no auto cal setting\n", + __func__); + return retval; + } + + if (setting) + data |= CONTROL_NO_AUTO_CAL; + else + data &= ~CONTROL_NO_AUTO_CAL; + + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->control_base_addr, + &data, + sizeof(data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write no auto cal setting\n", + __func__); + return retval; + } + + f54->no_auto_cal = (setting == 1); + + return count; +} + +static ssize_t read_report_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int ii; + unsigned int jj; + int cnt; + int count = 0; + int tx_num = f54->tx_assigned; + int rx_num = f54->rx_assigned; + char *report_data_8; + short *report_data_16; + int *report_data_32; + unsigned short *report_data_u16; + unsigned int *report_data_u32; + + switch (f54->report_type) { + case F54_8BIT_IMAGE: + report_data_8 = (char *)f54->report_data; + for (ii = 0; ii < f54->report_size; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, "%03d: %d\n", + ii, *report_data_8); + report_data_8++; + buf += cnt; + count += cnt; + } + break; + case F54_AMP_RAW_ADC: + report_data_u16 = (unsigned short *)f54->report_data; + cnt = snprintf(buf, PAGE_SIZE - count, "tx = %d\nrx = %d\n", + tx_num, rx_num); + buf += cnt; + count += cnt; + + for (ii = 0; ii < tx_num; ii++) { + for (jj = 0; jj < (rx_num - 1); jj++) { + cnt = snprintf(buf, PAGE_SIZE - count, "%-4d ", + *report_data_u16); + report_data_u16++; + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "%-4d\n", + *report_data_u16); + report_data_u16++; + buf += cnt; + count += cnt; + } + break; + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_NO_RX_COUPLING: + case F54_SENSOR_SPEED: + case F54_AMP_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_TDDI: + report_data_16 = (short *)f54->report_data; + cnt = snprintf(buf, PAGE_SIZE - count, "tx = %d\nrx = %d\n", + tx_num, rx_num); + buf += cnt; + count += cnt; + + for (ii = 0; ii < tx_num; ii++) { + for (jj = 0; jj < (rx_num - 1); jj++) { + cnt = snprintf(buf, PAGE_SIZE - count, "%-4d ", + *report_data_16); + report_data_16++; + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "%-4d\n", + *report_data_16); + report_data_16++; + buf += cnt; + count += cnt; + } + break; + case F54_HIGH_RESISTANCE: + case F54_FULL_RAW_CAP_MIN_MAX: + report_data_16 = (short *)f54->report_data; + for (ii = 0; ii < f54->report_size; ii += 2) { + cnt = snprintf(buf, PAGE_SIZE - count, "%03d: %d\n", + ii / 2, *report_data_16); + report_data_16++; + buf += cnt; + count += cnt; + } + break; + case F54_ABS_RAW_CAP: + case F54_ABS_HYBRID_RAW_CAP: + tx_num += f21->tx_assigned; + rx_num += f21->rx_assigned; + report_data_u32 = (unsigned int *)f54->report_data; + cnt = snprintf(buf, PAGE_SIZE - count, "rx "); + buf += cnt; + count += cnt; + for (ii = 0; ii < rx_num; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, " %2d", ii); + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "\n"); + buf += cnt; + count += cnt; + + cnt = snprintf(buf, PAGE_SIZE - count, " "); + buf += cnt; + count += cnt; + for (ii = 0; ii < rx_num; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, " %5u", + *report_data_u32); + report_data_u32++; + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "\n"); + buf += cnt; + count += cnt; + + cnt = snprintf(buf, PAGE_SIZE - count, "tx "); + buf += cnt; + count += cnt; + for (ii = 0; ii < tx_num; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, " %2d", ii); + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "\n"); + buf += cnt; + count += cnt; + + cnt = snprintf(buf, PAGE_SIZE - count, " "); + buf += cnt; + count += cnt; + for (ii = 0; ii < tx_num; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, " %5u", + *report_data_u32); + report_data_u32++; + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "\n"); + buf += cnt; + count += cnt; + break; + case F54_ABS_DELTA_CAP: + case F54_ABS_HYBRID_DELTA_CAP: + tx_num += f21->tx_assigned; + rx_num += f21->rx_assigned; + report_data_32 = (int *)f54->report_data; + cnt = snprintf(buf, PAGE_SIZE - count, "rx "); + buf += cnt; + count += cnt; + for (ii = 0; ii < rx_num; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, " %2d", ii); + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "\n"); + buf += cnt; + count += cnt; + + cnt = snprintf(buf, PAGE_SIZE - count, " "); + buf += cnt; + count += cnt; + for (ii = 0; ii < rx_num; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, " %5d", + *report_data_32); + report_data_32++; + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "\n"); + buf += cnt; + count += cnt; + + cnt = snprintf(buf, PAGE_SIZE - count, "tx "); + buf += cnt; + count += cnt; + for (ii = 0; ii < tx_num; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, " %2d", ii); + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "\n"); + buf += cnt; + count += cnt; + + cnt = snprintf(buf, PAGE_SIZE - count, " "); + buf += cnt; + count += cnt; + for (ii = 0; ii < tx_num; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, " %5d", + *report_data_32); + report_data_32++; + buf += cnt; + count += cnt; + } + cnt = snprintf(buf, PAGE_SIZE - count, "\n"); + buf += cnt; + count += cnt; + break; + default: + for (ii = 0; ii < f54->report_size; ii++) { + cnt = snprintf(buf, PAGE_SIZE - count, "%03d: 0x%02x\n", + ii, f54->report_data[ii]); + buf += cnt; + count += cnt; + } + } + + snprintf(buf, PAGE_SIZE - count, "\n"); + count++; + + return count; +} + +static ssize_t read_report_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned char timeout = GET_REPORT_TIMEOUT_S * 10; + unsigned char timeout_count; + const char cmd[] = {'1', 0}; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = report_type_store(dev, attr, buf, count); + if (retval < 0) + goto exit; + + retval = do_preparation_store(dev, attr, cmd, 1); + if (retval < 0) + goto exit; + + retval = get_report_store(dev, attr, cmd, 1); + if (retval < 0) + goto exit; + + timeout_count = 0; + do { + if (f54->status != STATUS_BUSY) + break; + msleep(100); + timeout_count++; + } while (timeout_count < timeout); + + if ((f54->status != STATUS_IDLE) || (f54->report_size == 0)) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read report\n", + __func__); + retval = -EINVAL; + goto exit; + } + + retval = resume_touch_store(dev, attr, cmd, 1); + if (retval < 0) + goto exit; + + return count; + +exit: + rmi4_data->reset_device(rmi4_data, false); + + return retval; +} + +static ssize_t test_sysfs_data_read(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int read_size; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->status_mutex); + + retval = test_check_for_idle_status(); + if (retval < 0) + goto exit; + + if (!f54->report_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Report type %d data not available\n", + __func__, f54->report_type); + retval = -EINVAL; + goto exit; + } + + if ((f54->data_pos + count) > f54->report_size) + read_size = f54->report_size - f54->data_pos; + else + read_size = min_t(unsigned int, count, f54->report_size); + + retval = secure_memcpy(buf, count, f54->report_data + f54->data_pos, + f54->data_buffer_size - f54->data_pos, read_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to copy report data\n", + __func__); + goto exit; + } + f54->data_pos += read_size; + retval = read_size; + +exit: + mutex_unlock(&f54->status_mutex); + + return retval; +} + +static void test_report_work(struct work_struct *work) +{ + int retval; + unsigned char report_index[2]; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->status_mutex); + + if (f54->status != STATUS_BUSY) { + retval = f54->status; + goto exit; + } + + retval = test_wait_for_command_completion(); + if (retval < 0) { + retval = STATUS_ERROR; + goto exit; + } + + test_set_report_size(); + if (f54->report_size == 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Report data size = 0\n", + __func__); + retval = STATUS_ERROR; + goto exit; + } + + if (f54->data_buffer_size < f54->report_size) { + if (f54->data_buffer_size) + kfree(f54->report_data); + f54->report_data = kzalloc(f54->report_size, GFP_KERNEL); + if (!f54->report_data) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for data buffer\n", + __func__); + f54->data_buffer_size = 0; + retval = STATUS_ERROR; + goto exit; + } + f54->data_buffer_size = f54->report_size; + } + + report_index[0] = 0; + report_index[1] = 0; + + retval = synaptics_rmi4_reg_write(rmi4_data, + f54->data_base_addr + REPORT_INDEX_OFFSET, + report_index, + sizeof(report_index)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to write report data index\n", + __func__); + retval = STATUS_ERROR; + goto exit; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->data_base_addr + REPORT_DATA_OFFSET, + f54->report_data, + f54->report_size); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read report data\n", + __func__); + retval = STATUS_ERROR; + goto exit; + } + + retval = STATUS_IDLE; + +exit: + mutex_unlock(&f54->status_mutex); + + if (retval == STATUS_ERROR) + f54->report_size = 0; + + f54->status = retval; +} + +static void test_remove_sysfs(void) +{ + sysfs_remove_group(f54->sysfs_dir, &attr_group); + sysfs_remove_bin_file(f54->sysfs_dir, &test_report_data); + kobject_put(f54->sysfs_dir); +} + +static int test_set_sysfs(void) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + f54->sysfs_dir = kobject_create_and_add(SYSFS_FOLDER_NAME, + &rmi4_data->input_dev->dev.kobj); + if (!f54->sysfs_dir) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs directory\n", + __func__); + goto exit_directory; + } + + retval = sysfs_create_bin_file(f54->sysfs_dir, &test_report_data); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs bin file\n", + __func__); + goto exit_bin_file; + } + + retval = sysfs_create_group(f54->sysfs_dir, &attr_group); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs attributes\n", + __func__); + goto exit_attributes; + } + + return 0; + +exit_attributes: + sysfs_remove_group(f54->sysfs_dir, &attr_group); + sysfs_remove_bin_file(f54->sysfs_dir, &test_report_data); + +exit_bin_file: + kobject_put(f54->sysfs_dir); + +exit_directory: + return -ENODEV; +} + +static void test_free_control_mem(void) +{ + struct f54_control control = f54->control; + + kfree(control.reg_7); + kfree(control.reg_41); + kfree(control.reg_57); + kfree(control.reg_86); + kfree(control.reg_88); + kfree(control.reg_110); + kfree(control.reg_149); + kfree(control.reg_188); +} + +static void test_set_data(void) +{ + unsigned short reg_addr; + + reg_addr = f54->data_base_addr + REPORT_DATA_OFFSET + 1; + + /* data 4 */ + if (f54->query.has_sense_frequency_control) + reg_addr++; + + /* data 5 reserved */ + + /* data 6 */ + if (f54->query.has_interference_metric) + reg_addr += 2; + + /* data 7 */ + if (f54->query.has_one_byte_report_rate | + f54->query.has_two_byte_report_rate) + reg_addr++; + if (f54->query.has_two_byte_report_rate) + reg_addr++; + + /* data 8 */ + if (f54->query.has_variance_metric) + reg_addr += 2; + + /* data 9 */ + if (f54->query.has_multi_metric_state_machine) + reg_addr += 2; + + /* data 10 */ + if (f54->query.has_multi_metric_state_machine | + f54->query.has_noise_state) + reg_addr++; + + /* data 11 */ + if (f54->query.has_status) + reg_addr++; + + /* data 12 */ + if (f54->query.has_slew_metric) + reg_addr += 2; + + /* data 13 */ + if (f54->query.has_multi_metric_state_machine) + reg_addr += 2; + + /* data 14 */ + if (f54->query_13.has_cidim) + reg_addr++; + + /* data 15 */ + if (f54->query_13.has_rail_im) + reg_addr++; + + /* data 16 */ + if (f54->query_13.has_noise_mitigation_enhancement) + reg_addr++; + + /* data 17 */ + if (f54->query_16.has_data17) + reg_addr++; + + /* data 18 */ + if (f54->query_21.has_query24_data18) + reg_addr++; + + /* data 19 */ + if (f54->query_21.has_data19) + reg_addr++; + + /* data_20 */ + if (f54->query_25.has_ctrl109) + reg_addr++; + + /* data 21 */ + if (f54->query_27.has_data21) + reg_addr++; + + /* data 22 */ + if (f54->query_27.has_data22) + reg_addr++; + + /* data 23 */ + if (f54->query_29.has_data23) + reg_addr++; + + /* data 24 */ + if (f54->query_32.has_data24) + reg_addr++; + + /* data 25 */ + if (f54->query_35.has_data25) + reg_addr++; + + /* data 26 */ + if (f54->query_35.has_data26) + reg_addr++; + + /* data 27 */ + if (f54->query_46.has_data27) + reg_addr++; + + /* data 28 */ + if (f54->query_46.has_data28) + reg_addr++; + + /* data 29 30 reserved */ + + /* data 31 */ + if (f54->query_49.has_data31) { + f54->data_31.address = reg_addr; + reg_addr++; + } +} + +static int test_set_controls(void) +{ + int retval; + unsigned char length = 0; + unsigned char num_of_sensing_freqs; + unsigned short reg_addr = f54->control_base_addr; + struct f54_control *control = &f54->control; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + num_of_sensing_freqs = f54->query.number_of_sensing_frequencies; + + /* control 0 */ + reg_addr += CONTROL_0_SIZE; + + /* control 1 */ + if ((f54->query.touch_controller_family == 0) || + (f54->query.touch_controller_family == 1)) + reg_addr += CONTROL_1_SIZE; + + /* control 2 */ + reg_addr += CONTROL_2_SIZE; + + /* control 3 */ + if (f54->query.has_pixel_touch_threshold_adjustment) + reg_addr += CONTROL_3_SIZE; + + /* controls 4 5 6 */ + if ((f54->query.touch_controller_family == 0) || + (f54->query.touch_controller_family == 1)) + reg_addr += CONTROL_4_6_SIZE; + + /* control 7 */ + if (f54->query.touch_controller_family == 1) { + control->reg_7 = kzalloc(sizeof(*(control->reg_7)), + GFP_KERNEL); + if (!control->reg_7) + goto exit_no_mem; + control->reg_7->address = reg_addr; + reg_addr += CONTROL_7_SIZE; + } + + /* controls 8 9 */ + if ((f54->query.touch_controller_family == 0) || + (f54->query.touch_controller_family == 1)) + reg_addr += CONTROL_8_9_SIZE; + + /* control 10 */ + if (f54->query.has_interference_metric) + reg_addr += CONTROL_10_SIZE; + + /* control 11 */ + if (f54->query.has_ctrl11) + reg_addr += CONTROL_11_SIZE; + + /* controls 12 13 */ + if (f54->query.has_relaxation_control) + reg_addr += CONTROL_12_13_SIZE; + + /* controls 14 15 16 */ + if (f54->query.has_sensor_assignment) { + reg_addr += CONTROL_14_SIZE; + reg_addr += CONTROL_15_SIZE * f54->query.num_of_rx_electrodes; + reg_addr += CONTROL_16_SIZE * f54->query.num_of_tx_electrodes; + } + + /* controls 17 18 19 */ + if (f54->query.has_sense_frequency_control) { + reg_addr += CONTROL_17_SIZE * num_of_sensing_freqs; + reg_addr += CONTROL_18_SIZE * num_of_sensing_freqs; + reg_addr += CONTROL_19_SIZE * num_of_sensing_freqs; + } + + /* control 20 */ + reg_addr += CONTROL_20_SIZE; + + /* control 21 */ + if (f54->query.has_sense_frequency_control) + reg_addr += CONTROL_21_SIZE; + + /* controls 22 23 24 25 26 */ + if (f54->query.has_firmware_noise_mitigation) + reg_addr += CONTROL_22_26_SIZE; + + /* control 27 */ + if (f54->query.has_iir_filter) + reg_addr += CONTROL_27_SIZE; + + /* control 28 */ + if (f54->query.has_firmware_noise_mitigation) + reg_addr += CONTROL_28_SIZE; + + /* control 29 */ + if (f54->query.has_cmn_removal) + reg_addr += CONTROL_29_SIZE; + + /* control 30 */ + if (f54->query.has_cmn_maximum) + reg_addr += CONTROL_30_SIZE; + + /* control 31 */ + if (f54->query.has_touch_hysteresis) + reg_addr += CONTROL_31_SIZE; + + /* controls 32 33 34 35 */ + if (f54->query.has_edge_compensation) + reg_addr += CONTROL_32_35_SIZE; + + /* control 36 */ + if ((f54->query.curve_compensation_mode == 1) || + (f54->query.curve_compensation_mode == 2)) { + if (f54->query.curve_compensation_mode == 1) { + length = max(f54->query.num_of_rx_electrodes, + f54->query.num_of_tx_electrodes); + } else if (f54->query.curve_compensation_mode == 2) { + length = f54->query.num_of_rx_electrodes; + } + reg_addr += CONTROL_36_SIZE * length; + } + + /* control 37 */ + if (f54->query.curve_compensation_mode == 2) + reg_addr += CONTROL_37_SIZE * f54->query.num_of_tx_electrodes; + + /* controls 38 39 40 */ + if (f54->query.has_per_frequency_noise_control) { + reg_addr += CONTROL_38_SIZE * num_of_sensing_freqs; + reg_addr += CONTROL_39_SIZE * num_of_sensing_freqs; + reg_addr += CONTROL_40_SIZE * num_of_sensing_freqs; + } + + /* control 41 */ + if (f54->query.has_signal_clarity) { + control->reg_41 = kzalloc(sizeof(*(control->reg_41)), + GFP_KERNEL); + if (!control->reg_41) + goto exit_no_mem; + control->reg_41->address = reg_addr; + reg_addr += CONTROL_41_SIZE; + } + + /* control 42 */ + if (f54->query.has_variance_metric) + reg_addr += CONTROL_42_SIZE; + + /* controls 43 44 45 46 47 48 49 50 51 52 53 54 */ + if (f54->query.has_multi_metric_state_machine) + reg_addr += CONTROL_43_54_SIZE; + + /* controls 55 56 */ + if (f54->query.has_0d_relaxation_control) + reg_addr += CONTROL_55_56_SIZE; + + /* control 57 */ + if (f54->query.has_0d_acquisition_control) { + control->reg_57 = kzalloc(sizeof(*(control->reg_57)), + GFP_KERNEL); + if (!control->reg_57) + goto exit_no_mem; + control->reg_57->address = reg_addr; + reg_addr += CONTROL_57_SIZE; + } + + /* control 58 */ + if (f54->query.has_0d_acquisition_control) + reg_addr += CONTROL_58_SIZE; + + /* control 59 */ + if (f54->query.has_h_blank) + reg_addr += CONTROL_59_SIZE; + + /* controls 60 61 62 */ + if ((f54->query.has_h_blank) || + (f54->query.has_v_blank) || + (f54->query.has_long_h_blank)) + reg_addr += CONTROL_60_62_SIZE; + + /* control 63 */ + if ((f54->query.has_h_blank) || + (f54->query.has_v_blank) || + (f54->query.has_long_h_blank) || + (f54->query.has_slew_metric) || + (f54->query.has_slew_option) || + (f54->query.has_noise_mitigation2)) + reg_addr += CONTROL_63_SIZE; + + /* controls 64 65 66 67 */ + if (f54->query.has_h_blank) + reg_addr += CONTROL_64_67_SIZE * 7; + else if ((f54->query.has_v_blank) || + (f54->query.has_long_h_blank)) + reg_addr += CONTROL_64_67_SIZE; + + /* controls 68 69 70 71 72 73 */ + if ((f54->query.has_h_blank) || + (f54->query.has_v_blank) || + (f54->query.has_long_h_blank)) { + if (f54->query_68.is_tddi_hic) + reg_addr += CONTROL_70_73_SIZE; + else + reg_addr += CONTROL_68_73_SIZE; + } + + /* control 74 */ + if (f54->query.has_slew_metric) + reg_addr += CONTROL_74_SIZE; + + /* control 75 */ + if (f54->query.has_enhanced_stretch) + reg_addr += CONTROL_75_SIZE * num_of_sensing_freqs; + + /* control 76 */ + if (f54->query.has_startup_fast_relaxation) + reg_addr += CONTROL_76_SIZE; + + /* controls 77 78 */ + if (f54->query.has_esd_control) + reg_addr += CONTROL_77_78_SIZE; + + /* controls 79 80 81 82 83 */ + if (f54->query.has_noise_mitigation2) + reg_addr += CONTROL_79_83_SIZE; + + /* controls 84 85 */ + if (f54->query.has_energy_ratio_relaxation) + reg_addr += CONTROL_84_85_SIZE; + + /* control 86 */ + if (f54->query_13.has_ctrl86) { + control->reg_86 = kzalloc(sizeof(*(control->reg_86)), + GFP_KERNEL); + if (!control->reg_86) + goto exit_no_mem; + control->reg_86->address = reg_addr; + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->control.reg_86->address, + f54->control.reg_86->data, + sizeof(f54->control.reg_86->data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read sense display ratio\n", + __func__); + return retval; + } + reg_addr += CONTROL_86_SIZE; + } + + /* control 87 */ + if (f54->query_13.has_ctrl87) + reg_addr += CONTROL_87_SIZE; + + /* control 88 */ + if (f54->query.has_ctrl88) { + control->reg_88 = kzalloc(sizeof(*(control->reg_88)), + GFP_KERNEL); + if (!control->reg_88) + goto exit_no_mem; + control->reg_88->address = reg_addr; + reg_addr += CONTROL_88_SIZE; + } + + /* control 89 */ + if (f54->query_13.has_cidim || + f54->query_13.has_noise_mitigation_enhancement || + f54->query_13.has_rail_im) + reg_addr += CONTROL_89_SIZE; + + /* control 90 */ + if (f54->query_15.has_ctrl90) + reg_addr += CONTROL_90_SIZE; + + /* control 91 */ + if (f54->query_21.has_ctrl91) + reg_addr += CONTROL_91_SIZE; + + /* control 92 */ + if (f54->query_16.has_ctrl92) + reg_addr += CONTROL_92_SIZE; + + /* control 93 */ + if (f54->query_16.has_ctrl93) + reg_addr += CONTROL_93_SIZE; + + /* control 94 */ + if (f54->query_16.has_ctrl94_query18) + reg_addr += CONTROL_94_SIZE; + + /* control 95 */ + if (f54->query_16.has_ctrl95_query19) + reg_addr += CONTROL_95_SIZE; + + /* control 96 */ + if (f54->query_21.has_ctrl96) + reg_addr += CONTROL_96_SIZE; + + /* control 97 */ + if (f54->query_21.has_ctrl97) + reg_addr += CONTROL_97_SIZE; + + /* control 98 */ + if (f54->query_21.has_ctrl98) + reg_addr += CONTROL_98_SIZE; + + /* control 99 */ + if (f54->query.touch_controller_family == 2) + reg_addr += CONTROL_99_SIZE; + + /* control 100 */ + if (f54->query_16.has_ctrl100) + reg_addr += CONTROL_100_SIZE; + + /* control 101 */ + if (f54->query_22.has_ctrl101) + reg_addr += CONTROL_101_SIZE; + + /* control 102 */ + if (f54->query_23.has_ctrl102) + reg_addr += CONTROL_102_SIZE; + + /* control 103 */ + if (f54->query_22.has_ctrl103_query26) { + f54->skip_preparation = true; + reg_addr += CONTROL_103_SIZE; + } + + /* control 104 */ + if (f54->query_22.has_ctrl104) + reg_addr += CONTROL_104_SIZE; + + /* control 105 */ + if (f54->query_22.has_ctrl105) + reg_addr += CONTROL_105_SIZE; + + /* control 106 */ + if (f54->query_25.has_ctrl106) + reg_addr += CONTROL_106_SIZE; + + /* control 107 */ + if (f54->query_25.has_ctrl107) + reg_addr += CONTROL_107_SIZE; + + /* control 108 */ + if (f54->query_25.has_ctrl108) + reg_addr += CONTROL_108_SIZE; + + /* control 109 */ + if (f54->query_25.has_ctrl109) + reg_addr += CONTROL_109_SIZE; + + /* control 110 */ + if (f54->query_27.has_ctrl110) { + control->reg_110 = kzalloc(sizeof(*(control->reg_110)), + GFP_KERNEL); + if (!control->reg_110) + goto exit_no_mem; + control->reg_110->address = reg_addr; + reg_addr += CONTROL_110_SIZE; + } + + /* control 111 */ + if (f54->query_27.has_ctrl111) + reg_addr += CONTROL_111_SIZE; + + /* control 112 */ + if (f54->query_27.has_ctrl112) + reg_addr += CONTROL_112_SIZE; + + /* control 113 */ + if (f54->query_27.has_ctrl113) + reg_addr += CONTROL_113_SIZE; + + /* control 114 */ + if (f54->query_27.has_ctrl114) + reg_addr += CONTROL_114_SIZE; + + /* control 115 */ + if (f54->query_29.has_ctrl115) + reg_addr += CONTROL_115_SIZE; + + /* control 116 */ + if (f54->query_29.has_ctrl116) + reg_addr += CONTROL_116_SIZE; + + /* control 117 */ + if (f54->query_29.has_ctrl117) + reg_addr += CONTROL_117_SIZE; + + /* control 118 */ + if (f54->query_30.has_ctrl118) + reg_addr += CONTROL_118_SIZE; + + /* control 119 */ + if (f54->query_30.has_ctrl119) + reg_addr += CONTROL_119_SIZE; + + /* control 120 */ + if (f54->query_30.has_ctrl120) + reg_addr += CONTROL_120_SIZE; + + /* control 121 */ + if (f54->query_30.has_ctrl121) + reg_addr += CONTROL_121_SIZE; + + /* control 122 */ + if (f54->query_30.has_ctrl122_query31) + reg_addr += CONTROL_122_SIZE; + + /* control 123 */ + if (f54->query_30.has_ctrl123) + reg_addr += CONTROL_123_SIZE; + + /* control 124 */ + if (f54->query_30.has_ctrl124) + reg_addr += CONTROL_124_SIZE; + + /* control 125 */ + if (f54->query_32.has_ctrl125) + reg_addr += CONTROL_125_SIZE; + + /* control 126 */ + if (f54->query_32.has_ctrl126) + reg_addr += CONTROL_126_SIZE; + + /* control 127 */ + if (f54->query_32.has_ctrl127) + reg_addr += CONTROL_127_SIZE; + + /* control 128 */ + if (f54->query_33.has_ctrl128) + reg_addr += CONTROL_128_SIZE; + + /* control 129 */ + if (f54->query_33.has_ctrl129) + reg_addr += CONTROL_129_SIZE; + + /* control 130 */ + if (f54->query_33.has_ctrl130) + reg_addr += CONTROL_130_SIZE; + + /* control 131 */ + if (f54->query_33.has_ctrl131) + reg_addr += CONTROL_131_SIZE; + + /* control 132 */ + if (f54->query_33.has_ctrl132) + reg_addr += CONTROL_132_SIZE; + + /* control 133 */ + if (f54->query_33.has_ctrl133) + reg_addr += CONTROL_133_SIZE; + + /* control 134 */ + if (f54->query_33.has_ctrl134) + reg_addr += CONTROL_134_SIZE; + + /* control 135 */ + if (f54->query_35.has_ctrl135) + reg_addr += CONTROL_135_SIZE; + + /* control 136 */ + if (f54->query_35.has_ctrl136) + reg_addr += CONTROL_136_SIZE; + + /* control 137 */ + if (f54->query_35.has_ctrl137) + reg_addr += CONTROL_137_SIZE; + + /* control 138 */ + if (f54->query_35.has_ctrl138) + reg_addr += CONTROL_138_SIZE; + + /* control 139 */ + if (f54->query_35.has_ctrl139) + reg_addr += CONTROL_139_SIZE; + + /* control 140 */ + if (f54->query_35.has_ctrl140) + reg_addr += CONTROL_140_SIZE; + + /* control 141 */ + if (f54->query_36.has_ctrl141) + reg_addr += CONTROL_141_SIZE; + + /* control 142 */ + if (f54->query_36.has_ctrl142) + reg_addr += CONTROL_142_SIZE; + + /* control 143 */ + if (f54->query_36.has_ctrl143) + reg_addr += CONTROL_143_SIZE; + + /* control 144 */ + if (f54->query_36.has_ctrl144) + reg_addr += CONTROL_144_SIZE; + + /* control 145 */ + if (f54->query_36.has_ctrl145) + reg_addr += CONTROL_145_SIZE; + + /* control 146 */ + if (f54->query_36.has_ctrl146) + reg_addr += CONTROL_146_SIZE; + + /* control 147 */ + if (f54->query_38.has_ctrl147) + reg_addr += CONTROL_147_SIZE; + + /* control 148 */ + if (f54->query_38.has_ctrl148) + reg_addr += CONTROL_148_SIZE; + + /* control 149 */ + if (f54->query_38.has_ctrl149) { + control->reg_149 = kzalloc(sizeof(*(control->reg_149)), + GFP_KERNEL); + if (!control->reg_149) + goto exit_no_mem; + control->reg_149->address = reg_addr; + reg_addr += CONTROL_149_SIZE; + } + + /* control 150 */ + if (f54->query_38.has_ctrl150) + reg_addr += CONTROL_150_SIZE; + + /* control 151 */ + if (f54->query_38.has_ctrl151) + reg_addr += CONTROL_151_SIZE; + + /* control 152 */ + if (f54->query_38.has_ctrl152) + reg_addr += CONTROL_152_SIZE; + + /* control 153 */ + if (f54->query_38.has_ctrl153) + reg_addr += CONTROL_153_SIZE; + + /* control 154 */ + if (f54->query_39.has_ctrl154) + reg_addr += CONTROL_154_SIZE; + + /* control 155 */ + if (f54->query_39.has_ctrl155) + reg_addr += CONTROL_155_SIZE; + + /* control 156 */ + if (f54->query_39.has_ctrl156) + reg_addr += CONTROL_156_SIZE; + + /* controls 157 158 */ + if (f54->query_39.has_ctrl157_ctrl158) + reg_addr += CONTROL_157_158_SIZE; + + /* controls 159 to 162 reserved */ + + /* control 163 */ + if (f54->query_40.has_ctrl163_query41) + reg_addr += CONTROL_163_SIZE; + + /* control 164 reserved */ + + /* control 165 */ + if (f54->query_40.has_ctrl165_query42) + reg_addr += CONTROL_165_SIZE; + + /* control 166 */ + if (f54->query_40.has_ctrl166) + reg_addr += CONTROL_166_SIZE; + + /* control 167 */ + if (f54->query_40.has_ctrl167) + reg_addr += CONTROL_167_SIZE; + + /* control 168 */ + if (f54->query_40.has_ctrl168) + reg_addr += CONTROL_168_SIZE; + + /* control 169 */ + if (f54->query_40.has_ctrl169) + reg_addr += CONTROL_169_SIZE; + + /* control 170 reserved */ + + /* control 171 */ + if (f54->query_43.has_ctrl171) + reg_addr += CONTROL_171_SIZE; + + /* control 172 */ + if (f54->query_43.has_ctrl172_query44_query45) + reg_addr += CONTROL_172_SIZE; + + /* control 173 */ + if (f54->query_43.has_ctrl173) + reg_addr += CONTROL_173_SIZE; + + /* control 174 */ + if (f54->query_43.has_ctrl174) + reg_addr += CONTROL_174_SIZE; + + /* control 175 */ + if (f54->query_43.has_ctrl175) + reg_addr += CONTROL_175_SIZE; + + /* control 176 */ + if (f54->query_46.has_ctrl176) + reg_addr += CONTROL_176_SIZE; + + /* controls 177 178 */ + if (f54->query_46.has_ctrl177_ctrl178) + reg_addr += CONTROL_177_178_SIZE; + + /* control 179 */ + if (f54->query_46.has_ctrl179) + reg_addr += CONTROL_179_SIZE; + + /* controls 180 to 181 reserved */ + + /* control 182 */ + if (f54->query_47.has_ctrl182) + reg_addr += CONTROL_182_SIZE; + + /* control 183 */ + if (f54->query_47.has_ctrl183) + reg_addr += CONTROL_183_SIZE; + + /* control 184 reserved */ + + /* control 185 */ + if (f54->query_47.has_ctrl185) + reg_addr += CONTROL_185_SIZE; + + /* control 186 */ + if (f54->query_47.has_ctrl186) + reg_addr += CONTROL_186_SIZE; + + /* control 187 */ + if (f54->query_47.has_ctrl187) + reg_addr += CONTROL_187_SIZE; + + /* control 188 */ + if (f54->query_49.has_ctrl188) { + control->reg_188 = kzalloc(sizeof(*(control->reg_188)), + GFP_KERNEL); + if (!control->reg_188) + goto exit_no_mem; + control->reg_188->address = reg_addr; + reg_addr += CONTROL_188_SIZE; + } + + return 0; + +exit_no_mem: + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for control registers\n", + __func__); + return -ENOMEM; +} + +static int test_set_queries(void) +{ + int retval; + unsigned char offset; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr, + f54->query.data, + sizeof(f54->query.data)); + if (retval < 0) + return retval; + + offset = sizeof(f54->query.data); + + /* query 12 */ + if (f54->query.has_sense_frequency_control == 0) + offset -= 1; + + /* query 13 */ + if (f54->query.has_query13) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_13.data, + sizeof(f54->query_13.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 14 */ + if (f54->query_13.has_ctrl87) + offset += 1; + + /* query 15 */ + if (f54->query.has_query15) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_15.data, + sizeof(f54->query_15.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 16 */ + if (f54->query_15.has_query16) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_16.data, + sizeof(f54->query_16.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 17 */ + if (f54->query_16.has_query17) + offset += 1; + + /* query 18 */ + if (f54->query_16.has_ctrl94_query18) + offset += 1; + + /* query 19 */ + if (f54->query_16.has_ctrl95_query19) + offset += 1; + + /* query 20 */ + if (f54->query_15.has_query20) + offset += 1; + + /* query 21 */ + if (f54->query_15.has_query21) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_21.data, + sizeof(f54->query_21.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 22 */ + if (f54->query_15.has_query22) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_22.data, + sizeof(f54->query_22.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 23 */ + if (f54->query_22.has_query23) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_23.data, + sizeof(f54->query_23.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 24 */ + if (f54->query_21.has_query24_data18) + offset += 1; + + /* query 25 */ + if (f54->query_15.has_query25) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_25.data, + sizeof(f54->query_25.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 26 */ + if (f54->query_22.has_ctrl103_query26) + offset += 1; + + /* query 27 */ + if (f54->query_25.has_query27) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_27.data, + sizeof(f54->query_27.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 28 */ + if (f54->query_22.has_query28) + offset += 1; + + /* query 29 */ + if (f54->query_27.has_query29) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_29.data, + sizeof(f54->query_29.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 30 */ + if (f54->query_29.has_query30) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_30.data, + sizeof(f54->query_30.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 31 */ + if (f54->query_30.has_ctrl122_query31) + offset += 1; + + /* query 32 */ + if (f54->query_30.has_query32) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_32.data, + sizeof(f54->query_32.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 33 */ + if (f54->query_32.has_query33) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_33.data, + sizeof(f54->query_33.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 34 */ + if (f54->query_32.has_query34) + offset += 1; + + /* query 35 */ + if (f54->query_32.has_query35) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_35.data, + sizeof(f54->query_35.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 36 */ + if (f54->query_33.has_query36) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_36.data, + sizeof(f54->query_36.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 37 */ + if (f54->query_36.has_query37) + offset += 1; + + /* query 38 */ + if (f54->query_36.has_query38) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_38.data, + sizeof(f54->query_38.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 39 */ + if (f54->query_38.has_query39) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_39.data, + sizeof(f54->query_39.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 40 */ + if (f54->query_39.has_query40) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_40.data, + sizeof(f54->query_40.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 41 */ + if (f54->query_40.has_ctrl163_query41) + offset += 1; + + /* query 42 */ + if (f54->query_40.has_ctrl165_query42) + offset += 1; + + /* query 43 */ + if (f54->query_40.has_query43) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_43.data, + sizeof(f54->query_43.data)); + if (retval < 0) + return retval; + offset += 1; + } + + if (f54->query_43.has_ctrl172_query44_query45) + offset += 2; + + /* query 46 */ + if (f54->query_43.has_query46) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_46.data, + sizeof(f54->query_46.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 47 */ + if (f54->query_46.has_query47) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_47.data, + sizeof(f54->query_47.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 48 reserved */ + + /* query 49 */ + if (f54->query_47.has_query49) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_49.data, + sizeof(f54->query_49.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 50 */ + if (f54->query_49.has_query50) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_50.data, + sizeof(f54->query_50.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 51 */ + if (f54->query_50.has_query51) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_51.data, + sizeof(f54->query_51.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 53 54 */ + if (f54->query_51.has_query53_query54_ctrl198) + offset += 2; + + /* query 55 */ + if (f54->query_51.has_query55) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_55.data, + sizeof(f54->query_55.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 56 */ + if (f54->query_55.has_query56) + offset += 1; + + /* query 57 */ + if (f54->query_55.has_query57) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_57.data, + sizeof(f54->query_57.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 58 */ + if (f54->query_57.has_query58) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_58.data, + sizeof(f54->query_58.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 59 */ + if (f54->query_58.has_query59) + offset += 1; + + /* query 60 */ + if (f54->query_58.has_query60) + offset += 1; + + /* query 61 */ + if (f54->query_58.has_query61) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_61.data, + sizeof(f54->query_61.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 62 63 */ + if (f54->query_61.has_ctrl215_query62_query63) + offset += 2; + + /* query 64 */ + if (f54->query_61.has_query64) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_64.data, + sizeof(f54->query_64.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 65 */ + if (f54->query_64.has_query65) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_65.data, + sizeof(f54->query_65.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 66 */ + if (f54->query_65.has_query66_ctrl231) + offset += 1; + + /* query 67 */ + if (f54->query_65.has_query67) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_67.data, + sizeof(f54->query_67.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 68 */ + if (f54->query_67.has_query68) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_68.data, + sizeof(f54->query_68.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 68 */ + if (f54->query_68.has_query69) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f54->query_base_addr + offset, + f54->query_69.data, + sizeof(f54->query_69.data)); + if (retval < 0) + return retval; + offset += 1; + } + + return 0; +} + +static void test_f54_set_regs(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count, + unsigned char page) +{ + unsigned char ii; + unsigned char intr_offset; + + f54->query_base_addr = fd->query_base_addr | (page << 8); + f54->control_base_addr = fd->ctrl_base_addr | (page << 8); + f54->data_base_addr = fd->data_base_addr | (page << 8); + f54->command_base_addr = fd->cmd_base_addr | (page << 8); + + f54->intr_reg_num = (intr_count + 7) / 8; + if (f54->intr_reg_num != 0) + f54->intr_reg_num -= 1; + + f54->intr_mask = 0; + intr_offset = intr_count % 8; + for (ii = intr_offset; + ii < (fd->intr_src_count + intr_offset); + ii++) { + f54->intr_mask |= 1 << ii; + } +} + +static int test_f55_set_controls(void) +{ + unsigned char offset = 0; + + /* controls 0 1 2 */ + if (f55->query.has_sensor_assignment) + offset += 3; + + /* control 3 */ + if (f55->query.has_edge_compensation) + offset++; + + /* control 4 */ + if (f55->query.curve_compensation_mode == 0x1 || + f55->query.curve_compensation_mode == 0x2) + offset++; + + /* control 5 */ + if (f55->query.curve_compensation_mode == 0x2) + offset++; + + /* control 6 */ + if (f55->query.has_ctrl6) + offset++; + + /* control 7 */ + if (f55->query.has_alternate_transmitter_assignment) + offset++; + + /* control 8 */ + if (f55->query_3.has_ctrl8) + offset++; + + /* control 9 */ + if (f55->query_3.has_ctrl9) + offset++; + + /* control 10 */ + if (f55->query_5.has_corner_compensation) + offset++; + + /* control 11 */ + if (f55->query.curve_compensation_mode == 0x3) + offset++; + + /* control 12 */ + if (f55->query_5.has_ctrl12) + offset++; + + /* control 13 */ + if (f55->query_5.has_ctrl13) + offset++; + + /* control 14 */ + if (f55->query_5.has_ctrl14) + offset++; + + /* control 15 */ + if (f55->query_5.has_basis_function) + offset++; + + /* control 16 */ + if (f55->query_17.has_ctrl16) + offset++; + + /* control 17 */ + if (f55->query_17.has_ctrl17) + offset++; + + /* controls 18 19 */ + if (f55->query_17.has_ctrl18_ctrl19) + offset += 2; + + /* control 20 */ + if (f55->query_17.has_ctrl20) + offset++; + + /* control 21 */ + if (f55->query_17.has_ctrl21) + offset++; + + /* control 22 */ + if (f55->query_17.has_ctrl22) + offset++; + + /* control 23 */ + if (f55->query_18.has_ctrl23) + offset++; + + /* control 24 */ + if (f55->query_18.has_ctrl24) + offset++; + + /* control 25 */ + if (f55->query_18.has_ctrl25) + offset++; + + /* control 26 */ + if (f55->query_18.has_ctrl26) + offset++; + + /* control 27 */ + if (f55->query_18.has_ctrl27_query20) + offset++; + + /* control 28 */ + if (f55->query_18.has_ctrl28_query21) + offset++; + + /* control 29 */ + if (f55->query_22.has_ctrl29) + offset++; + + /* control 30 */ + if (f55->query_22.has_ctrl30) + offset++; + + /* control 31 */ + if (f55->query_22.has_ctrl31) + offset++; + + /* control 32 */ + if (f55->query_22.has_ctrl32) + offset++; + + /* controls 33 34 35 36 reserved */ + + /* control 37 */ + if (f55->query_28.has_ctrl37) + offset++; + + /* control 38 */ + if (f55->query_30.has_ctrl38) + offset++; + + /* control 39 */ + if (f55->query_30.has_ctrl39) + offset++; + + /* control 40 */ + if (f55->query_30.has_ctrl40) + offset++; + + /* control 41 */ + if (f55->query_30.has_ctrl41) + offset++; + + /* control 42 */ + if (f55->query_30.has_ctrl42) + offset++; + + /* controls 43 44 */ + if (f55->query_30.has_ctrl43_ctrl44) { + f55->afe_mux_offset = offset; + offset += 2; + } + + /* controls 45 46 */ + if (f55->query_33.has_ctrl45_ctrl46) { + f55->has_force = true; + f55->force_tx_offset = offset; + f55->force_rx_offset = offset + 1; + offset += 2; + } + + return 0; +} + +static int test_f55_set_queries(void) +{ + int retval; + unsigned char offset; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr, + f55->query.data, + sizeof(f55->query.data)); + if (retval < 0) + return retval; + + offset = sizeof(f55->query.data); + + /* query 3 */ + if (f55->query.has_single_layer_multi_touch) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_3.data, + sizeof(f55->query_3.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 4 */ + if (f55->query_3.has_ctrl9) + offset += 1; + + /* query 5 */ + if (f55->query.has_query5) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_5.data, + sizeof(f55->query_5.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* queries 6 7 */ + if (f55->query.curve_compensation_mode == 0x3) + offset += 2; + + /* query 8 */ + if (f55->query_3.has_ctrl8) + offset += 1; + + /* query 9 */ + if (f55->query_3.has_query9) + offset += 1; + + /* queries 10 11 12 13 14 15 16 */ + if (f55->query_5.has_basis_function) + offset += 7; + + /* query 17 */ + if (f55->query_5.has_query17) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_17.data, + sizeof(f55->query_17.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 18 */ + if (f55->query_17.has_query18) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_18.data, + sizeof(f55->query_18.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 19 */ + if (f55->query_18.has_query19) + offset += 1; + + /* query 20 */ + if (f55->query_18.has_ctrl27_query20) + offset += 1; + + /* query 21 */ + if (f55->query_18.has_ctrl28_query21) + offset += 1; + + /* query 22 */ + if (f55->query_18.has_query22) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_22.data, + sizeof(f55->query_22.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 23 */ + if (f55->query_22.has_query23) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_23.data, + sizeof(f55->query_23.data)); + if (retval < 0) + return retval; + offset += 1; + + f55->amp_sensor = f55->query_23.amp_sensor_enabled; + f55->size_of_column2mux = f55->query_23.size_of_column2mux; + } + + /* queries 24 25 26 27 reserved */ + + /* query 28 */ + if (f55->query_22.has_query28) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_28.data, + sizeof(f55->query_28.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* query 29 */ + if (f55->query_28.has_query29) + offset += 1; + + /* query 30 */ + if (f55->query_28.has_query30) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_30.data, + sizeof(f55->query_30.data)); + if (retval < 0) + return retval; + offset += 1; + } + + /* queries 31 32 */ + if (f55->query_30.has_query31_query32) + offset += 2; + + /* query 33 */ + if (f55->query_30.has_query33) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->query_base_addr + offset, + f55->query_33.data, + sizeof(f55->query_33.data)); + if (retval < 0) + return retval; + offset += 1; + + f55->extended_amp = f55->query_33.has_extended_amp_pad; + } + + return 0; +} + +static void test_f55_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char ii; + unsigned char rx_electrodes; + unsigned char tx_electrodes; + struct f55_control_43 ctrl_43; + + retval = test_f55_set_queries(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F55 query registers\n", + __func__); + return; + } + + if (!f55->query.has_sensor_assignment) + return; + + retval = test_f55_set_controls(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set up F55 control registers\n", + __func__); + return; + } + + tx_electrodes = f55->query.num_of_tx_electrodes; + rx_electrodes = f55->query.num_of_rx_electrodes; + + f55->tx_assignment = kzalloc(tx_electrodes, GFP_KERNEL); + f55->rx_assignment = kzalloc(rx_electrodes, GFP_KERNEL); + + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->control_base_addr + SENSOR_TX_MAPPING_OFFSET, + f55->tx_assignment, + tx_electrodes); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F55 tx assignment\n", + __func__); + return; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->control_base_addr + SENSOR_RX_MAPPING_OFFSET, + f55->rx_assignment, + rx_electrodes); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F55 rx assignment\n", + __func__); + return; + } + + f54->tx_assigned = 0; + for (ii = 0; ii < tx_electrodes; ii++) { + if (f55->tx_assignment[ii] != 0xff) + f54->tx_assigned++; + } + + f54->rx_assigned = 0; + for (ii = 0; ii < rx_electrodes; ii++) { + if (f55->rx_assignment[ii] != 0xff) + f54->rx_assigned++; + } + + if (f55->amp_sensor) { + f54->tx_assigned = f55->size_of_column2mux; + f54->rx_assigned /= 2; + } + + if (f55->extended_amp) { + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->control_base_addr + f55->afe_mux_offset, + ctrl_43.data, + sizeof(ctrl_43.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F55 AFE mux sizes\n", + __func__); + return; + } + + f54->tx_assigned = ctrl_43.afe_l_mux_size + + ctrl_43.afe_r_mux_size; + } + + /* force mapping */ + if (f55->has_force) { + f55->force_tx_assignment = kzalloc(tx_electrodes, GFP_KERNEL); + f55->force_rx_assignment = kzalloc(rx_electrodes, GFP_KERNEL); + + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->control_base_addr + f55->force_tx_offset, + f55->force_tx_assignment, + tx_electrodes); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F55 force tx assignment\n", + __func__); + return; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + f55->control_base_addr + f55->force_rx_offset, + f55->force_rx_assignment, + rx_electrodes); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F55 force rx assignment\n", + __func__); + return; + } + + for (ii = 0; ii < tx_electrodes; ii++) { + if (f55->force_tx_assignment[ii] != 0xff) + f54->tx_assigned++; + } + + for (ii = 0; ii < rx_electrodes; ii++) { + if (f55->force_rx_assignment[ii] != 0xff) + f54->rx_assigned++; + } + } +} + +static void test_f55_set_regs(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn_desc *fd, + unsigned char page) +{ + f55 = kzalloc(sizeof(*f55), GFP_KERNEL); + if (!f55) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for F55\n", + __func__); + return; + } + + f55->query_base_addr = fd->query_base_addr | (page << 8); + f55->control_base_addr = fd->ctrl_base_addr | (page << 8); + f55->data_base_addr = fd->data_base_addr | (page << 8); + f55->command_base_addr = fd->cmd_base_addr | (page << 8); +} + +static void test_f21_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char ii; + unsigned char size_of_query2; + unsigned char size_of_query5; + unsigned char query_11_offset; + unsigned char ctrl_4_offset; + struct f21_query_2 *query_2 = NULL; + struct f21_query_5 *query_5 = NULL; + struct f21_query_11 *query_11 = NULL; + + query_2 = kzalloc(sizeof(*query_2), GFP_KERNEL); + if (!query_2) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for query_2\n", + __func__); + goto exit; + } + + query_5 = kzalloc(sizeof(*query_5), GFP_KERNEL); + if (!query_5) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for query_5\n", + __func__); + goto exit; + } + + query_11 = kzalloc(sizeof(*query_11), GFP_KERNEL); + if (!query_11) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for query_11\n", + __func__); + goto exit; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + f21->query_base_addr + 1, + &size_of_query2, + sizeof(size_of_query2)); + if (retval < 0) + goto exit; + + if (size_of_query2 > sizeof(query_2->data)) + size_of_query2 = sizeof(query_2->data); + + retval = synaptics_rmi4_reg_read(rmi4_data, + f21->query_base_addr + 2, + query_2->data, + size_of_query2); + if (retval < 0) + goto exit; + + if (!query_2->query11_is_present) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: No F21 force capabilities\n", + __func__); + goto exit; + } + + query_11_offset = query_2->query0_is_present + + query_2->query1_is_present + + query_2->query2_is_present + + query_2->query3_is_present + + query_2->query4_is_present + + query_2->query5_is_present + + query_2->query6_is_present + + query_2->query7_is_present + + query_2->query8_is_present + + query_2->query9_is_present + + query_2->query10_is_present; + + retval = synaptics_rmi4_reg_read(rmi4_data, + f21->query_base_addr + 11, + query_11->data, + sizeof(query_11->data)); + if (retval < 0) + goto exit; + + if (!query_11->has_force_sensing_txrx_mapping) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: No F21 force mapping\n", + __func__); + goto exit; + } + + f21->max_num_of_tx = query_11->max_number_of_force_txs; + f21->max_num_of_rx = query_11->max_number_of_force_rxs; + f21->max_num_of_txrx = f21->max_num_of_tx + f21->max_num_of_rx; + + f21->force_txrx_assignment = kzalloc(f21->max_num_of_txrx, GFP_KERNEL); + + retval = synaptics_rmi4_reg_read(rmi4_data, + f21->query_base_addr + 4, + &size_of_query5, + sizeof(size_of_query5)); + if (retval < 0) + goto exit; + + if (size_of_query5 > sizeof(query_5->data)) + size_of_query5 = sizeof(query_5->data); + + retval = synaptics_rmi4_reg_read(rmi4_data, + f21->query_base_addr + 5, + query_5->data, + size_of_query5); + if (retval < 0) + goto exit; + + ctrl_4_offset = query_5->ctrl0_is_present + + query_5->ctrl1_is_present + + query_5->ctrl2_is_present + + query_5->ctrl3_is_present; + + retval = synaptics_rmi4_reg_read(rmi4_data, + f21->control_base_addr + ctrl_4_offset, + f21->force_txrx_assignment, + f21->max_num_of_txrx); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F21 force txrx assignment\n", + __func__); + goto exit; + } + + f21->has_force = true; + + for (ii = 0; ii < f21->max_num_of_tx; ii++) { + if (f21->force_txrx_assignment[ii] != 0xff) + f21->tx_assigned++; + } + + for (ii = f21->max_num_of_tx; ii < f21->max_num_of_txrx; ii++) { + if (f21->force_txrx_assignment[ii] != 0xff) + f21->rx_assigned++; + } + +exit: + kfree(query_2); + kfree(query_5); + kfree(query_11); +} + +static void test_f21_set_regs(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn_desc *fd, + unsigned char page) +{ + f21 = kzalloc(sizeof(*f21), GFP_KERNEL); + if (!f21) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for F21\n", + __func__); + return; + } + + f21->query_base_addr = fd->query_base_addr | (page << 8); + f21->control_base_addr = fd->ctrl_base_addr | (page << 8); + f21->data_base_addr = fd->data_base_addr | (page << 8); + f21->command_base_addr = fd->cmd_base_addr | (page << 8); +} + +static int test_scan_pdt(void) +{ + int retval; + unsigned char intr_count = 0; + unsigned char page; + unsigned short addr; + bool f54found = false; + bool f55found = false; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + for (page = 0; page < PAGES_TO_SERVICE; page++) { + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + addr |= (page << 8); + + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + addr &= ~(MASK_8BIT << 8); + + if (!rmi_fd.fn_number) + break; + + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F54: + test_f54_set_regs(rmi4_data, + &rmi_fd, intr_count, page); + f54found = true; + break; + case SYNAPTICS_RMI4_F55: + test_f55_set_regs(rmi4_data, + &rmi_fd, page); + f55found = true; + break; + case SYNAPTICS_RMI4_F21: + test_f21_set_regs(rmi4_data, + &rmi_fd, page); + break; + default: + break; + } + + if (f54found && f55found) + goto pdt_done; + + intr_count += rmi_fd.intr_src_count; + } + } + + if (!f54found) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F54\n", + __func__); + return -EINVAL; + } + +pdt_done: + return 0; +} + +static void synaptics_rmi4_test_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + if (!f54) + return; + + if (f54->intr_mask & intr_mask) + queue_work(f54->test_report_workqueue, &f54->test_report_work); + + return; +} + +static int synaptics_rmi4_test_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + + if (f54) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Handle already exists\n", + __func__); + return 0; + } + + f54 = kzalloc(sizeof(*f54), GFP_KERNEL); + if (!f54) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for F54\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + f54->rmi4_data = rmi4_data; + + f55 = NULL; + + f21 = NULL; + + retval = test_scan_pdt(); + if (retval < 0) + goto exit_free_mem; + + retval = test_set_queries(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F54 query registers\n", + __func__); + goto exit_free_mem; + } + + f54->tx_assigned = f54->query.num_of_tx_electrodes; + f54->rx_assigned = f54->query.num_of_rx_electrodes; + + retval = test_set_controls(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set up F54 control registers\n", + __func__); + goto exit_free_control; + } + + test_set_data(); + + if (f55) + test_f55_init(rmi4_data); + + if (f21) + test_f21_init(rmi4_data); + + if (rmi4_data->external_afe_buttons) + f54->tx_assigned++; + + retval = test_set_sysfs(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs entries\n", + __func__); + goto exit_sysfs; + } + + f54->test_report_workqueue = + create_singlethread_workqueue("test_report_workqueue"); + INIT_WORK(&f54->test_report_work, test_report_work); + + hrtimer_init(&f54->watchdog, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + f54->watchdog.function = test_get_report_timeout; + INIT_WORK(&f54->timeout_work, test_timeout_work); + + mutex_init(&f54->status_mutex); + f54->status = STATUS_IDLE; + + return 0; + +exit_sysfs: + if (f21) + kfree(f21->force_txrx_assignment); + + if (f55) { + kfree(f55->tx_assignment); + kfree(f55->rx_assignment); + kfree(f55->force_tx_assignment); + kfree(f55->force_rx_assignment); + } + +exit_free_control: + test_free_control_mem(); + +exit_free_mem: + kfree(f21); + f21 = NULL; + kfree(f55); + f55 = NULL; + kfree(f54); + f54 = NULL; + +exit: + return retval; +} + +static void synaptics_rmi4_test_remove(struct synaptics_rmi4_data *rmi4_data) +{ + if (!f54) + goto exit; + + hrtimer_cancel(&f54->watchdog); + + cancel_work_sync(&f54->test_report_work); + flush_workqueue(f54->test_report_workqueue); + destroy_workqueue(f54->test_report_workqueue); + + test_remove_sysfs(); + + if (f21) + kfree(f21->force_txrx_assignment); + + if (f55) { + kfree(f55->tx_assignment); + kfree(f55->rx_assignment); + kfree(f55->force_tx_assignment); + kfree(f55->force_rx_assignment); + } + + test_free_control_mem(); + + if (f54->data_buffer_size) + kfree(f54->report_data); + + kfree(f21); + f21 = NULL; + + kfree(f55); + f55 = NULL; + + kfree(f54); + f54 = NULL; + +exit: + complete(&test_remove_complete); +} + +static void synaptics_rmi4_test_reset(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + + if (!f54) { + synaptics_rmi4_test_init(rmi4_data); + return; + } + + if (f21) + kfree(f21->force_txrx_assignment); + + if (f55) { + kfree(f55->tx_assignment); + kfree(f55->rx_assignment); + kfree(f55->force_tx_assignment); + kfree(f55->force_rx_assignment); + } + + test_free_control_mem(); + + kfree(f55); + f55 = NULL; + + kfree(f21); + f21 = NULL; + + retval = test_scan_pdt(); + if (retval < 0) + goto exit_free_mem; + + retval = test_set_queries(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to read F54 query registers\n", + __func__); + goto exit_free_mem; + } + + f54->tx_assigned = f54->query.num_of_tx_electrodes; + f54->rx_assigned = f54->query.num_of_rx_electrodes; + + retval = test_set_controls(); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to set up F54 control registers\n", + __func__); + goto exit_free_control; + } + + test_set_data(); + + if (f55) + test_f55_init(rmi4_data); + + if (f21) + test_f21_init(rmi4_data); + + if (rmi4_data->external_afe_buttons) + f54->tx_assigned++; + + f54->status = STATUS_IDLE; + + return; + +exit_free_control: + test_free_control_mem(); + +exit_free_mem: + hrtimer_cancel(&f54->watchdog); + + cancel_work_sync(&f54->test_report_work); + flush_workqueue(f54->test_report_workqueue); + destroy_workqueue(f54->test_report_workqueue); + + test_remove_sysfs(); + + if (f54->data_buffer_size) + kfree(f54->report_data); + + kfree(f21); + f21 = NULL; + + kfree(f55); + f55 = NULL; + + kfree(f54); + f54 = NULL; + + return; +} + +static struct synaptics_rmi4_exp_fn test_module = { + .fn_type = RMI_TEST_REPORTING, + .init = synaptics_rmi4_test_init, + .remove = synaptics_rmi4_test_remove, + .reset = synaptics_rmi4_test_reset, + .reinit = NULL, + .early_suspend = NULL, + .suspend = NULL, + .resume = NULL, + .late_resume = NULL, + .attn = synaptics_rmi4_test_attn, +}; + +static int __init rmi4_test_module_init(void) +{ + synaptics_rmi4_new_function(&test_module, true); + + return 0; +} + +static void __exit rmi4_test_module_exit(void) +{ + synaptics_rmi4_new_function(&test_module, false); + + wait_for_completion(&test_remove_complete); +} + +module_init(rmi4_test_module_init); +module_exit(rmi4_test_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX Test Reporting Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_dsx/synaptics_dsx_video.c b/synaptics_dsx/synaptics_dsx_video.c new file mode 100644 index 0000000000..5067d85ca1 --- /dev/null +++ b/synaptics_dsx/synaptics_dsx_video.c @@ -0,0 +1,403 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_core.h" + +#define SYSFS_FOLDER_NAME "video" + +static ssize_t video_sysfs_dcs_write_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t video_sysfs_param_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static int video_send_dcs_command(unsigned char command_opcode); + +struct f38_command { + union { + struct { + unsigned char command_opcode; + unsigned char register_access:1; + unsigned char gamma_page:1; + unsigned char f38_control1_b2__7:6; + unsigned char parameter_field_1; + unsigned char parameter_field_2; + unsigned char parameter_field_3; + unsigned char parameter_field_4; + unsigned char send_to_dcs:1; + unsigned char f38_command6_b1__7:7; + } __packed; + unsigned char data[7]; + }; +}; + +struct synaptics_rmi4_video_handle { + unsigned char param; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; + struct synaptics_rmi4_data *rmi4_data; + struct kobject *sysfs_dir; +}; + +#ifdef RMI_DCS_SUSPEND_RESUME +struct dcs_command { + unsigned char command; + unsigned int wait_time; +}; + +static struct dcs_command suspend_sequence[] = { + { + .command = 0x28, + .wait_time = 200, + }, + { + .command = 0x10, + .wait_time = 200, + }, +}; + +static struct dcs_command resume_sequence[] = { + { + .command = 0x11, + .wait_time = 200, + }, + { + .command = 0x29, + .wait_time = 200, + }, +}; +#endif + +static struct device_attribute attrs[] = { + __ATTR(dcs_write, 0220, + synaptics_rmi4_show_error, + video_sysfs_dcs_write_store), + __ATTR(param, 0220, + synaptics_rmi4_show_error, + video_sysfs_param_store), +}; + +static struct synaptics_rmi4_video_handle *video; + +DECLARE_COMPLETION(video_remove_complete); + +static ssize_t video_sysfs_dcs_write_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + + if (kstrtouint(buf, 16, &input) != 1) + return -EINVAL; + + retval = video_send_dcs_command((unsigned char)input); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t video_sysfs_param_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 16, &input) != 1) + return -EINVAL; + + video->param = (unsigned char)input; + + return count; +} + +static int video_send_dcs_command(unsigned char command_opcode) +{ + int retval; + struct f38_command command; + struct synaptics_rmi4_data *rmi4_data = video->rmi4_data; + + memset(&command, 0x00, sizeof(command)); + + command.command_opcode = command_opcode; + command.parameter_field_1 = video->param; + command.send_to_dcs = 1; + + video->param = 0; + + retval = synaptics_rmi4_reg_write(rmi4_data, + video->command_base_addr, + command.data, + sizeof(command.data)); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to send DCS command\n", + __func__); + return retval; + } + + return 0; +} + +static int video_scan_pdt(void) +{ + int retval; + unsigned char page; + unsigned short addr; + bool f38_found = false; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_data *rmi4_data = video->rmi4_data; + + for (page = 0; page < PAGES_TO_SERVICE; page++) { + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + addr |= (page << 8); + + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + addr &= ~(MASK_8BIT << 8); + + if (!rmi_fd.fn_number) + break; + + if (rmi_fd.fn_number == SYNAPTICS_RMI4_F38) { + f38_found = true; + goto f38_found; + } + } + } + + if (!f38_found) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to find F38\n", + __func__); + return -EINVAL; + } + +f38_found: + video->query_base_addr = rmi_fd.query_base_addr | (page << 8); + video->control_base_addr = rmi_fd.ctrl_base_addr | (page << 8); + video->data_base_addr = rmi_fd.data_base_addr | (page << 8); + video->command_base_addr = rmi_fd.cmd_base_addr | (page << 8); + + return 0; +} + +static int synaptics_rmi4_video_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char attr_count; + + if (video) { + dev_dbg(rmi4_data->pdev->dev.parent, + "%s: Handle already exists\n", + __func__); + return 0; + } + + video = kzalloc(sizeof(*video), GFP_KERNEL); + if (!video) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to alloc mem for video\n", + __func__); + retval = -ENOMEM; + goto exit; + } + + video->rmi4_data = rmi4_data; + + retval = video_scan_pdt(); + if (retval < 0) { + retval = 0; + goto exit_scan_pdt; + } + + video->sysfs_dir = kobject_create_and_add(SYSFS_FOLDER_NAME, + &rmi4_data->input_dev->dev.kobj); + if (!video->sysfs_dir) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs directory\n", + __func__); + retval = -ENODEV; + goto exit_sysfs_dir; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(video->sysfs_dir, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(rmi4_data->pdev->dev.parent, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto exit_sysfs_attrs; + } + } + + return 0; + +exit_sysfs_attrs: + for (attr_count--; attr_count >= 0; attr_count--) + sysfs_remove_file(video->sysfs_dir, &attrs[attr_count].attr); + + kobject_put(video->sysfs_dir); + +exit_sysfs_dir: +exit_scan_pdt: + kfree(video); + video = NULL; + +exit: + return retval; +} + +static void synaptics_rmi4_video_remove(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char attr_count; + + if (!video) + goto exit; + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) + sysfs_remove_file(video->sysfs_dir, &attrs[attr_count].attr); + + kobject_put(video->sysfs_dir); + + kfree(video); + video = NULL; + +exit: + complete(&video_remove_complete); +} + +static void synaptics_rmi4_video_reset(struct synaptics_rmi4_data *rmi4_data) +{ + if (!video) + synaptics_rmi4_video_init(rmi4_data); +} + +#ifdef RMI_DCS_SUSPEND_RESUME +static void synaptics_rmi4_video_suspend(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char ii; + unsigned char command; + unsigned char num_of_cmds; + + if (!video) + return; + + num_of_cmds = ARRAY_SIZE(suspend_sequence); + + for (ii = 0; ii < num_of_cmds; ii++) { + command = suspend_sequence[ii].command; + retval = video_send_dcs_command(command); + if (retval < 0) + return; + msleep(suspend_sequence[ii].wait_time); + } +} + +static void synaptics_rmi4_video_resume(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char ii; + unsigned char command; + unsigned char num_of_cmds; + + if (!video) + return; + + num_of_cmds = ARRAY_SIZE(resume_sequence); + + for (ii = 0; ii < num_of_cmds; ii++) { + command = resume_sequence[ii].command; + retval = video_send_dcs_command(command); + if (retval < 0) + return; + msleep(resume_sequence[ii].wait_time); + } +} +#endif + +static struct synaptics_rmi4_exp_fn video_module = { + .fn_type = RMI_VIDEO, + .init = synaptics_rmi4_video_init, + .remove = synaptics_rmi4_video_remove, + .reset = synaptics_rmi4_video_reset, + .reinit = NULL, + .early_suspend = NULL, +#ifdef RMI_DCS_SUSPEND_RESUME + .suspend = synaptics_rmi4_video_suspend, + .resume = synaptics_rmi4_video_resume, +#else + .suspend = NULL, + .resume = NULL, +#endif + .late_resume = NULL, + .attn = NULL, +}; + +static int __init rmi4_video_module_init(void) +{ + synaptics_rmi4_new_function(&video_module, true); + + return 0; +} + +static void __exit rmi4_video_module_exit(void) +{ + synaptics_rmi4_new_function(&video_module, false); + + wait_for_completion(&video_remove_complete); +} + +module_init(rmi4_video_module_init); +module_exit(rmi4_video_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX Video Module"); +MODULE_LICENSE("GPL v2"); From 9eca8d1fc6989b1a1800559968d1156ba6f18dfd Mon Sep 17 00:00:00 2001 From: Fei Mao Date: Wed, 20 Oct 2021 18:16:36 +0800 Subject: [PATCH 003/170] touch: enable touch drivers Enable new drivers for internal projects Change-Id: I902bec374358a836ff01de6f7a88c4122bc9c366 Signed-off-by: Fei Mao --- Android.mk | 60 +++++++++++++++++++++++ Kbuild | 84 +++++++++++++++++++++++++++++++ Makefile | 15 ++++++ Makefile.am | 20 ++++++++ NOTICE | 95 ++++++++++++++++++++++++++++++++++++ config/gki_waipiotouch.conf | 6 +++ config/gki_waipiotouchconf.h | 10 ++++ touch_driver_board.mk | 8 +++ touch_driver_product.mk | 4 ++ 9 files changed, 302 insertions(+) create mode 100644 Android.mk create mode 100644 Kbuild create mode 100644 Makefile create mode 100644 Makefile.am create mode 100644 NOTICE create mode 100644 config/gki_waipiotouch.conf create mode 100644 config/gki_waipiotouchconf.h create mode 100644 touch_driver_board.mk create mode 100644 touch_driver_product.mk diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000000..31cdca928b --- /dev/null +++ b/Android.mk @@ -0,0 +1,60 @@ +# Android makefile for display kernel modules +TOUCH_SELECT := CONFIG_MSM_TOUCH=m + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +# This makefile is only for DLKM +ifneq ($(findstring vendor,$(LOCAL_PATH)),) + +ifneq ($(findstring opensource,$(LOCAL_PATH)),) + TOUCH_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/touch-drivers +endif # opensource + +DLKM_DIR := $(TOP)/device/qcom/common/dlkm + +LOCAL_ADDITIONAL_DEPENDENCIES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + +# Build +########################################################### +# This is set once per LOCAL_PATH, not per (kernel) module +KBUILD_OPTIONS := TOUCH_ROOT=$(TOUCH_BLD_DIR) + +KBUILD_OPTIONS += MODNAME=touch_dlkm +KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) +KBUILD_OPTIONS += $(TOUCH_SELECT) + +########################################################### +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) +LOCAL_MODULE := focaltech_fts.ko +LOCAL_MODULE_KBUILD_NAME := focaltech_fts.ko +LOCAL_MODULE_TAGS := optional +#LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/Build_external_kernelmodule.mk +########################################################### + +########################################################### +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) +LOCAL_MODULE := synaptics_dsx.ko +LOCAL_MODULE_KBUILD_NAME := synaptics_dsx.ko +LOCAL_MODULE_TAGS := optional +#LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/Build_external_kernelmodule.mk +########################################################### + +########################################################### +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) +LOCAL_MODULE := nt36xxx-i2c.ko +LOCAL_MODULE_KBUILD_NAME := nt36xxx-i2c.ko +LOCAL_MODULE_TAGS := optional +#LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/Build_external_kernelmodule.mk +########################################################### + +endif # DLKM check diff --git a/Kbuild b/Kbuild new file mode 100644 index 0000000000..adbbc2a690 --- /dev/null +++ b/Kbuild @@ -0,0 +1,84 @@ + +KDIR := $(TOP)/kernel_platform/common + +ifeq ($(CONFIG_ARCH_WAIPIO), y) + include $(TOUCH_ROOT)/config/gki_waipiotouch.conf + LINUX_INC += -include $(TOUCH_ROOT)/config/gki_waipiotouchconf.h +endif + +LINUX_INC += -Iinclude/linux \ + -Iinclude/linux/drm \ + -Iinclude/linux/gunyah \ + -Iinclude/linux/input + +CDEFINES += -DANI_LITTLE_BYTE_ENDIAN \ + -DANI_LITTLE_BIT_ENDIAN \ + -DDOT11F_LITTLE_ENDIAN_HOST \ + -DANI_COMPILER_TYPE_GCC \ + -DANI_OS_TYPE_ANDROID=6 \ + -DPTT_SOCK_SVC_ENABLE \ + -Wall\ + -Werror\ + -D__linux__ + +KBUILD_CPPFLAGS += $(CDEFINES) + +ccflags-y += $(LINUX_INC) + +ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y) +EXTRA_CFLAGS += -Wmaybe-uninitialized +endif + +ifeq ($(call cc-option-yn, -Wheader-guard),y) +EXTRA_CFLAGS += -Wheader-guard +endif + +######### CONFIG_MSM_TOUCH ######## + +ifeq ($(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX), y) + + LINUX_INC += -include $(TOUCH_ROOT)/synaptics_dsx/synaptics_dsx.h + LINUX_INC += -include $(TOUCH_ROOT)/synaptics_dsx/synaptics_dsx_core.h + + synaptics_dsx-y := \ + ./synaptics_dsx/synaptics_dsx_core.o \ + ./synaptics_dsx/synaptics_dsx_i2c.o + + obj-$(CONFIG_MSM_TOUCH) += synaptics_dsx.o +endif + +ifeq ($(CONFIG_TOUCH_FOCALTECH), y) + LINUX_INC += -include $(TOUCH_ROOT)/focaltech_touch/focaltech_common.h + LINUX_INC += -include $(TOUCH_ROOT)/focaltech_touch/focaltech_config.h + LINUX_INC += -include $(TOUCH_ROOT)/focaltech_touch/focaltech_core.h + LINUX_INC += -include $(TOUCH_ROOT)/focaltech_touch/focaltech_flash.h + + focaltech_fts-y := \ + ./focaltech_touch/focaltech_core.o \ + ./focaltech_touch/focaltech_ex_fun.o \ + ./focaltech_touch/focaltech_ex_mode.o \ + ./focaltech_touch/focaltech_gesture.o \ + ./focaltech_touch/focaltech_esdcheck.o \ + ./focaltech_touch/focaltech_point_report_check.o \ + ./focaltech_touch/focaltech_i2c.o \ + ./focaltech_touch/focaltech_flash.o \ + ./focaltech_touch/focaltech_flash/focaltech_upgrade_ft3518.o + + obj-$(CONFIG_MSM_TOUCH) += focaltech_fts.o +endif + +ifeq ($(CONFIG_TOUCHSCREEN_NT36XXX_I2C), y) + LINUX_INC += -include $(TOUCH_ROOT)/nt36xxx/nt36xxx.h + LINUX_INC += -include $(TOUCH_ROOT)/nt36xxx/nt36xxx_mem_map.h + LINUX_INC += -include $(TOUCH_ROOT)/nt36xxx/nt36xxx_mp_ctrlram.h + + nt36xxx-i2c-y := \ + ./nt36xxx/nt36xxx.o \ + ./nt36xxx/nt36xxx_fw_update.o \ + ./nt36xxx/nt36xxx_ext_proc.o \ + ./nt36xxx/nt36xxx_mp_ctrlram.o + + obj-$(CONFIG_MSM_TOUCH) += nt36xxx-i2c.o +endif + +CDEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..05c3bcc66c --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ + +KBUILD_OPTIONS+= TOUCH_ROOT=$(KERNEL_SRC)/$(M) + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install + +%: + $(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS) + +clean: + rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers + rm -rf .tmp_versions diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000000..106a9a89ae --- /dev/null +++ b/Makefile.am @@ -0,0 +1,20 @@ + +TOUCH_ROOT=$(ROOTDIR)vendor/qcom/opensource/touch-drivers +KBUILD_OPTIONS := TOUCH_ROOT=$(TOUCH_ROOT) CONFIG_MSM_TOUCH=m + +ifeq ($(TARGET_SUPPORT),genericarmv8) + KBUILD_OPTIONS += CONFIG_ARCH_WAIPIO=y +endif + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install + +%: + $(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS) + +clean: + rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers + rm -rf .tmp_versions diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000000..35e497ec37 --- /dev/null +++ b/NOTICE @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. +*/ + +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2019, Focaltech Ltd. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. + * + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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. + */ + +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 47247 $ + * $Date: 2019-07-10 10:41:36 +0800 (Wed, 10 Jul 2019) $ + * + * 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. + * + */ + +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016-2019, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ diff --git a/config/gki_waipiotouch.conf b/config/gki_waipiotouch.conf new file mode 100644 index 0000000000..8fa39f562b --- /dev/null +++ b/config/gki_waipiotouch.conf @@ -0,0 +1,6 @@ +export CONFIG_TOUCH_FOCALTECH=y +export CONFIG_TOUCHSCREEN_FTS_DIRECTORY="focaltech_touch" +export CONFIG_FTS_TRUSTED_TOUCH=y +export CONFIG_TOUCHSCREEN_SYNAPTICS_DSX=y +export CONFIG_TOUCHSCREEN_NT36XXX_I2C=y +export CONFIG_TOUCH_BUILD=m diff --git a/config/gki_waipiotouchconf.h b/config/gki_waipiotouchconf.h new file mode 100644 index 0000000000..ef2175bb56 --- /dev/null +++ b/config/gki_waipiotouchconf.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#define CONFIG_TOUCH_FOCALTECH 1 +#define CONFIG_TOUCHSCREEN_FTS_DIRECTORY "focaltech_touch" +#define CONFIG_FTS_TRUSTED_TOUCH 1 +#define CONFIG_TOUCHSCREEN_SYNAPTICS_DSX 1 +#define CONFIG_TOUCHSCREEN_NT36XXX_I2C 1 diff --git a/touch_driver_board.mk b/touch_driver_board.mk new file mode 100644 index 0000000000..3d41172a10 --- /dev/null +++ b/touch_driver_board.mk @@ -0,0 +1,8 @@ + +ifneq ($(TARGET_BOARD_AUTO),true) + ifeq ($(call is-board-platform-in-list,$(TARGET_BOARD_PLATFORM)),true) + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko \ + $(KERNEL_MODULES_OUT)/synaptics_dsx.ko \ + $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko + endif +endif diff --git a/touch_driver_product.mk b/touch_driver_product.mk new file mode 100644 index 0000000000..d978089f32 --- /dev/null +++ b/touch_driver_product.mk @@ -0,0 +1,4 @@ + +PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko \ + $(KERNEL_MODULES_OUT)/synaptics_dsx.ko \ + $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ No newline at end of file From 16cd18826398bd68814ccdfd55bcd7a43115465d Mon Sep 17 00:00:00 2001 From: Fei Mao Date: Sun, 14 Nov 2021 16:42:49 +0800 Subject: [PATCH 004/170] touch: support kalama platform Add configuration files for Kalama project and suport only NT36xxx touch driver. Change-Id: Ieb25dd9e8c68a7a97fbffc7537223c1bd88f1596 --- Android.mk | 22 ---------------------- Kbuild | 5 +++++ config/gki_kalamatouch.conf | 2 ++ config/gki_kalamatouchconf.h | 7 +++++++ touch_driver_board.mk | 4 +--- touch_driver_product.mk | 4 +--- 6 files changed, 16 insertions(+), 28 deletions(-) create mode 100644 config/gki_kalamatouch.conf create mode 100644 config/gki_kalamatouchconf.h diff --git a/Android.mk b/Android.mk index 31cdca928b..f433727261 100644 --- a/Android.mk +++ b/Android.mk @@ -24,28 +24,6 @@ KBUILD_OPTIONS += MODNAME=touch_dlkm KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) KBUILD_OPTIONS += $(TOUCH_SELECT) -########################################################### -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) -LOCAL_MODULE := focaltech_fts.ko -LOCAL_MODULE_KBUILD_NAME := focaltech_fts.ko -LOCAL_MODULE_TAGS := optional -#LOCAL_MODULE_DEBUG_ENABLE := true -LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) -include $(DLKM_DIR)/Build_external_kernelmodule.mk -########################################################### - -########################################################### -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) -LOCAL_MODULE := synaptics_dsx.ko -LOCAL_MODULE_KBUILD_NAME := synaptics_dsx.ko -LOCAL_MODULE_TAGS := optional -#LOCAL_MODULE_DEBUG_ENABLE := true -LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) -include $(DLKM_DIR)/Build_external_kernelmodule.mk -########################################################### - ########################################################### include $(CLEAR_VARS) LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) diff --git a/Kbuild b/Kbuild index adbbc2a690..ee962bdcb4 100644 --- a/Kbuild +++ b/Kbuild @@ -6,6 +6,11 @@ ifeq ($(CONFIG_ARCH_WAIPIO), y) LINUX_INC += -include $(TOUCH_ROOT)/config/gki_waipiotouchconf.h endif +#ifeq ($(CONFIG_ARCH_KALAMA), y) + include $(TOUCH_ROOT)/config/gki_kalamatouch.conf + LINUX_INC += -include $(TOUCH_ROOT)/config/gki_kalamatouchconf.h +#endif + LINUX_INC += -Iinclude/linux \ -Iinclude/linux/drm \ -Iinclude/linux/gunyah \ diff --git a/config/gki_kalamatouch.conf b/config/gki_kalamatouch.conf new file mode 100644 index 0000000000..24e0f11348 --- /dev/null +++ b/config/gki_kalamatouch.conf @@ -0,0 +1,2 @@ +export CONFIG_TOUCHSCREEN_NT36XXX_I2C=y +export CONFIG_MSM_TOUCH=m diff --git a/config/gki_kalamatouchconf.h b/config/gki_kalamatouchconf.h new file mode 100644 index 0000000000..fdb5f87a31 --- /dev/null +++ b/config/gki_kalamatouchconf.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define CONFIG_TOUCHSCREEN_NT36XXX_I2C 1 + diff --git a/touch_driver_board.mk b/touch_driver_board.mk index 3d41172a10..92e64a0ea9 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -1,8 +1,6 @@ ifneq ($(TARGET_BOARD_AUTO),true) ifeq ($(call is-board-platform-in-list,$(TARGET_BOARD_PLATFORM)),true) - BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko \ - $(KERNEL_MODULES_OUT)/synaptics_dsx.ko \ - $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko endif endif diff --git a/touch_driver_product.mk b/touch_driver_product.mk index d978089f32..170700f977 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -1,4 +1,2 @@ -PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko \ - $(KERNEL_MODULES_OUT)/synaptics_dsx.ko \ - $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ No newline at end of file +PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ No newline at end of file From 37268c41709237f719ae39039ffd7cfec3742e2d Mon Sep 17 00:00:00 2001 From: xulinkun Date: Mon, 24 Jan 2022 15:22:30 +0800 Subject: [PATCH 005/170] touch: goodix: v1.2.3 V1.2.3 from vendor. Change-Id: Idf75e5ebf2947a53a5d243ddb0a9bc71362bbdd6 Signed-off-by: xulinkun Git-commit: 58cc7874cb5e13490a6c8720b308fa56847a1f51 Git-repo: https://github.com/goodix/goodix_ts_berlin Signed-off-by: Fei Mao --- Kconfig | 20 + Makefile | 27 +- docs/Porting_Guide.md | 192 +++ goodix_brl_fwupdate.c | 1398 ++++++++++++++++++++ goodix_brl_hw.c | 1434 ++++++++++++++++++++ goodix_brl_i2c.c | 264 ++++ goodix_brl_spi.c | 299 +++++ goodix_cfg_bin.c | 329 +++++ goodix_ts_core.c | 2263 ++++++++++++++++++++++++++++++++ goodix_ts_core.h | 630 +++++++++ goodix_ts_gesture.c | 384 ++++++ goodix_ts_inspect.c | 2919 +++++++++++++++++++++++++++++++++++++++++ goodix_ts_tools.c | 503 +++++++ goodix_ts_utils.c | 179 +++ 14 files changed, 10826 insertions(+), 15 deletions(-) create mode 100644 Kconfig create mode 100644 docs/Porting_Guide.md create mode 100644 goodix_brl_fwupdate.c create mode 100644 goodix_brl_hw.c create mode 100644 goodix_brl_i2c.c create mode 100644 goodix_brl_spi.c create mode 100644 goodix_cfg_bin.c create mode 100644 goodix_ts_core.c create mode 100644 goodix_ts_core.h create mode 100644 goodix_ts_gesture.c create mode 100644 goodix_ts_inspect.c create mode 100644 goodix_ts_tools.c create mode 100644 goodix_ts_utils.c diff --git a/Kconfig b/Kconfig new file mode 100644 index 0000000000..375b5473a1 --- /dev/null +++ b/Kconfig @@ -0,0 +1,20 @@ +# +# Goodix touchscreen driver configuration +# +menuconfig TOUCHSCREEN_GOODIX_BRL + tristate "Goodix berlin touchscreen" + help + Say Y here if you have a Goodix berlin series touch controller + to your system. + + If build module, say M. + If unsure, say N. + +if TOUCHSCREEN_GOODIX_BRL + +config TOUCHSCREEN_GOODIX_BRL_SPI + bool "support SPI bus connection" + help + Say Y here if the touchscreen is connected via SPI bus. + +endif diff --git a/Makefile b/Makefile index 05c3bcc66c..903a1cccb5 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,12 @@ - -KBUILD_OPTIONS+= TOUCH_ROOT=$(KERNEL_SRC)/$(M) - -all: - $(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS) - -modules_install: - $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install - -%: - $(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS) - -clean: - rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers - rm -rf .tmp_versions +obj-$(CONFIG_TOUCHSCREEN_GOODIX_BRL) += goodix_core.o +goodix_core-y := \ + goodix_brl_i2c.o \ + goodix_brl_spi.o \ + goodix_ts_core.o \ + goodix_brl_hw.o \ + goodix_cfg_bin.o \ + goodix_ts_utils.o \ + goodix_brl_fwupdate.o \ + goodix_ts_gesture.o \ + goodix_ts_inspect.o \ + goodix_ts_tools.o diff --git a/docs/Porting_Guide.md b/docs/Porting_Guide.md new file mode 100644 index 0000000000..83a45ecc19 --- /dev/null +++ b/docs/Porting_Guide.md @@ -0,0 +1,192 @@ +# **Berlin Driver Porting Guide** + +## **Introduce** +Berlin series driver is currently support BerlinA(GT9897), BerlinB(GT9966, GT7986). And support I2C or SPI connection. + +## **Driver source file prepare** +1. Move driver source code to $KERNEL_SRC/drivers/input/touchscree/ +2. Change $KERNEL_SRC/drivers/input/touchscree/Makefile + Add this line to the Makefile + ``` + obj-y += goodix_berlin_driver/ + ``` +3. Change $KERNEL_SRC/drivers/input/touchscree/Kconfg + Add this line to the Kconfig + ``` + source "drivers/input/touchscreen/goodix_berlin_driver/Kconfig" + ``` + +## **Add device declaration in the board devicetree** +Please add Goodix touch device declaration info in the board devicetree, you can refer the Appendix goodix-ts-i2c-dtsi or goodix-ts-spi-dtsi to see how to set deivce properties. + +## **Build driver** +When build kernel you will see the following promt to let you confirm how to build the driver. This driver support built-in kernel or build as modules. + +**Setting up in the menu:** +1. In the $KERNEL_SRC directory, exec `make menuconfig`, then select +`Device Drivers ---> Input device support ---> Touchscreens --->` +2. Find `Goodix berlin touchscreen` menu, you can select `<*>`(build in kernel) or ``(build a module). +3. Enter `Goodix berlin touchscreen`, you can see `support SPI bus connection` item. If +you are on SPI connection, select `<*>`, or on I2C connection. + +**Setting up in the defconfig file:** +1. Add the following in you defconfig file. + ``` + CONFIG_TOUCHSCREEN_GOODIX_BRL=y + ``` + or + ``` + CONFIG_TOUCHSCREEN_GOODIX_BRL=m + ``` +2. If you are on SPI connection, add the following. + ``` + CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI=y + ``` + +## **Appendix** + +**goodix-ts-i2c-dtsi** + +```dts +devicetree binding for Goodix i2c touchdriver +Required properties: +- compatible: device & driver matching. + * for berlin series touch device, souch as "goodix,gt9897", "goodix,gt9966" + +- reg: i2c client address, value can be 0x14 or 0x5d. please refer to datasheet. +- goodix,reset-gpio: reset gpio. +- goodix,irq-gpio: interrupt gpio. +- goodix,irq-flags: irq trigger type config, value should be: + 1 - rising edge, + 2 - falling edge, + 4 - high level, + 5 - low level. +- goodix,panel-max-x: max resolution of x direction. +- goodix,panel-max-y: max resolution of y direction. +- goodix,panel-max-w: panel max width value. +- goodix,panel-max-p: pen device max pressure value. + +Optional properties: +- goodix,avdd-name: set name of regulator. +- avdd-supply: power supply for the touch device. + example of regulator: + goodix,avdd-name = "avdd"; + avdd-supply = <&pm8916_l15>; +- iovdd-supply: power supply for digital io circuit + example of regulator: + goodix,iovdd-name = "iovdd"; + iovdd-supply = <&pm8916_l16>; +- goodix,pen-enable: set this property if you want support stylus. + goodix,pen-enable; +- goodix,firmware-name: set firmware file name, if not configured, use the default name. +- goodix,config-name: set config file name, if not configured, use the default name. +Example 1: +goodix-berlin@5d { + compatible = "goodix,gt9897"; + reg = <0x5d>; + goodix,reset-gpio = <&msm_gpio 12 0x0>; + goodix,irq-gpio = <&msm_gpio 13 0x2800>; + goodix,irq-flags = <2>; /* 1:trigger rising, 2:trigger falling;*/ + goodix,panel-max-x = <720>; + goodix,panel-max-y = <1280>; + goodix,panel-max-w = <255>; +}; + +Example 2: +goodix-berlin@5d { + compatible = "goodix,gt9966"; + goodix,avdd-name = "avdd"; + avdd-supply = <&pm8916_l15>; + goodix,iovdd-name = "iovdd"; + iovdd-supply = <&pm8916_l16>; + + reg = <0x5d>; + goodix,reset-gpio = <&msm_gpio 12 0x0>; + goodix,irq-gpio = <&msm_gpio 13 0x2800>; + goodix,irq-flags = <2>; /* 1:trigger rising, 2:trigger falling;*/ + goodix,panel-max-x = <720>; + goodix,panel-max-y = <1280>; + goodix,panel-max-w = <255>; + goodix,panel-max-p = <4096>; /* max pressure that pen device supported */ + goodix,pen-enable; /* support active stylus device*/ + + goodix,firmware-name = "goodix_firmware.bin"; + goodix,config-name = "goodix_cfg_group.bin"; +}; +``` + +**goodix-ts-spi-dtsi** + +```dts +devicetree binding for Goodix spi touchdriver + +Required properties: +- compatible: device & driver matching. + * for berlin series touch device, souch as "goodix,gt9897T" + +- spi-max-frequency: set spi transfer speed. +- reg: depend on CS gpio. +- goodix,reset-gpio: reset gpio. +- goodix,irq-gpio: interrupt gpio. +- goodix,irq-flags: irq trigger type config, value should be: + 1 - rising edge, + 2 - falling edge, + 4 - high level, + 5 - low level. +- goodix,panel-max-x: max resolution of x direction. +- goodix,panel-max-y: max resolution of y direction. +- goodix,panel-max-w: panel max width value. +- goodix,panel-max-p: pen device max pressure value. + +Optional properties: +- goodix,avdd-name: set name of regulator. +- avdd-supply: power supply for the touch device. + example of regulator: + goodix,avdd-name = "avdd"; + avdd-supply = <&pm8916_l15>; +- iovdd-supply: power supply for digital io circuit + example of regulator: + goodix,iovdd-name = "iovdd"; + iovdd-supply = <&pm8916_l16>; +- goodix,pen-enable: set this property if you want support stylus. + goodix,pen-enable; +- goodix,firmware-name: set firmware file name, if not configured, use the default name. +- goodix,config-name: set config file name, if not configured, use the default name. +Example 1: +goodix-berlin@0 { + compatible = "goodix,gt9897"; + reg = <0>; + spi-max-frequency = <1000000>; + goodix,reset-gpio = <&msm_gpio 12 0x0>; + goodix,irq-gpio = <&msm_gpio 13 0x2800>; + goodix,irq-flags = <2>; /* 1:trigger rising, 2:trigger falling;*/ + goodix,panel-max-x = <720>; + goodix,panel-max-y = <1280>; + goodix,panel-max-w = <255>; +}; + +Example 2: +goodix-berlin@0 { + compatible = "goodix,gt9966S"; + reg = <0>; + spi-max-frequency = <1000000>; + + goodix,avdd-name = "avdd"; + avdd-supply = <&pm8916_l15>; + goodix,iovdd-name = "iovdd"; + iovdd-supply = <&pm8916_l16>; + + goodix,reset-gpio = <&msm_gpio 12 0x0>; + goodix,irq-gpio = <&msm_gpio 13 0x2800>; + goodix,irq-flags = <2>; /* 1:trigger rising, 2:trigger falling;*/ + goodix,panel-max-x = <720>; + goodix,panel-max-y = <1280>; + goodix,panel-max-w = <255>; + goodix,panel-max-p = <4096>; /* max pressure that pen device supported */ + goodix,pen-enable; /* support active stylus device*/ + + goodix,firmware-name = "goodix_firmware.bin"; + goodix,config-name = "goodix_cfg_group.bin"; +}; +``` + diff --git a/goodix_brl_fwupdate.c b/goodix_brl_fwupdate.c new file mode 100644 index 0000000000..726ea76d00 --- /dev/null +++ b/goodix_brl_fwupdate.c @@ -0,0 +1,1398 @@ + /* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ +#include "goodix_ts_core.h" + +#define BUS_TYPE_SPI 1 +#define BUS_TYPE_I2C 0 + +#define GOODIX_BUS_RETRY_TIMES 3 + +#define FW_HEADER_SIZE_BRA 256 +#define FW_HEADER_SIZE 512 +#define FW_SUBSYS_INFO_SIZE 10 +#define FW_SUBSYS_INFO_OFFSET_BRA 36 +#define FW_SUBSYS_INFO_OFFSET 42 +#define FW_SUBSYS_MAX_NUM 47 + +#define ISP_MAX_BUFFERSIZE 4096 + +#define FW_PID_LEN 8 +#define FW_VID_LEN 4 +#define FLASH_CMD_LEN 11 + +#define FW_FILE_CHECKSUM_OFFSET 8 +#define CONFIG_DATA_TYPE 4 + +#define ISP_RAM_ADDR_BRA 0x18400 +#define ISP_RAM_ADDR_BRB 0x57000 +#define ISP_RAM_ADDR_BRD 0x23800 +#define HW_REG_CPU_RUN_FROM 0x10000 +#define FLASH_CMD_REG_BRA 0x10400 +#define FLASH_CMD_REG_BRB 0x13400 +#define FLASH_CMD_REG_BRD 0x12400 +#define HW_REG_ISP_BUFFER_BRA 0x10410 +#define HW_REG_ISP_BUFFER_BRB 0x13410 +#define HW_REG_ISP_BUFFER_BRD 0x12410 +#define CONFIG_DATA_ADDR_BRA 0x3E000 +#define CONFIG_DATA_ADDR_BRB 0x40000 +#define CONFIG_DATA_ADDR_BRD 0x3E000 +#define GOODIX_CFG_ID_ADDR_BRA 0x1006E +#define GOODIX_CFG_ID_ADDR_BRB 0x10076 +#define GOODIX_CFG_ID_ADDR_BRD 0x10076 + +#define HOLD_CPU_REG_W 0x0002 +#define HOLD_CPU_REG_R 0x2000 +#define MISCTL_REG_BRA 0xD807 +#define MISCTL_REG_BRB 0xD80B +#define MISCTL_REG_BRD 0xD804 +#define ENABLE_MISCTL_BRA 0x08 +#define ENABLE_MISCTL_BRB 0x40 +#define ENABLE_MISCTL_BRD 0x20700000 +#define ESD_KEY_REG 0xCC58 +#define WATCH_DOG_REG_BRA 0xCC54 +#define WATCH_DOG_REG_BRB 0xD054 +#define WATCH_DOG_REG_BRD 0xD040 + +#define FLASH_CMD_TYPE_READ 0xAA +#define FLASH_CMD_TYPE_WRITE 0xBB +#define FLASH_CMD_ACK_CHK_PASS 0xEE +#define FLASH_CMD_ACK_CHK_ERROR 0x33 +#define FLASH_CMD_ACK_IDLE 0x11 +#define FLASH_CMD_W_STATUS_CHK_PASS 0x22 +#define FLASH_CMD_W_STATUS_CHK_FAIL 0x33 +#define FLASH_CMD_W_STATUS_ADDR_ERR 0x44 +#define FLASH_CMD_W_STATUS_WRITE_ERR 0x55 +#define FLASH_CMD_W_STATUS_WRITE_OK 0xEE + +#define CHIP_TYPE_BRA 0x96 +#define CHIP_TYPE_BRB 0x97 +#define CHIP_TYPE_BRD 0x98 + + +struct update_info_t { + int header_size; + int subsys_info_offset; + u32 isp_ram_reg; + u32 flash_cmd_reg; + u32 isp_buffer_reg; + u32 config_data_reg; + u32 misctl_reg; + u32 watch_dog_reg; + u32 config_id_reg; + u32 enable_misctl_val; +}; + +/* berlinA update into */ +struct update_info_t update_bra = { + FW_HEADER_SIZE_BRA, + FW_SUBSYS_INFO_OFFSET_BRA, + ISP_RAM_ADDR_BRA, + FLASH_CMD_REG_BRA, + HW_REG_ISP_BUFFER_BRA, + CONFIG_DATA_ADDR_BRA, + MISCTL_REG_BRA, + WATCH_DOG_REG_BRA, + GOODIX_CFG_ID_ADDR_BRA, + ENABLE_MISCTL_BRA, +}; + +/* berlinB update info */ +struct update_info_t update_brb = { + FW_HEADER_SIZE, + FW_SUBSYS_INFO_OFFSET, + ISP_RAM_ADDR_BRB, + FLASH_CMD_REG_BRB, + HW_REG_ISP_BUFFER_BRB, + CONFIG_DATA_ADDR_BRB, + MISCTL_REG_BRB, + WATCH_DOG_REG_BRB, + GOODIX_CFG_ID_ADDR_BRB, + ENABLE_MISCTL_BRB, +}; + +/* berlinD update info */ +struct update_info_t update_brd = { + FW_HEADER_SIZE, + FW_SUBSYS_INFO_OFFSET, + ISP_RAM_ADDR_BRD, + FLASH_CMD_REG_BRD, + HW_REG_ISP_BUFFER_BRD, + CONFIG_DATA_ADDR_BRD, + MISCTL_REG_BRD, + WATCH_DOG_REG_BRD, + GOODIX_CFG_ID_ADDR_BRD, + ENABLE_MISCTL_BRD, +}; + +/** + * fw_subsys_info - subsytem firmware information + * @type: sybsystem type + * @size: firmware size + * @flash_addr: flash address + * @data: firmware data + */ +struct fw_subsys_info { + u8 type; + u32 size; + u32 flash_addr; + const u8 *data; +}; + +/** + * firmware_summary + * @size: fw total length + * @checksum: checksum of fw + * @hw_pid: mask pid string + * @hw_pid: mask vid code + * @fw_pid: fw pid string + * @fw_vid: fw vid code + * @subsys_num: number of fw subsystem + * @chip_type: chip type + * @protocol_ver: firmware packing + * protocol version + * @bus_type: 0 represent I2C, 1 for SPI + * @subsys: sybsystem info + */ +#pragma pack(1) +struct firmware_summary { + u32 size; + u32 checksum; + u8 hw_pid[6]; + u8 hw_vid[3]; + u8 fw_pid[FW_PID_LEN]; + u8 fw_vid[FW_VID_LEN]; + u8 subsys_num; + u8 chip_type; + u8 protocol_ver; + u8 bus_type; + u8 flash_protect; + // u8 reserved[8]; + struct fw_subsys_info subsys[FW_SUBSYS_MAX_NUM]; +}; +#pragma pack() + +/** + * firmware_data - firmware data structure + * @fw_summary: firmware information + * @firmware: firmware data structure + */ +struct firmware_data { + struct firmware_summary fw_summary; + const struct firmware *firmware; +}; + +struct config_data { + u8 *data; + int size; +}; + +#pragma pack(1) +struct goodix_flash_cmd { + union { + struct { + u8 status; + u8 ack; + u8 len; + u8 cmd; + u8 fw_type; + u16 fw_len; + u32 fw_addr; + //u16 checksum; + }; + u8 buf[16]; + }; +}; +#pragma pack() + +enum update_status { + UPSTA_NOTWORK = 0, + UPSTA_PREPARING, + UPSTA_UPDATING, + UPSTA_SUCCESS, + UPSTA_FAILED +}; + +enum compare_status { + COMPARE_EQUAL = 0, + COMPARE_NOCODE, + COMPARE_PIDMISMATCH, + COMPARE_FW_NOTEQUAL, + COMPARE_CFG_NOTEQUAL, +}; + +/** + * fw_update_ctrl - structure used to control the + * firmware update process + * @initialized: struct init state + * @mode: indicate weather reflash config or not, fw data source, + * and run on block mode or not. + * @status: update status + * @progress: indicate the progress of update + * @fw_data: firmware data + * @fw_name: firmware name + * @attr_fwimage: sysfs bin attrs, for storing fw image + * @fw_data_src: firmware data source form sysfs, request or head file + * @kobj: pointer to the sysfs kobject + */ +struct fw_update_ctrl { + struct mutex mutex; + int initialized; + char fw_name[GOODIX_MAX_STR_LABLE_LEN]; + int mode; + enum update_status status; + int spend_time; + + struct firmware_data fw_data; + struct goodix_ic_config *ic_config; + struct goodix_ts_core *core_data; + struct update_info_t *update_info; + + struct bin_attribute attr_fwimage; + struct kobject *kobj; +}; +static struct fw_update_ctrl goodix_fw_update_ctrl; + +static int goodix_fw_update_reset(int delay) +{ + struct goodix_ts_hw_ops *hw_ops; + + hw_ops = goodix_fw_update_ctrl.core_data->hw_ops; + return hw_ops->reset(goodix_fw_update_ctrl.core_data, delay); +} + +static int get_fw_version_info(struct goodix_fw_version *fw_version) +{ + struct goodix_ts_hw_ops *hw_ops = + goodix_fw_update_ctrl.core_data->hw_ops; + + return hw_ops->read_version(goodix_fw_update_ctrl.core_data, + fw_version); +} + +static int goodix_reg_write(unsigned int addr, + unsigned char *data, unsigned int len) +{ + struct goodix_ts_hw_ops *hw_ops = + goodix_fw_update_ctrl.core_data->hw_ops; + + return hw_ops->write(goodix_fw_update_ctrl.core_data, + addr, data, len); +} + +static int goodix_reg_read(unsigned int addr, + unsigned char *data, unsigned int len) +{ + struct goodix_ts_hw_ops *hw_ops = + goodix_fw_update_ctrl.core_data->hw_ops; + + return hw_ops->read(goodix_fw_update_ctrl.core_data, + addr, data, len); +} + +/** + * goodix_parse_firmware - parse firmware header information + * and subsystem information from firmware data buffer + * + * @fw_data: firmware struct, contains firmware header info + * and firmware data. + * return: 0 - OK, < 0 - error + */ +/* sizeof(length) + sizeof(checksum) */ + +static int goodix_parse_firmware(struct firmware_data *fw_data) +{ + const struct firmware *firmware; + struct firmware_summary *fw_summary; + unsigned int i, fw_offset, info_offset; + u32 checksum; + int ic_type = + goodix_fw_update_ctrl.core_data->bus->ic_type; + int subsys_info_offset = + goodix_fw_update_ctrl.update_info->subsys_info_offset; + int header_size = + goodix_fw_update_ctrl.update_info->header_size; + int r = 0; + + fw_summary = &fw_data->fw_summary; + + /* copy firmware head info */ + firmware = fw_data->firmware; + if (firmware->size < subsys_info_offset) { + ts_err("Invalid firmware size:%zu", firmware->size); + r = -EINVAL; + goto err_size; + } + memcpy(fw_summary, firmware->data, sizeof(*fw_summary)); + + /* check firmware size */ + fw_summary->size = le32_to_cpu(fw_summary->size); + if (firmware->size != fw_summary->size + FW_FILE_CHECKSUM_OFFSET) { + ts_err("Bad firmware, size not match, %zu != %d", + firmware->size, fw_summary->size + 6); + r = -EINVAL; + goto err_size; + } + + for (i = FW_FILE_CHECKSUM_OFFSET, checksum = 0; + i < firmware->size; i += 2) + checksum += firmware->data[i] + (firmware->data[i+1] << 8); + + /* byte order change, and check */ + fw_summary->checksum = le32_to_cpu(fw_summary->checksum); + if (checksum != fw_summary->checksum) { + ts_err("Bad firmware, cheksum error"); + r = -EINVAL; + goto err_size; + } + + if (fw_summary->subsys_num > FW_SUBSYS_MAX_NUM) { + ts_err("Bad firmware, invalid subsys num: %d", + fw_summary->subsys_num); + r = -EINVAL; + goto err_size; + } + + /* parse subsystem info */ + fw_offset = header_size; + for (i = 0; i < fw_summary->subsys_num; i++) { + info_offset = subsys_info_offset + + i * FW_SUBSYS_INFO_SIZE; + + fw_summary->subsys[i].type = firmware->data[info_offset]; + fw_summary->subsys[i].size = + le32_to_cpup((__le32 *)&firmware->data[info_offset + 1]); + + fw_summary->subsys[i].flash_addr = + le32_to_cpup((__le32 *)&firmware->data[info_offset + 5]); + if (fw_offset > firmware->size) { + ts_err("Sybsys offset exceed Firmware size"); + goto err_size; + } + + fw_summary->subsys[i].data = firmware->data + fw_offset; + fw_offset += fw_summary->subsys[i].size; + } + + ts_info("Firmware package protocol: V%u", fw_summary->protocol_ver); + ts_info("Firmware PID:GT%s", fw_summary->fw_pid); + ts_info("Firmware VID:%*ph", 4, fw_summary->fw_vid); + ts_info("Firmware chip type:0x%02X", fw_summary->chip_type); + ts_info("Firmware bus type:%s", + (fw_summary->bus_type & BUS_TYPE_SPI) ? "SPI" : "I2C"); + ts_info("Firmware size:%u", fw_summary->size); + ts_info("Firmware subsystem num:%u", fw_summary->subsys_num); + + for (i = 0; i < fw_summary->subsys_num; i++) { + ts_debug("------------------------------------------"); + ts_debug("Index:%d", i); + ts_debug("Subsystem type:%02X", fw_summary->subsys[i].type); + ts_debug("Subsystem size:%u", fw_summary->subsys[i].size); + ts_debug("Subsystem flash_addr:%08X", + fw_summary->subsys[i].flash_addr); + ts_debug("Subsystem Ptr:%p", fw_summary->subsys[i].data); + } + + if (fw_summary->chip_type == CHIP_TYPE_BRA && + ic_type != IC_TYPE_BERLIN_A) { + ts_err("ic type mismatch!"); + r = -EINVAL; + } else if (fw_summary->chip_type == CHIP_TYPE_BRB && + ic_type != IC_TYPE_BERLIN_B) { + ts_err("ic type mismatch!"); + r = -EINVAL; + } else if (fw_summary->chip_type == CHIP_TYPE_BRD && + ic_type != IC_TYPE_BERLIN_D) { + ts_err("ic type mismatch!"); + r = -EINVAL; + } + +err_size: + return r; +} + +/** + * goodix_fw_version_compare - compare the active version with + * firmware file version. + * @fwu_ctrl: firmware information to be compared + * return: 0 equal, < 0 unequal + */ +#define GOODIX_NOCODE "NOCODE" +static int goodix_fw_version_compare(struct fw_update_ctrl *fwu_ctrl) +{ + int ret = 0; + struct goodix_fw_version fw_version; + struct firmware_summary *fw_summary = &fwu_ctrl->fw_data.fw_summary; + u32 config_id_reg = goodix_fw_update_ctrl.update_info->config_id_reg; + u32 file_cfg_id; + u32 ic_cfg_id; + + /* compare fw_version */ + ret = get_fw_version_info(&fw_version); + if (ret) + return -EINVAL; + + if (!memcmp(fw_version.rom_pid, GOODIX_NOCODE, 6) || + !memcmp(fw_version.patch_pid, GOODIX_NOCODE, 6)) { + ts_info("there is no code in the chip"); + return COMPARE_NOCODE; + } + + if (memcmp(fw_version.patch_pid, fw_summary->fw_pid, FW_PID_LEN)) { + ts_err("Product ID mismatch:%s != %s", + fw_version.patch_pid, fw_summary->fw_pid); + return COMPARE_PIDMISMATCH; + } + + ret = memcmp(fw_version.patch_vid, fw_summary->fw_vid, FW_VID_LEN); + if (ret) { + ts_info("active firmware version:%*ph", FW_VID_LEN, + fw_version.patch_vid); + ts_info("firmware file version: %*ph", FW_VID_LEN, + fw_summary->fw_vid); + return COMPARE_FW_NOTEQUAL; + } + ts_info("fw_version equal"); + + /* compare config id */ + if (fwu_ctrl->ic_config && fwu_ctrl->ic_config->len > 0) { + file_cfg_id = + goodix_get_file_config_id(fwu_ctrl->ic_config->data); + goodix_reg_read(config_id_reg, + (u8 *)&ic_cfg_id, sizeof(ic_cfg_id)); + if (ic_cfg_id != file_cfg_id) { + ts_info("ic_cfg_id:0x%x != file_cfg_id:0x%x", + ic_cfg_id, file_cfg_id); + return COMPARE_CFG_NOTEQUAL; + } + ts_info("config_id equal"); + } + + return COMPARE_EQUAL; +} + +/** + * goodix_reg_write_confirm - write register and confirm the value + * in the register. + * @dev: pointer to touch device + * @addr: register address + * @data: pointer to data buffer + * @len: data length + * return: 0 write success and confirm ok + * < 0 failed + */ +static int goodix_reg_write_confirm(unsigned int addr, + unsigned char *data, unsigned int len) +{ + u8 *cfm = NULL; + u8 cfm_buf[32]; + int r, i; + + if (len > sizeof(cfm_buf)) { + cfm = kzalloc(len, GFP_KERNEL); + if (!cfm) + return -ENOMEM; + } else { + cfm = &cfm_buf[0]; + } + + for (i = 0; i < GOODIX_BUS_RETRY_TIMES; i++) { + r = goodix_reg_write(addr, data, len); + if (r < 0) + goto exit; + + r = goodix_reg_read(addr, cfm, len); + if (r < 0) + goto exit; + + if (memcmp(data, cfm, len)) { + r = -EINVAL; + continue; + } else { + r = 0; + break; + } + } + +exit: + if (cfm != &cfm_buf[0]) + kfree(cfm); + return r; +} + + +/** + * goodix_load_isp - load ISP program to device ram + * @dev: pointer to touch device + * @fw_data: firmware data + * return 0 ok, <0 error + */ +static int goodix_load_isp(struct firmware_data *fw_data) +{ + struct goodix_fw_version isp_fw_version; + struct fw_subsys_info *fw_isp; + u32 isp_ram_reg = goodix_fw_update_ctrl.update_info->isp_ram_reg; + u8 reg_val[8] = {0x00}; + int r; + + memset(&isp_fw_version, 0, sizeof(isp_fw_version)); + fw_isp = &fw_data->fw_summary.subsys[0]; + + ts_info("Loading ISP start"); + r = goodix_reg_write_confirm(isp_ram_reg, + (u8 *)fw_isp->data, fw_isp->size); + if (r < 0) { + ts_err("Loading ISP error"); + return r; + } + + ts_info("Success send ISP data"); + + /* SET BOOT OPTION TO 0X55 */ + memset(reg_val, 0x55, 8); + r = goodix_reg_write_confirm(HW_REG_CPU_RUN_FROM, reg_val, 8); + if (r < 0) { + ts_err("Failed set REG_CPU_RUN_FROM flag"); + return r; + } + ts_info("Success write [8]0x55 to 0x%x", HW_REG_CPU_RUN_FROM); + + if (goodix_fw_update_reset(100)) + ts_err("reset abnormal"); + /*check isp state */ + if (get_fw_version_info(&isp_fw_version)) { + ts_err("failed read isp version"); + return -2; + } + if (memcmp(&isp_fw_version.patch_pid[3], "ISP", 3)) { + ts_err("patch id error %c%c%c != %s", + isp_fw_version.patch_pid[3], isp_fw_version.patch_pid[4], + isp_fw_version.patch_pid[5], "ISP"); + return -3; + } + ts_info("ISP running successfully"); + return 0; +} + +/** + * goodix_update_prepare - update prepare, loading ISP program + * and make sure the ISP is running. + * @fwu_ctrl: pointer to fimrware control structure + * return: 0 ok, <0 error + */ +static int goodix_update_prepare(struct fw_update_ctrl *fwu_ctrl) +{ + u32 misctl_reg = fwu_ctrl->update_info->misctl_reg; + u32 watch_dog_reg = fwu_ctrl->update_info->watch_dog_reg; + u32 enable_misctl_val = fwu_ctrl->update_info->enable_misctl_val; + u8 reg_val[4] = {0}; + u8 temp_buf[64] = {0}; + int retry = 20; + int r; + + /*reset IC*/ + ts_info("firmware update, reset"); + if (goodix_fw_update_reset(5)) + ts_err("reset abnormal"); + + retry = 100; + /* Hold cpu*/ + do { + reg_val[0] = 0x01; + reg_val[1] = 0x00; + r = goodix_reg_write(HOLD_CPU_REG_W, reg_val, 2); + r |= goodix_reg_read(HOLD_CPU_REG_R, &temp_buf[0], 4); + r |= goodix_reg_read(HOLD_CPU_REG_R, &temp_buf[4], 4); + r |= goodix_reg_read(HOLD_CPU_REG_R, &temp_buf[8], 4); + if (!r && !memcmp(&temp_buf[0], &temp_buf[4], 4) && + !memcmp(&temp_buf[4], &temp_buf[8], 4) && + !memcmp(&temp_buf[0], &temp_buf[8], 4)) { + break; + } + usleep_range(1000, 1100); + ts_info("retry hold cpu %d", retry); + ts_debug("data:%*ph", 12, temp_buf); + } while (--retry); + if (!retry) { + ts_err("Failed to hold CPU, return =%d", r); + return -1; + } + ts_info("Success hold CPU"); + + /* enable misctl clock */ + if (fwu_ctrl->core_data->bus->ic_type == IC_TYPE_BERLIN_D) + goodix_reg_write(misctl_reg, (u8 *)&enable_misctl_val, 4); + else + goodix_reg_write(misctl_reg, (u8 *)&enable_misctl_val, 1); + ts_info("enbale misctl clock"); + + if (fwu_ctrl->core_data->bus->ic_type == IC_TYPE_BERLIN_A) { + /* open ESD_KEY */ + retry = 20; + do { + reg_val[0] = 0x95; + r = goodix_reg_write(ESD_KEY_REG, reg_val, 1); + r |= goodix_reg_read(ESD_KEY_REG, temp_buf, 1); + if (!r && temp_buf[0] == 0x01) + break; + usleep_range(1000, 1100); + ts_info("retry %d enable esd key, 0x%x", + retry, temp_buf[0]); + } while (--retry); + if (!retry) { + ts_err("Failed to enable esd key, return =%d", r); + return -2; + } + ts_info("success enable esd key"); + } + + /* disable watch dog */ + reg_val[0] = 0x00; + r = goodix_reg_write(watch_dog_reg, reg_val, 1); + ts_info("disable watch dog"); + + /* load ISP code and run form isp */ + r = goodix_load_isp(&fwu_ctrl->fw_data); + if (r < 0) + ts_err("Failed load and run isp"); + + return r; +} + +/* goodix_send_flash_cmd: send command to read or write flash data + * @flash_cmd: command need to send. + */ +static int goodix_send_flash_cmd(struct goodix_flash_cmd *flash_cmd) +{ + int i, ret, retry; + struct goodix_flash_cmd tmp_cmd; + u32 flash_cmd_reg = goodix_fw_update_ctrl.update_info->flash_cmd_reg; + + ts_info("try send flash cmd:%*ph", (int)sizeof(flash_cmd->buf), + flash_cmd->buf); + memset(tmp_cmd.buf, 0, sizeof(tmp_cmd)); + ret = goodix_reg_write(flash_cmd_reg, + flash_cmd->buf, sizeof(flash_cmd->buf)); + if (ret) { + ts_err("failed send flash cmd %d", ret); + return ret; + } + + retry = 5; + for (i = 0; i < retry; i++) { + ret = goodix_reg_read(flash_cmd_reg, + tmp_cmd.buf, sizeof(tmp_cmd.buf)); + if (!ret && tmp_cmd.ack == FLASH_CMD_ACK_CHK_PASS) + break; + usleep_range(5000, 5100); + ts_info("flash cmd ack error retry %d, ack 0x%x, ret %d", + i, tmp_cmd.ack, ret); + } + if (tmp_cmd.ack != FLASH_CMD_ACK_CHK_PASS) { + ts_err("flash cmd ack error, ack 0x%x, ret %d", + tmp_cmd.ack, ret); + ts_err("data:%*ph", (int)sizeof(tmp_cmd.buf), tmp_cmd.buf); + return -EINVAL; + } + ts_info("flash cmd ack check pass"); + + msleep(80); + retry = 20; + for (i = 0; i < retry; i++) { + ret = goodix_reg_read(flash_cmd_reg, + tmp_cmd.buf, sizeof(tmp_cmd.buf)); + if (!ret && tmp_cmd.ack == FLASH_CMD_ACK_CHK_PASS && + tmp_cmd.status == FLASH_CMD_W_STATUS_WRITE_OK) { + ts_info("flash status check pass"); + return 0; + } + + ts_info("flash cmd status not ready, retry %d, ack 0x%x, status 0x%x, ret %d", + i, tmp_cmd.ack, tmp_cmd.status, ret); + msleep(20); + } + + ts_err("flash cmd status error %d, ack 0x%x, status 0x%x, ret %d", + i, tmp_cmd.ack, tmp_cmd.status, ret); + if (ret) { + ts_info("reason: bus or paltform error"); + return -EINVAL; + } + + switch (tmp_cmd.status) { + case FLASH_CMD_W_STATUS_CHK_PASS: + ts_err("data check pass, but failed get follow-up results"); + return -EFAULT; + case FLASH_CMD_W_STATUS_CHK_FAIL: + ts_err("data check failed, please retry"); + return -EAGAIN; + case FLASH_CMD_W_STATUS_ADDR_ERR: + ts_err("flash target addr error, please check"); + return -EFAULT; + case FLASH_CMD_W_STATUS_WRITE_ERR: + ts_err("flash data write err, please retry"); + return -EAGAIN; + default: + ts_err("unknown status"); + return -EFAULT; + } +} + +static int goodix_flash_package(u8 subsys_type, u8 *pkg, + u32 flash_addr, u16 pkg_len) +{ + int ret, retry; + struct goodix_flash_cmd flash_cmd; + u32 isp_buffer_reg = goodix_fw_update_ctrl.update_info->isp_buffer_reg; + + retry = 2; + do { + ret = goodix_reg_write(isp_buffer_reg, pkg, pkg_len); + if (ret < 0) { + ts_err("Failed to write firmware packet"); + return ret; + } + + flash_cmd.status = 0; + flash_cmd.ack = 0; + flash_cmd.len = FLASH_CMD_LEN; + flash_cmd.cmd = FLASH_CMD_TYPE_WRITE; + flash_cmd.fw_type = subsys_type; + flash_cmd.fw_len = cpu_to_le16(pkg_len); + flash_cmd.fw_addr = cpu_to_le32(flash_addr); + + goodix_append_checksum(&(flash_cmd.buf[2]), + 9, CHECKSUM_MODE_U8_LE); + + ret = goodix_send_flash_cmd(&flash_cmd); + if (!ret) { + ts_info("success write package to 0x%x, len %d", + flash_addr, pkg_len - 4); + return 0; + } + } while (ret == -EAGAIN && --retry); + + return ret; +} + +/** + * goodix_flash_subsystem - flash subsystem firmware, + * Main flow of flashing firmware. + * Each firmware subsystem is divided into several + * packets, the max size of packet is limited to + * @{ISP_MAX_BUFFERSIZE} + * @dev: pointer to touch device + * @subsys: subsystem information + * return: 0 ok, < 0 error + */ +static int goodix_flash_subsystem(struct fw_subsys_info *subsys) +{ + u32 data_size, offset; + u32 total_size; + //TODO: confirm flash addr ,<< 8?? + u32 subsys_base_addr = subsys->flash_addr; + u8 *fw_packet = NULL; + int r = 0; + + /* + * if bus(i2c/spi) error occued, then exit, we will do + * hardware reset and re-prepare ISP and then retry + * flashing + */ + total_size = subsys->size; + fw_packet = kzalloc(ISP_MAX_BUFFERSIZE + 4, GFP_KERNEL); + if (!fw_packet) { + ts_err("Failed alloc memory"); + return -EINVAL; + } + + offset = 0; + while (total_size > 0) { + data_size = total_size > ISP_MAX_BUFFERSIZE ? + ISP_MAX_BUFFERSIZE : total_size; + ts_info("Flash firmware to %08x,size:%u bytes", + subsys_base_addr + offset, data_size); + + memcpy(fw_packet, &subsys->data[offset], data_size); + /* set checksum for package data */ + goodix_append_checksum(fw_packet, + data_size, CHECKSUM_MODE_U16_LE); + + r = goodix_flash_package(subsys->type, fw_packet, + subsys_base_addr + offset, data_size + 4); + if (r) { + ts_err("failed flash to %08x,size:%u bytes", + subsys_base_addr + offset, data_size); + break; + } + offset += data_size; + total_size -= data_size; + } /* end while */ + + kfree(fw_packet); + return r; +} + +/** + * goodix_flash_firmware - flash firmware + * @dev: pointer to touch device + * @fw_data: firmware data + * return: 0 ok, < 0 error + */ +static int goodix_flash_firmware(struct fw_update_ctrl *fw_ctrl) +{ + struct firmware_data *fw_data = &fw_ctrl->fw_data; + struct firmware_summary *fw_summary; + struct fw_subsys_info *fw_x; + struct fw_subsys_info subsys_cfg = {0}; + u32 config_data_reg = fw_ctrl->update_info->config_data_reg; + int retry = GOODIX_BUS_RETRY_TIMES; + int i, r = 0, fw_num; + + /* start from subsystem 1, + * subsystem 0 is the ISP program + */ + + fw_summary = &fw_data->fw_summary; + fw_num = fw_summary->subsys_num; + + /* flash config data first if we have */ + if (fw_ctrl->ic_config && fw_ctrl->ic_config->len) { + subsys_cfg.data = fw_ctrl->ic_config->data; + subsys_cfg.size = fw_ctrl->ic_config->len; + subsys_cfg.flash_addr = config_data_reg; + subsys_cfg.type = CONFIG_DATA_TYPE; + r = goodix_flash_subsystem(&subsys_cfg); + if (r) { + ts_err("failed flash config with ISP, %d", r); + return r; + } + ts_info("success flash config with ISP"); + } + + for (i = 1; i < fw_num && retry;) { + ts_info("--- Start to flash subsystem[%d] ---", i); + fw_x = &fw_summary->subsys[i]; + r = goodix_flash_subsystem(fw_x); + if (r == 0) { + ts_info("--- End flash subsystem[%d]: OK ---", i); + i++; + } else if (r == -EAGAIN) { + retry--; + ts_err("--- End flash subsystem%d: Fail, errno:%d, retry:%d ---", + i, r, GOODIX_BUS_RETRY_TIMES - retry); + } else if (r < 0) { /* bus error */ + ts_err("--- End flash subsystem%d: Fatal error:%d exit ---", + i, r); + goto exit_flash; + } + } + +exit_flash: + return r; +} + +/** + * goodix_update_finish - update finished, FREE resource + * and reset flags--- + * @fwu_ctrl: pointer to fw_update_ctrl structrue + * return: 0 ok, < 0 error + */ +static int goodix_update_finish(struct fw_update_ctrl *fwu_ctrl) +{ + int ret; + + if (goodix_fw_update_reset(100)) + ts_err("reset abnormal"); + ret = goodix_fw_version_compare(fwu_ctrl); + if (ret == COMPARE_EQUAL || ret == COMPARE_CFG_NOTEQUAL) + return 0; + + return -EINVAL; +} + +/** + * goodix_fw_update_proc - firmware update process, the entry of + * firmware update flow + * @fwu_ctrl: firmware control + * return: = 0 update ok, < 0 error or NO_NEED_UPDATE + */ +int goodix_fw_update_proc(struct fw_update_ctrl *fwu_ctrl) +{ +#define FW_UPDATE_RETRY 2 + int retry0 = FW_UPDATE_RETRY; + int retry1 = FW_UPDATE_RETRY; + int ret = 0; + + ret = goodix_parse_firmware(&fwu_ctrl->fw_data); + if (ret < 0) + return ret; + + if (!(fwu_ctrl->mode & UPDATE_MODE_FORCE)) { + ret = goodix_fw_version_compare(fwu_ctrl); + if (!ret) { + ts_info("firmware upgraded"); + return 0; + } else + ts_info("need to upgrade"); + } + +start_update: + fwu_ctrl->status = UPSTA_PREPARING; + do { + ret = goodix_update_prepare(fwu_ctrl); + if (ret) { + ts_err("failed prepare ISP, retry %d", + FW_UPDATE_RETRY - retry0); + } + } while (ret && --retry0 > 0); + if (ret) { + ts_err("Failed to prepare ISP, exit update:%d", ret); + goto err_fw_prepare; + } + + /* progress: 20%~100% */ + fwu_ctrl->status = UPSTA_UPDATING; + ret = goodix_flash_firmware(fwu_ctrl); + if (ret < 0 && --retry1 > 0) { + ts_err("Bus error, retry firmware update:%d", + FW_UPDATE_RETRY - retry1); + goto start_update; + } + if (ret) + ts_err("flash fw data enter error, ret:%d", ret); + else + ts_info("flash fw data success, need check version"); + +err_fw_prepare: + ret = goodix_update_finish(fwu_ctrl); + if (!ret) + ts_info("Firmware update successfully"); + else + ts_err("Firmware update failed, ret:%d", ret); + + return ret; +} + +/* + * goodix_sysfs_update_en_store: start fw update manually + * @buf: '1'[001] update in blocking mode with fwdata from sysfs + * '2'[010] update in blocking mode with fwdata from request + * '5'[101] update in unblocking mode with fwdata from sysfs + * '6'[110] update in unblocking mode with fwdata from request + */ +static ssize_t goodix_sysfs_update_en_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + int mode = 0; + struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; + + if (!buf || count <= 0) { + ts_err("invalid params"); + return -EINVAL; + } + if (!fw_ctrl || !fw_ctrl->initialized) { + ts_err("fw module uninit"); + return -EINVAL; + } + + ts_info("set update mode:0x%x", buf[0]); + if (buf[0] == '1') { + mode = UPDATE_MODE_FORCE | UPDATE_MODE_BLOCK | + UPDATE_MODE_SRC_SYSFS; + } else if (buf[0] == '2') { + mode = UPDATE_MODE_FORCE | UPDATE_MODE_BLOCK | + UPDATE_MODE_SRC_REQUEST; + } else if (buf[0] == '5') { + mode = UPDATE_MODE_FORCE | UPDATE_MODE_SRC_SYSFS; + } else if (buf[0] == '6') { + mode = UPDATE_MODE_FORCE | UPDATE_MODE_SRC_REQUEST; + } else { + ts_err("invalid update mode:0x%x", buf[0]); + return -EINVAL; + } + + ret = goodix_do_fw_update(NULL, mode); + if (!ret) { + ts_info("success do update work"); + return count; + } + ts_err("failed do fw update work"); + return -EINVAL; +} + +static ssize_t goodix_sysfs_fwsize_show( + struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; + int r = -EINVAL; + + if (fw_ctrl && fw_ctrl->fw_data.firmware) + r = snprintf(buf, PAGE_SIZE, "%zu\n", + fw_ctrl->fw_data.firmware->size); + return r; +} + +static ssize_t goodix_sysfs_fwsize_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; + struct firmware *fw; + u8 **data; + size_t size = 0; + + if (!fw_ctrl) + return -EINVAL; + + if (sscanf(buf, "%zu", &size) < 0 || !size) { + ts_err("Failed to get fwsize"); + return -EFAULT; + } + + /* use vmalloc to alloc huge memory */ + fw = vmalloc(sizeof(*fw) + size); + if (!fw) + return -ENOMEM; + mutex_lock(&fw_ctrl->mutex); + memset(fw, 0x00, sizeof(*fw) + size); + data = (u8 **)&fw->data; + *data = (u8 *)fw + sizeof(struct firmware); + fw->size = size; + fw_ctrl->fw_data.firmware = fw; + fw_ctrl->mode = UPDATE_MODE_SRC_SYSFS; + mutex_unlock(&fw_ctrl->mutex); + return count; +} + +static ssize_t goodix_sysfs_fwimage_store(struct file *file, + struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t pos, size_t count) +{ + struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; + struct firmware_data *fw_data; + + fw_data = &fw_ctrl->fw_data; + + if (!fw_data->firmware) { + ts_err("Need set fw image size first"); + return -ENOMEM; + } + + if (fw_data->firmware->size == 0) { + ts_err("Invalid firmware size"); + return -EINVAL; + } + + if (pos + count > fw_data->firmware->size) + return -EFAULT; + mutex_lock(&fw_ctrl->mutex); + memcpy((u8 *)&fw_data->firmware->data[pos], buf, count); + mutex_unlock(&fw_ctrl->mutex); + return count; +} + +/* return fw_update result */ +static ssize_t goodix_sysfs_result_show( + struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; + char str[GOODIX_MAX_STR_LABLE_LEN] = {0}; + int r = -EINVAL; + + if (!fw_ctrl) + return r; + + switch (fw_ctrl->status) { + case UPSTA_PREPARING: + sprintf(str, "preparing"); + break; + case UPSTA_UPDATING: + sprintf(str, "updating"); + break; + case UPSTA_SUCCESS: + sprintf(str, "success"); + break; + case UPSTA_FAILED: + sprintf(str, "failed"); + break; + case UPSTA_NOTWORK: + default: + sprintf(str, "notwork"); + break; + } + + r = snprintf(buf, PAGE_SIZE, "result:%s spend_time:%dms\n", + str, fw_ctrl->spend_time); + + return r; +} + +static DEVICE_ATTR(update_en, 0220, NULL, goodix_sysfs_update_en_store); +static DEVICE_ATTR(fwsize, 0664, goodix_sysfs_fwsize_show, + goodix_sysfs_fwsize_store); +static DEVICE_ATTR(result, 0664, goodix_sysfs_result_show, NULL); + +static struct attribute *goodix_fwu_attrs[] = { + &dev_attr_update_en.attr, + &dev_attr_fwsize.attr, + &dev_attr_result.attr +}; + +static int goodix_fw_sysfs_init(struct goodix_ts_core *core_data, + struct fw_update_ctrl *fw_ctrl) +{ + int ret = 0, i; + + fw_ctrl->kobj = kobject_create_and_add("fwupdate", + &core_data->pdev->dev.kobj); + if (!fw_ctrl->kobj) { + ts_err("failed create sub dir for fwupdate"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(goodix_fwu_attrs) && !ret; i++) + ret = sysfs_create_file(fw_ctrl->kobj, goodix_fwu_attrs[i]); + + if (ret) { + ts_err("failed create fwu sysfs files"); + while (--i >= 0) + sysfs_remove_file(fw_ctrl->kobj, goodix_fwu_attrs[i]); + + kobject_put(fw_ctrl->kobj); + return -EINVAL; + } + + fw_ctrl->attr_fwimage.attr.name = "fwimage"; + fw_ctrl->attr_fwimage.attr.mode = 0666; + fw_ctrl->attr_fwimage.size = 0; + fw_ctrl->attr_fwimage.write = goodix_sysfs_fwimage_store; + ret = sysfs_create_bin_file(fw_ctrl->kobj, &fw_ctrl->attr_fwimage); + if (ret) { + ts_err("failed create fwimage bin node, %d", ret); + for (i = 0; i < ARRAY_SIZE(goodix_fwu_attrs); i++) + sysfs_remove_file(fw_ctrl->kobj, goodix_fwu_attrs[i]); + kobject_put(fw_ctrl->kobj); + } + + return ret; +} + +static void goodix_fw_sysfs_remove(void) +{ + struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; + int i; + + sysfs_remove_bin_file(fw_ctrl->kobj, &fw_ctrl->attr_fwimage); + + for (i = 0; i < ARRAY_SIZE(goodix_fwu_attrs); i++) + sysfs_remove_file(fw_ctrl->kobj, + goodix_fwu_attrs[i]); + + kobject_put(fw_ctrl->kobj); +} + + +/** + * goodix_request_firmware - request firmware data from user space + * + * @fw_data: firmware struct, contains firmware header info + * and firmware data pointer. + * return: 0 - OK, < 0 - error + */ +static int goodix_request_firmware(struct firmware_data *fw_data, + const char *name) +{ + struct fw_update_ctrl *fw_ctrl = + container_of(fw_data, struct fw_update_ctrl, fw_data); + struct device *dev = &(fw_ctrl->core_data->pdev->dev); + int r; + int retry = GOODIX_RETRY_3; + + ts_info("Request firmware image [%s]", name); + + while (retry--) { + r = request_firmware(&fw_data->firmware, name, dev); + if (!r) + break; + ts_info("get fw bin retry:[%d]", GOODIX_RETRY_3 - retry); + msleep(200); + } + if (retry < 0) { + ts_err("Firmware image [%s] not available,errno:%d", name, r); + return r; + } + + ts_info("Firmware image [%s] is ready", name); + return 0; +} + +/** + * relase firmware resources + * + */ +static inline void goodix_release_firmware(struct firmware_data *fw_data) +{ + if (fw_data->firmware) { + release_firmware(fw_data->firmware); + fw_data->firmware = NULL; + } +} + +static int goodix_fw_update_thread(void *data) +{ + struct fw_update_ctrl *fwu_ctrl = data; + struct firmware *temp_firmware = NULL; + ktime_t start, end; + int r = -EINVAL; + + start = ktime_get(); + fwu_ctrl->spend_time = 0; + fwu_ctrl->status = UPSTA_NOTWORK; + mutex_lock(&fwu_ctrl->mutex); + + if (fwu_ctrl->mode & UPDATE_MODE_SRC_REQUEST) { + ts_info("Firmware request update starts"); + r = goodix_request_firmware(&fwu_ctrl->fw_data, + fwu_ctrl->fw_name); + if (r < 0) + goto out; + + } else if (fwu_ctrl->mode & UPDATE_MODE_SRC_SYSFS) { + if (!fwu_ctrl->fw_data.firmware) { + ts_err("Invalid firmware from sysfs"); + r = -EINVAL; + goto out; + } + } else { + ts_err("unknown update mode 0x%x", fwu_ctrl->mode); + r = -EINVAL; + goto out; + } + + ts_debug("notify update start"); + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_START, NULL); + + /* ready to update */ + ts_debug("start update proc"); + r = goodix_fw_update_proc(fwu_ctrl); + + /* clean */ + if (fwu_ctrl->mode & UPDATE_MODE_SRC_HEAD) { + kfree(fwu_ctrl->fw_data.firmware); + fwu_ctrl->fw_data.firmware = NULL; + temp_firmware = NULL; + } else if (fwu_ctrl->mode & UPDATE_MODE_SRC_REQUEST) { + goodix_release_firmware(&fwu_ctrl->fw_data); + } +out: + fwu_ctrl->mode = UPDATE_MODE_DEFAULT; + mutex_unlock(&fwu_ctrl->mutex); + + if (r) { + ts_err("fw update failed, %d", r); + fwu_ctrl->status = UPSTA_FAILED; + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_FAILED, NULL); + } else { + ts_info("fw update success"); + fwu_ctrl->status = UPSTA_SUCCESS; + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_SUCCESS, NULL); + } + + end = ktime_get(); + fwu_ctrl->spend_time = ktime_to_ms(ktime_sub(end, start)); + + return r; +} + +int goodix_do_fw_update(struct goodix_ic_config *ic_config, int mode) +{ + struct task_struct *fwu_thrd; + struct fw_update_ctrl *fwu_ctrl = &goodix_fw_update_ctrl; + int ret; + + if (!fwu_ctrl->initialized) { + ts_err("fw mode uninit"); + return -EINVAL; + } + + fwu_ctrl->mode = mode; + fwu_ctrl->ic_config = ic_config; + ts_debug("fw update mode 0x%x", mode); + if (fwu_ctrl->mode & UPDATE_MODE_BLOCK) { + ret = goodix_fw_update_thread(fwu_ctrl); + ts_info("fw update return %d", ret); + return ret; + } + /* create and run update thread */ + fwu_thrd = kthread_run(goodix_fw_update_thread, + fwu_ctrl, "goodix-fwu"); + if (IS_ERR_OR_NULL(fwu_thrd)) { + ts_err("Failed to create update thread:%ld", + PTR_ERR(fwu_thrd)); + return -EFAULT; + } + ts_info("success create fw update thread"); + return 0; +} + +int goodix_fw_update_init(struct goodix_ts_core *core_data) +{ + int ret; + + if (!core_data || !core_data->hw_ops) { + ts_err("core_data && hw_ops cann't be null"); + return -ENODEV; + } + + mutex_init(&goodix_fw_update_ctrl.mutex); + goodix_fw_update_ctrl.core_data = core_data; + goodix_fw_update_ctrl.mode = 0; + + strlcpy(goodix_fw_update_ctrl.fw_name, core_data->board_data.fw_name, + sizeof(goodix_fw_update_ctrl.fw_name)); + + ret = goodix_fw_sysfs_init(core_data, &goodix_fw_update_ctrl); + if (ret) { + ts_err("failed create fwupate sysfs node"); + return ret; + } + if (core_data->bus->ic_type == IC_TYPE_BERLIN_A) + goodix_fw_update_ctrl.update_info = &update_bra; + else if (core_data->bus->ic_type == IC_TYPE_BERLIN_B) + goodix_fw_update_ctrl.update_info = &update_brb; + else + goodix_fw_update_ctrl.update_info = &update_brd; + + goodix_fw_update_ctrl.initialized = 1; + return 0; +} + +void goodix_fw_update_uninit(void) +{ + if (!goodix_fw_update_ctrl.initialized) + return; + + mutex_lock(&goodix_fw_update_ctrl.mutex); + goodix_fw_sysfs_remove(); + goodix_fw_update_ctrl.initialized = 0; + mutex_unlock(&goodix_fw_update_ctrl.mutex); + mutex_destroy(&goodix_fw_update_ctrl.mutex); +} diff --git a/goodix_brl_hw.c b/goodix_brl_hw.c new file mode 100644 index 0000000000..49a03f9719 --- /dev/null +++ b/goodix_brl_hw.c @@ -0,0 +1,1434 @@ + /* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ +#include "goodix_ts_core.h" + +/* berlin_A SPI mode setting */ +#define GOODIX_SPI_MODE_REG 0xC900 +#define GOODIX_SPI_NORMAL_MODE_0 0x01 + +/* berlin_A D12 setting */ +#define GOODIX_REG_CLK_STA0 0xD807 +#define GOODIX_CLK_STA0_ENABLE 0xFF +#define GOODIX_REG_CLK_STA1 0xD806 +#define GOODIX_CLK_STA1_ENABLE 0x77 +#define GOODIX_REG_TRIM_D12 0xD006 +#define GOODIX_TRIM_D12_LEVEL 0x3C +#define GOODIX_REG_RESET 0xD808 +#define GOODIX_RESET_EN 0xFA +#define HOLD_CPU_REG_W 0x0002 +#define HOLD_CPU_REG_R 0x2000 + +#define DEV_CONFIRM_VAL 0xAA +#define BOOTOPTION_ADDR 0x10000 +#define FW_VERSION_INFO_ADDR_BRA 0x1000C +#define FW_VERSION_INFO_ADDR 0x10014 + +#define GOODIX_IC_INFO_MAX_LEN 1024 +#define GOODIX_IC_INFO_ADDR_BRA 0x10068 +#define GOODIX_IC_INFO_ADDR 0x10070 + + +enum brl_request_code { + BRL_REQUEST_CODE_CONFIG = 0x01, + BRL_REQUEST_CODE_REF_ERR = 0x02, + BRL_REQUEST_CODE_RESET = 0x03, + BRL_REQUEST_CODE_CLOCK = 0x04, +}; + +static int brl_select_spi_mode(struct goodix_ts_core *cd) +{ + int ret; + int i; + u8 w_value = GOODIX_SPI_NORMAL_MODE_0; + u8 r_value; + + if (cd->bus->bus_type == GOODIX_BUS_TYPE_I2C || + cd->bus->ic_type != IC_TYPE_BERLIN_A) + return 0; + + for (i = 0; i < GOODIX_RETRY_5; i++) { + cd->hw_ops->write(cd, GOODIX_SPI_MODE_REG, + &w_value, 1); + ret = cd->hw_ops->read(cd, GOODIX_SPI_MODE_REG, + &r_value, 1); + if (!ret && r_value == w_value) + return 0; + } + ts_err("failed switch SPI mode after reset, ret:%d r_value:%02x", ret, r_value); + return -EINVAL; +} + +static int brl_dev_confirm(struct goodix_ts_core *cd) +{ + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + int ret = 0; + int retry = GOODIX_RETRY_3; + u8 tx_buf[8] = {0}; + u8 rx_buf[8] = {0}; + + memset(tx_buf, DEV_CONFIRM_VAL, sizeof(tx_buf)); + while (retry--) { + ret = hw_ops->write(cd, BOOTOPTION_ADDR, + tx_buf, sizeof(tx_buf)); + if (ret < 0) + return ret; + ret = hw_ops->read(cd, BOOTOPTION_ADDR, + rx_buf, sizeof(rx_buf)); + if (ret < 0) + return ret; + if (!memcmp(tx_buf, rx_buf, sizeof(tx_buf))) + break; + usleep_range(5000, 5100); + } + + if (retry < 0) { + ret = -EINVAL; + ts_err("device confirm failed, rx_buf:%*ph", 8, rx_buf); + } + + ts_info("device connected"); + return ret; +} + +static int brl_reset_after(struct goodix_ts_core *cd) +{ + u8 reg_val[2] = {0}; + u8 temp_buf[12] = {0}; + int ret; + int retry; + + if (cd->bus->ic_type != IC_TYPE_BERLIN_A) + return 0; + + ts_info("IN"); + + /* select spi mode */ + ret = brl_select_spi_mode(cd); + if (ret < 0) + return ret; + + /* hold cpu */ + retry = GOODIX_RETRY_10; + while (retry--) { + reg_val[0] = 0x01; + reg_val[1] = 0x00; + ret = cd->hw_ops->write(cd, HOLD_CPU_REG_W, reg_val, 2); + ret |= cd->hw_ops->read(cd, HOLD_CPU_REG_R, &temp_buf[0], 4); + ret |= cd->hw_ops->read(cd, HOLD_CPU_REG_R, &temp_buf[4], 4); + ret |= cd->hw_ops->read(cd, HOLD_CPU_REG_R, &temp_buf[8], 4); + if (!ret && !memcmp(&temp_buf[0], &temp_buf[4], 4) && + !memcmp(&temp_buf[4], &temp_buf[8], 4) && + !memcmp(&temp_buf[0], &temp_buf[8], 4)) { + break; + } + } + if (retry < 0) { + ts_err("failed to hold cpu, status:%*ph", 12, temp_buf); + return -EINVAL; + } + + /* enable sta0 clk */ + retry = GOODIX_RETRY_5; + while (retry--) { + reg_val[0] = GOODIX_CLK_STA0_ENABLE; + ret = cd->hw_ops->write(cd, GOODIX_REG_CLK_STA0, reg_val, 1); + ret |= cd->hw_ops->read(cd, GOODIX_REG_CLK_STA0, temp_buf, 1); + if (!ret && temp_buf[0] == GOODIX_CLK_STA0_ENABLE) + break; + } + if (retry < 0) { + ts_err("failed to enable group0 clock, ret:%d status:%02x", ret, temp_buf[0]); + return -EINVAL; + } + + /* enable sta1 clk */ + retry = GOODIX_RETRY_5; + while (retry--) { + reg_val[0] = GOODIX_CLK_STA1_ENABLE; + ret = cd->hw_ops->write(cd, GOODIX_REG_CLK_STA1, reg_val, 1); + ret |= cd->hw_ops->read(cd, GOODIX_REG_CLK_STA1, temp_buf, 1); + if (!ret && temp_buf[0] == GOODIX_CLK_STA1_ENABLE) + break; + } + if (retry < 0) { + ts_err("failed to enable group1 clock, ret:%d status:%02x", ret, temp_buf[0]); + return -EINVAL; + } + + /* set D12 level */ + retry = GOODIX_RETRY_5; + while (retry--) { + reg_val[0] = GOODIX_TRIM_D12_LEVEL; + ret = cd->hw_ops->write(cd, GOODIX_REG_TRIM_D12, reg_val, 1); + ret |= cd->hw_ops->read(cd, GOODIX_REG_TRIM_D12, temp_buf, 1); + if (!ret && temp_buf[0] == GOODIX_TRIM_D12_LEVEL) + break; + } + if (retry < 0) { + ts_err("failed to set D12, ret:%d status:%02x", ret, temp_buf[0]); + return -EINVAL; + } + + usleep_range(5000, 5100); + /* soft reset */ + reg_val[0] = GOODIX_RESET_EN; + ret = cd->hw_ops->write(cd, GOODIX_REG_RESET, reg_val, 1); + if (ret < 0) + return ret; + + /* select spi mode */ + ret = brl_select_spi_mode(cd); + if (ret < 0) + return ret; + + ts_info("OUT"); + + return 0; +} + +static int brl_power_on(struct goodix_ts_core *cd, bool on) +{ + int ret = 0; + int iovdd_gpio = cd->board_data.iovdd_gpio; + int avdd_gpio = cd->board_data.avdd_gpio; + int reset_gpio = cd->board_data.reset_gpio; + + if (on) { + if (iovdd_gpio > 0) { + gpio_direction_output(iovdd_gpio, 1); + } else if (cd->iovdd) { + ret = regulator_enable(cd->iovdd); + if (ret < 0) { + ts_err("Failed to enable iovdd:%d", ret); + goto power_off; + } + } + usleep_range(3000, 3100); + if (avdd_gpio > 0) { + gpio_direction_output(avdd_gpio, 1); + } else if (cd->avdd) { + ret = regulator_enable(cd->avdd); + if (ret < 0) { + ts_err("Failed to enable avdd:%d", ret); + goto power_off; + } + } + usleep_range(15000, 15100); + gpio_direction_output(reset_gpio, 1); + usleep_range(4000, 4100); + ret = brl_dev_confirm(cd); + if (ret < 0) + goto power_off; + ret = brl_reset_after(cd); + if (ret < 0) + goto power_off; + + msleep(GOODIX_NORMAL_RESET_DELAY_MS); + return 0; + } + +power_off: + gpio_direction_output(reset_gpio, 0); + if (iovdd_gpio > 0) + gpio_direction_output(iovdd_gpio, 0); + else if (cd->iovdd) + regulator_disable(cd->iovdd); + if (avdd_gpio > 0) + gpio_direction_output(avdd_gpio, 0); + else if (cd->avdd) + regulator_disable(cd->avdd); + return ret; +} + +#define GOODIX_SLEEP_CMD 0x84 +int brl_suspend(struct goodix_ts_core *cd) +{ + struct goodix_ts_cmd sleep_cmd; + + sleep_cmd.cmd = GOODIX_SLEEP_CMD; + sleep_cmd.len = 4; + if (cd->hw_ops->send_cmd(cd, &sleep_cmd)) + ts_err("failed send sleep cmd"); + + return 0; +} + +int brl_resume(struct goodix_ts_core *cd) +{ + return cd->hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); +} + +#define GOODIX_GESTURE_CMD 0x12 +int brl_gesture(struct goodix_ts_core *cd, int gesture_type) +{ + struct goodix_ts_cmd cmd; + + cmd.cmd = GOODIX_GESTURE_CMD; + cmd.len = 5; + cmd.data[0] = gesture_type; + if (cd->hw_ops->send_cmd(cd, &cmd)) + ts_err("failed send gesture cmd"); + + return 0; +} + +static int brl_reset(struct goodix_ts_core *cd, int delay) +{ + ts_info("chip_reset"); + + gpio_direction_output(cd->board_data.reset_gpio, 0); + usleep_range(2000, 2100); + gpio_direction_output(cd->board_data.reset_gpio, 1); + if (delay < 20) + usleep_range(delay * 1000, delay * 1000 + 100); + else + msleep(delay); + + return brl_select_spi_mode(cd); +} + +static int brl_irq_enbale(struct goodix_ts_core *cd, bool enable) +{ + if (enable && !atomic_cmpxchg(&cd->irq_enabled, 0, 1)) { + enable_irq(cd->irq); + ts_debug("Irq enabled"); + return 0; + } + + if (!enable && atomic_cmpxchg(&cd->irq_enabled, 1, 0)) { + disable_irq(cd->irq); + ts_debug("Irq disabled"); + return 0; + } + ts_info("warnning: irq deepth inbalance!"); + return 0; +} + +static int brl_read(struct goodix_ts_core *cd, unsigned int addr, + unsigned char *data, unsigned int len) +{ + struct goodix_bus_interface *bus = cd->bus; + + return bus->read(bus->dev, addr, data, len); +} + +static int brl_write(struct goodix_ts_core *cd, unsigned int addr, + unsigned char *data, unsigned int len) +{ + struct goodix_bus_interface *bus = cd->bus; + + return bus->write(bus->dev, addr, data, len); +} + +/* command ack info */ +#define CMD_ACK_IDLE 0x01 +#define CMD_ACK_BUSY 0x02 +#define CMD_ACK_BUFFER_OVERFLOW 0x03 +#define CMD_ACK_CHECKSUM_ERROR 0x04 +#define CMD_ACK_OK 0x80 + +#define GOODIX_CMD_RETRY 6 +static int brl_send_cmd(struct goodix_ts_core *cd, + struct goodix_ts_cmd *cmd) +{ + int ret, retry, i; + struct goodix_ts_cmd cmd_ack; + struct goodix_ic_info_misc *misc = &cd->ic_info.misc; + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + + cmd->state = 0; + cmd->ack = 0; + goodix_append_checksum(&(cmd->buf[2]), cmd->len - 2, + CHECKSUM_MODE_U8_LE); + ts_debug("cmd data %*ph", cmd->len, &(cmd->buf[2])); + + retry = 0; + while (retry++ < GOODIX_CMD_RETRY) { + ret = hw_ops->write(cd, misc->cmd_addr, + cmd->buf, sizeof(*cmd)); + if (ret < 0) { + ts_err("failed write command"); + return ret; + } + for (i = 0; i < GOODIX_CMD_RETRY; i++) { + /* check command result */ + ret = hw_ops->read(cd, misc->cmd_addr, + cmd_ack.buf, sizeof(cmd_ack)); + if (ret < 0) { + ts_err("failed read command ack, %d", ret); + return ret; + } + ts_debug("cmd ack data %*ph", + (int)sizeof(cmd_ack), cmd_ack.buf); + if (cmd_ack.ack == CMD_ACK_OK) { + usleep_range(2000, 2100); + return 0; + } + if (cmd_ack.ack == CMD_ACK_BUSY || + cmd_ack.ack == 0x00) { + usleep_range(1000, 1100); + continue; + } + if (cmd_ack.ack == CMD_ACK_BUFFER_OVERFLOW) + usleep_range(10000, 11000); + usleep_range(1000, 1100); + break; + } + } + ts_err("failed get valid cmd ack"); + return -EINVAL; +} + +#pragma pack(1) +struct goodix_config_head { + union { + struct { + u8 panel_name[8]; + u8 fw_pid[8]; + u8 fw_vid[4]; + u8 project_name[8]; + u8 file_ver[2]; + u32 cfg_id; + u8 cfg_ver; + u8 cfg_time[8]; + u8 reserved[15]; + u8 flag; + u16 cfg_len; + u8 cfg_num; + u16 checksum; + }; + u8 buf[64]; + }; +}; +#pragma pack() + +#define CONFIG_CND_LEN 4 +#define CONFIG_CMD_START 0x04 +#define CONFIG_CMD_WRITE 0x05 +#define CONFIG_CMD_EXIT 0x06 +#define CONFIG_CMD_READ_START 0x07 +#define CONFIG_CMD_READ_EXIT 0x08 + +#define CONFIG_CMD_STATUS_PASS 0x80 +#define CONFIG_CMD_WAIT_RETRY 20 + +static int wait_cmd_status(struct goodix_ts_core *cd, + u8 target_status, int retry) +{ + struct goodix_ts_cmd cmd_ack; + struct goodix_ic_info_misc *misc = &cd->ic_info.misc; + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + int i, ret; + + for (i = 0; i < retry; i++) { + ret = hw_ops->read(cd, misc->cmd_addr, cmd_ack.buf, + sizeof(cmd_ack)); + if (!ret && cmd_ack.state == target_status) { + ts_debug("status check pass"); + return 0; + } + ts_debug("cmd buf %*ph", (int)sizeof(cmd_ack), cmd_ack.buf); + msleep(20); + } + + ts_err("cmd status not ready, retry %d, ack 0x%x, status 0x%x, ret %d", + i, cmd_ack.ack, cmd_ack.state, ret); + return -EINVAL; +} + +static int send_cfg_cmd(struct goodix_ts_core *cd, + struct goodix_ts_cmd *cfg_cmd) +{ + int ret; + + ret = cd->hw_ops->send_cmd(cd, cfg_cmd); + if (ret) { + ts_err("failed write cfg prepare cmd %d", ret); + return ret; + } + ret = wait_cmd_status(cd, CONFIG_CMD_STATUS_PASS, + CONFIG_CMD_WAIT_RETRY); + if (ret) { + ts_err("failed wait for fw ready for config, %d", ret); + return ret; + } + return 0; +} + +static int brl_send_config(struct goodix_ts_core *cd, u8 *cfg, int len) +{ + int ret; + u8 *tmp_buf; + struct goodix_ts_cmd cfg_cmd; + struct goodix_ic_info_misc *misc = &cd->ic_info.misc; + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + + if (len > misc->fw_buffer_max_len) { + ts_err("config len exceed limit %d > %d", + len, misc->fw_buffer_max_len); + return -EINVAL; + } + + tmp_buf = kzalloc(len, GFP_KERNEL); + if (!tmp_buf) + return -ENOMEM; + + cfg_cmd.len = CONFIG_CND_LEN; + cfg_cmd.cmd = CONFIG_CMD_START; + ret = send_cfg_cmd(cd, &cfg_cmd); + if (ret) { + ts_err("failed write cfg prepare cmd %d", ret); + goto exit; + } + + ts_debug("try send config to 0x%x, len %d", misc->fw_buffer_addr, len); + ret = hw_ops->write(cd, misc->fw_buffer_addr, cfg, len); + if (ret) { + ts_err("failed write config data, %d", ret); + goto exit; + } + ret = hw_ops->read(cd, misc->fw_buffer_addr, tmp_buf, len); + if (ret) { + ts_err("failed read back config data"); + goto exit; + } + + if (memcmp(cfg, tmp_buf, len)) { + ts_err("config data read back compare file"); + ret = -EINVAL; + goto exit; + } + /* notify fw for recive config */ + memset(cfg_cmd.buf, 0, sizeof(cfg_cmd)); + cfg_cmd.len = CONFIG_CND_LEN; + cfg_cmd.cmd = CONFIG_CMD_WRITE; + ret = send_cfg_cmd(cd, &cfg_cmd); + if (ret) + ts_err("failed send config data ready cmd %d", ret); + +exit: + memset(cfg_cmd.buf, 0, sizeof(cfg_cmd)); + cfg_cmd.len = CONFIG_CND_LEN; + cfg_cmd.cmd = CONFIG_CMD_EXIT; + if (send_cfg_cmd(cd, &cfg_cmd)) { + ts_err("failed send config write end command"); + ret = -EINVAL; + } + + if (!ret) { + ts_info("success send config"); + msleep(100); + } + + kfree(tmp_buf); + return ret; +} + +/* + * return: return config length on succes, other wise return < 0 + **/ +static int brl_read_config(struct goodix_ts_core *cd, u8 *cfg, int size) +{ + int ret; + struct goodix_ts_cmd cfg_cmd; + struct goodix_ic_info_misc *misc = &cd->ic_info.misc; + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + struct goodix_config_head cfg_head; + + if (!cfg) + return -EINVAL; + + cfg_cmd.len = CONFIG_CND_LEN; + cfg_cmd.cmd = CONFIG_CMD_READ_START; + ret = send_cfg_cmd(cd, &cfg_cmd); + if (ret) { + ts_err("failed send config read prepare command"); + return ret; + } + + ret = hw_ops->read(cd, misc->fw_buffer_addr, + cfg_head.buf, sizeof(cfg_head)); + if (ret) { + ts_err("failed read config head %d", ret); + goto exit; + } + + if (checksum_cmp(cfg_head.buf, sizeof(cfg_head), CHECKSUM_MODE_U8_LE)) { + ts_err("config head checksum error"); + ret = -EINVAL; + goto exit; + } + + cfg_head.cfg_len = le16_to_cpu(cfg_head.cfg_len); + if (cfg_head.cfg_len > misc->fw_buffer_max_len || + cfg_head.cfg_len > size) { + ts_err("cfg len exceed buffer size %d > %d", cfg_head.cfg_len, + misc->fw_buffer_max_len); + ret = -EINVAL; + goto exit; + } + + memcpy(cfg, cfg_head.buf, sizeof(cfg_head)); + ret = hw_ops->read(cd, misc->fw_buffer_addr + sizeof(cfg_head), + cfg + sizeof(cfg_head), cfg_head.cfg_len); + if (ret) { + ts_err("failed read cfg pack, %d", ret); + goto exit; + } + + ts_info("config len %d", cfg_head.cfg_len); + if (checksum_cmp(cfg + sizeof(cfg_head), + cfg_head.cfg_len, CHECKSUM_MODE_U16_LE)) { + ts_err("config body checksum error"); + ret = -EINVAL; + goto exit; + } + ts_info("success read config data: len %zu", + cfg_head.cfg_len + sizeof(cfg_head)); +exit: + memset(cfg_cmd.buf, 0, sizeof(cfg_cmd)); + cfg_cmd.len = CONFIG_CND_LEN; + cfg_cmd.cmd = CONFIG_CMD_READ_EXIT; + if (send_cfg_cmd(cd, &cfg_cmd)) { + ts_err("failed send config read finish command"); + ret = -EINVAL; + } + if (ret) + return -EINVAL; + return cfg_head.cfg_len + sizeof(cfg_head); +} + +/* + * return: 0 for no error. + * GOODIX_EBUS when encounter a bus error + * GOODIX_ECHECKSUM version checksum error + * GOODIX_EVERSION patch ID compare failed, + * in this case the sensorID is valid. + */ +static int brl_read_version(struct goodix_ts_core *cd, + struct goodix_fw_version *version) +{ + int ret, i; + u32 fw_addr; + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + u8 buf[sizeof(struct goodix_fw_version)] = {0}; + u8 temp_pid[8] = {0}; + + if (cd->bus->ic_type == IC_TYPE_BERLIN_A) + fw_addr = FW_VERSION_INFO_ADDR_BRA; + else + fw_addr = FW_VERSION_INFO_ADDR; + + for (i = 0; i < 2; i++) { + ret = hw_ops->read(cd, fw_addr, buf, sizeof(buf)); + if (ret) { + ts_info("read fw version: %d, retry %d", ret, i); + ret = -GOODIX_EBUS; + usleep_range(5000, 5100); + continue; + } + + if (!checksum_cmp(buf, sizeof(buf), CHECKSUM_MODE_U8_LE)) + break; + + ts_info("invalid fw version: checksum error!"); + ts_info("fw version:%*ph", (int)sizeof(buf), buf); + ret = -GOODIX_ECHECKSUM; + usleep_range(10000, 11000); + } + if (ret) { + ts_err("failed get valied fw version"); + return ret; + } + memcpy(version, buf, sizeof(*version)); + memcpy(temp_pid, version->rom_pid, sizeof(version->rom_pid)); + ts_info("rom_pid:%s", temp_pid); + ts_info("rom_vid:%*ph", (int)sizeof(version->rom_vid), + version->rom_vid); + ts_info("pid:%s", version->patch_pid); + ts_info("vid:%*ph", (int)sizeof(version->patch_vid), + version->patch_vid); + ts_info("sensor_id:%d", version->sensor_id); + + return 0; +} + +#define LE16_TO_CPU(x) (x = le16_to_cpu(x)) +#define LE32_TO_CPU(x) (x = le32_to_cpu(x)) +static int convert_ic_info(struct goodix_ic_info *info, const u8 *data) +{ + int i; + struct goodix_ic_info_version *version = &info->version; + struct goodix_ic_info_feature *feature = &info->feature; + struct goodix_ic_info_param *parm = &info->parm; + struct goodix_ic_info_misc *misc = &info->misc; + + info->length = le16_to_cpup((__le16 *)data); + + data += 2; + memcpy(version, data, sizeof(*version)); + version->config_id = le32_to_cpu(version->config_id); + + data += sizeof(struct goodix_ic_info_version); + memcpy(feature, data, sizeof(*feature)); + feature->freqhop_feature = + le16_to_cpu(feature->freqhop_feature); + feature->calibration_feature = + le16_to_cpu(feature->calibration_feature); + feature->gesture_feature = + le16_to_cpu(feature->gesture_feature); + feature->side_touch_feature = + le16_to_cpu(feature->side_touch_feature); + feature->stylus_feature = + le16_to_cpu(feature->stylus_feature); + + data += sizeof(struct goodix_ic_info_feature); + parm->drv_num = *(data++); + parm->sen_num = *(data++); + parm->button_num = *(data++); + parm->force_num = *(data++); + parm->active_scan_rate_num = *(data++); + if (parm->active_scan_rate_num > MAX_SCAN_RATE_NUM) { + ts_err("invalid scan rate num %d > %d", + parm->active_scan_rate_num, MAX_SCAN_RATE_NUM); + return -EINVAL; + } + for (i = 0; i < parm->active_scan_rate_num; i++) + parm->active_scan_rate[i] = + le16_to_cpup((__le16 *)(data + i * 2)); + + data += parm->active_scan_rate_num * 2; + parm->mutual_freq_num = *(data++); + if (parm->mutual_freq_num > MAX_SCAN_FREQ_NUM) { + ts_err("invalid mntual freq num %d > %d", + parm->mutual_freq_num, MAX_SCAN_FREQ_NUM); + return -EINVAL; + } + for (i = 0; i < parm->mutual_freq_num; i++) + parm->mutual_freq[i] = + le16_to_cpup((__le16 *)(data + i * 2)); + + data += parm->mutual_freq_num * 2; + parm->self_tx_freq_num = *(data++); + if (parm->self_tx_freq_num > MAX_SCAN_FREQ_NUM) { + ts_err("invalid tx freq num %d > %d", + parm->self_tx_freq_num, MAX_SCAN_FREQ_NUM); + return -EINVAL; + } + for (i = 0; i < parm->self_tx_freq_num; i++) + parm->self_tx_freq[i] = + le16_to_cpup((__le16 *)(data + i * 2)); + + data += parm->self_tx_freq_num * 2; + parm->self_rx_freq_num = *(data++); + if (parm->self_rx_freq_num > MAX_SCAN_FREQ_NUM) { + ts_err("invalid rx freq num %d > %d", + parm->self_rx_freq_num, MAX_SCAN_FREQ_NUM); + return -EINVAL; + } + for (i = 0; i < parm->self_rx_freq_num; i++) + parm->self_rx_freq[i] = + le16_to_cpup((__le16 *)(data + i * 2)); + + data += parm->self_rx_freq_num * 2; + parm->stylus_freq_num = *(data++); + if (parm->stylus_freq_num > MAX_FREQ_NUM_STYLUS) { + ts_err("invalid stylus freq num %d > %d", + parm->stylus_freq_num, MAX_FREQ_NUM_STYLUS); + return -EINVAL; + } + for (i = 0; i < parm->stylus_freq_num; i++) + parm->stylus_freq[i] = + le16_to_cpup((__le16 *)(data + i * 2)); + + data += parm->stylus_freq_num * 2; + memcpy(misc, data, sizeof(*misc)); + misc->cmd_addr = le32_to_cpu(misc->cmd_addr); + misc->cmd_max_len = le16_to_cpu(misc->cmd_max_len); + misc->cmd_reply_addr = le32_to_cpu(misc->cmd_reply_addr); + misc->cmd_reply_len = le16_to_cpu(misc->cmd_reply_len); + misc->fw_state_addr = le32_to_cpu(misc->fw_state_addr); + misc->fw_state_len = le16_to_cpu(misc->fw_state_len); + misc->fw_buffer_addr = le32_to_cpu(misc->fw_buffer_addr); + misc->fw_buffer_max_len = le16_to_cpu(misc->fw_buffer_max_len); + misc->frame_data_addr = le32_to_cpu(misc->frame_data_addr); + misc->frame_data_head_len = le16_to_cpu(misc->frame_data_head_len); + + misc->fw_attr_len = le16_to_cpu(misc->fw_attr_len); + misc->fw_log_len = le16_to_cpu(misc->fw_log_len); + misc->stylus_struct_len = le16_to_cpu(misc->stylus_struct_len); + misc->mutual_struct_len = le16_to_cpu(misc->mutual_struct_len); + misc->self_struct_len = le16_to_cpu(misc->self_struct_len); + misc->noise_struct_len = le16_to_cpu(misc->noise_struct_len); + misc->touch_data_addr = le32_to_cpu(misc->touch_data_addr); + misc->touch_data_head_len = le16_to_cpu(misc->touch_data_head_len); + misc->point_struct_len = le16_to_cpu(misc->point_struct_len); + LE32_TO_CPU(misc->mutual_rawdata_addr); + LE32_TO_CPU(misc->mutual_diffdata_addr); + LE32_TO_CPU(misc->mutual_refdata_addr); + LE32_TO_CPU(misc->self_rawdata_addr); + LE32_TO_CPU(misc->self_diffdata_addr); + LE32_TO_CPU(misc->self_refdata_addr); + LE32_TO_CPU(misc->iq_rawdata_addr); + LE32_TO_CPU(misc->iq_refdata_addr); + LE32_TO_CPU(misc->im_rawdata_addr); + LE16_TO_CPU(misc->im_readata_len); + LE32_TO_CPU(misc->noise_rawdata_addr); + LE16_TO_CPU(misc->noise_rawdata_len); + LE32_TO_CPU(misc->stylus_rawdata_addr); + LE16_TO_CPU(misc->stylus_rawdata_len); + LE32_TO_CPU(misc->noise_data_addr); + LE32_TO_CPU(misc->esd_addr); + + return 0; +} + +static void print_ic_info(struct goodix_ic_info *ic_info) +{ + struct goodix_ic_info_version *version = &ic_info->version; + struct goodix_ic_info_feature *feature = &ic_info->feature; + struct goodix_ic_info_param *parm = &ic_info->parm; + struct goodix_ic_info_misc *misc = &ic_info->misc; + + ts_info("ic_info_length: %d", + ic_info->length); + ts_info("info_customer_id: 0x%01X", + version->info_customer_id); + ts_info("info_version_id: 0x%01X", + version->info_version_id); + ts_info("ic_die_id: 0x%01X", + version->ic_die_id); + ts_info("ic_version_id: 0x%01X", + version->ic_version_id); + ts_info("config_id: 0x%4X", + version->config_id); + ts_info("config_version: 0x%01X", + version->config_version); + ts_info("frame_data_customer_id: 0x%01X", + version->frame_data_customer_id); + ts_info("frame_data_version_id: 0x%01X", + version->frame_data_version_id); + ts_info("touch_data_customer_id: 0x%01X", + version->touch_data_customer_id); + ts_info("touch_data_version_id: 0x%01X", + version->touch_data_version_id); + + ts_info("freqhop_feature: 0x%04X", + feature->freqhop_feature); + ts_info("calibration_feature: 0x%04X", + feature->calibration_feature); + ts_info("gesture_feature: 0x%04X", + feature->gesture_feature); + ts_info("side_touch_feature: 0x%04X", + feature->side_touch_feature); + ts_info("stylus_feature: 0x%04X", + feature->stylus_feature); + + ts_info("Drv*Sen,Button,Force num: %d x %d, %d, %d", + parm->drv_num, parm->sen_num, + parm->button_num, parm->force_num); + + ts_info("Cmd: 0x%04X, %d", + misc->cmd_addr, misc->cmd_max_len); + ts_info("Cmd-Reply: 0x%04X, %d", + misc->cmd_reply_addr, misc->cmd_reply_len); + ts_info("FW-State: 0x%04X, %d", + misc->fw_state_addr, misc->fw_state_len); + ts_info("FW-Buffer: 0x%04X, %d", + misc->fw_buffer_addr, misc->fw_buffer_max_len); + ts_info("Touch-Data: 0x%04X, %d", + misc->touch_data_addr, misc->touch_data_head_len); + ts_info("point_struct_len: %d", + misc->point_struct_len); + ts_info("mutual_rawdata_addr: 0x%04X", + misc->mutual_rawdata_addr); + ts_info("mutual_diffdata_addr: 0x%04X", + misc->mutual_diffdata_addr); + ts_info("self_rawdata_addr: 0x%04X", + misc->self_rawdata_addr); + ts_info("self_diffdata_addr: 0x%04X", + misc->self_diffdata_addr); + ts_info("stylus_rawdata_addr: 0x%04X, %d", + misc->stylus_rawdata_addr, misc->stylus_rawdata_len); + ts_info("esd_addr: 0x%04X", + misc->esd_addr); +} + +static int brl_get_ic_info(struct goodix_ts_core *cd, + struct goodix_ic_info *ic_info) +{ + int ret, i; + u16 length = 0; + u32 ic_addr; + u8 afe_data[GOODIX_IC_INFO_MAX_LEN] = {0}; + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + + if (cd->bus->ic_type == IC_TYPE_BERLIN_A) + ic_addr = GOODIX_IC_INFO_ADDR_BRA; + else + ic_addr = GOODIX_IC_INFO_ADDR; + + for (i = 0; i < GOODIX_RETRY_3; i++) { + ret = hw_ops->read(cd, ic_addr, + (u8 *)&length, sizeof(length)); + if (ret) { + ts_info("failed get ic info length, %d", ret); + usleep_range(5000, 5100); + continue; + } + length = le16_to_cpu(length); + if (length >= GOODIX_IC_INFO_MAX_LEN) { + ts_info("invalid ic info length %d, retry %d", + length, i); + continue; + } + + ret = hw_ops->read(cd, ic_addr, afe_data, length); + if (ret) { + ts_info("failed get ic info data, %d", ret); + usleep_range(5000, 5100); + continue; + } + /* judge whether the data is valid */ + if (is_risk_data((const uint8_t *)afe_data, length)) { + ts_info("fw info data invalid"); + usleep_range(5000, 5100); + continue; + } + if (checksum_cmp((const uint8_t *)afe_data, + length, CHECKSUM_MODE_U8_LE)) { + ts_info("fw info checksum error!"); + usleep_range(5000, 5100); + continue; + } + break; + } + if (i == GOODIX_RETRY_3) { + ts_err("failed get ic info"); + return -EINVAL; + } + + ret = convert_ic_info(ic_info, afe_data); + if (ret) { + ts_err("convert ic info encounter error"); + return ret; + } + + print_ic_info(ic_info); + + /* check some key info */ + if (!ic_info->misc.cmd_addr || !ic_info->misc.fw_buffer_addr || + !ic_info->misc.touch_data_addr) { + ts_err("cmd_addr fw_buf_addr and touch_data_addr is null"); + return -EINVAL; + } + + return 0; +} + +#define GOODIX_ESD_TICK_WRITE_DATA 0xAA +static int brl_esd_check(struct goodix_ts_core *cd) +{ + int ret; + u32 esd_addr; + u8 esd_value; + + if (!cd->ic_info.misc.esd_addr) + return 0; + + esd_addr = cd->ic_info.misc.esd_addr; + ret = cd->hw_ops->read(cd, esd_addr, &esd_value, 1); + if (ret) { + ts_err("failed get esd value, %d", ret); + return ret; + } + + if (esd_value == GOODIX_ESD_TICK_WRITE_DATA) { + ts_err("esd check failed, 0x%x", esd_value); + return -EINVAL; + } + esd_value = GOODIX_ESD_TICK_WRITE_DATA; + ret = cd->hw_ops->write(cd, esd_addr, &esd_value, 1); + if (ret) { + ts_err("failed refrash esd value"); + return ret; + } + return 0; +} + +#define IRQ_EVENT_HEAD_LEN 8 +#define BYTES_PER_POINT 8 +#define COOR_DATA_CHECKSUM_SIZE 2 + +#define GOODIX_TOUCH_EVENT 0x80 +#define GOODIX_REQUEST_EVENT 0x40 +#define GOODIX_GESTURE_EVENT 0x20 +#define POINT_TYPE_STYLUS_HOVER 0x01 +#define POINT_TYPE_STYLUS 0x03 + +static void goodix_parse_finger(struct goodix_touch_data *touch_data, + u8 *buf, int touch_num) +{ + unsigned int id = 0, x = 0, y = 0, w = 0; + u8 *coor_data; + int i; + + coor_data = &buf[IRQ_EVENT_HEAD_LEN]; + for (i = 0; i < touch_num; i++) { + id = (coor_data[0] >> 4) & 0x0F; + if (id >= GOODIX_MAX_TOUCH) { + ts_info("invalid finger id =%d", id); + touch_data->touch_num = 0; + return; + } + x = le16_to_cpup((__le16 *)(coor_data + 2)); + y = le16_to_cpup((__le16 *)(coor_data + 4)); + w = le16_to_cpup((__le16 *)(coor_data + 6)); + touch_data->coords[id].status = TS_TOUCH; + touch_data->coords[id].x = x; + touch_data->coords[id].y = y; + touch_data->coords[id].w = w; + coor_data += BYTES_PER_POINT; + } + touch_data->touch_num = touch_num; +} + +static unsigned int goodix_pen_btn_code[] = {BTN_STYLUS, BTN_STYLUS2}; +static void goodix_parse_pen(struct goodix_pen_data *pen_data, + u8 *buf, int touch_num) +{ + unsigned int id = 0; + u8 cur_key_map = 0; + u8 *coor_data; + int16_t x_angle, y_angle; + int i; + + pen_data->coords.tool_type = BTN_TOOL_PEN; + + if (touch_num) { + pen_data->coords.status = TS_TOUCH; + coor_data = &buf[IRQ_EVENT_HEAD_LEN]; + + id = (coor_data[0] >> 4) & 0x0F; + pen_data->coords.x = le16_to_cpup((__le16 *)(coor_data + 2)); + pen_data->coords.y = le16_to_cpup((__le16 *)(coor_data + 4)); + pen_data->coords.p = le16_to_cpup((__le16 *)(coor_data + 6)); + x_angle = le16_to_cpup((__le16 *)(coor_data + 8)); + y_angle = le16_to_cpup((__le16 *)(coor_data + 10)); + pen_data->coords.tilt_x = x_angle / 100; + pen_data->coords.tilt_y = y_angle / 100; + } else { + pen_data->coords.status = TS_RELEASE; + } + + cur_key_map = (buf[3] & 0x0F) >> 1; + for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) { + pen_data->keys[i].code = goodix_pen_btn_code[i]; + if (!(cur_key_map & (1 << i))) + continue; + pen_data->keys[i].status = TS_TOUCH; + } +} + +static int goodix_touch_handler(struct goodix_ts_core *cd, + struct goodix_ts_event *ts_event, + u8 *pre_buf, u32 pre_buf_len) +{ + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + struct goodix_ic_info_misc *misc = &cd->ic_info.misc; + struct goodix_touch_data *touch_data = &ts_event->touch_data; + struct goodix_pen_data *pen_data = &ts_event->pen_data; + static u8 buffer[IRQ_EVENT_HEAD_LEN + + BYTES_PER_POINT * GOODIX_MAX_TOUCH + 2]; + u8 touch_num = 0; + int ret = 0; + u8 point_type = 0; + static u8 pre_finger_num; + static u8 pre_pen_num; + + /* clean event buffer */ + memset(ts_event, 0, sizeof(*ts_event)); + /* copy pre-data to buffer */ + memcpy(buffer, pre_buf, pre_buf_len); + + touch_num = buffer[2] & 0x0F; + + if (touch_num > GOODIX_MAX_TOUCH) { + ts_debug("invalid touch num %d", touch_num); + return -EINVAL; + } + + if (unlikely(touch_num > 2)) { + ret = hw_ops->read(cd, + misc->touch_data_addr + pre_buf_len, + &buffer[pre_buf_len], + (touch_num - 2) * BYTES_PER_POINT); + if (ret) { + ts_debug("failed get touch data"); + return ret; + } + } + + if (touch_num > 0) { + point_type = buffer[IRQ_EVENT_HEAD_LEN] & 0x0F; + if (point_type == POINT_TYPE_STYLUS || + point_type == POINT_TYPE_STYLUS_HOVER) { + ret = checksum_cmp(&buffer[IRQ_EVENT_HEAD_LEN], + BYTES_PER_POINT * 2 + 2, CHECKSUM_MODE_U8_LE); + if (ret) { + ts_debug("touch data checksum error"); + ts_debug("data:%*ph", BYTES_PER_POINT * 2 + 2, + &buffer[IRQ_EVENT_HEAD_LEN]); + return -EINVAL; + } + } else { + ret = checksum_cmp(&buffer[IRQ_EVENT_HEAD_LEN], + touch_num * BYTES_PER_POINT + 2, CHECKSUM_MODE_U8_LE); + if (ret) { + ts_debug("touch data checksum error"); + ts_debug("data:%*ph", touch_num * BYTES_PER_POINT + 2, + &buffer[IRQ_EVENT_HEAD_LEN]); + return -EINVAL; + } + } + } + + if (touch_num > 0 && (point_type == POINT_TYPE_STYLUS + || point_type == POINT_TYPE_STYLUS_HOVER)) { + /* stylus info */ + if (pre_finger_num) { + ts_event->event_type = EVENT_TOUCH; + goodix_parse_finger(touch_data, buffer, 0); + pre_finger_num = 0; + } else { + pre_pen_num = 1; + ts_event->event_type = EVENT_PEN; + goodix_parse_pen(pen_data, buffer, touch_num); + } + } else { + /* finger info */ + if (pre_pen_num) { + ts_event->event_type = EVENT_PEN; + goodix_parse_pen(pen_data, buffer, 0); + pre_pen_num = 0; + } else { + ts_event->event_type = EVENT_TOUCH; + goodix_parse_finger(touch_data, + buffer, touch_num); + pre_finger_num = touch_num; + } + } + + /* process custom info */ + if (buffer[3] & 0x01) + ts_debug("TODO add custom info process function"); + + return 0; +} + +static int brl_event_handler(struct goodix_ts_core *cd, + struct goodix_ts_event *ts_event) +{ + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + struct goodix_ic_info_misc *misc = &cd->ic_info.misc; + int pre_read_len; + u8 pre_buf[32]; + u8 event_status; + int ret; + + pre_read_len = IRQ_EVENT_HEAD_LEN + + BYTES_PER_POINT * 2 + COOR_DATA_CHECKSUM_SIZE; + ret = hw_ops->read(cd, misc->touch_data_addr, + pre_buf, pre_read_len); + if (ret) { + ts_debug("failed get event head data"); + return ret; + } + + if (checksum_cmp(pre_buf, IRQ_EVENT_HEAD_LEN, CHECKSUM_MODE_U8_LE)) { + ts_debug("touch head checksum err"); + ts_debug("touch_head %*ph", IRQ_EVENT_HEAD_LEN, pre_buf); + ts_event->retry = 1; + return -EINVAL; + } + + event_status = pre_buf[0]; + if (event_status & GOODIX_TOUCH_EVENT) + return goodix_touch_handler(cd, ts_event, + pre_buf, pre_read_len); + + if (event_status & GOODIX_REQUEST_EVENT) { + ts_event->event_type = EVENT_REQUEST; + if (pre_buf[2] == BRL_REQUEST_CODE_CONFIG) + ts_event->request_code = REQUEST_TYPE_CONFIG; + else if (pre_buf[2] == BRL_REQUEST_CODE_RESET) + ts_event->request_code = REQUEST_TYPE_RESET; + else + ts_debug("unsupported request code 0x%x", pre_buf[2]); + } + if (event_status & GOODIX_GESTURE_EVENT) { + ts_event->event_type = EVENT_GESTURE; + ts_event->gesture_type = pre_buf[4]; + } + return 0; +} + +static int brl_after_event_handler(struct goodix_ts_core *cd) +{ + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + struct goodix_ic_info_misc *misc = &cd->ic_info.misc; + u8 sync_clean = 0; + + return hw_ops->write(cd, misc->touch_data_addr, + &sync_clean, 1); +} + +static int brld_get_framedata(struct goodix_ts_core *cd, + struct ts_rawdata_info *info) +{ + int ret; + unsigned char val; + int retry = 20; + struct frame_head *frame_head; + unsigned char frame_buf[GOODIX_MAX_FRAMEDATA_LEN]; + unsigned char *cur_ptr; + unsigned int flag_addr = cd->ic_info.misc.frame_data_addr; + + /* clean touch event flag */ + val = 0; + ret = brl_write(cd, flag_addr, &val, 1); + if (ret < 0) { + ts_err("clean touch event failed, exit!"); + return ret; + } + + while (retry--) { + usleep_range(2000, 2100); + ret = brl_read(cd, flag_addr, &val, 1); + if (!ret && (val & GOODIX_TOUCH_EVENT)) + break; + } + if (retry < 0) { + ts_err("framedata is not ready val:0x%02x, exit!", val); + return -EINVAL; + } + + ret = brl_read(cd, flag_addr, frame_buf, GOODIX_MAX_FRAMEDATA_LEN); + if (ret < 0) { + ts_err("read frame data failed"); + return ret; + } + + if (checksum_cmp(frame_buf, cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) { + ts_err("frame head checksum error"); + return -EINVAL; + } + + frame_head = (struct frame_head *)frame_buf; + if (checksum_cmp(frame_buf, frame_head->cur_frame_len, CHECKSUM_MODE_U16_LE)) { + ts_err("frame body checksum error"); + return -EINVAL; + } + cur_ptr = frame_buf; + cur_ptr += cd->ic_info.misc.frame_data_head_len; + cur_ptr += cd->ic_info.misc.fw_attr_len; + cur_ptr += cd->ic_info.misc.fw_log_len; + memcpy((u8 *)(info->buff + info->used_size), cur_ptr + 8, + cd->ic_info.misc.mutual_struct_len - 8); + + return 0; +} + +static int brld_get_cap_data(struct goodix_ts_core *cd, + struct ts_rawdata_info *info) +{ + struct goodix_ts_cmd temp_cmd; + int tx = cd->ic_info.parm.drv_num; + int rx = cd->ic_info.parm.sen_num; + int size = tx * rx; + int ret; + + /* disable irq & close esd */ + brl_irq_enbale(cd, false); + goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); + + info->buff[0] = rx; + info->buff[1] = tx; + info->used_size = 2; + + temp_cmd.cmd = 0x90; + temp_cmd.data[0] = 0x81; + temp_cmd.len = 5; + ret = brl_send_cmd(cd, &temp_cmd); + if (ret < 0) { + ts_err("report rawdata failed, exit!"); + goto exit; + } + + ret = brld_get_framedata(cd, info); + if (ret < 0) { + ts_err("brld get rawdata failed"); + goto exit; + } + goodix_rotate_abcd2cbad(tx, rx, &info->buff[info->used_size]); + info->used_size += size; + + temp_cmd.cmd = 0x90; + temp_cmd.data[0] = 0x82; + temp_cmd.len = 5; + ret = brl_send_cmd(cd, &temp_cmd); + if (ret < 0) { + ts_err("report diffdata failed, exit!"); + goto exit; + } + + ret = brld_get_framedata(cd, info); + if (ret < 0) { + ts_err("brld get diffdata failed"); + goto exit; + } + goodix_rotate_abcd2cbad(tx, rx, &info->buff[info->used_size]); + info->used_size += size; + +exit: + temp_cmd.cmd = 0x90; + temp_cmd.data[0] = 0; + temp_cmd.len = 5; + brl_send_cmd(cd, &temp_cmd); + /* enable irq & esd */ + brl_irq_enbale(cd, true); + goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); + return ret; +} + +#define GOODIX_CMD_RAWDATA 2 +#define GOODIX_CMD_COORD 0 +static int brl_get_capacitance_data(struct goodix_ts_core *cd, + struct ts_rawdata_info *info) +{ + int ret; + int retry = 20; + struct goodix_ts_cmd temp_cmd; + u32 flag_addr = cd->ic_info.misc.touch_data_addr; + u32 raw_addr = cd->ic_info.misc.mutual_rawdata_addr; + u32 diff_addr = cd->ic_info.misc.mutual_diffdata_addr; + int tx = cd->ic_info.parm.drv_num; + int rx = cd->ic_info.parm.sen_num; + int size = tx * rx; + u8 val; + + if (!info) { + ts_err("input null ptr"); + return -EIO; + } + + if (cd->bus->ic_type == IC_TYPE_BERLIN_D) + return brld_get_cap_data(cd, info); + + /* disable irq & close esd */ + brl_irq_enbale(cd, false); + goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); + + /* switch rawdata mode */ + temp_cmd.cmd = GOODIX_CMD_RAWDATA; + temp_cmd.len = 4; + ret = brl_send_cmd(cd, &temp_cmd); + if (ret < 0) { + ts_err("switch rawdata mode failed, exit!"); + goto exit; + } + + /* clean touch event flag */ + val = 0; + ret = brl_write(cd, flag_addr, &val, 1); + if (ret < 0) { + ts_err("clean touch event failed, exit!"); + goto exit; + } + + while (retry--) { + usleep_range(5000, 5100); + ret = brl_read(cd, flag_addr, &val, 1); + if (!ret && (val & GOODIX_TOUCH_EVENT)) + break; + } + if (retry < 0) { + ts_err("rawdata is not ready val:0x%02x, exit!", val); + goto exit; + } + + /* obtain rawdata & diff_rawdata */ + info->buff[0] = rx; + info->buff[1] = tx; + info->used_size = 2; + + ret = brl_read(cd, raw_addr, (u8 *)&info->buff[info->used_size], + size * sizeof(s16)); + if (ret < 0) { + ts_err("obtian raw_data failed, exit!"); + goto exit; + } + goodix_rotate_abcd2cbad(tx, rx, &info->buff[info->used_size]); + info->used_size += size; + + ret = brl_read(cd, diff_addr, (u8 *)&info->buff[info->used_size], + size * sizeof(s16)); + if (ret < 0) { + ts_err("obtian diff_data failed, exit!"); + goto exit; + } + goodix_rotate_abcd2cbad(tx, rx, &info->buff[info->used_size]); + info->used_size += size; + +exit: + /* switch coor mode */ + temp_cmd.cmd = GOODIX_CMD_COORD; + temp_cmd.len = 4; + brl_send_cmd(cd, &temp_cmd); + /* clean touch event flag */ + val = 0; + brl_write(cd, flag_addr, &val, 1); + /* enable irq & esd */ + brl_irq_enbale(cd, true); + goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); + return ret; +} + +static struct goodix_ts_hw_ops brl_hw_ops = { + .power_on = brl_power_on, + .resume = brl_resume, + .suspend = brl_suspend, + .gesture = brl_gesture, + .reset = brl_reset, + .irq_enable = brl_irq_enbale, + .read = brl_read, + .write = brl_write, + .send_cmd = brl_send_cmd, + .send_config = brl_send_config, + .read_config = brl_read_config, + .read_version = brl_read_version, + .get_ic_info = brl_get_ic_info, + .esd_check = brl_esd_check, + .event_handler = brl_event_handler, + .after_event_handler = brl_after_event_handler, + .get_capacitance_data = brl_get_capacitance_data, +}; + +struct goodix_ts_hw_ops *goodix_get_hw_ops(void) +{ + return &brl_hw_ops; +} diff --git a/goodix_brl_i2c.c b/goodix_brl_i2c.c new file mode 100644 index 0000000000..a0238800ca --- /dev/null +++ b/goodix_brl_i2c.c @@ -0,0 +1,264 @@ + /* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ +#include +#include +#include + +#include "goodix_ts_core.h" + +#define TS_DRIVER_NAME "gtx8_i2c" +#define I2C_MAX_TRANSFER_SIZE 256 +#define GOODIX_BUS_RETRY_TIMES 2 +#define GOODIX_REG_ADDR_SIZE 4 + +static struct platform_device *goodix_pdev; +struct goodix_bus_interface goodix_i2c_bus; + +static int goodix_i2c_read(struct device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned int transfer_length = 0; + unsigned int pos = 0, address = reg; + unsigned char get_buf[128], addr_buf[GOODIX_REG_ADDR_SIZE]; + int retry, r = 0; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = !I2C_M_RD, + .buf = &addr_buf[0], + .len = GOODIX_REG_ADDR_SIZE, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + } + }; + + if (likely(len < sizeof(get_buf))) { + /* code optimize, use stack memory */ + msgs[1].buf = &get_buf[0]; + } else { + msgs[1].buf = kzalloc(len, GFP_KERNEL); + if (msgs[1].buf == NULL) + return -ENOMEM; + } + + while (pos != len) { + if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE)) + transfer_length = I2C_MAX_TRANSFER_SIZE; + else + transfer_length = len - pos; + + msgs[0].buf[0] = (address >> 24) & 0xFF; + msgs[0].buf[1] = (address >> 16) & 0xFF; + msgs[0].buf[2] = (address >> 8) & 0xFF; + msgs[0].buf[3] = address & 0xFF; + msgs[1].len = transfer_length; + + for (retry = 0; retry < GOODIX_BUS_RETRY_TIMES; retry++) { + if (likely(i2c_transfer(client->adapter, + msgs, 2) == 2)) { + memcpy(&data[pos], msgs[1].buf, + transfer_length); + pos += transfer_length; + address += transfer_length; + break; + } + ts_info("I2c read retry[%d]:0x%x", retry + 1, reg); + usleep_range(2000, 2100); + } + if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) { + ts_err("I2c read failed,dev:%02x,reg:%04x,size:%u", + client->addr, reg, len); + r = -EAGAIN; + goto read_exit; + } + } + +read_exit: + if (unlikely(len >= sizeof(get_buf))) + kfree(msgs[1].buf); + return r; +} + +static int goodix_i2c_write(struct device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned int pos = 0, transfer_length = 0; + unsigned int address = reg; + unsigned char put_buf[128]; + int retry, r = 0; + struct i2c_msg msg = { + .addr = client->addr, + .flags = !I2C_M_RD, + }; + + if (likely(len + GOODIX_REG_ADDR_SIZE < sizeof(put_buf))) { + /* code optimize,use stack memory*/ + msg.buf = &put_buf[0]; + } else { + msg.buf = kmalloc(len + GOODIX_REG_ADDR_SIZE, GFP_KERNEL); + if (msg.buf == NULL) + return -ENOMEM; + } + + while (pos != len) { + if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE - + GOODIX_REG_ADDR_SIZE)) + transfer_length = I2C_MAX_TRANSFER_SIZE - + GOODIX_REG_ADDR_SIZE; + else + transfer_length = len - pos; + msg.buf[0] = (address >> 24) & 0xFF; + msg.buf[1] = (address >> 16) & 0xFF; + msg.buf[2] = (address >> 8) & 0xFF; + msg.buf[3] = address & 0xFF; + + msg.len = transfer_length + GOODIX_REG_ADDR_SIZE; + memcpy(&msg.buf[GOODIX_REG_ADDR_SIZE], + &data[pos], transfer_length); + + for (retry = 0; retry < GOODIX_BUS_RETRY_TIMES; retry++) { + if (likely(i2c_transfer(client->adapter, + &msg, 1) == 1)) { + pos += transfer_length; + address += transfer_length; + break; + } + ts_debug("I2c write retry[%d]", retry + 1); + msleep(20); + } + if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) { + ts_err("I2c write failed,dev:%02x,reg:%04x,size:%u", + client->addr, reg, len); + r = -EAGAIN; + goto write_exit; + } + } + +write_exit: + if (likely(len + GOODIX_REG_ADDR_SIZE >= sizeof(put_buf))) + kfree(msg.buf); + return r; +} + +static void goodix_pdev_release(struct device *dev) +{ + ts_info("goodix pdev released"); + kfree(goodix_pdev); +} + +static int goodix_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int ret = 0; + + ts_info("goodix i2c probe in"); + ret = i2c_check_functionality(client->adapter, + I2C_FUNC_I2C); + if (!ret) + return -EIO; + + /* get ic type */ + ret = goodix_get_ic_type(client->dev.of_node); + if (ret < 0) + return ret; + + goodix_i2c_bus.ic_type = ret; + goodix_i2c_bus.bus_type = GOODIX_BUS_TYPE_I2C; + goodix_i2c_bus.dev = &client->dev; + goodix_i2c_bus.read = goodix_i2c_read; + goodix_i2c_bus.write = goodix_i2c_write; + /* ts core device */ + goodix_pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); + if (!goodix_pdev) + return -ENOMEM; + + goodix_pdev->name = GOODIX_CORE_DRIVER_NAME; + goodix_pdev->id = 0; + goodix_pdev->num_resources = 0; + /* + * you can find this platform dev in + * /sys/devices/platform/goodix_ts.0 + * goodix_pdev->dev.parent = &client->dev; + */ + goodix_pdev->dev.platform_data = &goodix_i2c_bus; + goodix_pdev->dev.release = goodix_pdev_release; + + /* register platform device, then the goodix_ts_core + * module will probe the touch device. + */ + ret = platform_device_register(goodix_pdev); + if (ret) { + ts_err("failed register goodix platform device, %d", ret); + goto err_pdev; + } + ts_info("i2c probe out"); + return ret; + +err_pdev: + kfree(goodix_pdev); + goodix_pdev = NULL; + ts_info("i2c probe out, %d", ret); + return ret; +} + +static int goodix_i2c_remove(struct i2c_client *client) +{ + platform_device_unregister(goodix_pdev); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id i2c_matchs[] = { + {.compatible = "goodix,gt9897",}, + {.compatible = "goodix,gt9966",}, + {.compatible = "goodix,gt9916",}, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_matchs); +#endif + +static const struct i2c_device_id i2c_id_table[] = { + {TS_DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, i2c_id_table); + +static struct i2c_driver goodix_i2c_driver = { + .driver = { + .name = TS_DRIVER_NAME, + //.owner = THIS_MODULE, + .of_match_table = of_match_ptr(i2c_matchs), + }, + .probe = goodix_i2c_probe, + .remove = goodix_i2c_remove, + .id_table = i2c_id_table, +}; + +int goodix_i2c_bus_init(void) +{ + ts_info("Goodix i2c driver init"); + return i2c_add_driver(&goodix_i2c_driver); +} + +void goodix_i2c_bus_exit(void) +{ + ts_info("Goodix i2c driver exit"); + i2c_del_driver(&goodix_i2c_driver); +} diff --git a/goodix_brl_spi.c b/goodix_brl_spi.c new file mode 100644 index 0000000000..f84961d814 --- /dev/null +++ b/goodix_brl_spi.c @@ -0,0 +1,299 @@ + /* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ +#include +#include +#include + +#include "goodix_ts_core.h" +#define TS_DRIVER_NAME "gtx8_spi" + +#define SPI_TRANS_PREFIX_LEN 1 +#define REGISTER_WIDTH 4 +#define SPI_READ_DUMMY_LEN 4 +#define SPI_READ_PREFIX_LEN (SPI_TRANS_PREFIX_LEN + REGISTER_WIDTH + SPI_READ_DUMMY_LEN) +#define SPI_WRITE_PREFIX_LEN (SPI_TRANS_PREFIX_LEN + REGISTER_WIDTH) + +#define SPI_WRITE_FLAG 0xF0 +#define SPI_READ_FLAG 0xF1 + +static struct platform_device *goodix_pdev; +struct goodix_bus_interface goodix_spi_bus; + +/** + * goodix_spi_read_bra- read device register through spi bus + * @dev: pointer to device data + * @addr: register address + * @data: read buffer + * @len: bytes to read + * return: 0 - read ok, < 0 - spi transter error + */ +static int goodix_spi_read_bra(struct device *dev, unsigned int addr, + unsigned char *data, unsigned int len) +{ + struct spi_device *spi = to_spi_device(dev); + u8 *rx_buf = NULL; + u8 *tx_buf = NULL; + struct spi_transfer xfers; + struct spi_message spi_msg; + int ret = 0; + + rx_buf = kzalloc(SPI_READ_PREFIX_LEN + len, GFP_KERNEL); + tx_buf = kzalloc(SPI_READ_PREFIX_LEN + len, GFP_KERNEL); + if (!rx_buf || !tx_buf) { + ts_err("alloc tx/rx_buf failed, size:%d", + SPI_READ_PREFIX_LEN + len); + return -ENOMEM; + } + + spi_message_init(&spi_msg); + memset(&xfers, 0, sizeof(xfers)); + + /*spi_read tx_buf format: 0xF1 + addr(4bytes) + data*/ + tx_buf[0] = SPI_READ_FLAG; + tx_buf[1] = (addr >> 24) & 0xFF; + tx_buf[2] = (addr >> 16) & 0xFF; + tx_buf[3] = (addr >> 8) & 0xFF; + tx_buf[4] = addr & 0xFF; + tx_buf[5] = 0xFF; + tx_buf[6] = 0xFF; + tx_buf[7] = 0xFF; + tx_buf[8] = 0xFF; + + xfers.tx_buf = tx_buf; + xfers.rx_buf = rx_buf; + xfers.len = SPI_READ_PREFIX_LEN + len; + xfers.cs_change = 0; + spi_message_add_tail(&xfers, &spi_msg); + ret = spi_sync(spi, &spi_msg); + if (ret < 0) { + ts_err("spi transfer error:%d",ret); + goto exit; + } + memcpy(data, &rx_buf[SPI_READ_PREFIX_LEN], len); + +exit: + kfree(rx_buf); + kfree(tx_buf); + return ret; +} + +static int goodix_spi_read(struct device *dev, unsigned int addr, + unsigned char *data, unsigned int len) +{ + struct spi_device *spi = to_spi_device(dev); + u8 *rx_buf = NULL; + u8 *tx_buf = NULL; + struct spi_transfer xfers; + struct spi_message spi_msg; + int ret = 0; + + rx_buf = kzalloc(SPI_READ_PREFIX_LEN - 1 + len, GFP_KERNEL); + tx_buf = kzalloc(SPI_READ_PREFIX_LEN - 1 + len, GFP_KERNEL); + if (!rx_buf || !tx_buf) { + ts_err("alloc tx/rx_buf failed, size:%d", + SPI_READ_PREFIX_LEN - 1 + len); + return -ENOMEM; + } + + spi_message_init(&spi_msg); + memset(&xfers, 0, sizeof(xfers)); + + /*spi_read tx_buf format: 0xF1 + addr(4bytes) + data*/ + tx_buf[0] = SPI_READ_FLAG; + tx_buf[1] = (addr >> 24) & 0xFF; + tx_buf[2] = (addr >> 16) & 0xFF; + tx_buf[3] = (addr >> 8) & 0xFF; + tx_buf[4] = addr & 0xFF; + tx_buf[5] = 0xFF; + tx_buf[6] = 0xFF; + tx_buf[7] = 0xFF; + + xfers.tx_buf = tx_buf; + xfers.rx_buf = rx_buf; + xfers.len = SPI_READ_PREFIX_LEN - 1 + len; + xfers.cs_change = 0; + spi_message_add_tail(&xfers, &spi_msg); + ret = spi_sync(spi, &spi_msg); + if (ret < 0) { + ts_err("spi transfer error:%d",ret); + goto exit; + } + memcpy(data, &rx_buf[SPI_READ_PREFIX_LEN - 1], len); + +exit: + kfree(rx_buf); + kfree(tx_buf); + return ret; +} + +/** + * goodix_spi_write- write device register through spi bus + * @dev: pointer to device data + * @addr: register address + * @data: write buffer + * @len: bytes to write + * return: 0 - write ok; < 0 - spi transter error. + */ +static int goodix_spi_write(struct device *dev, unsigned int addr, + unsigned char *data, unsigned int len) +{ + struct spi_device *spi = to_spi_device(dev); + u8 *tx_buf = NULL; + struct spi_transfer xfers; + struct spi_message spi_msg; + int ret = 0; + + tx_buf = kzalloc(SPI_WRITE_PREFIX_LEN + len, GFP_KERNEL); + if (!tx_buf) { + ts_err("alloc tx_buf failed, size:%d", + SPI_WRITE_PREFIX_LEN + len); + return -ENOMEM; + } + + spi_message_init(&spi_msg); + memset(&xfers, 0, sizeof(xfers)); + + tx_buf[0] = SPI_WRITE_FLAG; + tx_buf[1] = (addr >> 24) & 0xFF; + tx_buf[2] = (addr >> 16) & 0xFF; + tx_buf[3] = (addr >> 8) & 0xFF; + tx_buf[4] = addr & 0xFF; + memcpy(&tx_buf[SPI_WRITE_PREFIX_LEN], data, len); + xfers.tx_buf = tx_buf; + xfers.len = SPI_WRITE_PREFIX_LEN + len; + xfers.cs_change = 0; + spi_message_add_tail(&xfers, &spi_msg); + ret = spi_sync(spi, &spi_msg); + if (ret < 0) + ts_err("spi transfer error:%d",ret); + + kfree(tx_buf); + return ret; +} + +static void goodix_pdev_release(struct device *dev) +{ + ts_info("goodix pdev released"); + kfree(goodix_pdev); +} + +static int goodix_spi_probe(struct spi_device *spi) +{ + int ret = 0; + + ts_info("goodix spi probe in"); + + /* init spi_device */ + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + + ret = spi_setup(spi); + if (ret) { + ts_err("failed set spi mode, %d", ret); + return ret; + } + + /* get ic type */ + ret = goodix_get_ic_type(spi->dev.of_node); + if (ret < 0) + return ret; + + goodix_spi_bus.ic_type = ret; + goodix_spi_bus.bus_type = GOODIX_BUS_TYPE_SPI; + goodix_spi_bus.dev = &spi->dev; + if (goodix_spi_bus.ic_type == IC_TYPE_BERLIN_A) + goodix_spi_bus.read = goodix_spi_read_bra; + else + goodix_spi_bus.read = goodix_spi_read; + goodix_spi_bus.write = goodix_spi_write; + /* ts core device */ + goodix_pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); + if (!goodix_pdev) + return -ENOMEM; + + goodix_pdev->name = GOODIX_CORE_DRIVER_NAME; + goodix_pdev->id = 0; + goodix_pdev->num_resources = 0; + /* + * you can find this platform dev in + * /sys/devices/platfrom/goodix_ts.0 + * goodix_pdev->dev.parent = &client->dev; + */ + goodix_pdev->dev.platform_data = &goodix_spi_bus; + goodix_pdev->dev.release = goodix_pdev_release; + + /* register platform device, then the goodix_ts_core + * module will probe the touch deivce. + */ + ret = platform_device_register(goodix_pdev); + if (ret) { + ts_err("failed register goodix platform device, %d", ret); + goto err_pdev; + } + ts_info("spi probe out"); + return 0; + +err_pdev: + kfree(goodix_pdev); + goodix_pdev = NULL; + ts_info("spi probe out, %d", ret); + return ret; +} + +static int goodix_spi_remove(struct spi_device *spi) +{ + platform_device_unregister(goodix_pdev); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id spi_matchs[] = { + {.compatible = "goodix,gt9897S",}, + {.compatible = "goodix,gt9897T",}, + {.compatible = "goodix,gt9966S",}, + {.compatible = "goodix,gt9916S",}, + {}, +}; +#endif + +static const struct spi_device_id spi_id_table[] = { + {TS_DRIVER_NAME, 0}, + {}, +}; + +static struct spi_driver goodix_spi_driver = { + .driver = { + .name = TS_DRIVER_NAME, + //.owner = THIS_MODULE, + .of_match_table = spi_matchs, + }, + .id_table = spi_id_table, + .probe = goodix_spi_probe, + .remove = goodix_spi_remove, +}; + +int goodix_spi_bus_init(void) +{ + ts_info("Goodix spi driver init"); + return spi_register_driver(&goodix_spi_driver); +} + +void goodix_spi_bus_exit(void) +{ + ts_info("Goodix spi driver exit"); + spi_unregister_driver(&goodix_spi_driver); +} diff --git a/goodix_cfg_bin.c b/goodix_cfg_bin.c new file mode 100644 index 0000000000..620c110114 --- /dev/null +++ b/goodix_cfg_bin.c @@ -0,0 +1,329 @@ + /* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ +#include "goodix_ts_core.h" + +#define TS_BIN_VERSION_START_INDEX 5 +#define TS_BIN_VERSION_LEN 4 +#define TS_CFG_BIN_HEAD_RESERVED_LEN 6 +#define TS_CFG_OFFSET_LEN 2 +#define TS_IC_TYPE_NAME_MAX_LEN 15 +#define TS_CFG_BIN_HEAD_LEN (sizeof(struct goodix_cfg_bin_head) + \ + TS_CFG_BIN_HEAD_RESERVED_LEN) +#define TS_PKG_CONST_INFO_LEN (sizeof(struct goodix_cfg_pkg_const_info)) +#define TS_PKG_REG_INFO_LEN (sizeof(struct goodix_cfg_pkg_reg_info)) +#define TS_PKG_HEAD_LEN (TS_PKG_CONST_INFO_LEN + TS_PKG_REG_INFO_LEN) + +/*cfg block definitin*/ +#define TS_CFG_BLOCK_PID_LEN 8 +#define TS_CFG_BLOCK_VID_LEN 8 +#define TS_CFG_BLOCK_FW_MASK_LEN 9 +#define TS_CFG_BLOCK_FW_PATCH_LEN 4 +#define TS_CFG_BLOCK_RESERVED_LEN 9 + +#define TS_NORMAL_CFG 0x01 +#define TS_HIGH_SENSE_CFG 0x03 +#define TS_RQST_FW_RETRY_TIMES 2 + +#pragma pack(1) +struct goodix_cfg_pkg_reg { + u16 addr; + u8 reserved1; + u8 reserved2; +}; + +struct goodix_cfg_pkg_const_info { + u32 pkg_len; + u8 ic_type[TS_IC_TYPE_NAME_MAX_LEN]; + u8 cfg_type; + u8 sensor_id; + u8 hw_pid[TS_CFG_BLOCK_PID_LEN]; + u8 hw_vid[TS_CFG_BLOCK_VID_LEN]; + u8 fw_mask[TS_CFG_BLOCK_FW_MASK_LEN]; + u8 fw_patch[TS_CFG_BLOCK_FW_PATCH_LEN]; + u16 x_res_offset; + u16 y_res_offset; + u16 trigger_offset; +}; + +struct goodix_cfg_pkg_reg_info { + struct goodix_cfg_pkg_reg cfg_send_flag; + struct goodix_cfg_pkg_reg version_base; + struct goodix_cfg_pkg_reg pid; + struct goodix_cfg_pkg_reg vid; + struct goodix_cfg_pkg_reg sensor_id; + struct goodix_cfg_pkg_reg fw_mask; + struct goodix_cfg_pkg_reg fw_status; + struct goodix_cfg_pkg_reg cfg_addr; + struct goodix_cfg_pkg_reg esd; + struct goodix_cfg_pkg_reg command; + struct goodix_cfg_pkg_reg coor; + struct goodix_cfg_pkg_reg gesture; + struct goodix_cfg_pkg_reg fw_request; + struct goodix_cfg_pkg_reg proximity; + u8 reserved[TS_CFG_BLOCK_RESERVED_LEN]; +}; + +struct goodix_cfg_bin_head { + u32 bin_len; + u8 checksum; + u8 bin_version[TS_BIN_VERSION_LEN]; + u8 pkg_num; +}; + +#pragma pack() + +struct goodix_cfg_package { + struct goodix_cfg_pkg_const_info cnst_info; + struct goodix_cfg_pkg_reg_info reg_info; + const u8 *cfg; + u32 pkg_len; +}; + +struct goodix_cfg_bin { + unsigned char *bin_data; + unsigned int bin_data_len; + struct goodix_cfg_bin_head head; + struct goodix_cfg_package *cfg_pkgs; +}; + +static int goodix_read_cfg_bin(struct device *dev, const char *cfg_name, + struct goodix_cfg_bin *cfg_bin) +{ + const struct firmware *firmware = NULL; + int ret; + int retry = GOODIX_RETRY_3; + + ts_info("cfg_bin_name:%s", cfg_name); + + while (retry--) { + ret = request_firmware(&firmware, cfg_name, dev); + if (!ret) + break; + ts_info("get cfg bin retry:[%d]", GOODIX_RETRY_3 - retry); + msleep(200); + } + if (retry < 0) { + ts_err("failed get cfg bin[%s] error:%d", cfg_name, ret); + return ret; + } + + if (firmware->size <= 0) { + ts_err("request_firmware, cfg_bin length ERROR,len:%zu", + firmware->size); + ret = -EINVAL; + goto exit; + } + + cfg_bin->bin_data_len = firmware->size; + /* allocate memory for cfg_bin->bin_data */ + cfg_bin->bin_data = kzalloc(cfg_bin->bin_data_len, GFP_KERNEL); + if (!cfg_bin->bin_data) { + ret = -ENOMEM; + goto exit; + } + memcpy(cfg_bin->bin_data, firmware->data, cfg_bin->bin_data_len); + +exit: + release_firmware(firmware); + return ret; +} + +static int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin) +{ + u16 offset1, offset2; + u8 checksum; + int i; + + /* copy cfg_bin head info */ + if (cfg_bin->bin_data_len < sizeof(struct goodix_cfg_bin_head)) { + ts_err("Invalid cfg_bin size:%d", cfg_bin->bin_data_len); + return -EINVAL; + } + + memcpy(&cfg_bin->head, cfg_bin->bin_data, + sizeof(struct goodix_cfg_bin_head)); + cfg_bin->head.bin_len = le32_to_cpu(cfg_bin->head.bin_len); + + /*check length*/ + if (cfg_bin->bin_data_len != cfg_bin->head.bin_len) { + ts_err("cfg_bin len check failed,%d != %d", + cfg_bin->head.bin_len, cfg_bin->bin_data_len); + return -EINVAL; + } + + /*check cfg_bin valid*/ + checksum = 0; + for (i = TS_BIN_VERSION_START_INDEX; i < cfg_bin->bin_data_len; i++) { + checksum += cfg_bin->bin_data[i]; + } + if (checksum != cfg_bin->head.checksum) { + ts_err("cfg_bin checksum check filed 0x%02x != 0x%02x", + cfg_bin->head.checksum, checksum); + return -EINVAL; + } + + /*allocate memory for cfg packages*/ + cfg_bin->cfg_pkgs = kzalloc(sizeof(struct goodix_cfg_package) * + cfg_bin->head.pkg_num, GFP_KERNEL); + if (!cfg_bin->cfg_pkgs) { + ts_err("cfg_pkgs, allocate memory ERROR"); + return -ENOMEM; + } + + /*get cfg_pkg's info*/ + for (i = 0; i < cfg_bin->head.pkg_num; i++) { + /*get cfg pkg length*/ + if (i == cfg_bin->head.pkg_num - 1) { + offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN] + + (cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN + 1] << 8); + + cfg_bin->cfg_pkgs[i].pkg_len = cfg_bin->bin_data_len - offset1; + } else { + offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN] + + (cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN + 1] << 8); + + offset2 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN + 2] + + (cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN + 3] << 8); + + if (offset2 <= offset1) { + ts_err("offset error,pkg:%d, offset1:%d, offset2:%d", i, offset1, offset2); + goto exit; + } + + cfg_bin->cfg_pkgs[i].pkg_len = offset2 - offset1; + } + /*get cfg pkg head*/ + memcpy(&cfg_bin->cfg_pkgs[i].cnst_info, + &cfg_bin->bin_data[offset1], TS_PKG_CONST_INFO_LEN); + memcpy(&cfg_bin->cfg_pkgs[i].reg_info, + &cfg_bin->bin_data[offset1 + TS_PKG_CONST_INFO_LEN], + TS_PKG_REG_INFO_LEN); + + /*get configuration data*/ + cfg_bin->cfg_pkgs[i].cfg = &cfg_bin->bin_data[offset1 + TS_PKG_HEAD_LEN]; + } + + /*debug, print pkg information*/ + ts_info("Driver bin info: ver %s, len %d, pkgs %d", cfg_bin->head.bin_version, + cfg_bin->head.bin_len, cfg_bin->head.pkg_num); + + return 0; +exit: + kfree(cfg_bin->cfg_pkgs); + return -EINVAL; +} + +static int goodix_get_reg_and_cfg(struct goodix_ts_core *cd, u8 sensor_id, + struct goodix_cfg_bin *cfg_bin) +{ + int i; + u8 cfg_type; + u32 cfg_len; + struct goodix_cfg_package *cfg_pkg; + + if (!cfg_bin->head.pkg_num || !cfg_bin->cfg_pkgs) { + ts_err("there is none cfg package, pkg_num:%d", + cfg_bin->head.pkg_num); + return -EINVAL; + } + + /* find cfg packages with same sensor_id */ + for (i = 0; i < cfg_bin->head.pkg_num; i++) { + cfg_pkg = &cfg_bin->cfg_pkgs[i]; + if (sensor_id != cfg_pkg->cnst_info.sensor_id) { + ts_info("pkg:%d, sensor id contrast FAILED, bin %d != %d", + i, cfg_pkg->cnst_info.sensor_id, sensor_id); + continue; + } + cfg_type = cfg_pkg->cnst_info.cfg_type; + if (cfg_type >= GOODIX_MAX_CONFIG_GROUP) { + ts_err("usupported config type %d", + cfg_pkg->cnst_info.cfg_type); + goto err_out; + } + + cfg_len = cfg_pkg->pkg_len - TS_PKG_CONST_INFO_LEN - + TS_PKG_REG_INFO_LEN; + if (cfg_len > GOODIX_CFG_MAX_SIZE) { + ts_err("config len exceed limit %d > %d", + cfg_len, GOODIX_CFG_MAX_SIZE); + goto err_out; + } + if (cd->ic_configs[cfg_type]) { + ts_err("found same type config twice for sensor id %d, skiped", + sensor_id); + continue; + } + cd->ic_configs[cfg_type] = kzalloc(sizeof(struct goodix_ic_config), GFP_KERNEL); + if (!cd->ic_configs[cfg_type]) + goto err_out; + cd->ic_configs[cfg_type]->len = cfg_len; + memcpy(cd->ic_configs[cfg_type]->data, cfg_pkg->cfg, cfg_len); + ts_info("get config type %d, len %d, for sensor id %d", + cfg_type, cfg_len, sensor_id); + } + return 0; + +err_out: + /* parse config enter error, release memory alloced */ + for (i = 0; i < GOODIX_MAX_CONFIG_GROUP; i++) { + if (cd->ic_configs[i]) + kfree(cd->ic_configs[i]); + cd->ic_configs[i] = NULL; + } + return -EINVAL; +} + +static int goodix_get_config_data(struct goodix_ts_core *cd, u8 sensor_id) +{ + struct goodix_cfg_bin cfg_bin = {0}; + char *cfg_name = cd->board_data.cfg_bin_name; + int ret; + + /*get cfg_bin from file system*/ + ret = goodix_read_cfg_bin(&cd->pdev->dev, cfg_name, &cfg_bin); + if (ret) { + ts_err("failed get valid config bin data"); + return ret; + } + + /*parse cfg bin*/ + ret = goodix_parse_cfg_bin(&cfg_bin); + if (ret) { + ts_err("failed parse cfg bin"); + goto err_out; + } + + /*get register address and configuration from cfg bin*/ + ret = goodix_get_reg_and_cfg(cd, sensor_id, &cfg_bin); + if (!ret) + ts_info("success get reg and cfg info from cfg bin"); + else + ts_err("failed get cfg and reg info, update fw then retry"); + + kfree(cfg_bin.cfg_pkgs); +err_out: + kfree(cfg_bin.bin_data); + return ret; +} + +int goodix_get_config_proc(struct goodix_ts_core *cd) +{ + return goodix_get_config_data(cd, cd->fw_version.sensor_id); +} + + diff --git a/goodix_ts_core.c b/goodix_ts_core.c new file mode 100644 index 0000000000..c18ac85eb7 --- /dev/null +++ b/goodix_ts_core.c @@ -0,0 +1,2263 @@ + /* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ +#include +#include +#include +#include +#include + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) +#include +#define INPUT_TYPE_B_PROTOCOL +#endif + +#include "goodix_ts_core.h" + +#define GOODIX_DEFAULT_CFG_NAME "goodix_cfg_group.cfg" +#define GOOIDX_INPUT_PHYS "goodix_ts/input0" + +struct goodix_module goodix_modules; +int core_module_prob_sate = CORE_MODULE_UNPROBED; + +static int goodix_send_ic_config(struct goodix_ts_core *cd, int type); +/** + * __do_register_ext_module - register external module + * to register into touch core modules structure + * return 0 on success, otherwise return < 0 + */ +static int __do_register_ext_module(struct goodix_ext_module *module) +{ + struct goodix_ext_module *ext_module, *next; + struct list_head *insert_point = &goodix_modules.head; + + /* prority level *must* be set */ + if (module->priority == EXTMOD_PRIO_RESERVED) { + ts_err("Priority of module [%s] needs to be set", + module->name); + return -EINVAL; + } + mutex_lock(&goodix_modules.mutex); + /* find insert point for the specified priority */ + if (!list_empty(&goodix_modules.head)) { + list_for_each_entry_safe(ext_module, next, + &goodix_modules.head, list) { + if (ext_module == module) { + ts_info("Module [%s] already exists", + module->name); + mutex_unlock(&goodix_modules.mutex); + return 0; + } + } + + /* smaller priority value with higher priority level */ + list_for_each_entry_safe(ext_module, next, + &goodix_modules.head, list) { + if (ext_module->priority >= module->priority) { + insert_point = &ext_module->list; + break; + } + } + } + + if (module->funcs && module->funcs->init) { + if (module->funcs->init(goodix_modules.core_data, + module) < 0) { + ts_err("Module [%s] init error", + module->name ? module->name : " "); + mutex_unlock(&goodix_modules.mutex); + return -EFAULT; + } + } + + list_add(&module->list, insert_point->prev); + mutex_unlock(&goodix_modules.mutex); + + ts_info("Module [%s] registered,priority:%u", module->name, + module->priority); + return 0; +} + +static void goodix_register_ext_module_work(struct work_struct *work) { + struct goodix_ext_module *module = + container_of(work, struct goodix_ext_module, work); + + ts_info("module register work IN"); + + /* driver probe failed */ + if (core_module_prob_sate != CORE_MODULE_PROB_SUCCESS) { + ts_err("Can't register ext_module core error"); + return; + } + + if (__do_register_ext_module(module)) + ts_err("failed register module: %s", module->name); + else + ts_info("success register module: %s", module->name); +} + +static void goodix_core_module_init(void) +{ + if (goodix_modules.initilized) + return; + goodix_modules.initilized = true; + INIT_LIST_HEAD(&goodix_modules.head); + mutex_init(&goodix_modules.mutex); +} + +/** + * goodix_register_ext_module - interface for register external module + * to the core. This will create a workqueue to finish the real register + * work and return immediately. The user need to check the final result + * to make sure registe is success or fail. + * + * @module: pointer to external module to be register + * return: 0 ok, <0 failed + */ +int goodix_register_ext_module(struct goodix_ext_module *module) +{ + if (!module) + return -EINVAL; + + ts_info("goodix_register_ext_module IN"); + + goodix_core_module_init(); + INIT_WORK(&module->work, goodix_register_ext_module_work); + schedule_work(&module->work); + + ts_info("goodix_register_ext_module OUT"); + return 0; +} + +/** + * goodix_register_ext_module_no_wait + * return: 0 ok, <0 failed + */ +int goodix_register_ext_module_no_wait(struct goodix_ext_module *module) +{ + if (!module) + return -EINVAL; + ts_info("goodix_register_ext_module_no_wait IN"); + goodix_core_module_init(); + /* driver probe failed */ + if (core_module_prob_sate != CORE_MODULE_PROB_SUCCESS) { + ts_err("Can't register ext_module core error"); + return -EINVAL; + } + return __do_register_ext_module(module); +} + +/** + * goodix_unregister_ext_module - interface for external module + * to unregister external modules + * + * @module: pointer to external module + * return: 0 ok, <0 failed + */ +int goodix_unregister_ext_module(struct goodix_ext_module *module) +{ + struct goodix_ext_module *ext_module, *next; + bool found = false; + + if (!module) + return -EINVAL; + + if (!goodix_modules.initilized) + return -EINVAL; + + if (!goodix_modules.core_data) + return -ENODEV; + + mutex_lock(&goodix_modules.mutex); + if (!list_empty(&goodix_modules.head)) { + list_for_each_entry_safe(ext_module, next, + &goodix_modules.head, list) { + if (ext_module == module) { + found = true; + break; + } + } + } else { + mutex_unlock(&goodix_modules.mutex); + return 0; + } + + if (!found) { + ts_debug("Module [%s] never registed", + module->name); + mutex_unlock(&goodix_modules.mutex); + return 0; + } + + list_del(&module->list); + mutex_unlock(&goodix_modules.mutex); + + if (module->funcs && module->funcs->exit) + module->funcs->exit(goodix_modules.core_data, module); + + ts_info("Moudle [%s] unregistered", + module->name ? module->name : " "); + return 0; +} + +static void goodix_ext_sysfs_release(struct kobject *kobj) +{ + ts_info("Kobject released!"); +} + +#define to_ext_module(kobj) container_of(kobj,\ + struct goodix_ext_module, kobj) +#define to_ext_attr(attr) container_of(attr,\ + struct goodix_ext_attribute, attr) + +static ssize_t goodix_ext_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct goodix_ext_module *module = to_ext_module(kobj); + struct goodix_ext_attribute *ext_attr = to_ext_attr(attr); + + if (ext_attr->show) + return ext_attr->show(module, buf); + + return -EIO; +} + +static ssize_t goodix_ext_sysfs_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t count) +{ + struct goodix_ext_module *module = to_ext_module(kobj); + struct goodix_ext_attribute *ext_attr = to_ext_attr(attr); + + if (ext_attr->store) + return ext_attr->store(module, buf, count); + + return -EIO; +} + +static const struct sysfs_ops goodix_ext_ops = { + .show = goodix_ext_sysfs_show, + .store = goodix_ext_sysfs_store +}; + +static struct kobj_type goodix_ext_ktype = { + .release = goodix_ext_sysfs_release, + .sysfs_ops = &goodix_ext_ops, +}; + +struct kobj_type *goodix_get_default_ktype(void) +{ + return &goodix_ext_ktype; +} + +struct kobject *goodix_get_default_kobj(void) +{ + struct kobject *kobj = NULL; + + if (goodix_modules.core_data && + goodix_modules.core_data->pdev) + kobj = &goodix_modules.core_data->pdev->dev.kobj; + return kobj; +} + +/* show driver infomation */ +static ssize_t goodix_ts_driver_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "DriverVersion:%s\n", + GOODIX_DRIVER_VERSION); +} + +/* show chip infoamtion */ +static ssize_t goodix_ts_chip_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + struct goodix_fw_version chip_ver; + u8 temp_pid[8] = {0}; + int ret; + int cnt = -EINVAL; + + if (hw_ops->read_version) { + ret = hw_ops->read_version(core_data, &chip_ver); + if (!ret) { + memcpy(temp_pid, chip_ver.rom_pid, sizeof(chip_ver.rom_pid)); + cnt = snprintf(&buf[0], PAGE_SIZE, + "rom_pid:%s\nrom_vid:%02x%02x%02x\n", + temp_pid, chip_ver.rom_vid[0], + chip_ver.rom_vid[1], chip_ver.rom_vid[2]); + cnt += snprintf(&buf[cnt], PAGE_SIZE, + "patch_pid:%s\npatch_vid:%02x%02x%02x%02x\n", + chip_ver.patch_pid, chip_ver.patch_vid[0], + chip_ver.patch_vid[1], chip_ver.patch_vid[2], + chip_ver.patch_vid[3]); + cnt += snprintf(&buf[cnt], PAGE_SIZE, + "sensorid:%d\n", chip_ver.sensor_id); + } + } + + if (hw_ops->get_ic_info) { + ret = hw_ops->get_ic_info(core_data, &core_data->ic_info); + if (!ret) { + cnt += snprintf(&buf[cnt], PAGE_SIZE, + "config_id:%x\n", core_data->ic_info.version.config_id); + cnt += snprintf(&buf[cnt], PAGE_SIZE, + "config_version:%x\n", core_data->ic_info.version.config_version); + } + } + + return cnt; +} + +/* reset chip */ +static ssize_t goodix_ts_reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + + if (!buf || count <= 0) + return -EINVAL; + if (buf[0] != '0') + hw_ops->reset(core_data, GOODIX_NORMAL_RESET_DELAY_MS); + return count; +} + +/* read config */ +static ssize_t goodix_ts_read_cfg_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + int ret; + int i; + int offset; + char *cfg_buf = NULL; + + cfg_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!cfg_buf) + return -ENOMEM; + + if (hw_ops->read_config) + ret = hw_ops->read_config(core_data, cfg_buf, PAGE_SIZE); + else + ret = -EINVAL; + + if (ret > 0) { + offset = 0; + for (i = 0; i < 200; i++) { // only print 200 bytes + offset += snprintf(&buf[offset], PAGE_SIZE - offset, + "%02x,", cfg_buf[i]); + if ((i + 1) % 20 == 0) + buf[offset++] = '\n'; + } + } + + kfree(cfg_buf); + if (ret <= 0) + return ret; + + return offset; +} + +static u8 ascii2hex(u8 a) +{ + s8 value = 0; + + if (a >= '0' && a <= '9') + value = a - '0'; + else if (a >= 'A' && a <= 'F') + value = a - 'A' + 0x0A; + else if (a >= 'a' && a <= 'f') + value = a - 'a' + 0x0A; + else + value = 0xff; + + return value; +} + +static int goodix_ts_convert_0x_data(const u8 *buf, int buf_size, + u8 *out_buf, int *out_buf_len) +{ + int i, m_size = 0; + int temp_index = 0; + u8 high, low; + + for (i = 0; i < buf_size; i++) { + if (buf[i] == 'x' || buf[i] == 'X') + m_size++; + } + + if (m_size <= 1) { + ts_err("cfg file ERROR, valid data count:%d", m_size); + return -EINVAL; + } + *out_buf_len = m_size; + + for (i = 0; i < buf_size; i++) { + if (buf[i] != 'x' && buf[i] != 'X') + continue; + + if (temp_index >= m_size) { + ts_err("exchange cfg data error, overflow," + "temp_index:%d,m_size:%d", + temp_index, m_size); + return -EINVAL; + } + high = ascii2hex(buf[i + 1]); + low = ascii2hex(buf[i + 2]); + if (high == 0xff || low == 0xff) { + ts_err("failed convert: 0x%x, 0x%x", + buf[i + 1], buf[i + 2]); + return -EINVAL; + } + out_buf[temp_index++] = (high << 4) + low; + } + return 0; +} + +/* send config */ +static ssize_t goodix_ts_send_cfg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + struct goodix_ic_config *config = NULL; + const struct firmware *cfg_img = NULL; + int en; + int ret; + + if (sscanf(buf, "%d", &en) != 1) + return -EINVAL; + + if (en != 1) + return -EINVAL; + + hw_ops->irq_enable(core_data, false); + + ret = request_firmware(&cfg_img, GOODIX_DEFAULT_CFG_NAME, dev); + if (ret < 0) { + ts_err("cfg file [%s] not available,errno:%d", + GOODIX_DEFAULT_CFG_NAME, ret); + goto exit; + } else { + ts_info("cfg file [%s] is ready", GOODIX_DEFAULT_CFG_NAME); + } + + config = kzalloc(sizeof(*config), GFP_KERNEL); + if (!config) + goto exit; + + if (goodix_ts_convert_0x_data(cfg_img->data, cfg_img->size, + config->data, &config->len)) { + ts_err("convert config data FAILED"); + goto exit; + } + + if (hw_ops->send_config) { + ret = hw_ops->send_config(core_data, config->data, config->len); + if (ret < 0) + ts_err("send config failed"); + } + +exit: + hw_ops->irq_enable(core_data, true); + kfree(config); + if (cfg_img) + release_firmware(cfg_img); + + return count; +} + +/* reg read/write */ +static u32 rw_addr; +static u32 rw_len; +static u8 rw_flag; +static u8 store_buf[32]; +static u8 show_buf[PAGE_SIZE]; +static ssize_t goodix_ts_reg_rw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + int ret; + + if (!rw_addr || !rw_len) { + ts_err("address(0x%x) and length(%d) can't be null", + rw_addr, rw_len); + return -EINVAL; + } + + if (rw_flag != 1) { + ts_err("invalid rw flag %d, only support [1/2]", rw_flag); + return -EINVAL; + } + + ret = hw_ops->read(core_data, rw_addr, show_buf, rw_len); + if (ret < 0) { + ts_err("failed read addr(%x) length(%d)", rw_addr, rw_len); + return snprintf(buf, PAGE_SIZE, "failed read addr(%x), len(%d)\n", + rw_addr, rw_len); + } + + return snprintf(buf, PAGE_SIZE, "0x%x,%d {%*ph}\n", + rw_addr, rw_len, rw_len, show_buf); +} + +static ssize_t goodix_ts_reg_rw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + char *pos = NULL; + char *token = NULL; + long result = 0; + int ret; + int i; + + if (!buf || !count) { + ts_err("invalid parame"); + goto err_out; + } + + if (buf[0] == 'r') { + rw_flag = 1; + } else if (buf[0] == 'w') { + rw_flag = 2; + } else { + ts_err("string must start with 'r/w'"); + goto err_out; + } + + /* get addr */ + pos = (char *)buf; + pos += 2; + token = strsep(&pos, ":"); + if (!token) { + ts_err("invalid address info"); + goto err_out; + } else { + if (kstrtol(token, 16, &result)) { + ts_err("failed get addr info"); + goto err_out; + } + rw_addr = (u32)result; + ts_info("rw addr is 0x%x", rw_addr); + } + + /* get length */ + token = strsep(&pos, ":"); + if (!token) { + ts_err("invalid length info"); + goto err_out; + } else { + if (kstrtol(token, 0, &result)) { + ts_err("failed get length info"); + goto err_out; + } + rw_len = (u32)result; + ts_info("rw length info is %d", rw_len); + if (rw_len > sizeof(store_buf)) { + ts_err("data len > %lu", sizeof(store_buf)); + goto err_out; + } + } + + if (rw_flag == 1) + return count; + + for (i = 0; i < rw_len; i++) { + token = strsep(&pos, ":"); + if (!token) { + ts_err("invalid data info"); + goto err_out; + } else { + if (kstrtol(token, 16, &result)) { + ts_err("failed get data[%d] info", i); + goto err_out; + } + store_buf[i] = (u8)result; + ts_info("get data[%d]=0x%x", i, store_buf[i]); + } + } + ret = hw_ops->write(core_data, rw_addr, store_buf, rw_len); + if (ret < 0) { + ts_err("failed write addr(%x) data %*ph", rw_addr, + rw_len, store_buf); + goto err_out; + } + + ts_info("%s write to addr (%x) with data %*ph", + "success", rw_addr, rw_len, store_buf); + + return count; +err_out: + snprintf(show_buf, PAGE_SIZE, "%s\n", + "invalid params, format{r/w:4100:length:[41:21:31]}"); + return -EINVAL; + +} + +/* show irq infomation */ +static ssize_t goodix_ts_irq_info_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + struct irq_desc *desc; + size_t offset = 0; + int r; + + r = snprintf(&buf[offset], PAGE_SIZE, "irq:%u\n", core_data->irq); + if (r < 0) + return -EINVAL; + + offset += r; + r = snprintf(&buf[offset], PAGE_SIZE - offset, "state:%s\n", + atomic_read(&core_data->irq_enabled) ? + "enabled" : "disabled"); + if (r < 0) + return -EINVAL; + + desc = irq_to_desc(core_data->irq); + offset += r; + r = snprintf(&buf[offset], PAGE_SIZE - offset, "disable-depth:%d\n", + desc->depth); + if (r < 0) + return -EINVAL; + + offset += r; + r = snprintf(&buf[offset], PAGE_SIZE - offset, "trigger-count:%zu\n", + core_data->irq_trig_cnt); + if (r < 0) + return -EINVAL; + + offset += r; + r = snprintf(&buf[offset], PAGE_SIZE - offset, + "echo 0/1 > irq_info to disable/enable irq\n"); + if (r < 0) + return -EINVAL; + + offset += r; + return offset; +} + +/* enable/disable irq */ +static ssize_t goodix_ts_irq_info_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + + if (!buf || count <= 0) + return -EINVAL; + + if (buf[0] != '0') + hw_ops->irq_enable(core_data, true); + else + hw_ops->irq_enable(core_data, false); + return count; +} + +/* show esd status */ +static ssize_t goodix_ts_esd_info_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + struct goodix_ts_esd *ts_esd = &core_data->ts_esd; + int r = 0; + + r = snprintf(buf, PAGE_SIZE, "state:%s\n", + atomic_read(&ts_esd->esd_on) ? + "enabled" : "disabled"); + + return r; +} + +/* enable/disable esd */ +static ssize_t goodix_ts_esd_info_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + if (!buf || count <= 0) + return -EINVAL; + + if (buf[0] != '0') + goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); + else + goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); + return count; +} + +/* debug level show */ +static ssize_t goodix_ts_debug_log_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int r = 0; + + r = snprintf(buf, PAGE_SIZE, "state:%s\n", + debug_log_flag ? + "enabled" : "disabled"); + + return r; +} + +/* debug level store */ +static ssize_t goodix_ts_debug_log_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + if (!buf || count <= 0) + return -EINVAL; + + if (buf[0] != '0') + debug_log_flag = true; + else + debug_log_flag = false; + return count; +} + +static DEVICE_ATTR(driver_info, 0444, goodix_ts_driver_info_show, NULL); +static DEVICE_ATTR(chip_info, 0444, goodix_ts_chip_info_show, NULL); +static DEVICE_ATTR(reset, 0220, NULL, goodix_ts_reset_store); +static DEVICE_ATTR(send_cfg, 0220, NULL, goodix_ts_send_cfg_store); +static DEVICE_ATTR(read_cfg, 0444, goodix_ts_read_cfg_show, NULL); +static DEVICE_ATTR(reg_rw, 0664, goodix_ts_reg_rw_show, goodix_ts_reg_rw_store); +static DEVICE_ATTR(irq_info, 0664, goodix_ts_irq_info_show, goodix_ts_irq_info_store); +static DEVICE_ATTR(esd_info, 0664, goodix_ts_esd_info_show, goodix_ts_esd_info_store); +static DEVICE_ATTR(debug_log, 0664, goodix_ts_debug_log_show, goodix_ts_debug_log_store); + +static struct attribute *sysfs_attrs[] = { + &dev_attr_driver_info.attr, + &dev_attr_chip_info.attr, + &dev_attr_reset.attr, + &dev_attr_send_cfg.attr, + &dev_attr_read_cfg.attr, + &dev_attr_reg_rw.attr, + &dev_attr_irq_info.attr, + &dev_attr_esd_info.attr, + &dev_attr_debug_log.attr, + NULL, +}; + +static const struct attribute_group sysfs_group = { + .attrs = sysfs_attrs, +}; + +static int goodix_ts_sysfs_init(struct goodix_ts_core *core_data) +{ + int ret; + + ret = sysfs_create_group(&core_data->pdev->dev.kobj, &sysfs_group); + if (ret) { + ts_err("failed create core sysfs group"); + return ret; + } + + return ret; +} + +static void goodix_ts_sysfs_exit(struct goodix_ts_core *core_data) +{ + sysfs_remove_group(&core_data->pdev->dev.kobj, &sysfs_group); +} + +/* prosfs create */ +static int rawdata_proc_show(struct seq_file *m, void *v) +{ + struct ts_rawdata_info *info; + struct goodix_ts_core *cd = m->private; + int tx; + int rx; + int ret; + int i; + int index; + + if (!m || !v || !cd) { + ts_err("rawdata_proc_show, input null ptr"); + return -EIO; + } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ts_err("Failed to alloc rawdata info memory"); + return -ENOMEM; + } + + ret = cd->hw_ops->get_capacitance_data(cd, info); + if (ret < 0) { + ts_err("failed to get_capacitance_data, exit!"); + goto exit; + } + + rx = info->buff[0]; + tx = info->buff[1]; + seq_printf(m, "TX:%d RX:%d\n", tx, rx); + seq_printf(m, "mutual_rawdata:\n"); + index = 2; + for (i = 0; i < tx * rx; i++) { + seq_printf(m, "%5d,", info->buff[index + i]); + if ((i + 1) % tx == 0) + seq_printf(m, "\n"); + } + seq_printf(m, "mutual_diffdata:\n"); + index += tx * rx; + for (i = 0; i < tx * rx; i++) { + seq_printf(m, "%3d,", info->buff[index + i]); + if ((i + 1) % tx == 0) + seq_printf(m, "\n"); + } + +exit: + kfree(info); + return ret; +} + +static int rawdata_proc_open(struct inode *inode, struct file *file) +{ + return single_open_size(file, rawdata_proc_show, PDE_DATA(inode), PAGE_SIZE * 10); +} + +static const struct file_operations rawdata_proc_fops = { + .open = rawdata_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void goodix_ts_procfs_init(struct goodix_ts_core *core_data) +{ + if (!proc_mkdir("goodix_ts", NULL)) + return; + proc_create_data("goodix_ts/tp_capacitance_data", + 0666, NULL, &rawdata_proc_fops, core_data); +} + +static void goodix_ts_procfs_exit(struct goodix_ts_core *core_data) +{ + remove_proc_entry("goodix_ts/tp_capacitance_data", NULL); + remove_proc_entry("goodix_ts", NULL); +} + +/* event notifier */ +static BLOCKING_NOTIFIER_HEAD(ts_notifier_list); +/** + * goodix_ts_register_client - register a client notifier + * @nb: notifier block to callback on events + * see enum ts_notify_event in goodix_ts_core.h + */ +int goodix_ts_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&ts_notifier_list, nb); +} + +/** + * goodix_ts_unregister_client - unregister a client notifier + * @nb: notifier block to callback on events + * see enum ts_notify_event in goodix_ts_core.h + */ +int goodix_ts_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&ts_notifier_list, nb); +} + +/** + * fb_notifier_call_chain - notify clients of fb_events + * see enum ts_notify_event in goodix_ts_core.h + */ +int goodix_ts_blocking_notify(enum ts_notify_event evt, void *v) +{ + int ret; + + ret = blocking_notifier_call_chain(&ts_notifier_list, + (unsigned long)evt, v); + return ret; +} + +#ifdef CONFIG_OF +/** + * goodix_parse_dt_resolution - parse resolution from dt + * @node: devicetree node + * @board_data: pointer to board data structure + * return: 0 - no error, <0 error + */ +static int goodix_parse_dt_resolution(struct device_node *node, + struct goodix_ts_board_data *board_data) +{ + int ret; + + ret = of_property_read_u32(node, "goodix,panel-max-x", + &board_data->panel_max_x); + if (ret) { + ts_err("failed get panel-max-x"); + return ret; + } + + ret = of_property_read_u32(node, "goodix,panel-max-y", + &board_data->panel_max_y); + if (ret) { + ts_err("failed get panel-max-y"); + return ret; + } + + ret = of_property_read_u32(node, "goodix,panel-max-w", + &board_data->panel_max_w); + if (ret) { + ts_err("failed get panel-max-w"); + return ret; + } + + ret = of_property_read_u32(node, "goodix,panel-max-p", + &board_data->panel_max_p); + if (ret) { + ts_err("failed get panel-max-p, use default"); + board_data->panel_max_p = GOODIX_PEN_MAX_PRESSURE; + } + + return 0; +} + +/** + * goodix_parse_dt - parse board data from dt + * @dev: pointer to device + * @board_data: pointer to board data structure + * return: 0 - no error, <0 error + */ +static int goodix_parse_dt(struct device_node *node, + struct goodix_ts_board_data *board_data) +{ + const char *name_tmp; + int r; + + if (!board_data) { + ts_err("invalid board data"); + return -EINVAL; + } + + r = of_get_named_gpio(node, "goodix,avdd-gpio", 0); + if (r < 0) { + ts_info("can't find avdd-gpio, use other power supply"); + board_data->avdd_gpio = 0; + } else { + ts_info("get avdd-gpio[%d] from dt", r); + board_data->avdd_gpio = r; + } + + r = of_get_named_gpio(node, "goodix,iovdd-gpio", 0); + if (r < 0) { + ts_info("can't find iovdd-gpio, use other power supply"); + board_data->iovdd_gpio = 0; + } else { + ts_info("get iovdd-gpio[%d] from dt", r); + board_data->iovdd_gpio = r; + } + + r = of_get_named_gpio(node, "goodix,reset-gpio", 0); + if (r < 0) { + ts_err("invalid reset-gpio in dt: %d", r); + return -EINVAL; + } + ts_info("get reset-gpio[%d] from dt", r); + board_data->reset_gpio = r; + + r = of_get_named_gpio(node, "goodix,irq-gpio", 0); + if (r < 0) { + ts_err("invalid irq-gpio in dt: %d", r); + return -EINVAL; + } + ts_info("get irq-gpio[%d] from dt", r); + board_data->irq_gpio = r; + + r = of_property_read_u32(node, "goodix,irq-flags", + &board_data->irq_flags); + if (r) { + ts_err("invalid irq-flags"); + return -EINVAL; + } + + memset(board_data->avdd_name, 0, sizeof(board_data->avdd_name)); + r = of_property_read_string(node, "goodix,avdd-name", &name_tmp); + if (!r) { + ts_info("avdd name from dt: %s", name_tmp); + if (strlen(name_tmp) < sizeof(board_data->avdd_name)) + strncpy(board_data->avdd_name, + name_tmp, sizeof(board_data->avdd_name)); + else + ts_info("invalied avdd name length: %ld > %ld", + strlen(name_tmp), + sizeof(board_data->avdd_name)); + } + + memset(board_data->iovdd_name, 0, sizeof(board_data->iovdd_name)); + r = of_property_read_string(node, "goodix,iovdd-name", &name_tmp); + if (!r) { + ts_info("iovdd name from dt: %s", name_tmp); + if (strlen(name_tmp) < sizeof(board_data->iovdd_name)) + strncpy(board_data->iovdd_name, + name_tmp, sizeof(board_data->iovdd_name)); + else + ts_info("invalied iovdd name length: %ld > %ld", + strlen(name_tmp), + sizeof(board_data->iovdd_name)); + } + + /* get firmware file name */ + r = of_property_read_string(node, "goodix,firmware-name", &name_tmp); + if (!r) { + ts_info("firmware name from dt: %s", name_tmp); + strncpy(board_data->fw_name, name_tmp, sizeof(board_data->fw_name)); + } else { + ts_info("can't find firmware name, use default: %s", TS_DEFAULT_FIRMWARE); + strncpy(board_data->fw_name, TS_DEFAULT_FIRMWARE, sizeof(board_data->fw_name)); + } + + /* get config file name */ + r = of_property_read_string(node, "goodix,config-name", &name_tmp); + if (!r) { + ts_info("config name from dt: %s", name_tmp); + strncpy(board_data->cfg_bin_name, name_tmp, sizeof(board_data->cfg_bin_name)); + } else { + ts_info("can't find config name, use default: %s", TS_DEFAULT_CFG_BIN); + strncpy(board_data->cfg_bin_name, TS_DEFAULT_CFG_BIN, sizeof(board_data->cfg_bin_name)); + } + + /* get xyz resolutions */ + r = goodix_parse_dt_resolution(node, board_data); + if (r) { + ts_err("Failed to parse resolutions:%d", r); + return r; + } + + + /*get pen-enable switch and pen keys, must after "key map"*/ + board_data->pen_enable = of_property_read_bool(node, + "goodix,pen-enable"); + if (board_data->pen_enable) + ts_info("goodix pen enabled"); + + ts_debug("[DT]x:%d, y:%d, w:%d, p:%d", board_data->panel_max_x, + board_data->panel_max_y, board_data->panel_max_w, + board_data->panel_max_p); + return 0; +} +#endif + +static void goodix_ts_report_pen(struct input_dev *dev, + struct goodix_pen_data *pen_data) +{ + int i; + + mutex_lock(&dev->mutex); + + if (pen_data->coords.status == TS_TOUCH) { + input_report_key(dev, BTN_TOUCH, 1); + input_report_key(dev, pen_data->coords.tool_type, 1); + input_report_abs(dev, ABS_X, pen_data->coords.x); + input_report_abs(dev, ABS_Y, pen_data->coords.y); + input_report_abs(dev, ABS_PRESSURE, pen_data->coords.p); + input_report_abs(dev, ABS_TILT_X, pen_data->coords.tilt_x); + input_report_abs(dev, ABS_TILT_Y, pen_data->coords.tilt_y); + ts_debug("pen_data:x %d, y %d, p %d, tilt_x %d tilt_y %d key[%d %d]", + pen_data->coords.x, pen_data->coords.y, + pen_data->coords.p, pen_data->coords.tilt_x, + pen_data->coords.tilt_y, pen_data->keys[0].status == TS_TOUCH ? 1 : 0, + pen_data->keys[1].status == TS_TOUCH ? 1 : 0); + } else { + input_report_key(dev, BTN_TOUCH, 0); + input_report_key(dev, pen_data->coords.tool_type, 0); + } + /* report pen button */ + for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) { + if (pen_data->keys[i].status == TS_TOUCH) + input_report_key(dev, pen_data->keys[i].code, 1); + else + input_report_key(dev, pen_data->keys[i].code, 0); + } + + input_sync(dev); + mutex_unlock(&dev->mutex); +} + +static void goodix_ts_report_finger(struct input_dev *dev, + struct goodix_touch_data *touch_data) +{ + unsigned int touch_num = touch_data->touch_num; + int i; + + mutex_lock(&dev->mutex); + + for (i = 0; i < GOODIX_MAX_TOUCH; i++) { + if (touch_data->coords[i].status == TS_TOUCH) { + ts_debug("report: id %d, x %d, y %d, w %d", i, + touch_data->coords[i].x, touch_data->coords[i].y, + touch_data->coords[i].w); + input_mt_slot(dev, i); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); + input_report_abs(dev, ABS_MT_POSITION_X, + touch_data->coords[i].x); + input_report_abs(dev, ABS_MT_POSITION_Y, + touch_data->coords[i].y); + input_report_abs(dev, ABS_MT_TOUCH_MAJOR, + touch_data->coords[i].w); + } else { + input_mt_slot(dev, i); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, false); + } + } + + input_report_key(dev, BTN_TOUCH, touch_num > 0 ? 1 : 0); + input_sync(dev); + + mutex_unlock(&dev->mutex); +} + +static int goodix_ts_request_handle(struct goodix_ts_core *cd, + struct goodix_ts_event *ts_event) +{ + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + int ret = -1; + + if (ts_event->request_code == REQUEST_TYPE_CONFIG) + ret = goodix_send_ic_config(cd, CONFIG_TYPE_NORMAL); + else if (ts_event->request_code == REQUEST_TYPE_RESET) + ret = hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); + else + ts_info("can not handle request type 0x%x", + ts_event->request_code); + if (ret) + ts_err("failed handle request 0x%x", + ts_event->request_code); + else + ts_info("success handle ic request 0x%x", + ts_event->request_code); + return ret; +} + +/** + * goodix_ts_threadirq_func - Bottom half of interrupt + * This functions is excuted in thread context, + * sleep in this function is permit. + * + * @data: pointer to touch core data + * return: 0 ok, <0 failed + */ +static irqreturn_t goodix_ts_threadirq_func(int irq, void *data) +{ + struct goodix_ts_core *core_data = data; + struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + struct goodix_ext_module *ext_module, *next; + struct goodix_ts_event *ts_event = &core_data->ts_event; + struct goodix_ts_esd *ts_esd = &core_data->ts_esd; + int ret; + + ts_esd->irq_status = true; + core_data->irq_trig_cnt++; + /* inform external module */ + mutex_lock(&goodix_modules.mutex); + list_for_each_entry_safe(ext_module, next, + &goodix_modules.head, list) { + if (!ext_module->funcs->irq_event) + continue; + ret = ext_module->funcs->irq_event(core_data, ext_module); + if (ret == EVT_CANCEL_IRQEVT) { + mutex_unlock(&goodix_modules.mutex); + return IRQ_HANDLED; + } + } + mutex_unlock(&goodix_modules.mutex); + + /* read touch data from touch device */ + ret = hw_ops->event_handler(core_data, ts_event); + if (likely(!ret)) { + if (ts_event->event_type == EVENT_TOUCH) { + /* report touch */ + goodix_ts_report_finger(core_data->input_dev, + &ts_event->touch_data); + } + if (core_data->board_data.pen_enable && + ts_event->event_type == EVENT_PEN) { + goodix_ts_report_pen(core_data->pen_dev, + &ts_event->pen_data); + } + if (ts_event->event_type == EVENT_REQUEST) { + goodix_ts_request_handle(core_data, ts_event); + } + } + + if (!core_data->tools_ctrl_sync && !ts_event->retry) + hw_ops->after_event_handler(core_data); + ts_event->retry = 0; + + return IRQ_HANDLED; +} + +/** + * goodix_ts_init_irq - Requset interrput line from system + * @core_data: pointer to touch core data + * return: 0 ok, <0 failed + */ +static int goodix_ts_irq_setup(struct goodix_ts_core *core_data) +{ + const struct goodix_ts_board_data *ts_bdata = board_data(core_data); + int ret; + + /* if ts_bdata-> irq is invalid */ + core_data->irq = gpio_to_irq(ts_bdata->irq_gpio); + if (core_data->irq < 0) { + ts_err("failed get irq num %d", core_data->irq); + return -EINVAL; + } + + ts_info("IRQ:%u,flags:%d", core_data->irq, (int)ts_bdata->irq_flags); + ret = devm_request_threaded_irq(&core_data->pdev->dev, + core_data->irq, NULL, + goodix_ts_threadirq_func, + ts_bdata->irq_flags | IRQF_ONESHOT, + GOODIX_CORE_DRIVER_NAME, + core_data); + if (ret < 0) + ts_err("Failed to requeset threaded irq:%d", ret); + else + atomic_set(&core_data->irq_enabled, 1); + + return ret; +} + +/** + * goodix_ts_power_init - Get regulator for touch device + * @core_data: pointer to touch core data + * return: 0 ok, <0 failed + */ +static int goodix_ts_power_init(struct goodix_ts_core *core_data) +{ + struct goodix_ts_board_data *ts_bdata = board_data(core_data); + struct device *dev = core_data->bus->dev; + int ret = 0; + + ts_info("Power init"); + if (strlen(ts_bdata->avdd_name)) { + core_data->avdd = devm_regulator_get(dev, + ts_bdata->avdd_name); + if (IS_ERR_OR_NULL(core_data->avdd)) { + ret = PTR_ERR(core_data->avdd); + ts_err("Failed to get regulator avdd:%d", ret); + core_data->avdd = NULL; + return ret; + } + } else { + ts_info("Avdd name is NULL"); + } + + if (strlen(ts_bdata->iovdd_name)) { + core_data->iovdd = devm_regulator_get(dev, + ts_bdata->iovdd_name); + if (IS_ERR_OR_NULL(core_data->iovdd)) { + ret = PTR_ERR(core_data->iovdd); + ts_err("Failed to get regulator iovdd:%d", ret); + core_data->iovdd = NULL; + } + } else { + ts_info("iovdd name is NULL"); + } + + return ret; +} + +/** + * goodix_ts_power_on - Turn on power to the touch device + * @core_data: pointer to touch core data + * return: 0 ok, <0 failed + */ +int goodix_ts_power_on(struct goodix_ts_core *cd) +{ + int ret = 0; + + ts_info("Device power on"); + if (cd->power_on) + return 0; + + ret = cd->hw_ops->power_on(cd, true); + if (!ret) + cd->power_on = 1; + else + ts_err("failed power on, %d", ret); + return ret; +} + +/** + * goodix_ts_power_off - Turn off power to the touch device + * @core_data: pointer to touch core data + * return: 0 ok, <0 failed + */ +int goodix_ts_power_off(struct goodix_ts_core *cd) +{ + int ret; + + ts_info("Device power off"); + if (!cd->power_on) + return 0; + + ret = cd->hw_ops->power_on(cd, false); + if (!ret) + cd->power_on = 0; + else + ts_err("failed power off, %d", ret); + + return ret; +} + +/** + * goodix_ts_gpio_setup - Request gpio resources from GPIO subsysten + * @core_data: pointer to touch core data + * return: 0 ok, <0 failed + */ +static int goodix_ts_gpio_setup(struct goodix_ts_core *core_data) +{ + struct goodix_ts_board_data *ts_bdata = board_data(core_data); + int r = 0; + + ts_info("GPIO setup,reset-gpio:%d, irq-gpio:%d", + ts_bdata->reset_gpio, ts_bdata->irq_gpio); + /* + * after kenerl3.13, gpio_ api is deprecated, new + * driver should use gpiod_ api. + */ + r = devm_gpio_request_one(&core_data->pdev->dev, ts_bdata->reset_gpio, + GPIOF_OUT_INIT_LOW, "ts_reset_gpio"); + if (r < 0) { + ts_err("Failed to request reset gpio, r:%d", r); + return r; + } + + r = devm_gpio_request_one(&core_data->pdev->dev, ts_bdata->irq_gpio, + GPIOF_IN, "ts_irq_gpio"); + if (r < 0) { + ts_err("Failed to request irq gpio, r:%d", r); + return r; + } + + if (ts_bdata->avdd_gpio > 0) { + r = devm_gpio_request_one(&core_data->pdev->dev, ts_bdata->avdd_gpio, + GPIOF_OUT_INIT_LOW, "ts_avdd_gpio"); + if (r < 0) { + ts_err("Failed to request avdd-gpio, r:%d", r); + return r; + } + } + + if (ts_bdata->iovdd_gpio > 0) { + r = devm_gpio_request_one(&core_data->pdev->dev, ts_bdata->iovdd_gpio, + GPIOF_OUT_INIT_LOW, "ts_iovdd_gpio"); + if (r < 0) { + ts_err("Failed to request iovdd-gpio, r:%d", r); + return r; + } + } + + return 0; +} + +/** + * goodix_ts_input_dev_config - Requset and config a input device + * then register it to input sybsystem. + * @core_data: pointer to touch core data + * return: 0 ok, <0 failed + */ +static int goodix_ts_input_dev_config(struct goodix_ts_core *core_data) +{ + struct goodix_ts_board_data *ts_bdata = board_data(core_data); + struct input_dev *input_dev = NULL; + int r; + + input_dev = input_allocate_device(); + if (!input_dev) { + ts_err("Failed to allocated input device"); + return -ENOMEM; + } + + core_data->input_dev = input_dev; + input_set_drvdata(input_dev, core_data); + + input_dev->name = GOODIX_CORE_DRIVER_NAME; + input_dev->phys = GOOIDX_INPUT_PHYS; + input_dev->id.product = 0xDEAD; + input_dev->id.vendor = 0xBEEF; + input_dev->id.version = 10427; + + __set_bit(EV_SYN, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(BTN_TOOL_FINGER, input_dev->keybit); + +#ifdef INPUT_PROP_DIRECT + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); +#endif + + /* set input parameters */ + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, ts_bdata->panel_max_x, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, ts_bdata->panel_max_y, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, ts_bdata->panel_max_w, 0, 0); +#ifdef INPUT_TYPE_B_PROTOCOL +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0) + input_mt_init_slots(input_dev, GOODIX_MAX_TOUCH, + INPUT_MT_DIRECT); +#else + input_mt_init_slots(input_dev, GOODIX_MAX_TOUCH); +#endif +#endif + + input_set_capability(input_dev, EV_KEY, KEY_POWER); + + r = input_register_device(input_dev); + if (r < 0) { + ts_err("Unable to register input device"); + input_free_device(input_dev); + return r; + } + + return 0; +} + +static int goodix_ts_pen_dev_config(struct goodix_ts_core *core_data) +{ + struct goodix_ts_board_data *ts_bdata = board_data(core_data); + struct input_dev *pen_dev = NULL; + int r; + + pen_dev = input_allocate_device(); + if (!pen_dev) { + ts_err("Failed to allocated pen device"); + return -ENOMEM; + } + + core_data->pen_dev = pen_dev; + input_set_drvdata(pen_dev, core_data); + + pen_dev->name = GOODIX_PEN_DRIVER_NAME; + pen_dev->id.product = 0xDEAD; + pen_dev->id.vendor = 0xBEEF; + pen_dev->id.version = 10427; + + pen_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + __set_bit(ABS_X, pen_dev->absbit); + __set_bit(ABS_Y, pen_dev->absbit); + __set_bit(ABS_TILT_X, pen_dev->absbit); + __set_bit(ABS_TILT_Y, pen_dev->absbit); + __set_bit(BTN_STYLUS, pen_dev->keybit); + __set_bit(BTN_STYLUS2, pen_dev->keybit); + __set_bit(BTN_TOUCH, pen_dev->keybit); + __set_bit(BTN_TOOL_PEN, pen_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, pen_dev->propbit); + input_set_abs_params(pen_dev, ABS_X, 0, ts_bdata->panel_max_x, 0, 0); + input_set_abs_params(pen_dev, ABS_Y, 0, ts_bdata->panel_max_y, 0, 0); + input_set_abs_params(pen_dev, ABS_PRESSURE, 0, + ts_bdata->panel_max_p, 0, 0); + input_set_abs_params(pen_dev, ABS_TILT_X, + -GOODIX_PEN_MAX_TILT, GOODIX_PEN_MAX_TILT, 0, 0); + input_set_abs_params(pen_dev, ABS_TILT_Y, + -GOODIX_PEN_MAX_TILT, GOODIX_PEN_MAX_TILT, 0, 0); + + r = input_register_device(pen_dev); + if (r < 0) { + ts_err("Unable to register pen device"); + input_free_device(pen_dev); + return r; + } + + return 0; +} + +void goodix_ts_input_dev_remove(struct goodix_ts_core *core_data) +{ + if (!core_data->input_dev) + return; + input_unregister_device(core_data->input_dev); + input_free_device(core_data->input_dev); + core_data->input_dev = NULL; +} + +void goodix_ts_pen_dev_remove(struct goodix_ts_core *core_data) +{ + if (!core_data->pen_dev) + return; + input_unregister_device(core_data->pen_dev); + input_free_device(core_data->pen_dev); + core_data->pen_dev = NULL; +} + +/** + * goodix_ts_esd_work - check hardware status and recovery + * the hardware if needed. + */ +static void goodix_ts_esd_work(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct goodix_ts_esd *ts_esd = container_of(dwork, + struct goodix_ts_esd, esd_work); + struct goodix_ts_core *cd = container_of(ts_esd, + struct goodix_ts_core, ts_esd); + const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + int ret = 0; + + if (ts_esd->irq_status) + goto exit; + + if (!atomic_read(&ts_esd->esd_on)) + return; + + if (!hw_ops->esd_check) + return; + + ret = hw_ops->esd_check(cd); + if (ret) { + ts_err("esd check failed"); + goodix_ts_power_off(cd); + usleep_range(5000, 5100); + goodix_ts_power_on(cd); + } + +exit: + ts_esd->irq_status = false; + if (atomic_read(&ts_esd->esd_on)) + schedule_delayed_work(&ts_esd->esd_work, 2 * HZ); +} + +/** + * goodix_ts_esd_on - turn on esd protection + */ +static void goodix_ts_esd_on(struct goodix_ts_core *cd) +{ + struct goodix_ic_info_misc *misc = &cd->ic_info.misc; + struct goodix_ts_esd *ts_esd = &cd->ts_esd; + + if (!misc->esd_addr) + return; + + if (atomic_read(&ts_esd->esd_on)) + return; + + atomic_set(&ts_esd->esd_on, 1); + if (!schedule_delayed_work(&ts_esd->esd_work, 2 * HZ)) { + ts_info("esd work already in workqueue"); + } + ts_info("esd on"); +} + +/** + * goodix_ts_esd_off - turn off esd protection + */ +static void goodix_ts_esd_off(struct goodix_ts_core *cd) +{ + struct goodix_ts_esd *ts_esd = &cd->ts_esd; + int ret; + + if (!atomic_read(&ts_esd->esd_on)) + return; + + atomic_set(&ts_esd->esd_on, 0); + ret = cancel_delayed_work_sync(&ts_esd->esd_work); + ts_info("Esd off, esd work state %d", ret); +} + +/** + * goodix_esd_notifier_callback - notification callback + * under certain condition, we need to turn off/on the esd + * protector, we use kernel notify call chain to achieve this. + * + * for example: before firmware update we need to turn off the + * esd protector and after firmware update finished, we should + * turn on the esd protector. + */ +static int goodix_esd_notifier_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct goodix_ts_esd *ts_esd = container_of(nb, + struct goodix_ts_esd, esd_notifier); + + switch (action) { + case NOTIFY_FWUPDATE_START: + case NOTIFY_SUSPEND: + case NOTIFY_ESD_OFF: + goodix_ts_esd_off(ts_esd->ts_core); + break; + case NOTIFY_FWUPDATE_FAILED: + case NOTIFY_FWUPDATE_SUCCESS: + case NOTIFY_RESUME: + case NOTIFY_ESD_ON: + goodix_ts_esd_on(ts_esd->ts_core); + break; + default: + break; + } + + return 0; +} + +/** + * goodix_ts_esd_init - initialize esd protection + */ +int goodix_ts_esd_init(struct goodix_ts_core *cd) +{ + struct goodix_ic_info_misc *misc = &cd->ic_info.misc; + struct goodix_ts_esd *ts_esd = &cd->ts_esd; + + if (!cd->hw_ops->esd_check || !misc->esd_addr) { + ts_info("missing key info for esd check"); + return 0; + } + + INIT_DELAYED_WORK(&ts_esd->esd_work, goodix_ts_esd_work); + ts_esd->ts_core = cd; + atomic_set(&ts_esd->esd_on, 0); + ts_esd->esd_notifier.notifier_call = goodix_esd_notifier_callback; + goodix_ts_register_notifier(&ts_esd->esd_notifier); + goodix_ts_esd_on(cd); + + return 0; +} + +static void goodix_ts_release_connects(struct goodix_ts_core *core_data) +{ + struct input_dev *input_dev = core_data->input_dev; + int i; + + mutex_lock(&input_dev->mutex); + + for (i = 0; i < GOODIX_MAX_TOUCH; i++) { + input_mt_slot(input_dev, i); + input_mt_report_slot_state(input_dev, + MT_TOOL_FINGER, + false); + } + input_report_key(input_dev, BTN_TOUCH, 0); + input_mt_sync_frame(input_dev); + input_sync(input_dev); + + mutex_unlock(&input_dev->mutex); +} + +/** + * goodix_ts_suspend - Touchscreen suspend function + * Called by PM/FB/EARLYSUSPEN module to put the device to sleep + */ +static int goodix_ts_suspend(struct goodix_ts_core *core_data) +{ + struct goodix_ext_module *ext_module, *next; + struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + int ret; + + if (core_data->init_stage < CORE_INIT_STAGE2 || + atomic_read(&core_data->suspended)) + return 0; + + ts_info("Suspend start"); + atomic_set(&core_data->suspended, 1); + /* disable irq */ + hw_ops->irq_enable(core_data, false); + + /* + * notify suspend event, inform the esd protector + * and charger detector to turn off the work + */ + goodix_ts_blocking_notify(NOTIFY_SUSPEND, NULL); + + /* inform external module */ + mutex_lock(&goodix_modules.mutex); + if (!list_empty(&goodix_modules.head)) { + list_for_each_entry_safe(ext_module, next, + &goodix_modules.head, list) { + if (!ext_module->funcs->before_suspend) + continue; + + ret = ext_module->funcs->before_suspend(core_data, + ext_module); + if (ret == EVT_CANCEL_SUSPEND) { + mutex_unlock(&goodix_modules.mutex); + ts_info("Canceled by module:%s", + ext_module->name); + goto out; + } + } + } + mutex_unlock(&goodix_modules.mutex); + + /* enter sleep mode or power off */ + if (hw_ops->suspend) + hw_ops->suspend(core_data); + + /* inform exteranl modules */ + mutex_lock(&goodix_modules.mutex); + if (!list_empty(&goodix_modules.head)) { + list_for_each_entry_safe(ext_module, next, + &goodix_modules.head, list) { + if (!ext_module->funcs->after_suspend) + continue; + + ret = ext_module->funcs->after_suspend(core_data, + ext_module); + if (ret == EVT_CANCEL_SUSPEND) { + mutex_unlock(&goodix_modules.mutex); + ts_info("Canceled by module:%s", + ext_module->name); + goto out; + } + } + } + mutex_unlock(&goodix_modules.mutex); + +out: + goodix_ts_release_connects(core_data); + ts_info("Suspend end"); + return 0; +} + +/** + * goodix_ts_resume - Touchscreen resume function + * Called by PM/FB/EARLYSUSPEN module to wakeup device + */ +static int goodix_ts_resume(struct goodix_ts_core *core_data) +{ + struct goodix_ext_module *ext_module, *next; + struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + int ret; + + if (core_data->init_stage < CORE_INIT_STAGE2 || + !atomic_read(&core_data->suspended)) + return 0; + + ts_info("Resume start"); + atomic_set(&core_data->suspended, 0); + + mutex_lock(&goodix_modules.mutex); + if (!list_empty(&goodix_modules.head)) { + list_for_each_entry_safe(ext_module, next, + &goodix_modules.head, list) { + if (!ext_module->funcs->before_resume) + continue; + + ret = ext_module->funcs->before_resume(core_data, + ext_module); + if (ret == EVT_CANCEL_RESUME) { + mutex_unlock(&goodix_modules.mutex); + ts_info("Canceled by module:%s", + ext_module->name); + goto out; + } + } + } + mutex_unlock(&goodix_modules.mutex); + + /* reset device or power on*/ + if (hw_ops->resume) + hw_ops->resume(core_data); + + mutex_lock(&goodix_modules.mutex); + if (!list_empty(&goodix_modules.head)) { + list_for_each_entry_safe(ext_module, next, + &goodix_modules.head, list) { + if (!ext_module->funcs->after_resume) + continue; + + ret = ext_module->funcs->after_resume(core_data, + ext_module); + if (ret == EVT_CANCEL_RESUME) { + mutex_unlock(&goodix_modules.mutex); + ts_info("Canceled by module:%s", + ext_module->name); + goto out; + } + } + } + mutex_unlock(&goodix_modules.mutex); + +out: + /* enable irq */ + hw_ops->irq_enable(core_data, true); + /* open esd */ + goodix_ts_blocking_notify(NOTIFY_RESUME, NULL); + ts_info("Resume end"); + return 0; +} + +#ifdef CONFIG_FB +/** + * goodix_ts_fb_notifier_callback - Framebuffer notifier callback + * Called by kernel during framebuffer blanck/unblank phrase + */ +int goodix_ts_fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct goodix_ts_core *core_data = + container_of(self, struct goodix_ts_core, fb_notifier); + struct fb_event *fb_event = data; + + if (fb_event && fb_event->data && core_data) { + if (event == FB_EARLY_EVENT_BLANK) { + /* before fb blank */ + } else if (event == FB_EVENT_BLANK) { + int *blank = fb_event->data; + if (*blank == FB_BLANK_UNBLANK) + goodix_ts_resume(core_data); + else if (*blank == FB_BLANK_POWERDOWN) + goodix_ts_suspend(core_data); + } + } + + return 0; +} +#endif + + +#ifdef CONFIG_PM +#if !defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND) +/** + * goodix_ts_pm_suspend - PM suspend function + * Called by kernel during system suspend phrase + */ +static int goodix_ts_pm_suspend(struct device *dev) +{ + struct goodix_ts_core *core_data = + dev_get_drvdata(dev); + + return goodix_ts_suspend(core_data); +} +/** + * goodix_ts_pm_resume - PM resume function + * Called by kernel during system wakeup + */ +static int goodix_ts_pm_resume(struct device *dev) +{ + struct goodix_ts_core *core_data = + dev_get_drvdata(dev); + + return goodix_ts_resume(core_data); +} +#endif +#endif + +/** + * goodix_generic_noti_callback - generic notifier callback + * for goodix touch notification event. + */ +static int goodix_generic_noti_callback(struct notifier_block *self, + unsigned long action, void *data) +{ + struct goodix_ts_core *cd = container_of(self, + struct goodix_ts_core, ts_notifier); + const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + + if (cd->init_stage < CORE_INIT_STAGE2) + return 0; + + ts_info("notify event type 0x%x", (unsigned int)action); + switch (action) { + case NOTIFY_FWUPDATE_START: + hw_ops->irq_enable(cd, 0); + break; + case NOTIFY_FWUPDATE_SUCCESS: + case NOTIFY_FWUPDATE_FAILED: + if (hw_ops->read_version(cd, &cd->fw_version)) + ts_info("failed read fw version info[ignore]"); + hw_ops->irq_enable(cd, 1); + break; + default: + break; + } + return 0; +} + +int goodix_ts_stage2_init(struct goodix_ts_core *cd) +{ + int ret; + + /* alloc/config/register input device */ + ret = goodix_ts_input_dev_config(cd); + if (ret < 0) { + ts_err("failed set input device"); + return ret; + } + + if (cd->board_data.pen_enable) { + ret = goodix_ts_pen_dev_config(cd); + if (ret < 0) { + ts_err("failed set pen device"); + goto err_finger; + } + } + /* request irq line */ + ret = goodix_ts_irq_setup(cd); + if (ret < 0) { + ts_info("failed set irq"); + goto exit; + } + ts_info("success register irq"); + +#ifdef CONFIG_FB + cd->fb_notifier.notifier_call = goodix_ts_fb_notifier_callback; + if (fb_register_client(&cd->fb_notifier)) + ts_err("Failed to register fb notifier client:%d", ret); +#endif + /* create sysfs files */ + goodix_ts_sysfs_init(cd); + + /* create procfs files */ + goodix_ts_procfs_init(cd); + + /* esd protector */ + goodix_ts_esd_init(cd); + + /* gesture init */ + gesture_module_init(); + + /* inspect init */ + inspect_module_init(); + + return 0; +exit: + goodix_ts_pen_dev_remove(cd); +err_finger: + goodix_ts_input_dev_remove(cd); + return ret; +} + +/* try send the config specified with type */ +static int goodix_send_ic_config(struct goodix_ts_core *cd, int type) +{ + u32 config_id; + struct goodix_ic_config *cfg; + + if (type >= GOODIX_MAX_CONFIG_GROUP) { + ts_err("unsupproted config type %d", type); + return -EINVAL; + } + + cfg = cd->ic_configs[type]; + if (!cfg || cfg->len <= 0) { + ts_info("no valid normal config found"); + return -EINVAL; + } + + config_id = goodix_get_file_config_id(cfg->data); + if (cd->ic_info.version.config_id == config_id) { + ts_info("config id is equal 0x%x, skiped", config_id); + return 0; + } + + ts_info("try send config, id=0x%x", config_id); + return cd->hw_ops->send_config(cd, cfg->data, cfg->len); +} + +/** + * goodix_later_init_thread - init IC fw and config + * @data: point to goodix_ts_core + * + * This function respond for get fw version and try upgrade fw and config. + * Note: when init encounter error, need release all resource allocated here. + */ +static int goodix_later_init_thread(void *data) +{ + int ret, i; + int update_flag = UPDATE_MODE_BLOCK | UPDATE_MODE_SRC_REQUEST; + struct goodix_ts_core *cd = data; + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + + /* step 1: read version */ + ret = cd->hw_ops->read_version(cd, &cd->fw_version); + if (ret < 0) { + ts_err("failed to get version info, try to upgrade"); + update_flag |= UPDATE_MODE_FORCE; + goto upgrade; + } + + /* step 2: get config data from config bin */ + ret = goodix_get_config_proc(cd); + if (ret) + ts_info("no valid ic config found"); + else + ts_info("success get valid ic config"); + +upgrade: + /* step 3: init fw struct add try do fw upgrade */ + ret = goodix_fw_update_init(cd); + if (ret) { + ts_err("failed init fw update module"); + goto err_out; + } + + ts_info("update flag: 0x%X", update_flag); + ret = goodix_do_fw_update(cd->ic_configs[CONFIG_TYPE_NORMAL], update_flag); + if (ret) + ts_err("failed do fw update"); + + /* step 4: get fw version and ic_info + * at this step we believe that the ic is in normal mode, + * if the version info is invalid there must have some + * problem we cann't cover so exit init directly. + */ + ret = hw_ops->read_version(cd, &cd->fw_version); + if (ret) { + ts_err("invalid fw version, abort"); + goto uninit_fw; + } + ret = hw_ops->get_ic_info(cd, &cd->ic_info); + if (ret) { + ts_err("invalid ic info, abort"); + goto uninit_fw; + } + + /* the recomend way to update ic config is throuth ISP, + * if not we will send config with interactive mode + */ + goodix_send_ic_config(cd, CONFIG_TYPE_NORMAL); + + /* init other resources */ + ret = goodix_ts_stage2_init(cd); + if (ret) { + ts_err("stage2 init failed"); + goto uninit_fw; + } + cd->init_stage = CORE_INIT_STAGE2; + + return 0; + +uninit_fw: + goodix_fw_update_uninit(); +err_out: + ts_err("stage2 init failed"); + cd->init_stage = CORE_INIT_FAIL; + for (i = 0; i < GOODIX_MAX_CONFIG_GROUP; i++) { + if (cd->ic_configs[i]) + kfree(cd->ic_configs[i]); + cd->ic_configs[i] = NULL; + } + return ret; +} + +static int goodix_start_later_init(struct goodix_ts_core *ts_core) +{ + struct task_struct *init_thrd; + /* create and run update thread */ + init_thrd = kthread_run(goodix_later_init_thread, + ts_core, "goodix_init_thread"); + if (IS_ERR_OR_NULL(init_thrd)) { + ts_err("Failed to create update thread:%ld", + PTR_ERR(init_thrd)); + return -EFAULT; + } + return 0; +} + +/** + * goodix_ts_probe - called by kernel when Goodix touch + * platform driver is added. + */ +static int goodix_ts_probe(struct platform_device *pdev) +{ + struct goodix_ts_core *core_data = NULL; + struct goodix_bus_interface *bus_interface; + int ret; + + ts_info("goodix_ts_probe IN"); + + bus_interface = pdev->dev.platform_data; + if (!bus_interface) { + ts_err("Invalid touch device"); + core_module_prob_sate = CORE_MODULE_PROB_FAILED; + return -ENODEV; + } + + core_data = devm_kzalloc(&pdev->dev, + sizeof(struct goodix_ts_core), GFP_KERNEL); + if (!core_data) { + ts_err("Failed to allocate memory for core data"); + core_module_prob_sate = CORE_MODULE_PROB_FAILED; + return -ENOMEM; + } + + if (IS_ENABLED(CONFIG_OF) && bus_interface->dev->of_node) { + /* parse devicetree property */ + ret = goodix_parse_dt(bus_interface->dev->of_node, + &core_data->board_data); + if (ret) { + ts_err("failed parse device info form dts, %d", ret); + return -EINVAL; + } + } else { + ts_err("no valid device tree node found"); + return -ENODEV; + } + + core_data->hw_ops = goodix_get_hw_ops(); + if (!core_data->hw_ops) { + ts_err("hw ops is NULL"); + core_module_prob_sate = CORE_MODULE_PROB_FAILED; + return -EINVAL; + } + goodix_core_module_init(); + /* touch core layer is a platform driver */ + core_data->pdev = pdev; + core_data->bus = bus_interface; + platform_set_drvdata(pdev, core_data); + + /* get GPIO resource */ + ret = goodix_ts_gpio_setup(core_data); + if (ret) { + ts_err("failed init gpio"); + goto err_out; + } + + ret = goodix_ts_power_init(core_data); + if (ret) { + ts_err("failed init power"); + goto err_out; + } + + ret = goodix_ts_power_on(core_data); + if (ret) { + ts_err("failed power on"); + goto err_out; + } + + /* generic notifier callback */ + core_data->ts_notifier.notifier_call = goodix_generic_noti_callback; + goodix_ts_register_notifier(&core_data->ts_notifier); + + /* debug node init */ + goodix_tools_init(); + + core_data->init_stage = CORE_INIT_STAGE1; + goodix_modules.core_data = core_data; + core_module_prob_sate = CORE_MODULE_PROB_SUCCESS; + + /* Try start a thread to get config-bin info */ + goodix_start_later_init(core_data); + + ts_info("goodix_ts_core probe success"); + return 0; + +err_out: + core_data->init_stage = CORE_INIT_FAIL; + core_module_prob_sate = CORE_MODULE_PROB_FAILED; + ts_err("goodix_ts_core failed, ret:%d", ret); + return ret; +} + +static int goodix_ts_remove(struct platform_device *pdev) +{ + struct goodix_ts_core *core_data = platform_get_drvdata(pdev); + struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + struct goodix_ts_esd *ts_esd = &core_data->ts_esd; + + goodix_ts_unregister_notifier(&core_data->ts_notifier); + goodix_tools_exit(); + + if (core_data->init_stage >= CORE_INIT_STAGE2) { + gesture_module_exit(); + inspect_module_exit(); + hw_ops->irq_enable(core_data, false); + #ifdef CONFIG_FB + fb_unregister_client(&core_data->fb_notifier); + #endif + core_module_prob_sate = CORE_MODULE_REMOVED; + if (atomic_read(&core_data->ts_esd.esd_on)) + goodix_ts_esd_off(core_data); + goodix_ts_unregister_notifier(&ts_esd->esd_notifier); + + goodix_fw_update_uninit(); + goodix_ts_input_dev_remove(core_data); + goodix_ts_pen_dev_remove(core_data); + goodix_ts_sysfs_exit(core_data); + goodix_ts_procfs_exit(core_data); + goodix_ts_power_off(core_data); + } + + return 0; +} + +#ifdef CONFIG_PM +static const struct dev_pm_ops dev_pm_ops = { +#if !defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND) + .suspend = goodix_ts_pm_suspend, + .resume = goodix_ts_pm_resume, +#endif +}; +#endif + +static const struct platform_device_id ts_core_ids[] = { + {.name = GOODIX_CORE_DRIVER_NAME}, + {} +}; +MODULE_DEVICE_TABLE(platform, ts_core_ids); + +static struct platform_driver goodix_ts_driver = { + .driver = { + .name = GOODIX_CORE_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &dev_pm_ops, +#endif + }, + .probe = goodix_ts_probe, + .remove = goodix_ts_remove, + .id_table = ts_core_ids, +}; + +static int __init goodix_ts_core_init(void) +{ + int ret; + + ts_info("Core layer init:%s", GOODIX_DRIVER_VERSION); +#ifdef CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI + ret = goodix_spi_bus_init(); +#else + ret = goodix_i2c_bus_init(); +#endif + if (ret) { + ts_err("failed add bus driver"); + return ret; + } + return platform_driver_register(&goodix_ts_driver); +} + +static void __exit goodix_ts_core_exit(void) +{ + ts_info("Core layer exit"); + platform_driver_unregister(&goodix_ts_driver); +#ifdef CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI + goodix_spi_bus_exit(); +#else + goodix_i2c_bus_exit(); +#endif +} + +late_initcall(goodix_ts_core_init); +module_exit(goodix_ts_core_exit); + +MODULE_DESCRIPTION("Goodix Touchscreen Core Module"); +MODULE_AUTHOR("Goodix, Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/goodix_ts_core.h b/goodix_ts_core.h new file mode 100644 index 0000000000..8cb0343605 --- /dev/null +++ b/goodix_ts_core.h @@ -0,0 +1,630 @@ +#ifndef _GOODIX_TS_CORE_H_ +#define _GOODIX_TS_CORE_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#include +#endif +#ifdef CONFIG_FB +#include +#include +#endif + +#define GOODIX_CORE_DRIVER_NAME "goodix_ts" +#define GOODIX_PEN_DRIVER_NAME "goodix_ts,pen" +#define GOODIX_DRIVER_VERSION "v1.2.3" +#define GOODIX_MAX_TOUCH 10 +#define GOODIX_PEN_MAX_PRESSURE 4096 +#define GOODIX_MAX_PEN_KEY 2 +#define GOODIX_PEN_MAX_TILT 90 +#define GOODIX_CFG_MAX_SIZE 4096 +#define GOODIX_MAX_STR_LABLE_LEN 32 +#define GOODIX_MAX_FRAMEDATA_LEN 1700 + +#define GOODIX_NORMAL_RESET_DELAY_MS 100 +#define GOODIX_HOLD_CPU_RESET_DELAY_MS 5 + +#define GOODIX_RETRY_3 3 +#define GOODIX_RETRY_5 5 +#define GOODIX_RETRY_10 10 + +#define TS_DEFAULT_FIRMWARE "goodix_firmware.bin" +#define TS_DEFAULT_CFG_BIN "goodix_cfg_group.bin" + +enum CORD_PROB_STA { + CORE_MODULE_UNPROBED = 0, + CORE_MODULE_PROB_SUCCESS = 1, + CORE_MODULE_PROB_FAILED = -1, + CORE_MODULE_REMOVED = -2, +}; + +enum GOODIX_ERR_CODE { + GOODIX_EBUS = (1<<0), + GOODIX_ECHECKSUM = (1<<1), + GOODIX_EVERSION = (1<<2), + GOODIX_ETIMEOUT = (1<<3), + GOODIX_EMEMCMP = (1<<4), + + GOODIX_EOTHER = (1<<7) +}; + +enum IC_TYPE_ID { + IC_TYPE_NONE, + IC_TYPE_NORMANDY, + IC_TYPE_NANJING, + IC_TYPE_YELLOWSTONE, + IC_TYPE_BERLIN_A, + IC_TYPE_BERLIN_B, + IC_TYPE_BERLIN_D +}; + +enum GOODIX_IC_CONFIG_TYPE { + CONFIG_TYPE_TEST = 0, + CONFIG_TYPE_NORMAL = 1, + CONFIG_TYPE_HIGHSENSE = 2, + CONFIG_TYPE_CHARGER = 3, + CONFIG_TYPE_CHARGER_HS = 4, + CONFIG_TYPE_HOLSTER = 5, + CONFIG_TYPE_HOSTER_CH = 6, + CONFIG_TYPE_OTHER = 7, + /* keep this at the last */ + GOODIX_MAX_CONFIG_GROUP = 8, +}; + +enum CHECKSUM_MODE { + CHECKSUM_MODE_U8_LE, + CHECKSUM_MODE_U16_LE, +}; + +#define MAX_SCAN_FREQ_NUM 5 +#define MAX_SCAN_RATE_NUM 5 +#define MAX_FREQ_NUM_STYLUS 8 +#define MAX_STYLUS_SCAN_FREQ_NUM 6 +#pragma pack(1) +struct frame_head { + uint8_t sync; + uint16_t frame_index; + uint16_t cur_frame_len; + uint16_t next_frame_len; + uint32_t data_en; /* 0- 7 for pack_en; 8 - 31 for type en */ + uint8_t touch_pack_index; + uint8_t stylus_pack_index; + uint8_t res; + uint16_t checksum; +}; + +struct goodix_fw_version { + u8 rom_pid[6]; /* rom PID */ + u8 rom_vid[3]; /* Mask VID */ + u8 rom_vid_reserved; + u8 patch_pid[8]; /* Patch PID */ + u8 patch_vid[4]; /* Patch VID */ + u8 patch_vid_reserved; + u8 sensor_id; + u8 reserved[2]; + u16 checksum; +}; + +struct goodix_ic_info_version { + u8 info_customer_id; + u8 info_version_id; + u8 ic_die_id; + u8 ic_version_id; + u32 config_id; + u8 config_version; + u8 frame_data_customer_id; + u8 frame_data_version_id; + u8 touch_data_customer_id; + u8 touch_data_version_id; + u8 reserved[3]; +}; + +struct goodix_ic_info_feature { /* feature info*/ + u16 freqhop_feature; + u16 calibration_feature; + u16 gesture_feature; + u16 side_touch_feature; + u16 stylus_feature; +}; + +struct goodix_ic_info_param { /* param */ + u8 drv_num; + u8 sen_num; + u8 button_num; + u8 force_num; + u8 active_scan_rate_num; + u16 active_scan_rate[MAX_SCAN_RATE_NUM]; + u8 mutual_freq_num; + u16 mutual_freq[MAX_SCAN_FREQ_NUM]; + u8 self_tx_freq_num; + u16 self_tx_freq[MAX_SCAN_FREQ_NUM]; + u8 self_rx_freq_num; + u16 self_rx_freq[MAX_SCAN_FREQ_NUM]; + u8 stylus_freq_num; + u16 stylus_freq[MAX_FREQ_NUM_STYLUS]; +}; + +struct goodix_ic_info_misc { /* other data */ + u32 cmd_addr; + u16 cmd_max_len; + u32 cmd_reply_addr; + u16 cmd_reply_len; + u32 fw_state_addr; + u16 fw_state_len; + u32 fw_buffer_addr; + u16 fw_buffer_max_len; + u32 frame_data_addr; + u16 frame_data_head_len; + u16 fw_attr_len; + u16 fw_log_len; + u8 pack_max_num; + u8 pack_compress_version; + u16 stylus_struct_len; + u16 mutual_struct_len; + u16 self_struct_len; + u16 noise_struct_len; + u32 touch_data_addr; + u16 touch_data_head_len; + u16 point_struct_len; + u16 reserved1; + u16 reserved2; + u32 mutual_rawdata_addr; + u32 mutual_diffdata_addr; + u32 mutual_refdata_addr; + u32 self_rawdata_addr; + u32 self_diffdata_addr; + u32 self_refdata_addr; + u32 iq_rawdata_addr; + u32 iq_refdata_addr; + u32 im_rawdata_addr; + u16 im_readata_len; + u32 noise_rawdata_addr; + u16 noise_rawdata_len; + u32 stylus_rawdata_addr; + u16 stylus_rawdata_len; + u32 noise_data_addr; + u32 esd_addr; +}; + +struct goodix_ic_info { + u16 length; + struct goodix_ic_info_version version; + struct goodix_ic_info_feature feature; + struct goodix_ic_info_param parm; + struct goodix_ic_info_misc misc; +}; +#pragma pack() + +/* + * struct ts_rawdata_info + * + */ +#define TS_RAWDATA_BUFF_MAX 7000 +#define TS_RAWDATA_RESULT_MAX 100 +struct ts_rawdata_info { + int used_size; //fill in rawdata size + s16 buff[TS_RAWDATA_BUFF_MAX]; + char result[TS_RAWDATA_RESULT_MAX]; +}; + +/* + * struct goodix_module - external modules container + * @head: external modules list + * @initilized: whether this struct is initilized + * @mutex: mutex lock + * @wq: workqueue to do register work + * @core_data: core_data pointer + */ +struct goodix_module { + struct list_head head; + bool initilized; + struct mutex mutex; + struct workqueue_struct *wq; + struct goodix_ts_core *core_data; +}; + +/* + * struct goodix_ts_board_data - board data + * @avdd_name: name of analoy regulator + * @iovdd_name: name of analoy regulator + * @reset_gpio: reset gpio number + * @irq_gpio: interrupt gpio number + * @irq_flag: irq trigger type + * @swap_axis: whether swaw x y axis + * @panel_max_x/y/w/p: resolution and size + * @pannel_key_map: key map + * @fw_name: name of the firmware image + */ +struct goodix_ts_board_data { + char avdd_name[GOODIX_MAX_STR_LABLE_LEN]; + char iovdd_name[GOODIX_MAX_STR_LABLE_LEN]; + int reset_gpio; + int irq_gpio; + int avdd_gpio; + int iovdd_gpio; + unsigned int irq_flags; + + unsigned int swap_axis; + unsigned int panel_max_x; + unsigned int panel_max_y; + unsigned int panel_max_w; /*major and minor*/ + unsigned int panel_max_p; /*pressure*/ + + bool pen_enable; + char fw_name[GOODIX_MAX_STR_LABLE_LEN]; + char cfg_bin_name[GOODIX_MAX_STR_LABLE_LEN]; +}; + +enum goodix_fw_update_mode { + UPDATE_MODE_DEFAULT = 0, + UPDATE_MODE_FORCE = (1<<0), /* force update mode */ + UPDATE_MODE_BLOCK = (1<<1), /* update in block mode */ + UPDATE_MODE_FLASH_CFG = (1<<2), /* reflash config */ + UPDATE_MODE_SRC_SYSFS = (1<<4), /* firmware file from sysfs */ + UPDATE_MODE_SRC_HEAD = (1<<5), /* firmware file from head file */ + UPDATE_MODE_SRC_REQUEST = (1<<6), /* request firmware */ + UPDATE_MODE_SRC_ARGS = (1<<7), /* firmware data from function args */ +}; + +#define MAX_CMD_DATA_LEN 10 +#define MAX_CMD_BUF_LEN 16 +#pragma pack(1) +struct goodix_ts_cmd { + union { + struct { + u8 state; + u8 ack; + u8 len; + u8 cmd; + u8 data[MAX_CMD_DATA_LEN]; + }; + u8 buf[MAX_CMD_BUF_LEN]; + }; +}; +#pragma pack() + +/* interrupt event type */ +enum ts_event_type { + EVENT_INVALID = 0, + EVENT_TOUCH = (1 << 0), /* finger touch event */ + EVENT_PEN = (1 << 1), /* pen event */ + EVENT_REQUEST = (1 << 2), + EVENT_GESTURE = (1 << 3), +}; + +enum ts_request_type { + REQUEST_TYPE_CONFIG = 1, + REQUEST_TYPE_RESET = 3, +}; + +/* notifier event */ +enum ts_notify_event { + NOTIFY_FWUPDATE_START, + NOTIFY_FWUPDATE_FAILED, + NOTIFY_FWUPDATE_SUCCESS, + NOTIFY_SUSPEND, + NOTIFY_RESUME, + NOTIFY_ESD_OFF, + NOTIFY_ESD_ON, + NOTIFY_CFG_BIN_FAILED, + NOTIFY_CFG_BIN_SUCCESS, +}; + +enum touch_point_status { + TS_NONE, + TS_RELEASE, + TS_TOUCH, +}; +/* coordinate package */ +struct goodix_ts_coords { + int status; /* NONE, RELEASE, TOUCH */ + unsigned int x, y, w, p; +}; + +struct goodix_pen_coords { + int status; /* NONE, RELEASE, TOUCH */ + int tool_type; /* BTN_TOOL_RUBBER BTN_TOOL_PEN */ + unsigned int x, y, p; + signed char tilt_x; + signed char tilt_y; +}; + +/* touch event data */ +struct goodix_touch_data { + int touch_num; + struct goodix_ts_coords coords[GOODIX_MAX_TOUCH]; +}; + +struct goodix_ts_key { + int status; + int code; +}; + +struct goodix_pen_data { + struct goodix_pen_coords coords; + struct goodix_ts_key keys[GOODIX_MAX_PEN_KEY]; +}; + +/* + * struct goodix_ts_event - touch event struct + * @event_type: touch event type, touch data or + * request event + * @event_data: event data + */ +struct goodix_ts_event { + int retry; + enum ts_event_type event_type; + u8 request_code; /* represent the request type */ + u8 gesture_type; + struct goodix_touch_data touch_data; + struct goodix_pen_data pen_data; +}; + +enum goodix_ic_bus_type { + GOODIX_BUS_TYPE_I2C, + GOODIX_BUS_TYPE_SPI, + GOODIX_BUS_TYPE_I3C, +}; + +struct goodix_bus_interface { + int bus_type; + int ic_type; + struct device *dev; + int (*read)(struct device *dev, unsigned int addr, + unsigned char *data, unsigned int len); + int (*write)(struct device *dev, unsigned int addr, + unsigned char *data, unsigned int len); +}; + +struct goodix_ts_hw_ops { + int (*power_on)(struct goodix_ts_core *cd, bool on); + int (*resume)(struct goodix_ts_core *cd); + int (*suspend)(struct goodix_ts_core *cd); + int (*gesture)(struct goodix_ts_core *cd, int gesture_type); + int (*reset)(struct goodix_ts_core *cd, int delay_ms); + int (*irq_enable)(struct goodix_ts_core *cd, bool enable); + int (*read)(struct goodix_ts_core *cd, unsigned int addr, + unsigned char *data, unsigned int len); + int (*write)(struct goodix_ts_core *cd, unsigned int addr, + unsigned char *data, unsigned int len); + int (*send_cmd)(struct goodix_ts_core *cd, struct goodix_ts_cmd *cmd); + int (*send_config)(struct goodix_ts_core *cd, u8 *config, int len); + int (*read_config)(struct goodix_ts_core *cd, u8 *config_data, int size); + int (*read_version)(struct goodix_ts_core *cd, struct goodix_fw_version *version); + int (*get_ic_info)(struct goodix_ts_core *cd, struct goodix_ic_info *ic_info); + int (*esd_check)(struct goodix_ts_core *cd); + int (*event_handler)(struct goodix_ts_core *cd, struct goodix_ts_event *ts_event); + int (*after_event_handler)(struct goodix_ts_core *cd); /* clean sync flag */ + int (*get_capacitance_data)(struct goodix_ts_core *cd, struct ts_rawdata_info *info); +}; + +/* + * struct goodix_ts_esd - esd protector structure + * @esd_work: esd delayed work + * @esd_on: 1 - turn on esd protection, 0 - turn + * off esd protection + */ +struct goodix_ts_esd { + bool irq_status; + atomic_t esd_on; + struct delayed_work esd_work; + struct notifier_block esd_notifier; + struct goodix_ts_core *ts_core; +}; + +enum goodix_core_init_stage { + CORE_UNINIT, + CORE_INIT_FAIL, + CORE_INIT_STAGE1, + CORE_INIT_STAGE2 +}; + +struct goodix_ic_config { + int len; + u8 data[GOODIX_CFG_MAX_SIZE]; +}; + +struct goodix_ts_core { + int init_stage; + struct platform_device *pdev; + struct goodix_fw_version fw_version; + struct goodix_ic_info ic_info; + struct goodix_bus_interface *bus; + struct goodix_ts_board_data board_data; + struct goodix_ts_hw_ops *hw_ops; + struct input_dev *input_dev; + struct input_dev *pen_dev; + /* TODO counld we remove this from core data? */ + struct goodix_ts_event ts_event; + + /* every pointer of this array represent a kind of config */ + struct goodix_ic_config *ic_configs[GOODIX_MAX_CONFIG_GROUP]; + struct regulator *avdd; + struct regulator *iovdd; + + int power_on; + int irq; + size_t irq_trig_cnt; + + atomic_t irq_enabled; + atomic_t suspended; + /* when this flag is true, driver should not clean the sync flag */ + bool tools_ctrl_sync; + + struct notifier_block ts_notifier; + struct goodix_ts_esd ts_esd; + +#ifdef CONFIG_FB + struct notifier_block fb_notifier; +#endif +}; + +/* external module structures */ +enum goodix_ext_priority { + EXTMOD_PRIO_RESERVED = 0, + EXTMOD_PRIO_FWUPDATE, + EXTMOD_PRIO_GESTURE, + EXTMOD_PRIO_HOTKNOT, + EXTMOD_PRIO_DBGTOOL, + EXTMOD_PRIO_DEFAULT, +}; + +#define EVT_HANDLED 0 +#define EVT_CONTINUE 0 +#define EVT_CANCEL 1 +#define EVT_CANCEL_IRQEVT 1 +#define EVT_CANCEL_SUSPEND 1 +#define EVT_CANCEL_RESUME 1 +#define EVT_CANCEL_RESET 1 + +struct goodix_ext_module; +/* external module's operations callback */ +struct goodix_ext_module_funcs { + int (*init)(struct goodix_ts_core *core_data, + struct goodix_ext_module *module); + int (*exit)(struct goodix_ts_core *core_data, + struct goodix_ext_module *module); + int (*before_reset)(struct goodix_ts_core *core_data, + struct goodix_ext_module *module); + int (*after_reset)(struct goodix_ts_core *core_data, + struct goodix_ext_module *module); + int (*before_suspend)(struct goodix_ts_core *core_data, + struct goodix_ext_module *module); + int (*after_suspend)(struct goodix_ts_core *core_data, + struct goodix_ext_module *module); + int (*before_resume)(struct goodix_ts_core *core_data, + struct goodix_ext_module *module); + int (*after_resume)(struct goodix_ts_core *core_data, + struct goodix_ext_module *module); + int (*irq_event)(struct goodix_ts_core *core_data, + struct goodix_ext_module *module); +}; + +/* + * struct goodix_ext_module - external module struct + * @list: list used to link into modules manager + * @name: name of external module + * @priority: module priority vlaue, zero is invalid + * @funcs: operations callback + * @priv_data: private data region + * @kobj: kobject + * @work: used to queue one work to do registration + */ +struct goodix_ext_module { + struct list_head list; + char *name; + enum goodix_ext_priority priority; + const struct goodix_ext_module_funcs *funcs; + void *priv_data; + struct kobject kobj; + struct work_struct work; +}; + +/* + * struct goodix_ext_attribute - exteranl attribute struct + * @attr: attribute + * @show: show interface of external attribute + * @store: store interface of external attribute + */ +struct goodix_ext_attribute { + struct attribute attr; + ssize_t (*show)(struct goodix_ext_module *, char *); + ssize_t (*store)(struct goodix_ext_module *, const char *, size_t); +}; + +/* external attrs helper macro */ +#define __EXTMOD_ATTR(_name, _mode, _show, _store) { \ + .attr = {.name = __stringify(_name), .mode = _mode }, \ + .show = _show, \ + .store = _store, \ +} + +/* external attrs helper macro, used to define external attrs */ +#define DEFINE_EXTMOD_ATTR(_name, _mode, _show, _store) \ +static struct goodix_ext_attribute ext_attr_##_name = \ + __EXTMOD_ATTR(_name, _mode, _show, _store); + +/* log macro */ +extern bool debug_log_flag; +#define ts_info(fmt, arg...) pr_info("[GTP-INF][%s:%d] "fmt"\n", __func__, __LINE__, ##arg) +#define ts_err(fmt, arg...) pr_err("[GTP-ERR][%s:%d] "fmt"\n", __func__, __LINE__, ##arg) +#define ts_debug(fmt, arg...) {if (debug_log_flag) pr_info("[GTP-DBG][%s:%d] "fmt"\n", __func__, __LINE__, ##arg);} + +/* + * get board data pointer + */ +static inline struct goodix_ts_board_data *board_data( + struct goodix_ts_core *core) +{ + if (!core) + return NULL; + return &(core->board_data); +} + +/** + * goodix_register_ext_module - interface for external module + * to register into touch core modules structure + * + * @module: pointer to external module to be register + * return: 0 ok, <0 failed + */ +int goodix_register_ext_module(struct goodix_ext_module *module); +/* register module no wait */ +int goodix_register_ext_module_no_wait(struct goodix_ext_module *module); +/** + * goodix_unregister_ext_module - interface for external module + * to unregister external modules + * + * @module: pointer to external module + * return: 0 ok, <0 failed + */ +int goodix_unregister_ext_module(struct goodix_ext_module *module); +/* remove all registered ext module + * return 0 on success, otherwise return < 0 + */ +int goodix_ts_blocking_notify(enum ts_notify_event evt, void *v); +struct kobj_type *goodix_get_default_ktype(void); +struct kobject *goodix_get_default_kobj(void); + +struct goodix_ts_hw_ops *goodix_get_hw_ops(void); +int goodix_get_config_proc(struct goodix_ts_core *cd); + +int goodix_spi_bus_init(void); +void goodix_spi_bus_exit(void); +int goodix_i2c_bus_init(void); +void goodix_i2c_bus_exit(void); + +u32 goodix_append_checksum(u8 *data, int len, int mode); +int checksum_cmp(const u8 *data, int size, int mode); +int is_risk_data(const u8 *data, int size); +u32 goodix_get_file_config_id(u8 *ic_config); +void goodix_rotate_abcd2cbad(int tx, int rx, s16 *data); +int goodix_gesture_enable(int enable); + +int goodix_fw_update_init(struct goodix_ts_core *core_data); +void goodix_fw_update_uninit(void); +int goodix_do_fw_update(struct goodix_ic_config *ic_config, int mode); + +int goodix_get_ic_type(struct device_node *node); +int gesture_module_init(void); +void gesture_module_exit(void); +int inspect_module_init(void); +void inspect_module_exit(void); +int goodix_tools_init(void); +void goodix_tools_exit(void); + +#endif diff --git a/goodix_ts_gesture.c b/goodix_ts_gesture.c new file mode 100644 index 0000000000..1687a57086 --- /dev/null +++ b/goodix_ts_gesture.c @@ -0,0 +1,384 @@ +/* + * Goodix Gesture Module + * + * Copyright (C) 2019 - 2020 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "goodix_ts_core.h" + +#define QUERYBIT(longlong, bit) (!!(longlong[bit/8] & (1 << bit%8))) + +#define GSX_GESTURE_TYPE_LEN 32 + +/* + * struct gesture_module - gesture module data + * @registered: module register state + * @sysfs_node_created: sysfs node state + * @gesture_type: valid gesture type, each bit represent one gesture type + * @gesture_data: store latest gesture code get from irq event + * @gesture_ts_cmd: gesture command data + */ +struct gesture_module { + atomic_t registered; + rwlock_t rwlock; + u8 gesture_type[GSX_GESTURE_TYPE_LEN]; + u8 gesture_data; + struct goodix_ext_module module; +}; + +static struct gesture_module *gsx_gesture; /*allocated in gesture init module*/ +static bool module_initialized; + + +int goodix_gesture_enable(int enable) +{ + int ret = 0; + + if (!module_initialized) + return 0; + + if (enable) { + if (atomic_read(&gsx_gesture->registered)) + ts_info("gesture module has been already registered"); + else + ret = goodix_register_ext_module_no_wait(&gsx_gesture->module); + } else { + if (!atomic_read(&gsx_gesture->registered)) + ts_info("gesture module has been already unregistered"); + else + ret = goodix_unregister_ext_module(&gsx_gesture->module); + } + + return ret; +} + +/** + * gsx_gesture_type_show - show valid gesture type + * + * @module: pointer to goodix_ext_module struct + * @buf: pointer to output buffer + * Returns >=0 - succeed,< 0 - failed + */ +static ssize_t gsx_gesture_type_show(struct goodix_ext_module *module, + char *buf) +{ + int count = 0, i, ret = 0; + unsigned char *type; + + type = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!type) + return -ENOMEM; + read_lock(&gsx_gesture->rwlock); + for (i = 0; i < 256; i++) { + if (QUERYBIT(gsx_gesture->gesture_type, i)) { + count += scnprintf(type + count, + PAGE_SIZE, "%02x,", i); + } + } + if (count > 0) + ret = scnprintf(buf, PAGE_SIZE, "%s\n", type); + read_unlock(&gsx_gesture->rwlock); + + kfree(type); + return ret; +} + +/** + * gsx_gesture_type_store - set vailed gesture + * + * @module: pointer to goodix_ext_module struct + * @buf: pointer to valid gesture type + * @count: length of buf + * Returns >0 - valid gestures, < 0 - failed + */ +static ssize_t gsx_gesture_type_store(struct goodix_ext_module *module, + const char *buf, size_t count) +{ + int i; + + if (count <= 0 || count > 256 || buf == NULL) { + ts_err("Parameter error"); + return -EINVAL; + } + + write_lock(&gsx_gesture->rwlock); + memset(gsx_gesture->gesture_type, 0, GSX_GESTURE_TYPE_LEN); + for (i = 0; i < count; i++) + gsx_gesture->gesture_type[buf[i]/8] |= (0x1 << buf[i]%8); + write_unlock(&gsx_gesture->rwlock); + + return count; +} + +static ssize_t gsx_gesture_enable_show(struct goodix_ext_module *module, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", + atomic_read(&gsx_gesture->registered)); +} + +static ssize_t gsx_gesture_enable_store(struct goodix_ext_module *module, + const char *buf, size_t count) +{ + bool val; + int ret; + + ret = strtobool(buf, &val); + if (ret < 0) + return ret; + + if (val) { + ret = goodix_gesture_enable(1); + return ret ? ret : count; + } else { + ret = goodix_gesture_enable(0); + return ret ? ret : count; + } +} + +static ssize_t gsx_gesture_data_show(struct goodix_ext_module *module, + char *buf) +{ + ssize_t count; + + read_lock(&gsx_gesture->rwlock); + count = scnprintf(buf, PAGE_SIZE, "gesture type code:0x%x\n", + gsx_gesture->gesture_data); + read_unlock(&gsx_gesture->rwlock); + + return count; +} + +const struct goodix_ext_attribute gesture_attrs[] = { + __EXTMOD_ATTR(type, 0666, gsx_gesture_type_show, + gsx_gesture_type_store), + __EXTMOD_ATTR(enable, 0666, gsx_gesture_enable_show, + gsx_gesture_enable_store), + __EXTMOD_ATTR(data, 0444, gsx_gesture_data_show, NULL) +}; + +static int gsx_gesture_init(struct goodix_ts_core *cd, + struct goodix_ext_module *module) +{ + if (!cd || !cd->hw_ops->gesture) { + ts_err("gesture unsupported"); + return -EINVAL; + } + + ts_info("gesture switch: ON"); + ts_debug("enable all gesture type"); + /* set all bit to 1 to enable all gesture wakeup */ + memset(gsx_gesture->gesture_type, 0xff, GSX_GESTURE_TYPE_LEN); + atomic_set(&gsx_gesture->registered, 1); + + return 0; +} + +static int gsx_gesture_exit(struct goodix_ts_core *cd, + struct goodix_ext_module *module) +{ + if (!cd || !cd->hw_ops->gesture) { + ts_err("gesture unsupported"); + return -EINVAL; + } + + ts_info("gesture switch: OFF"); + ts_debug("disable all gesture type"); + memset(gsx_gesture->gesture_type, 0x00, GSX_GESTURE_TYPE_LEN); + atomic_set(&gsx_gesture->registered, 0); + + return 0; +} + +/** + * gsx_gesture_ist - Gesture Irq handle + * This functions is excuted when interrupt happended and + * ic in doze mode. + * + * @cd: pointer to touch core data + * @module: pointer to goodix_ext_module struct + * return: 0 goon execute, EVT_CANCEL_IRQEVT stop execute + */ +static int gsx_gesture_ist(struct goodix_ts_core *cd, + struct goodix_ext_module *module) +{ + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + struct goodix_ts_event gs_event = {0}; + int ret; + + if (atomic_read(&cd->suspended) == 0) + return EVT_CONTINUE; + + ret = hw_ops->event_handler(cd, &gs_event); + if (ret) { + ts_err("failed get gesture data"); + goto re_send_ges_cmd; + } + + if (!(gs_event.event_type & EVENT_GESTURE)) { + ts_err("invalid event type: 0x%x", + cd->ts_event.event_type); + goto re_send_ges_cmd; + } + + if (QUERYBIT(gsx_gesture->gesture_type, + gs_event.gesture_type)) { + gsx_gesture->gesture_data = gs_event.gesture_type; + /* do resume routine */ + ts_info("got valid gesture type 0x%x", + gs_event.gesture_type); + input_report_key(cd->input_dev, KEY_POWER, 1); + input_sync(cd->input_dev); + input_report_key(cd->input_dev, KEY_POWER, 0); + input_sync(cd->input_dev); + goto gesture_ist_exit; + } else { + ts_info("unsupported gesture:%x", gs_event.gesture_type); + } + +re_send_ges_cmd: + if (hw_ops->gesture(cd, 0)) + ts_info("warning: failed re_send gesture cmd"); +gesture_ist_exit: + if (!cd->tools_ctrl_sync) + hw_ops->after_event_handler(cd); + return EVT_CANCEL_IRQEVT; +} + +/** + * gsx_gesture_before_suspend - execute gesture suspend routine + * This functions is excuted to set ic into doze mode + * + * @cd: pointer to touch core data + * @module: pointer to goodix_ext_module struct + * return: 0 goon execute, EVT_IRQCANCLED stop execute + */ +static int gsx_gesture_before_suspend(struct goodix_ts_core *cd, + struct goodix_ext_module *module) +{ + int ret; + const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + + ret = hw_ops->gesture(cd, 0); + if (ret) + ts_err("failed enter gesture mode"); + else + ts_info("enter gesture mode"); + + hw_ops->irq_enable(cd, true); + enable_irq_wake(cd->irq); + + return EVT_CANCEL_SUSPEND; +} + +static int gsx_gesture_before_resume(struct goodix_ts_core *cd, + struct goodix_ext_module *module) +{ + const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + + hw_ops->irq_enable(cd, false); + disable_irq_wake(cd->irq); + hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); + + return EVT_CANCEL_RESUME; +} + +static struct goodix_ext_module_funcs gsx_gesture_funcs = { + .irq_event = gsx_gesture_ist, + .init = gsx_gesture_init, + .exit = gsx_gesture_exit, + .before_suspend = gsx_gesture_before_suspend, + .before_resume = gsx_gesture_before_resume, +}; + +int gesture_module_init(void) +{ + int ret; + int i; + struct kobject *def_kobj = goodix_get_default_kobj(); + struct kobj_type *def_kobj_type = goodix_get_default_ktype(); + + gsx_gesture = kzalloc(sizeof(struct gesture_module), GFP_KERNEL); + if (!gsx_gesture) + return -ENOMEM; + + gsx_gesture->module.funcs = &gsx_gesture_funcs; + gsx_gesture->module.priority = EXTMOD_PRIO_GESTURE; + gsx_gesture->module.name = "Goodix_gsx_gesture"; + gsx_gesture->module.priv_data = gsx_gesture; + + atomic_set(&gsx_gesture->registered, 0); + rwlock_init(&gsx_gesture->rwlock); + + /* gesture sysfs init */ + ret = kobject_init_and_add(&gsx_gesture->module.kobj, + def_kobj_type, def_kobj, "gesture"); + if (ret) { + ts_err("failed create gesture sysfs node!"); + goto err_out; + } + + for (i = 0; i < ARRAY_SIZE(gesture_attrs) && !ret; i++) + ret = sysfs_create_file(&gsx_gesture->module.kobj, + &gesture_attrs[i].attr); + if (ret) { + ts_err("failed create gst sysfs files"); + while (--i >= 0) + sysfs_remove_file(&gsx_gesture->module.kobj, + &gesture_attrs[i].attr); + + kobject_put(&gsx_gesture->module.kobj); + goto err_out; + } + + module_initialized = true; + ts_info("gesture module init success"); + + return 0; + +err_out: + ts_err("gesture module init failed!"); + kfree(gsx_gesture); + return ret; +} + +void gesture_module_exit(void) +{ + int i; + + ts_info("gesture module exit"); + if (!module_initialized) + return; + + goodix_gesture_enable(0); + + /* deinit sysfs */ + for (i = 0; i < ARRAY_SIZE(gesture_attrs); i++) + sysfs_remove_file(&gsx_gesture->module.kobj, + &gesture_attrs[i].attr); + + kobject_put(&gsx_gesture->module.kobj); + kfree(gsx_gesture); + module_initialized = false; +} \ No newline at end of file diff --git a/goodix_ts_inspect.c b/goodix_ts_inspect.c new file mode 100644 index 0000000000..a96eceb169 --- /dev/null +++ b/goodix_ts_inspect.c @@ -0,0 +1,2919 @@ + /* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#include "goodix_ts_core.h" +#include +#include +#include +#include +#include + + +/* test config */ +#define TOTAL_FRAME_NUM 1 /* rawdata test frames */ +#define NOISEDATA_TEST_TIMES 1 /* noise test frames */ +#define SAVE_IN_CSV + +#define GOODIX_RESULT_SAVE_PATH "/vendor/etc/Test_Data.csv" +#define GOODIX_TEST_FILE_NAME "goodix" +#define MAX_DATA_BUFFER 28000 +#define MAX_SHORT_NUM 15 +#define MAX_LINE_LEN (1024 * 3 * 7) +#define MAX_DRV_NUM 52 +#define MAX_SEN_NUM 75 + +#define STATISTICS_DATA_LEN 32 +#define MAX_STR_LEN 32 +#define MAX_TEST_ITEMS 10 /* 0P-1P-2P-3P-5P total test items */ +#define GTP_CAP_TEST 1 +#define GTP_DELTA_TEST 2 +#define GTP_NOISE_TEST 3 +#define GTP_SHORT_TEST 5 +#define GTP_SELFCAP_TEST 6 +#define GTP_SELFNOISE_TEST 7 + +#define GTP_TEST_PASS 1 +#define GTP_PANEL_REASON 2 +#define SYS_SOFTWARE_REASON 3 + +#define CHN_VDD 0xFF +#define CHN_GND 0x7F +#define DRV_CHANNEL_FLAG 0x80 + +#define CSV_TP_SPECIAL_RAW_MIN "special_raw_min" +#define CSV_TP_SPECIAL_RAW_MAX "special_raw_max" +#define CSV_TP_SPECIAL_RAW_DELTA "special_raw_delta" +#define CSV_TP_SHORT_THRESHOLD "shortciurt_threshold" +#define CSV_TP_SPECIAL_SELFRAW_MAX "special_selfraw_max" +#define CSV_TP_SPECIAL_SELFRAW_MIN "special_selfraw_min" +#define CSV_TP_NOISE_LIMIT "noise_data_limit" +#define CSV_TP_SELFNOISE_LIMIT "noise_selfdata_limit" +#define CSV_TP_TEST_CONFIG "test_config" + +#define MAX_TEST_TIME_MS 15000 +#define DEFAULT_TEST_TIME_MS 7000 + +/* berlin A */ +#define MAX_DRV_NUM_BRA 21 +#define MAX_SEN_NUM_BRA 42 +#define SHORT_TEST_TIME_REG_BRA 0x11FF2 +#define DFT_ADC_DUMP_NUM_BRA 1396 +#define DFT_SHORT_THRESHOLD_BRA 16 +#define DFT_DIFFCODE_SHORT_THRESHOLD_BRA 16 +#define SHORT_TEST_STATUS_REG_BRA 0x10400 +#define SHORT_TEST_RESULT_REG_BRA 0x10410 +#define DRV_DRV_SELFCODE_REG_BRA 0x1045E +#define SEN_SEN_SELFCODE_REG_BRA 0x1084E +#define DRV_SEN_SELFCODE_REG_BRA 0x11712 +#define DIFF_CODE_DATA_REG_BRA 0x11F72 + +/* berlin B */ +#define MAX_DRV_NUM_BRB 52 +#define MAX_SEN_NUM_BRB 75 +#define SHORT_TEST_TIME_REG_BRB 0x26AE0 +#define DFT_ADC_DUMP_NUM_BRB 762 +#define DFT_SHORT_THRESHOLD_BRB 100 +#define DFT_DIFFCODE_SHORT_THRESHOLD_BRB 32 +#define SHORT_TEST_STATUS_REG_BRB 0x20400 +#define SHORT_TEST_RESULT_REG_BRB 0x20410 +#define DRV_DRV_SELFCODE_REG_BRB 0x2049A +#define SEN_SEN_SELFCODE_REG_BRB 0x21AF2 +#define DRV_SEN_SELFCODE_REG_BRB 0x248A6 +#define DIFF_CODE_DATA_REG_BRB 0x269E0 + +/* berlinD */ +#define MAX_DRV_NUM_BRD 20 +#define MAX_SEN_NUM_BRD 40 +#define SHORT_TEST_TIME_REG_BRD 0x14D7A +#define DFT_ADC_DUMP_NUM_BRD 762 +#define DFT_SHORT_THRESHOLD_BRD 100 +#define DFT_DIFFCODE_SHORT_THRESHOLD_BRD 32 +#define SHORT_TEST_STATUS_REG_BRD 0x13400 +#define SHORT_TEST_RESULT_REG_BRD 0x13408 +#define DRV_DRV_SELFCODE_REG_BRD 0x1344E +#define SEN_SEN_SELFCODE_REG_BRD 0x137E6 +#define DRV_SEN_SELFCODE_REG_BRD 0x14556 +#define DIFF_CODE_DATA_REG_BRD 0x14D00 + + +#define ABS(val) ((val < 0)? -(val) : val) +#define MAX(a, b) ((a > b)? a : b) + +static bool module_initialized; + +/* berlin A drv-sen map */ +static u8 brl_a_drv_map[] = { + 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62 +}; + +static u8 brl_a_sen_map[] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41 +}; + +/* berlin B drv-sen map */ +static u8 brl_b_drv_map[] = { + 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, + 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126 +}; + +static u8 brl_b_sen_map[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74 +}; + +/* berlin D drv-sen map */ +static u8 brl_d_drv_map[] = { + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, +}; + +static u8 brl_d_sen_map[] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, +}; + +typedef struct __attribute__((packed)) { + u8 result; + u8 drv_drv_num; + u8 sen_sen_num; + u8 drv_sen_num; + u8 drv_gnd_avdd_num; + u8 sen_gnd_avdd_num; + u16 checksum; +} test_result_t; + +struct params_info_t { + u32 max_drv_num; + u32 max_sen_num; + u8 *drv_map; + u8 *sen_map; + u32 short_test_time_reg; + u32 short_test_status_reg; + u32 short_test_result_reg; + u32 drv_drv_selfcode_reg; + u32 sen_sen_selfcode_reg; + u32 drv_sen_selfcode_reg; + u32 diffcode_data_reg; + u16 short_test_dump_num; + u16 dft_short_threshold; + u16 short_diffcode_threshold; +}; + +struct params_info_t params_bra = { + MAX_DRV_NUM_BRA, + MAX_SEN_NUM_BRA, + brl_a_drv_map, + brl_a_sen_map, + SHORT_TEST_TIME_REG_BRA, + SHORT_TEST_STATUS_REG_BRA, + SHORT_TEST_RESULT_REG_BRA, + DRV_DRV_SELFCODE_REG_BRA, + SEN_SEN_SELFCODE_REG_BRA, + DRV_SEN_SELFCODE_REG_BRA, + DIFF_CODE_DATA_REG_BRA, + DFT_ADC_DUMP_NUM_BRA, + DFT_SHORT_THRESHOLD_BRA, + DFT_DIFFCODE_SHORT_THRESHOLD_BRA, +}; + +struct params_info_t params_brb = { + MAX_DRV_NUM_BRB, + MAX_SEN_NUM_BRB, + brl_b_drv_map, + brl_b_sen_map, + SHORT_TEST_TIME_REG_BRB, + SHORT_TEST_STATUS_REG_BRB, + SHORT_TEST_RESULT_REG_BRB, + DRV_DRV_SELFCODE_REG_BRB, + SEN_SEN_SELFCODE_REG_BRB, + DRV_SEN_SELFCODE_REG_BRB, + DIFF_CODE_DATA_REG_BRB, + DFT_ADC_DUMP_NUM_BRB, + DFT_SHORT_THRESHOLD_BRB, + DFT_DIFFCODE_SHORT_THRESHOLD_BRB, +}; + +struct params_info_t params_brd = { + MAX_DRV_NUM_BRD, + MAX_SEN_NUM_BRD, + brl_d_drv_map, + brl_d_sen_map, + SHORT_TEST_TIME_REG_BRD, + SHORT_TEST_STATUS_REG_BRD, + SHORT_TEST_RESULT_REG_BRD, + DRV_DRV_SELFCODE_REG_BRD, + SEN_SEN_SELFCODE_REG_BRD, + DRV_SEN_SELFCODE_REG_BRD, + DIFF_CODE_DATA_REG_BRD, + DFT_ADC_DUMP_NUM_BRD, + DFT_SHORT_THRESHOLD_BRD, + DFT_DIFFCODE_SHORT_THRESHOLD_BRD, +}; + +struct ts_test_params { + bool test_items[MAX_TEST_ITEMS]; + + u32 rawdata_addr; + u32 noisedata_addr; + u32 self_rawdata_addr; + u32 self_noisedata_addr; + + u32 drv_num; + u32 sen_num; + + struct params_info_t *params_info; + + s32 cfg_buf[GOODIX_CFG_MAX_SIZE]; + s32 max_limits[MAX_DRV_NUM * MAX_SEN_NUM]; + s32 min_limits[MAX_DRV_NUM * MAX_SEN_NUM]; + s32 deviation_limits[MAX_DRV_NUM * MAX_SEN_NUM]; + s32 self_max_limits[MAX_DRV_NUM + MAX_SEN_NUM]; + s32 self_min_limits[MAX_DRV_NUM + MAX_SEN_NUM]; + s32 noise_threshold; + s32 self_noise_threshold; + + u32 short_threshold; + u32 r_drv_drv_threshold; + u32 r_drv_sen_threshold; + u32 r_sen_sen_threshold; + u32 r_drv_gnd_threshold; + u32 r_sen_gnd_threshold; + u32 avdd_value; +}; + +struct ts_test_rawdata { + s16 data[MAX_DRV_NUM * MAX_SEN_NUM]; + u32 size; +}; + +struct ts_test_self_rawdata { + s16 data[MAX_DRV_NUM + MAX_SEN_NUM]; + u32 size; +}; + +struct ts_short_res { + u8 short_num; + s16 short_msg[4 * MAX_SHORT_NUM]; +}; + +struct ts_open_res { + u8 beyond_max_limit_cnt[MAX_DRV_NUM * MAX_SEN_NUM]; + u8 beyond_min_limit_cnt[MAX_DRV_NUM * MAX_SEN_NUM]; + u8 beyond_accord_limit_cnt[MAX_DRV_NUM * MAX_SEN_NUM]; +}; + +struct goodix_ts_test { + struct goodix_ts_core *ts; + struct ts_test_params test_params; + struct ts_test_rawdata rawdata[TOTAL_FRAME_NUM]; + struct ts_test_rawdata accord_arr[TOTAL_FRAME_NUM]; + struct ts_test_rawdata noisedata[NOISEDATA_TEST_TIMES]; + struct goodix_ic_config test_config; + struct ts_test_self_rawdata self_rawdata; + struct ts_test_self_rawdata self_noisedata; + struct ts_short_res short_res; + struct ts_open_res open_res; + + /*[0][0][0][0][0].. 0 without test; 1 pass, 2 panel failed; 3 software failed */ + char test_result[MAX_TEST_ITEMS]; + char test_info[TS_RAWDATA_RESULT_MAX]; +}; + +static int cal_cha_to_cha_res(struct goodix_ts_test *ts_test, int v1, int v2) +{ + if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) + return (v1 - v2) * 63 / v2; + else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B) + return (v1 - v2) * 74 / v2 + 20; + else + return (v1 / v2 - 1) * 70 + 59; +} + +static int cal_cha_to_avdd_res(struct goodix_ts_test *ts_test, int v1, int v2) +{ + if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) + return 125 * 1024 * (100 * v2 - 125) * 40 / (10000 * v1) - 40; + else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B) + return 125 * 1024 * (100 * v2 - 125) * 99 / (10000 * v1) - 60; + else + return 125 * 1024 * (100 * v2 - 125) * 93 / (10000 * v1) - 20; +} + +static int cal_cha_to_gnd_res(struct goodix_ts_test *ts_test, int v) +{ + if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) + return 64148 / v - 40; + else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B) + return 150500 / v - 60; + else + return 145000 / v - 15; +} + +static int ts_test_reset(struct goodix_ts_test *ts_test, + u32 delay_ms) +{ + return ts_test->ts->hw_ops->reset(ts_test->ts, delay_ms); +} + +static int ts_test_read(struct goodix_ts_test *ts_test, + u32 addr, u8 *data, u32 len) +{ + return ts_test->ts->hw_ops->read(ts_test->ts, addr, data, len); +} + +static int ts_test_write(struct goodix_ts_test *ts_test, + u32 addr, u8 *data, u32 len) +{ + return ts_test->ts->hw_ops->write(ts_test->ts, addr, data, len); +} + +static int ts_test_send_cmd(struct goodix_ts_test *ts_test, + struct goodix_ts_cmd *cmd) +{ + return ts_test->ts->hw_ops->send_cmd(ts_test->ts, cmd); +} + +static int ts_test_irq_enable(struct goodix_ts_test *ts_test, + bool flag) +{ + return ts_test->ts->hw_ops->irq_enable(ts_test->ts, flag); +} + +static int ts_test_send_config(struct goodix_ts_test *ts_test, + int type) +{ + struct goodix_ic_config *cfg; + + if (type >= GOODIX_MAX_CONFIG_GROUP) { + ts_err("unsupproted config type %d", type); + return -EINVAL; + } + cfg = ts_test->ts->ic_configs[type]; + if (!cfg || cfg->len <= 0) { + ts_err("no valid normal config found"); + return -EINVAL; + } + + return ts_test->ts->hw_ops->send_config(ts_test->ts, cfg->data, cfg->len); +} + +static int ts_test_read_version(struct goodix_ts_test *ts_test, + struct goodix_fw_version *version) +{ + return ts_test->ts->hw_ops->read_version(ts_test->ts, version); +} + +static void goto_next_line(char **ptr) +{ + do { + *ptr = *ptr + 1; + } while (**ptr != '\n' && **ptr != '\0'); + if (**ptr == '\0') { + return; + } + *ptr = *ptr + 1; +} + +static void copy_this_line(char *dest, char *src) +{ + char *copy_from; + char *copy_to; + + copy_from = src; + copy_to = dest; + do { + *copy_to = *copy_from; + copy_from++; + copy_to++; + } while((*copy_from != '\n') && (*copy_from != '\r') && (*copy_from != '\0')); + *copy_to = '\0'; +} + +static int getrid_space(s8* data, s32 len) +{ + u8* buf = NULL; + s32 i; + u32 count = 0; + + buf = (char*)kzalloc(len + 5, GFP_KERNEL); + if (buf == NULL){ + ts_err("get space kzalloc error"); + return -ESRCH; + } + + for (i = 0; i < len; i++) + { + if (data[i] == ' ' || data[i] == '\r' || data[i] == '\n') + { + continue; + } + buf[count++] = data[i]; + } + + buf[count++] = '\0'; + + memcpy(data, buf, count); + kfree(buf); + + return count; +} + +static int parse_valid_data(char *buf_start, loff_t buf_size, + char *ptr, s32 *data, s32 rows) +{ + int i = 0; + int j = 0; + char *token = NULL; + char *tok_ptr = NULL; + char *row_data = NULL; + long temp_val; + + if (!ptr) { + ts_err("ptr is NULL"); + return -EINVAL; + } + if (!data) { + ts_err("data is NULL"); + return -EINVAL; + } + + row_data = (char *)kzalloc(MAX_LINE_LEN, GFP_KERNEL); + if (!row_data) { + ts_err("alloc bytes %d failed.", MAX_LINE_LEN); + return -ENOMEM; + } + + for (i = 0; i < rows; i++) { + memset(row_data, 0, MAX_LINE_LEN); + copy_this_line(row_data, ptr); + getrid_space(row_data, strlen(row_data)); + tok_ptr = row_data; + while ((token = strsep(&tok_ptr,","))) { + if (strlen(token) == 0) + continue; + if (kstrtol(token, 0, &temp_val)) { + kfree(row_data); + return -EINVAL; + } + data[j++] = (s32)temp_val; + } + if (i == rows - 1) + break; + goto_next_line(&ptr); //next row + if(!ptr || (0 == strlen(ptr)) || (ptr >= (buf_start + buf_size))) { + ts_info("invalid ptr, return"); + kfree(row_data); + row_data = NULL; + return -EPERM; + } + } + kfree(row_data); + return j; +} + +static int parse_csvfile(char *buf, size_t size, char *target_name, + s32 *data, s32 rows, s32 col) +{ + int ret = 0; + char *ptr = NULL; + int read_ret; + + read_ret = size; + if (read_ret > 0) { + ptr = buf; + ptr = strstr(ptr, target_name); + if (!ptr) { + ts_info("load %s failed 1, maybe not this item", target_name); + return -EINTR; + } + + goto_next_line(&ptr); + if (!ptr || (0 == strlen(ptr))) { + ts_err("load %s failed 2!", target_name); + return -EIO; + } + + if (data) { + ret = parse_valid_data(buf, size, ptr, data, rows); + } else { + ts_err("load %s failed 3!", target_name); + return -EINTR; + } + } else { + ts_err("ret=%d, read_ret=%d", ret, read_ret); + ret = -ENXIO; + } + + return ret; +} + + +static void goodix_init_params(struct goodix_ts_test *ts_test) +{ + struct goodix_ts_core *ts = ts_test->ts; + struct ts_test_params *test_params = &ts_test->test_params; + + test_params->rawdata_addr = ts->ic_info.misc.mutual_rawdata_addr; + test_params->noisedata_addr = ts->ic_info.misc.mutual_diffdata_addr; + test_params->self_rawdata_addr = ts->ic_info.misc.self_rawdata_addr; + test_params->self_noisedata_addr = ts->ic_info.misc.self_diffdata_addr; + + test_params->drv_num = ts->ic_info.parm.drv_num; + test_params->sen_num = ts->ic_info.parm.sen_num; + + if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) + test_params->params_info = ¶ms_bra; + else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B) + test_params->params_info = ¶ms_brb; + else + test_params->params_info = ¶ms_brd; +} + +static int goodix_init_testlimits(struct goodix_ts_test *ts_test) +{ + int ret; + int i; + u32 data_buf[10] = {0}; + char *temp_buf = NULL; + struct ts_test_params *test_params = &ts_test->test_params; + struct goodix_ts_core *ts_core = ts_test->ts; + const struct firmware *firmware = NULL; + struct device *dev = &ts_core->pdev->dev; + char limit_file[100] = {0}; + u32 tx = test_params->drv_num; + u32 rx = test_params->sen_num; + + sprintf(limit_file, "%s_test_limits_%d.csv", GOODIX_TEST_FILE_NAME, + ts_core->fw_version.sensor_id); + ts_info("limit_file_name:%s", limit_file); + + ret = request_firmware(&firmware, limit_file, dev); + if (ret < 0) { + ts_err("limits file [%s] not available", limit_file); + return -EINVAL; + } + if (firmware->size <= 0) { + ts_err("request_firmware, limits param length error,len:%zu", + firmware->size); + ret = -EINVAL; + goto exit_free; + } + temp_buf = kzalloc(firmware->size + 1, GFP_KERNEL); + if (!temp_buf) { + ts_err("kzalloc bytes failed."); + ret = -ENOMEM; + goto exit_free; + } + memcpy(temp_buf, firmware->data, firmware->size); + + /* obtain config data */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_TEST_CONFIG, + test_params->cfg_buf, 1, GOODIX_CFG_MAX_SIZE); + if (ret < 0) { + ts_info("Can't find %s", CSV_TP_TEST_CONFIG); + } else { + ts_info("parse_csvfile %s OK, cfg_len:%d", CSV_TP_TEST_CONFIG, ret); + for (i = 0; i < ret; i++) + ts_test->test_config.data[i] = (u8)test_params->cfg_buf[i]; + ts_test->test_config.len = ret; + } + + /* obtain mutual_raw min */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_RAW_MIN, + test_params->min_limits, rx, tx); + if (ret < 0) { + ts_err("Failed get min_limits"); + goto exit_free; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_RAW_MIN); + } + /* obtain mutual_raw max */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_RAW_MAX, + test_params->max_limits, rx, tx); + if (ret < 0) { + ts_err("Failed get max_limits"); + goto exit_free; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_RAW_MAX); + } + /* obtain delta limit */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_RAW_DELTA, + test_params->deviation_limits, rx, tx); + if (ret < 0) { + ts_err("Failed get delta limit"); + goto exit_free; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_RAW_DELTA); + } + + /* obtain self_raw min */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_SELFRAW_MIN, + test_params->self_min_limits, 1, tx + rx); + /* obtain self_raw max */ + ret |= parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_SELFRAW_MAX, + test_params->self_max_limits, 1, tx + rx); + if (ret < 0) { + ts_info("Can't find self_min_max_limits, ship this item"); + ret = 0; + test_params->test_items[GTP_SELFCAP_TEST] = false; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_SELFRAW_MIN); + ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_SELFRAW_MAX); + test_params->test_items[GTP_SELFCAP_TEST] = true; + } + + /* obtain noise_threshold */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_NOISE_LIMIT, + &test_params->noise_threshold, 1, 1); + if (ret < 0) { + ts_info("Can't find noise_threshold, skip this item"); + ret = 0; + test_params->test_items[GTP_NOISE_TEST] = false; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_NOISE_LIMIT); + test_params->test_items[GTP_NOISE_TEST] = true; + } + + /* obtain self_noise_threshold */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SELFNOISE_LIMIT, + &test_params->self_noise_threshold, 1, 1); + if (ret < 0) { + ts_info("Can't find self_noise_threshold, skip this item"); + ret = 0; + test_params->test_items[GTP_SELFNOISE_TEST] = false; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_SELFNOISE_LIMIT); + test_params->test_items[GTP_SELFNOISE_TEST] = true; + } + + /* obtain short_params */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SHORT_THRESHOLD, + (s32 *)data_buf, 1, 7); + if (ret < 0) { + ts_info("Can't find short shortciurt_threshold, skip this item"); + ret = 0; + test_params->test_items[GTP_SHORT_TEST] = false; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_SHORT_THRESHOLD); + test_params->test_items[GTP_SHORT_TEST] = true; + test_params->short_threshold = data_buf[0]; + test_params->r_drv_drv_threshold = data_buf[1]; + test_params->r_drv_sen_threshold = data_buf[2]; + test_params->r_sen_sen_threshold = data_buf[3]; + test_params->r_drv_gnd_threshold = data_buf[4]; + test_params->r_sen_gnd_threshold = data_buf[5]; + test_params->avdd_value = data_buf[6]; + } + +exit_free: + kfree(temp_buf); + if (firmware) + release_firmware(firmware); + return ret; +} + +static int goodix_tptest_prepare(struct goodix_ts_test *ts_test) +{ + int ret; + struct goodix_ic_config *cfg = &ts_test->test_config; + + ts_info("TP test prepare IN"); + + goodix_init_params(ts_test); + /* parse test limits from csv */ + ret = goodix_init_testlimits(ts_test); + if (ret < 0) { + ts_err("Failed to init testlimits from csv."); + return ret; + } + + /* disable irq */ + ts_test_irq_enable(ts_test, false); + /* close esd */ + goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); + + /* send test config if exist */ + if (cfg->len > 0) { + ts_info("Test config exists and send it"); + ret = ts_test->ts->hw_ops->send_config(ts_test->ts, cfg->data, cfg->len); + if (ret < 0) { + ts_err("Send test config failed, exit"); + goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); + ts_test_irq_enable(ts_test, true); + return ret; + } + } + + return 0; +} + +static void goodix_tptest_finish(struct goodix_ts_test *ts_test) +{ + ts_info("TP test finish IN"); + /* reset chip */ + ts_test_reset(ts_test, 100); + /* send normal config */ + if (ts_test->test_config.len > 0) { + if (ts_test_send_config(ts_test, CONFIG_TYPE_NORMAL)) + ts_err("Send normal config failed"); + } + + /* open esd */ + goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); + /* enable irq */ + ts_test_irq_enable(ts_test, true); +} + +#define SHORT_TEST_RUN_REG 0x10400 +#define SHORT_TEST_RUN_FLAG 0xAA +#define INSPECT_FW_SWITCH_CMD 0x85 +#define TEST_FW_PID "OST" +static int goodix_short_test_prepare(struct goodix_ts_test *ts_test) +{ + struct goodix_ts_cmd tmp_cmd; + struct goodix_fw_version fw_ver; + int ret; + int retry = 3; + u8 status; + + ts_info("short test prepare IN"); + ts_test->test_result[GTP_SHORT_TEST] = SYS_SOFTWARE_REASON; + tmp_cmd.len = 4; + tmp_cmd.cmd = INSPECT_FW_SWITCH_CMD; + + ret = ts_test_send_cmd(ts_test, &tmp_cmd); + if (ret < 0) { + ts_err("send test mode failed"); + return ret; + } + + while (retry--) { + msleep(40); + if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) { + ret = ts_test_read_version(ts_test, &fw_ver); + if (ret < 0) { + ts_err("read test version failed"); + return ret; + } + ret = memcmp(&(fw_ver.patch_pid[3]), TEST_FW_PID, strlen(TEST_FW_PID)); + if (ret == 0) + return 0; + else + ts_info("patch ID dismatch %s != %s", fw_ver.patch_pid, TEST_FW_PID); + } else { + ret = ts_test_read(ts_test, SHORT_TEST_RUN_REG, &status, 1); + if (!ret && status == SHORT_TEST_RUN_FLAG) + return 0; + ts_info("short_mode_status=0x%02x ret=%d", status, ret); + } + } + + return -EINVAL; +} + +static u32 map_die2pin(struct ts_test_params *test_params, u32 chn_num) +{ + int i = 0; + u32 res = 255; + + if (chn_num & DRV_CHANNEL_FLAG) + chn_num = (chn_num & ~DRV_CHANNEL_FLAG) + test_params->params_info->max_sen_num; + + for (i = 0; i < test_params->params_info->max_sen_num; i++) { + if (test_params->params_info->sen_map[i] == chn_num) { + res = i; + break; + } + } + /* res != 255 mean found the corresponding channel num */ + if (res != 255) + return res; + /* if cannot find in SenMap try find in DrvMap */ + for (i = 0; i < test_params->params_info->max_drv_num; i++) { + if (test_params->params_info->drv_map[i] == chn_num) { + res = i; + break; + } + } + if (i >= test_params->params_info->max_drv_num) + ts_err("Faild found corrresponding channel num:%d", chn_num); + else + res |= DRV_CHANNEL_FLAG; + + return res; +} + +static void goodix_save_short_res(struct ts_test_params *params, + u16 chn1, u16 chn2, int r) +{ + int i; + u8 repeat_cnt = 0; + u8 repeat = 0; + struct goodix_ts_test *ts_test = container_of(params, + struct goodix_ts_test, test_params); + struct ts_short_res *short_res = &ts_test->short_res; + + if (chn1 == chn2 || short_res->short_num >= MAX_SHORT_NUM) + return; + + for (i = 0; i < short_res->short_num; i++) { + repeat_cnt = 0; + if (short_res->short_msg[4 * i] == chn1) + repeat_cnt++; + if (short_res->short_msg[4 * i] == chn2) + repeat_cnt++; + if (short_res->short_msg[4 * i + 1] == chn1) + repeat_cnt++; + if (short_res->short_msg[4 * i + 1] == chn2) + repeat_cnt++; + if (repeat_cnt >= 2){ + repeat = 1; + break; + } + } + if (repeat == 0) { + short_res->short_msg[4 * short_res->short_num + 0] = chn1; + short_res->short_msg[4 * short_res->short_num + 1] = chn2; + short_res->short_msg[4 * short_res->short_num + 2] = (r >> 8) & 0xFF; + short_res->short_msg[4 * short_res->short_num + 3] = r & 0xFF; + if (short_res->short_num < MAX_SHORT_NUM) + short_res->short_num++; + } +} + +static int gdix_check_tx_tx_shortcircut(struct goodix_ts_test *ts_test, + u8 short_ch_num) +{ + int ret = 0, err = 0; + u32 r_threshold = 0, short_r = 0; + int size = 0, i = 0, j = 0; + u16 adc_signal = 0; + u8 master_pin_num, slave_pin_num; + u8 *data_buf; + u32 data_reg; + struct ts_test_params *test_params = &ts_test->test_params; + int max_drv_num = test_params->params_info->max_drv_num; + int max_sen_num = test_params->params_info->max_sen_num; + u16 self_capdata, short_die_num = 0; + + size = 4 + max_drv_num * 2 + 2; + data_buf = kzalloc(size, GFP_KERNEL); + if (!data_buf) { + ts_err("Failed to alloc memory"); + return -ENOMEM; + } + /* drv&drv shortcircut check */ + data_reg = test_params->params_info->drv_drv_selfcode_reg; + for (i = 0; i < short_ch_num; i++) { + ret = ts_test_read(ts_test, data_reg, data_buf, size); + if (ret < 0) { + ts_err("Failed read Drv-to-Drv short rawdata"); + err = -EINVAL; + break; + } + + if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { + ts_err("Drv-to-Drv adc data checksum error"); + err = -EINVAL; + break; + } + + r_threshold = test_params->r_drv_drv_threshold; + short_die_num = le16_to_cpup((__le16 *)&data_buf[0]); + short_die_num -= max_sen_num; + if (short_die_num >= max_drv_num) { + ts_info("invalid short pad num:%d", + short_die_num + max_sen_num); + continue; + } + + /* TODO: j start position need recheck */ + self_capdata = le16_to_cpup((__le16 *)&data_buf[2]); + if (self_capdata == 0xffff || self_capdata == 0) { + ts_info("invalid self_capdata:0x%x", self_capdata); + continue; + } + + for (j = short_die_num + 1; j < max_drv_num; j++) { + adc_signal = le16_to_cpup((__le16 *)&data_buf[4 + j * 2]); + + if (adc_signal < test_params->short_threshold) + continue; + + short_r = (u32)cal_cha_to_cha_res(ts_test, self_capdata, adc_signal); + if (short_r < r_threshold) { + master_pin_num = + map_die2pin(test_params, short_die_num + max_sen_num); + slave_pin_num = + map_die2pin(test_params, j + max_sen_num); + if (master_pin_num == 0xFF || slave_pin_num == 0xFF) { + ts_info("WARNNING invalid pin"); + continue; + } + goodix_save_short_res(test_params, master_pin_num, + slave_pin_num, short_r); + ts_err("short circut:R=%dK,R_Threshold=%dK", + short_r, r_threshold); + ts_err("%s%d--%s%d shortcircut", + (master_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", + (master_pin_num & ~DRV_CHANNEL_FLAG), + (slave_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", + (slave_pin_num & ~DRV_CHANNEL_FLAG)); + err = -EINVAL; + } + } + data_reg += size; + } + + kfree(data_buf); + return err; +} + +static int gdix_check_rx_rx_shortcircut(struct goodix_ts_test *ts_test, + u8 short_ch_num) +{ + int ret = 0, err = 0; + u32 r_threshold = 0, short_r = 0; + int size = 0, i = 0, j = 0; + u16 adc_signal = 0; + u8 master_pin_num, slave_pin_num; + u8 *data_buf; + u32 data_reg; + struct ts_test_params *test_params = &ts_test->test_params; + int max_sen_num = test_params->params_info->max_sen_num; + u16 self_capdata, short_die_num = 0; + + size = 4 + max_sen_num * 2 + 2; + data_buf = kzalloc(size, GFP_KERNEL); + if (!data_buf) { + ts_err("Failed to alloc memory"); + return -ENOMEM; + } + /* drv&drv shortcircut check */ + data_reg = test_params->params_info->sen_sen_selfcode_reg; + for (i = 0; i < short_ch_num; i++) { + ret = ts_test_read(ts_test, data_reg, data_buf, size); + if (ret) { + ts_err("Failed read Sen-to-Sen short rawdata"); + err = -EINVAL; + break; + } + + if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { + ts_err("Sen-to-Sen adc data checksum error"); + err = -EINVAL; + break; + } + + r_threshold = test_params->r_sen_sen_threshold; + short_die_num = le16_to_cpup((__le16 *)&data_buf[0]); + if (short_die_num >= max_sen_num) { + ts_info("invalid short pad num:%d", short_die_num); + continue; + } + + /* TODO: j start position need recheck */ + self_capdata = le16_to_cpup((__le16 *)&data_buf[2]); + if (self_capdata == 0xffff || self_capdata == 0) { + ts_info("invalid self_capdata:0x%x", self_capdata); + continue; + } + + for (j = short_die_num + 1; j < max_sen_num; j++) { + adc_signal = le16_to_cpup((__le16 *)&data_buf[4 + j * 2]); + + if (adc_signal < test_params->short_threshold) + continue; + + short_r = (u32)cal_cha_to_cha_res(ts_test, self_capdata, adc_signal); + if (short_r < r_threshold) { + master_pin_num = map_die2pin(test_params, short_die_num); + slave_pin_num = map_die2pin(test_params, j); + if (master_pin_num == 0xFF || slave_pin_num == 0xFF) { + ts_info("WARNNING invalid pin"); + continue; + } + goodix_save_short_res(test_params, master_pin_num, + slave_pin_num, short_r); + ts_err("short circut:R=%dK,R_Threshold=%dK", + short_r, r_threshold); + ts_err("%s%d--%s%d shortcircut", + (master_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", + (master_pin_num & ~DRV_CHANNEL_FLAG), + (slave_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", + (slave_pin_num & ~DRV_CHANNEL_FLAG)); + err = -EINVAL; + } + } + data_reg += size; + } + + kfree(data_buf); + return err; +} + +static int gdix_check_tx_rx_shortcircut(struct goodix_ts_test *ts_test, + u8 short_ch_num) +{ + int ret = 0, err = 0; + u32 r_threshold = 0, short_r = 0; + int size = 0, i = 0, j = 0; + u16 adc_signal = 0; + u8 master_pin_num, slave_pin_num; + u8 *data_buf = NULL; + u32 data_reg; + struct ts_test_params *test_params = &ts_test->test_params; + int max_drv_num = test_params->params_info->max_drv_num; + int max_sen_num = test_params->params_info->max_sen_num; + u16 self_capdata, short_die_num = 0; + + size = 4 + max_drv_num * 2 + 2; + data_buf = kzalloc(size, GFP_KERNEL); + if (!data_buf) { + ts_err("Failed to alloc memory"); + return -ENOMEM; + } + /* drv&sen shortcircut check */ + data_reg = test_params->params_info->drv_sen_selfcode_reg; + for (i = 0; i < short_ch_num; i++) { + ret = ts_test_read(ts_test, data_reg, data_buf, size); + if (ret) { + ts_err("Failed read Drv-to-Sen short rawdata"); + err = -EINVAL; + break; + } + + if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { + ts_err("Drv-to-Sen adc data checksum error"); + err = -EINVAL; + break; + } + + r_threshold = test_params->r_drv_sen_threshold; + short_die_num = le16_to_cpup((__le16 *)&data_buf[0]); + if (short_die_num >= max_sen_num) { + ts_info("invalid short pad num:%d", short_die_num); + continue; + } + + /* TODO: j start position need recheck */ + self_capdata = le16_to_cpup((__le16 *)&data_buf[2]); + if (self_capdata == 0xffff || self_capdata == 0) { + ts_info("invalid self_capdata:0x%x", self_capdata); + continue; + } + + for (j = 0; j < max_drv_num; j++) { + adc_signal = le16_to_cpup((__le16 *)&data_buf[4 + j * 2]); + + if (adc_signal < test_params->short_threshold) + continue; + + short_r = (u32)cal_cha_to_cha_res(ts_test, self_capdata, adc_signal); + if (short_r < r_threshold) { + master_pin_num = map_die2pin(test_params, short_die_num); + slave_pin_num = map_die2pin(test_params, j + max_sen_num); + if (master_pin_num == 0xFF || slave_pin_num == 0xFF) { + ts_info("WARNNING invalid pin"); + continue; + } + goodix_save_short_res(test_params, master_pin_num, + slave_pin_num, short_r); + ts_err("short circut:R=%dK,R_Threshold=%dK", + short_r, r_threshold); + ts_err("%s%d--%s%d shortcircut", + (master_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", + (master_pin_num & ~DRV_CHANNEL_FLAG), + (slave_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", + (slave_pin_num & ~DRV_CHANNEL_FLAG)); + err = -EINVAL; + } + } + data_reg += size; + } + + kfree(data_buf); + return err; +} + +static int gdix_check_resistance_to_gnd(struct ts_test_params *test_params, + u16 adc_signal, u32 pos) +{ + long r = 0; + u16 r_th = 0, avdd_value = 0; + u16 chn_id_tmp = 0; + u8 pin_num = 0; + struct goodix_ts_test *ts_test = container_of(test_params, + struct goodix_ts_test, test_params); + int max_drv_num = test_params->params_info->max_drv_num; + int max_sen_num = test_params->params_info->max_sen_num; + + avdd_value = test_params->avdd_value; + if (adc_signal == 0 || adc_signal == 0x8000) + adc_signal |= 1; + + if ((adc_signal & 0x8000) == 0) { + /* short to GND */ + r = cal_cha_to_gnd_res(ts_test, adc_signal); + } else { + /* short to VDD */ + r = cal_cha_to_avdd_res(ts_test, adc_signal & ~0x8000, avdd_value); + } + + if (pos < max_drv_num) + r_th = test_params->r_drv_gnd_threshold; + else + r_th = test_params->r_sen_gnd_threshold; + + chn_id_tmp = pos; + if (chn_id_tmp < max_drv_num) + chn_id_tmp += max_sen_num; + else + chn_id_tmp -= max_drv_num; + + if (r < r_th) { + pin_num = map_die2pin(test_params, chn_id_tmp); + goodix_save_short_res(test_params, pin_num, + (adc_signal & 0x8000)? CHN_VDD : CHN_GND, r); + ts_err("%s%d shortcircut to %s,R=%ldK,R_Threshold=%dK", + (pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", + (pin_num & ~DRV_CHANNEL_FLAG), + (adc_signal & 0x8000) ? "VDD" : "GND", + r, r_th); + + return -EINVAL; + } + + return 0; +} + +static int gdix_check_gndvdd_shortcircut(struct goodix_ts_test *ts_test) +{ + int ret = 0, err = 0; + int size = 0, i = 0; + u16 adc_signal = 0; + u32 data_reg; + u8 *data_buf = NULL; + int max_drv_num = ts_test->test_params.params_info->max_drv_num; + int max_sen_num = ts_test->test_params.params_info->max_sen_num; + + size = (max_drv_num + max_sen_num) * 2 + 2; + data_buf = kzalloc(size, GFP_KERNEL); + if (!data_buf) { + ts_err("Failed to alloc memory"); + return -ENOMEM; + } + /* read diff code, diff code will be used to calculate + * resistance between channel and GND */ + data_reg = ts_test->test_params.params_info->diffcode_data_reg; + ret = ts_test_read(ts_test, data_reg, data_buf, size); + if (ret < 0) { + ts_err("Failed read to-gnd rawdata"); + err = -EINVAL; + goto err_out; + } + + if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { + ts_err("diff code checksum error"); + err = -EINVAL; + goto err_out; + } + + for (i = 0; i < max_drv_num + max_sen_num; i++) { + adc_signal = le16_to_cpup((__le16 *)&data_buf[i * 2]); + ret = gdix_check_resistance_to_gnd(&ts_test->test_params, + adc_signal, i); + if (ret != 0) { + ts_err("Resistance to-gnd/vdd short"); + err = ret; + } + } + +err_out: + kfree(data_buf); + return err; +} + +static int goodix_shortcircut_analysis(struct goodix_ts_test *ts_test) +{ + int ret; + int err = 0; + test_result_t test_result; + + ret = ts_test_read(ts_test, ts_test->test_params.params_info->short_test_result_reg, + (u8 *)&test_result, sizeof(test_result)); + if (ret < 0) { + ts_err("Read TEST_RESULT_REG failed"); + return ret; + } + + if (checksum_cmp((u8 *)&test_result, sizeof(test_result), + CHECKSUM_MODE_U8_LE)) { + ts_err("shrot result checksum err"); + return -EINVAL; + } + + if (!(test_result.result & 0x0F)) { + ts_info(">>>>> No shortcircut"); + return 0; + } + ts_info("short flag 0x%02x, drv&drv:%d, sen&sen:%d, drv&sen:%d, drv/GNDVDD:%d, sen/GNDVDD:%d", + test_result.result, test_result.drv_drv_num, test_result.sen_sen_num, + test_result.drv_sen_num, test_result.drv_gnd_avdd_num, test_result.sen_gnd_avdd_num); + + if (test_result.drv_drv_num) + err |= gdix_check_tx_tx_shortcircut(ts_test, test_result.drv_drv_num); + if (test_result.sen_sen_num) + err |= gdix_check_rx_rx_shortcircut(ts_test, test_result.sen_sen_num); + if (test_result.drv_sen_num) + err |= gdix_check_tx_rx_shortcircut(ts_test, test_result.drv_sen_num); + if (test_result.drv_gnd_avdd_num || test_result.sen_gnd_avdd_num) + err |= gdix_check_gndvdd_shortcircut(ts_test); + + ts_info(">>>>> short check return 0x%x", err); + + return err; +} + +#define SHORT_FW_CMD_REG 0x10400 +static int send_test_cmd(struct goodix_ts_test *ts_test, + struct goodix_ts_cmd *cmd) +{ + int ret; + u32 reg = SHORT_FW_CMD_REG; + cmd->state = 0; + cmd->ack = 0; + goodix_append_checksum(&(cmd->buf[2]), cmd->len - 2, + CHECKSUM_MODE_U8_LE); + ret = ts_test_write(ts_test, reg, cmd->buf, cmd->len + 2); + if (ret < 0) + return ret; + usleep_range(10000, 11000); + return ret; +} + + +#define INSPECT_PARAM_CMD 0xAA +#define SHORT_TEST_FINISH_FLAG 0x88 +#define SHORT_TEST_THRESHOLD_REG 0x20402 +static void goodix_shortcircut_test(struct goodix_ts_test *ts_test) +{ + int ret = 0; + int retry; + u16 test_time; + u8 status; + int ic_type = ts_test->ts->bus->ic_type; + struct goodix_ts_cmd test_parm_cmd; + // u8 test_param[6]; + + ts_info("---------------------- short_test begin ----------------------"); + ret = goodix_short_test_prepare(ts_test); + if (ret < 0) { + ts_err("Failed enter short test mode"); + return; + } + + /* get short test time */ + ret = ts_test_read(ts_test, ts_test->test_params.params_info->short_test_time_reg, (u8 *)&test_time, 2); + if (ret < 0) { + ts_err("Failed to get test_time, default %dms", DEFAULT_TEST_TIME_MS); + test_time = DEFAULT_TEST_TIME_MS; + } else { + if (ic_type == IC_TYPE_BERLIN_A) + test_time /= 10; + if (test_time > MAX_TEST_TIME_MS) { + ts_info("test time too long %d > %d", + test_time, MAX_TEST_TIME_MS); + test_time = MAX_TEST_TIME_MS; + } + ts_info("get test time %dms", test_time); + } + + /* start short test */ + if (ic_type == IC_TYPE_BERLIN_A) { + test_parm_cmd.len = 0x0A; + test_parm_cmd.cmd = INSPECT_PARAM_CMD; + test_parm_cmd.data[0] = ts_test->test_params.params_info->dft_short_threshold & 0xFF; + test_parm_cmd.data[1] = (ts_test->test_params.params_info->dft_short_threshold >> 8) & 0xFF; + test_parm_cmd.data[2] = ts_test->test_params.params_info->short_diffcode_threshold & 0xFF; + test_parm_cmd.data[3] = (ts_test->test_params.params_info->short_diffcode_threshold >> 8) & 0xFF; + test_parm_cmd.data[4] = ts_test->test_params.params_info->short_test_dump_num & 0xFF; + test_parm_cmd.data[5] = (ts_test->test_params.params_info->short_test_dump_num >> 8) & 0xFF; + ret = send_test_cmd(ts_test, &test_parm_cmd); + if (ret < 0) { + ts_err("send INSPECT_PARAM_CMD failed"); + return; + } + } else { + // test_param[0] = ts_test->test_params.params_info->dft_short_threshold & 0xFF; + // test_param[1] = (ts_test->test_params.params_info->dft_short_threshold >> 8) & 0xFF; + // test_param[2] = ts_test->test_params.params_info->short_diffcode_threshold & 0xFF; + // test_param[3] = (ts_test->test_params.params_info->short_diffcode_threshold >> 8) & 0xFF; + // test_param[4] = ts_test->test_params.params_info->short_test_dump_num & 0xFF; + // test_param[5] = (ts_test->test_params.params_info->short_test_dump_num >> 8) & 0xFF; + // ts_test_write(ts_test, SHORT_TEST_THRESHOLD_REG, test_param, sizeof(test_param)); + status = 0; + ts_test_write(ts_test, SHORT_TEST_RUN_REG, &status, 1); + } + + /* wait short test finish */ + msleep(test_time); + retry = 50; + while (retry--) { + ret = ts_test_read(ts_test, ts_test->test_params.params_info->short_test_status_reg, &status, 1); + if (!ret && status == SHORT_TEST_FINISH_FLAG) + break; + msleep(50); + } + if (retry < 0) { + ts_err("short test failed, status:0x%02x", status); + return; + } + + /* start analysis short result */ + ts_info("short_test finished, start analysis"); + ret = goodix_shortcircut_analysis(ts_test); + if (ret < 0) + ts_test->test_result[GTP_SHORT_TEST] = GTP_PANEL_REASON; + else + ts_test->test_result[GTP_SHORT_TEST] = GTP_TEST_PASS; +} + +#define GOODIX_CMD_RAWDATA 2 +#define GOODIX_TOUCH_EVENT 0x80 +static int goodix_cap_test_prepare(struct goodix_ts_test *ts_test) +{ + int ret; + struct goodix_ts_cmd temp_cmd; + + ts_info("cap test prepare IN"); + ts_test->test_result[GTP_CAP_TEST] = SYS_SOFTWARE_REASON; + ts_test->test_result[GTP_DELTA_TEST] = SYS_SOFTWARE_REASON; + if (ts_test->test_params.test_items[GTP_SELFCAP_TEST]) + ts_test->test_result[GTP_SELFCAP_TEST] = SYS_SOFTWARE_REASON; + if (ts_test->test_params.test_items[GTP_NOISE_TEST]) + ts_test->test_result[GTP_NOISE_TEST] = SYS_SOFTWARE_REASON; + if (ts_test->test_params.test_items[GTP_SELFNOISE_TEST]) + ts_test->test_result[GTP_SELFNOISE_TEST] = SYS_SOFTWARE_REASON; + + /* switch rawdata mode */ + if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D) { + temp_cmd.cmd = 0x90; + temp_cmd.data[0] = 0x81; + temp_cmd.len = 5; + } else { + temp_cmd.cmd = GOODIX_CMD_RAWDATA; + temp_cmd.len = 4; + } + ret = ts_test_send_cmd(ts_test, &temp_cmd); + if (ret < 0) + ts_err("Enter rawdata mode failed"); + + return ret; +} + +static int goodix_cap_test_finish(struct goodix_ts_test *ts_test) +{ + ts_info("cap_test finished"); + /* switch coor mode */ + ts_test_reset(ts_test, 100); + return 0; +} + +static int goodix_cache_rawdata(struct goodix_ts_test *ts_test) +{ + int ret; + int i; + int retry; + u8 val; + unsigned char frame_buf[GOODIX_MAX_FRAMEDATA_LEN]; + struct frame_head *frame_head; + struct goodix_ts_core *cd = ts_test->ts; + unsigned char *cur_ptr; + u32 sen_num = ts_test->test_params.sen_num; + u32 drv_num = ts_test->test_params.drv_num; + u32 data_size = sen_num * drv_num; + u32 data_addr = ts_test->test_params.rawdata_addr; + u32 flag_addr = ts_test->ts->ic_info.misc.touch_data_addr; + + if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D) + flag_addr = ts_test->ts->ic_info.misc.frame_data_addr; + + for (i = 0; i < TOTAL_FRAME_NUM; i++) { + val = 0; + ret = ts_test_write(ts_test, flag_addr, &val, 1); + if (ret < 0) { + ts_err("clean touch event failed, exit"); + return -EAGAIN; + } + retry = 20; + while (retry--) { + usleep_range(5000, 5100); + ret = ts_test_read(ts_test, flag_addr, &val, 1); + if (!ret && (val & 0x80)) + break; + } + if (retry < 0) { + ts_err("rawdata is not ready val:0x%02x i:%d, exit", val, i); + return -EAGAIN; + } + + if (cd->bus->ic_type == IC_TYPE_BERLIN_D) { + ret = ts_test_read(ts_test, flag_addr, frame_buf, sizeof(frame_buf)); + if (ret < 0) + return ret; + if (checksum_cmp(frame_buf, cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) { + ts_err("frame head checksum error"); + return -EINVAL; + } + frame_head = (struct frame_head *)frame_buf; + if (checksum_cmp(frame_buf, frame_head->cur_frame_len, CHECKSUM_MODE_U16_LE)) { + ts_err("frame body checksum error"); + return -EINVAL; + } + cur_ptr = frame_buf; + cur_ptr += cd->ic_info.misc.frame_data_head_len; + cur_ptr += cd->ic_info.misc.fw_attr_len; + cur_ptr += cd->ic_info.misc.fw_log_len; + memcpy((u8 *)ts_test->rawdata[i].data, cur_ptr + 8, + cd->ic_info.misc.mutual_struct_len - 8); + } else { + ret = ts_test_read(ts_test, data_addr, + (u8 *)ts_test->rawdata[i].data, data_size * sizeof(s16)); + if (ret < 0) + return ret; + } + + ts_test->rawdata[i].size = data_size; + goodix_rotate_abcd2cbad(drv_num, sen_num, ts_test->rawdata[i].data); + } + + return ret; +} + +static void goodix_cache_deltadata(struct goodix_ts_test *ts_test) +{ + u32 data_size; + int tx = ts_test->test_params.drv_num; + int i; + int j; + int max_val; + int raw; + int temp; + + for (i = 0; i < TOTAL_FRAME_NUM; i++) { + data_size = ts_test->rawdata[i].size; + if (data_size == 0) + continue; + for (j = 0; j < data_size; j++) { + raw = ts_test->rawdata[i].data[j]; + max_val = 0; + /* calcu delta with above node */ + if (j - tx >= 0) { + temp = ts_test->rawdata[i].data[j - tx]; + temp = ABS(temp - raw); + max_val = MAX(max_val, temp); + } + /* calcu delta with bellow node */ + if (j + tx < data_size) { + temp = ts_test->rawdata[i].data[j + tx]; + temp = ABS(temp - raw); + max_val = MAX(max_val, temp); + } + /* calcu delta with left node */ + if (j % tx) { + temp = ts_test->rawdata[i].data[j - 1]; + temp = ABS(temp - raw); + max_val = MAX(max_val, temp); + } + /* calcu delta with right node */ + if ((j + 1) % tx) { + temp = ts_test->rawdata[i].data[j + 1]; + temp = ABS(temp - raw); + max_val = MAX(max_val, temp); + } + ts_test->accord_arr[i].data[j] = max_val * 1000 / raw; + } + ts_test->accord_arr[i].size = data_size; + } +} + +static int goodix_cache_self_rawdata(struct goodix_ts_test *ts_test) +{ + int ret; + u32 sen_num = ts_test->test_params.sen_num; + u32 drv_num = ts_test->test_params.drv_num; + u32 data_size = sen_num + drv_num; + u32 data_addr = ts_test->test_params.self_rawdata_addr; + u32 flag_addr = ts_test->ts->ic_info.misc.frame_data_addr; + struct frame_head *frame_head; + struct goodix_ts_core *cd = ts_test->ts; + unsigned char frame_buf[GOODIX_MAX_FRAMEDATA_LEN]; + unsigned char *cur_ptr; + + if (cd->bus->ic_type == IC_TYPE_BERLIN_D) { + ret = ts_test_read(ts_test, flag_addr, frame_buf, sizeof(frame_buf)); + if (ret < 0) + return ret; + if (checksum_cmp(frame_buf, cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) { + ts_err("frame head checksum error"); + return -EINVAL; + } + frame_head = (struct frame_head *)frame_buf; + if (checksum_cmp(frame_buf, frame_head->cur_frame_len, CHECKSUM_MODE_U16_LE)) { + ts_err("frame body checksum error"); + return -EINVAL; + } + cur_ptr = frame_buf; + cur_ptr += cd->ic_info.misc.frame_data_head_len; + cur_ptr += cd->ic_info.misc.fw_attr_len; + cur_ptr += cd->ic_info.misc.fw_log_len; + cur_ptr += cd->ic_info.misc.mutual_struct_len; + memcpy((u8 *)ts_test->self_rawdata.data, cur_ptr + 10, + cd->ic_info.misc.self_struct_len - 10); + } else { + ret = ts_test_read(ts_test, data_addr, + (u8 *)ts_test->self_rawdata.data, data_size * sizeof(s16)); + if (ret < 0) + return ret; + } + ts_test->self_rawdata.size = data_size; + + return ret; +} + +static int goodix_cache_noisedata(struct goodix_ts_test *ts_test) +{ + int ret; + int i; + int cnt; + int retry; + u8 val; + unsigned char frame_buf[GOODIX_MAX_FRAMEDATA_LEN]; + unsigned char *cur_ptr; + struct frame_head *frame_head; + struct goodix_ts_cmd temp_cmd; + struct goodix_ts_core *cd = ts_test->ts; + u32 sen_num = ts_test->test_params.sen_num; + u32 drv_num = ts_test->test_params.drv_num; + u32 data_size = sen_num * drv_num; + u32 data_addr = ts_test->test_params.noisedata_addr; + u32 flag_addr = ts_test->ts->ic_info.misc.touch_data_addr; + + if (cd->bus->ic_type == IC_TYPE_BERLIN_D) { + flag_addr = ts_test->ts->ic_info.misc.frame_data_addr; + temp_cmd.cmd = 0x90; + temp_cmd.data[0] = 0x82; + temp_cmd.len = 5; + ret = ts_test_send_cmd(ts_test, &temp_cmd); + if (ret < 0) { + ts_err("switch diffdata mode failed, exit!"); + return ret; + } + } + + for (cnt = 0; cnt < NOISEDATA_TEST_TIMES; cnt++) { + val = 0; + ret = ts_test_write(ts_test, flag_addr, &val, 1); + if (ret < 0) { + ts_err("clean touch event failed, exit"); + return -EAGAIN; + } + retry = 20; + while (retry--) { + usleep_range(5000, 5100); + ret = ts_test_read(ts_test, flag_addr, &val, 1); + if (!ret && (val & 0x80)) + break; + } + if (retry < 0) { + ts_err("noisedata is not ready val:0x%02x i:%d, exit", val, cnt); + return -EAGAIN; + } + + if (cd->bus->ic_type == IC_TYPE_BERLIN_D) { + ret = ts_test_read(ts_test, flag_addr, frame_buf, sizeof(frame_buf)); + if (ret < 0) + return ret; + if (checksum_cmp(frame_buf, cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) { + ts_err("frame head checksum error"); + return -EINVAL; + } + frame_head = (struct frame_head *)frame_buf; + if (checksum_cmp(frame_buf, frame_head->cur_frame_len, CHECKSUM_MODE_U16_LE)) { + ts_err("frame body checksum error"); + return -EINVAL; + } + cur_ptr = frame_buf; + cur_ptr += cd->ic_info.misc.frame_data_head_len; + cur_ptr += cd->ic_info.misc.fw_attr_len; + cur_ptr += cd->ic_info.misc.fw_log_len; + memcpy((u8 *)ts_test->noisedata[cnt].data, cur_ptr + 8, + cd->ic_info.misc.mutual_struct_len - 8); + } else { + ret = ts_test_read(ts_test, data_addr, + (u8 *)ts_test->noisedata[cnt].data, data_size * sizeof(s16)); + if (ret < 0) + return ret; + } + + ts_test->noisedata[cnt].size = data_size; + goodix_rotate_abcd2cbad(drv_num, sen_num, ts_test->noisedata[cnt].data); + for (i = 0; i < data_size; i++) + ts_test->noisedata[cnt].data[i] = ABS(ts_test->noisedata[cnt].data[i]); + } + + return ret; +} + +static int goodix_cache_self_noisedata(struct goodix_ts_test *ts_test) +{ + int ret; + int i; + u32 sen_num = ts_test->test_params.sen_num; + u32 drv_num = ts_test->test_params.drv_num; + u32 data_size = sen_num + drv_num; + u32 data_addr = ts_test->test_params.self_noisedata_addr; + u32 flag_addr = ts_test->ts->ic_info.misc.frame_data_addr; + struct frame_head *frame_head; + struct goodix_ts_core *cd = ts_test->ts; + unsigned char frame_buf[GOODIX_MAX_FRAMEDATA_LEN]; + unsigned char *cur_ptr; + + if (cd->bus->ic_type == IC_TYPE_BERLIN_D) { + ret = ts_test_read(ts_test, flag_addr, frame_buf, sizeof(frame_buf)); + if (ret < 0) + return ret; + if (checksum_cmp(frame_buf, cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) { + ts_err("frame head checksum error"); + return -EINVAL; + } + frame_head = (struct frame_head *)frame_buf; + if (checksum_cmp(frame_buf, frame_head->cur_frame_len, CHECKSUM_MODE_U16_LE)) { + ts_err("frame body checksum error"); + return -EINVAL; + } + cur_ptr = frame_buf; + cur_ptr += cd->ic_info.misc.frame_data_head_len; + cur_ptr += cd->ic_info.misc.fw_attr_len; + cur_ptr += cd->ic_info.misc.fw_log_len; + cur_ptr += cd->ic_info.misc.mutual_struct_len; + memcpy((u8 *)ts_test->self_noisedata.data, cur_ptr + 10, + cd->ic_info.misc.self_struct_len - 10); + } else { + ret = ts_test_read(ts_test, data_addr, + (u8 *)ts_test->self_noisedata.data, data_size * sizeof(s16)); + if (ret < 0) + return ret; + } + + ts_test->self_noisedata.size = data_size; + for (i = 0; i < data_size; i++) { + ts_test->self_noisedata.data[i] = ABS(ts_test->self_noisedata.data[i]); + } + + return ret; +} + +static int goodix_analysis_rawdata(struct goodix_ts_test *ts_test) +{ + int i; + int j; + bool fail_flag = false; + int err_cnt = 0; + int times = TOTAL_FRAME_NUM; + s16 val; + u32 data_size = ts_test->rawdata[0].size; + + for (i = 0; i < times; i++) { + for (j = 0; j < data_size; j++) { + val = ts_test->rawdata[i].data[j]; + if (val < ts_test->test_params.min_limits[j]) { + fail_flag = true; + ts_test->open_res.beyond_min_limit_cnt[j]++; + } + if (val > ts_test->test_params.max_limits[j]) { + fail_flag = true; + ts_test->open_res.beyond_max_limit_cnt[j]++; + } + } + if (fail_flag) + err_cnt++; + fail_flag = false; + } + + if (err_cnt > 0) + ts_err("rawdata have %d frames out of range", err_cnt); + + err_cnt *= 100; + if (err_cnt > times * 100 * 9 / 10) + return -EINVAL; + + return 0; +} + +static int goodix_analysis_deltadata(struct goodix_ts_test *ts_test) +{ + int i; + int j; + int ret = 0; + s16 val; + u32 data_size = ts_test->accord_arr[0].size; + + for (i = 0; i < TOTAL_FRAME_NUM; i++) { + for (j = 0; j < data_size; j++) { + val = ts_test->accord_arr[i].data[j]; + if (val > ts_test->test_params.deviation_limits[j]) { + ts_test->open_res.beyond_accord_limit_cnt[j]++; + ret = -EINVAL; + } + } + } + + return ret; +} + +static int goodix_analysis_self_rawdata(struct goodix_ts_test *ts_test) +{ + int i; + s16 val; + u32 data_size = ts_test->self_rawdata.size; + + for (i = 0; i < data_size; i++) { + val = ts_test->self_rawdata.data[i]; + if (val < ts_test->test_params.self_min_limits[i] || + val > ts_test->test_params.self_max_limits[i]) { + ts_err("self_rawdata isn't in range, val:%d threshold:[%d,%d]", + val, ts_test->test_params.self_min_limits[i], + ts_test->test_params.self_max_limits[i]); + return -EINVAL; + } + } + + return 0; +} + +static int goodix_analysis_noisedata(struct goodix_ts_test *ts_test) +{ + int cnt; + int i; + bool fail_flag = false; + int err_cnt = 0; + int times = NOISEDATA_TEST_TIMES; + s16 val; + u32 data_size = ts_test->noisedata[0].size; + + for (cnt = 0; cnt < times; cnt++) { + for (i = 0; i < data_size; i++) { + val = ts_test->noisedata[cnt].data[i]; + if (val > ts_test->test_params.noise_threshold) + fail_flag = true; + } + if (fail_flag) + err_cnt++; + fail_flag = false; + } + + if (err_cnt > 0) + ts_err("noisedata have %d frames out of range", err_cnt); + + err_cnt *= 100; + if (err_cnt > times * 100 * 2 / 10) + return -EINVAL; + + return 0; +} + +static int goodix_analysis_self_noisedata(struct goodix_ts_test *ts_test) +{ + int i; + s16 val; + u32 data_size = ts_test->self_noisedata.size; + + for (i = 0; i < data_size; i++) { + val = ts_test->self_noisedata.data[i]; + if (val > ts_test->test_params.self_noise_threshold) { + ts_err("self noisedata isn't in range, val:%d threshold:[0,%d]", + val, ts_test->test_params.self_noise_threshold); + return -EINVAL; + } + } + + return 0; +} + +static void goodix_capacitance_test(struct goodix_ts_test *ts_test) +{ + int ret; + + ts_info("---------------------- cap_test begin ----------------------"); + ret = goodix_cap_test_prepare(ts_test); + if (ret < 0) { + ts_err("cap_test prepare failed, exit"); + goto exit; + } + ts_info("cap rawdata prepare OK"); + + /* obtain rawdata */ + ret = goodix_cache_rawdata(ts_test); + if (ret < 0) { + if (ret == -EAGAIN) { + ts_err("Capacitance exit"); + goto exit; + } else { + ts_err("Failed to read capdata"); + } + } else { + ts_info("get rawdata finish, start analysis"); + ret = goodix_analysis_rawdata(ts_test); + if (ret < 0) + ts_test->test_result[GTP_CAP_TEST] = GTP_PANEL_REASON; + else + ts_test->test_result[GTP_CAP_TEST] = GTP_TEST_PASS; + } + + /* obtain delta_data */ + goodix_cache_deltadata(ts_test); + ts_info("get deltadata finish, start analysis"); + ret = goodix_analysis_deltadata(ts_test); + if (ret < 0) + ts_test->test_result[GTP_DELTA_TEST] = GTP_PANEL_REASON; + else + ts_test->test_result[GTP_DELTA_TEST] = GTP_TEST_PASS; + + /* obtain self_rawdata */ + if (ts_test->test_params.test_items[GTP_SELFCAP_TEST]) { + ret = goodix_cache_self_rawdata(ts_test); + if (ret < 0) { + ts_err("Failed to read self_capdata"); + } else { + ts_info("get self_rawdata finish, start analysis"); + ret = goodix_analysis_self_rawdata(ts_test); + if (ret < 0) + ts_test->test_result[GTP_SELFCAP_TEST] = GTP_PANEL_REASON; + else + ts_test->test_result[GTP_SELFCAP_TEST] = GTP_TEST_PASS; + } + } + + /* obtain noisedata */ + if (ts_test->test_params.test_items[GTP_NOISE_TEST]) { + ret = goodix_cache_noisedata(ts_test); + if (ret < 0) { + ts_err("Failed to read noisedata"); + } else { + ts_info("get noisedata finish, start analysis"); + ret = goodix_analysis_noisedata(ts_test); + if (ret < 0) + ts_test->test_result[GTP_NOISE_TEST] = GTP_PANEL_REASON; + else + ts_test->test_result[GTP_NOISE_TEST] = GTP_TEST_PASS; + } + } + + /* obtain self_noisedata */ + if (ts_test->test_params.test_items[GTP_SELFNOISE_TEST]) { + ret = goodix_cache_self_noisedata(ts_test); + if (ret < 0) { + ts_err("Failed to read self_noisedata"); + } else { + ts_info("get self_noisedata finish, start analysis"); + ret = goodix_analysis_self_noisedata(ts_test); + if (ret < 0) + ts_test->test_result[GTP_SELFNOISE_TEST] = GTP_PANEL_REASON; + else + ts_test->test_result[GTP_SELFNOISE_TEST] = GTP_TEST_PASS; + } + } + +exit: + goodix_cap_test_finish(ts_test); +} + +char *goodix_strncat(char *dest, char *src, size_t dest_size) +{ + size_t dest_len = 0; + + dest_len = strnlen(dest, dest_size); + return strncat(&dest[dest_len], src, dest_size - dest_len - 1); +} + +char *goodix_strncatint(char *dest, int src, char *format, size_t dest_size) +{ + char src_str[MAX_STR_LEN] = {0}; + + snprintf(src_str, MAX_STR_LEN, format, src); + return goodix_strncat(dest, src_str, dest_size); +} + +static void goodix_data_cal(s16 *data, size_t data_size, s16 *stat_result) +{ + int i = 0; + s16 avg = 0; + s16 min = 0; + s16 max = 0; + long long sum = 0; + + min = data[0]; + max = data[0]; + for (i = 0; i < data_size; i++) { + sum += data[i]; + if (max < data[i]) + max = data[i]; + if (min > data[i]) + min = data[i]; + } + avg = div_s64(sum, data_size); + stat_result[0] = avg; + stat_result[1] = max; + stat_result[2] = min; +} + +static void goodix_data_statistics(s16 *data, size_t data_size, + char *result, size_t res_size) +{ + s16 stat_value[3]; + + if (!data || !result) { + ts_err("parameters error please check *data and *result value"); + return; + } + + if (data_size <= 0 || res_size <= 0) { + ts_err("input parameter is illegva:data_size=%ld, res_size=%ld", + data_size, res_size); + return; + } + goodix_data_cal(data, data_size, stat_value); + + memset(result, 0, res_size); + snprintf(result, res_size, "[%d,%d,%d]", + stat_value[0], stat_value[1], stat_value[2]); + return; +} + +#ifdef SAVE_IN_CSV +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) +static ssize_t fs_write(const void* buf, size_t size, struct file* fp) +{ + loff_t pos; + ssize_t len; + + pos = fp->f_pos; + len = kernel_write(fp, buf, size, &pos); + fp->f_pos = pos; + + return len; +} +#else +static ssize_t fs_write(const void* buf, size_t size, struct file* fp) +{ + mm_segment_t old_fs; + loff_t pos; + ssize_t len; + + pos = fp->f_pos; + old_fs = get_fs(); + set_fs(KERNEL_DS); + len = vfs_write(fp, buf, size, &pos); + set_fs(old_fs); + fp->f_pos = pos; + + return len; +} +#endif + +static int goodix_save_test_config(struct goodix_ts_test *ts_test, + struct file *fp) +{ + int ret = 0; + int i; + int bytes = 0; + char *data; + struct goodix_ic_config *cfg = &ts_test->test_config; + + if (cfg->len <= 0) { + ts_info("Can't find vaild test config"); + return 0; + } + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (!data) { + ts_err("alloc memory failed"); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < cfg->len; i++) { + bytes += sprintf(&data[bytes], "0x%02x,", cfg->data[i]); + } + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("test config write failed"); + goto save_end; + } + +save_end: + kfree(data); + return ret; +} + +static int goodix_save_header(struct goodix_ts_test *ts_test, + struct file *fp) +{ + int ret; + int i; + int bytes = 0; + bool result = false; + char *data = NULL; + struct goodix_ts_core *ts = ts_test->ts; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (!data) { + ts_err("alloc memory failed"); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "
\n"); + /* sava test result */ + for (i = 0; i < MAX_TEST_ITEMS; i++) { + if ((ts_test->test_result[i] > 0) && + (ts_test->test_result[i] != GTP_TEST_PASS)) { + result = true; + break; + } + } + if (result) + bytes += sprintf(&data[bytes], "NG\n"); + else + bytes += sprintf(&data[bytes], "OK\n"); + bytes += sprintf(&data[bytes], "GT%s\n", + ts->fw_version.patch_pid); + bytes += sprintf(&data[bytes], "%d\n", + ts_test->ts->fw_version.sensor_id); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("header write failed"); + goto save_end; + } + bytes = 0; + /* save test config */ + ret = goodix_save_test_config(ts_test, fp); + if (ret < 0) { + ts_err("save test config failed"); + goto save_end; + } + + bytes += sprintf(&data[bytes], "
\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("header write failed"); + goto save_end; + } + bytes = 0; + + /* item list */ + bytes += sprintf(&data[bytes], "\n"); + if (ts_test->test_result[GTP_CAP_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_CAP_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + if (ts_test->test_result[GTP_DELTA_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_DELTA_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + if (ts_test->test_result[GTP_NOISE_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_NOISE_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + if (ts_test->test_result[GTP_SELFNOISE_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_SELFNOISE_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + if (ts_test->test_result[GTP_SELFCAP_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_SELFCAP_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + if (ts_test->test_result[GTP_SHORT_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_SHORT_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("item list write failed"); + goto save_end; + } + +save_end: + kfree(data); + return ret; +} + +static int goodix_save_limits(struct goodix_ts_test *ts_test, + struct file *fp) +{ + int ret; + int i; + int bytes = 0; + char *data = NULL; + int tx = ts_test->test_params.drv_num; + int rx = ts_test->test_params.sen_num; + int chn1; + int chn2; + int r; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (!data) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "\n"); + + /* save short result */ + if (ts_test->test_result[GTP_SHORT_TEST]) { + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "%d\n", + ts_test->short_res.short_num); + for (i = 0; i < ts_test->short_res.short_num; i++) { + chn1 = ts_test->short_res.short_msg[4 * i]; + chn2 = ts_test->short_res.short_msg[4 * i + 1]; + r = (ts_test->short_res.short_msg[4 * i + 2] << 8) + + ts_test->short_res.short_msg[4 * i + 3]; + if (chn1 == CHN_VDD) + bytes += sprintf(&data[bytes], "\n", r); + else if (chn2 == CHN_GND) + bytes += sprintf(&data[bytes], + "Chn2=\"GND\" ShortResistor= \"%dKom\"/>\n", r); + else if (chn2 & DRV_CHANNEL_FLAG) + bytes += sprintf(&data[bytes], + "Chn2=\"Tx%d\" ShortResistor= \"%dKom\"/>\n", + chn2 & 0x7f, r); + else + bytes += sprintf(&data[bytes], + "Chn2=\"Rx%d\" ShortResistor= \"%dKom\"/>\n", + chn2 & 0x7f, r); + } + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("short res write fail."); + goto save_end; + } + bytes = 0; + } + + /* rawdata max limit */ + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "%d\n", + TOTAL_FRAME_NUM); + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->test_params.max_limits[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + /* BeyondRawdataUpperLimit */ + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->open_res.beyond_max_limit_cnt[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("rawdata limit write failed"); + goto save_end; + } + bytes = 0; + + /* rawdata min limit */ + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->test_params.min_limits[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + /* BeyondRawdataLower limit */ + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->open_res.beyond_min_limit_cnt[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("rawdata limit write failed"); + goto save_end; + } + bytes = 0; + + /* Max Accord limit */ + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->test_params.deviation_limits[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + /* BeyondAccordLimitCnt */ + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->open_res.beyond_accord_limit_cnt[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("rawdata limit write failed"); + goto save_end; + } + bytes = 0; + + /* save noise limit */ + if (ts_test->test_result[GTP_NOISE_TEST]) { + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "%d\n", + NOISEDATA_TEST_TIMES); + bytes += sprintf(&data[bytes], "%d\n", + ts_test->test_params.noise_threshold); + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("noise limit write failed"); + goto save_end; + } + bytes = 0; + } + + /* save self rawdata limit */ + if (ts_test->test_result[GTP_SELFCAP_TEST]) { + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "1\n"); + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx + rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->test_params.self_max_limits[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + if ((tx + rx) % tx != 0) + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx + rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->test_params.self_min_limits[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + if ((tx + rx) % tx != 0) + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("self rawdata limit write failed"); + goto save_end; + } + bytes = 0; + } + + /* save selfnoise limit */ + if (ts_test->test_result[GTP_SELFNOISE_TEST]) { + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "1\n"); + bytes += sprintf(&data[bytes], "%d\n", + ts_test->test_params.self_noise_threshold); + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("raw limit write failed"); + goto save_end; + } + bytes = 0; + } + + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) + ts_err("limit write fail."); + +save_end: + kfree(data); + return ret; +} + +static int goodix_save_rawdata(struct goodix_ts_test *ts_test, + struct file *fp) +{ + int i; + int j; + int ret; + int bytes = 0; + s16 stat_result[3]; + char *data = NULL; + int tx = ts_test->test_params.drv_num; + int rx = ts_test->test_params.sen_num; + int len = tx * rx; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (!data) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < TOTAL_FRAME_NUM; i++) { + goodix_data_cal(ts_test->rawdata[i].data, len, stat_result); + bytes += sprintf(&data[bytes], + "\n", + i, len, stat_result[1], stat_result[2], stat_result[0]); + for (j = 0; j < len; j++) { + bytes += sprintf(&data[bytes], "%d,", ts_test->rawdata[i].data[j]); + if ((j + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + goodix_data_cal(ts_test->accord_arr[i].data, len, stat_result); + bytes += sprintf(&data[bytes], + "\n", + i, len, stat_result[1], stat_result[2], stat_result[0]); + for (j = 0; j < len; j++) { + bytes += sprintf(&data[bytes], "%d,", ts_test->accord_arr[i].data[j]); + if ((j + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("rawdata write fail."); + goto save_end; + } + bytes = 0; + } + + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) + ts_err("rawdata write fail."); + +save_end: + kfree(data); + return ret; +} + +static int goodix_save_noise_data(struct goodix_ts_test *ts_test, struct file *fp) +{ + int i; + int j; + int ret = 0; + int bytes = 0; + s16 stat_result[3]; + char *data = NULL; + int tx = ts_test->test_params.drv_num; + int rx = ts_test->test_params.sen_num; + int len = tx * rx; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (!data) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < NOISEDATA_TEST_TIMES; i++) { + goodix_data_cal(ts_test->noisedata[i].data, len, stat_result); + bytes += sprintf(&data[bytes], + "\n", + i, len, stat_result[1], stat_result[2], stat_result[0]); + for (j = 0; j < len; j++) { + bytes += sprintf(&data[bytes], "%d,", ts_test->noisedata[i].data[j]); + if ((j + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("noisedata write fail."); + goto save_end; + } + bytes = 0; + } + + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) + ts_err("noisedata write fail."); + +save_end: + kfree(data); + return ret; +} + +static int goodix_save_self_data(struct goodix_ts_test *ts_test, + struct file *fp, s16 *src_data, u8 *title, int len) +{ + int i; + int ret = 0; + s32 bytes = 0; + char *data; + s16 stat_result[3]; + int tx = ts_test->test_params.drv_num; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (!data) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "<%s>\n",title); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("rawdata write fail."); + goto save_end; + } + bytes = 0; + + goodix_data_cal(src_data, len, stat_result); + bytes += sprintf(&data[bytes], + "\n", + len, stat_result[1], stat_result[2], stat_result[0]); + for (i = 0; i < len; i++) { + bytes += sprintf(&data[bytes], "%d,", src_data[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + if (len % tx != 0) + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n",title); + ret = fs_write(data, bytes, fp); + if (ret < 0) + ts_err("rawdata write fail."); + +save_end: + kfree(data); + return ret; +} + +static int goodix_save_data(struct goodix_ts_test *ts_test, + struct file *fp) +{ + int ret; + int bytes = 0; + char *data = NULL; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (!data) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("rawdata record lable failed"); + goto save_end; + } + bytes = 0; + + ret = goodix_save_rawdata(ts_test, fp); + if (ret < 0) + goto save_end; + + if (ts_test->test_result[GTP_NOISE_TEST]) { + ret = goodix_save_noise_data(ts_test, fp); + if (ret < 0) + goto save_end; + } + + if (ts_test->test_result[GTP_SELFCAP_TEST]) { + ret = goodix_save_self_data(ts_test, fp, ts_test->self_rawdata.data, + "selfDataRecord", ts_test->self_rawdata.size); + if (ret < 0) + goto save_end; + } + + if (ts_test->test_result[GTP_SELFNOISE_TEST]) { + ret = goodix_save_self_data(ts_test, fp, ts_test->self_noisedata.data, + "selfDiffDataRecord", ts_test->self_noisedata.size); + if (ret < 0) + goto save_end; + } + + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) + ts_err("rawdata data record lable fail."); + +save_end: + kfree(data); + return ret; +} + +/* save end tag in csv file */ +static int goodix_save_tail(struct goodix_ts_test *ts_test, + struct file *fp) +{ + int ret = 0; + int bytes = 0; + char *data = NULL; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (!data) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "
\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) + ts_err("tail write failed"); + + kfree(data); + return ret; +} + +static void goodix_save_result_data(struct goodix_ts_test *ts_test) +{ + int ret = 0; + char save_path[100]; + struct file *fp = NULL; + + /* format result file */ + sprintf(save_path, GOODIX_RESULT_SAVE_PATH); + ts_info("save result IN, file_name:%s", save_path); + + fp = filp_open(save_path, O_CREAT | O_WRONLY | O_TRUNC, 0666); + if (IS_ERR(fp)) { + ts_err("create file:%s failed, fp:%ld", save_path, PTR_ERR(fp)); + return; + } + + /* save header */ + ret = goodix_save_header(ts_test, fp); + if (ret < 0) + goto save_end; + + /* save limits */ + ret = goodix_save_limits(ts_test, fp); + if (ret < 0) + goto save_end; + + /* save data */ + ret = goodix_save_data(ts_test, fp); + if (ret < 0) + goto save_end; + + /* save tail */ + ret = goodix_save_tail(ts_test, fp); + if (ret < 0) + goto save_end; + + ts_info("the test result save in %s", save_path); +save_end: + filp_close(fp, NULL); +} +#endif // SAVE_IN_CSV + +static void goodix_put_test_result(struct goodix_ts_test *ts_test, + struct ts_rawdata_info *info) +{ + int i; + bool have_bus_error = false; + bool have_panel_error = false; + char statistics_data[STATISTICS_DATA_LEN] = {0}; + struct goodix_ts_core *ts = ts_test->ts; + + ts_info("put test result IN"); + + info->buff[0] = ts_test->test_params.sen_num; + info->buff[1] = ts_test->test_params.drv_num; + info->used_size = 2; + /* save rawdata to info->buff, only one frame */ + if (ts_test->rawdata[0].size) { + for (i = 0; i < ts_test->rawdata[0].size; i++) + info->buff[info->used_size + i] = ts_test->rawdata[0].data[i]; + info->used_size += ts_test->rawdata[0].size; + } + + /* save noisedata to info->buff */ + if (ts_test->noisedata[0].size) { + for (i = 0; i < ts_test->noisedata[0].size; i++) + info->buff[info->used_size + i] = ts_test->noisedata[0].data[i]; + info->used_size += ts_test->noisedata[0].size; + } + + /* save self_noisedata to info->buff */ + if (ts_test->self_noisedata.size) { + for (i = 0; i < ts_test->self_noisedata.size; i++) + info->buff[info->used_size + i] = ts_test->self_noisedata.data[i]; + info->used_size += ts_test->self_noisedata.size; + } + + /* save self_rawdata to info->buff */ + if (ts_test->self_rawdata.size) { + for (i = 0; i < ts_test->self_rawdata.size; i++) + info->buff[info->used_size + i] = ts_test->self_rawdata.data[i]; + info->used_size += ts_test->self_rawdata.size; + } + + /* check if there have bus error */ + for (i = 0; i < MAX_TEST_ITEMS; i++) { + if (ts_test->test_result[i] == SYS_SOFTWARE_REASON) + have_bus_error = true; + else if (ts_test->test_result[i] == GTP_PANEL_REASON) + have_panel_error = true; + } + ts_info("Have bus error:%d", have_bus_error); + if (have_bus_error || have_panel_error) + goodix_strncat(ts_test->test_info, "[FAIL]-", TS_RAWDATA_RESULT_MAX); + else + goodix_strncat(ts_test->test_info, "[PASS]-", TS_RAWDATA_RESULT_MAX); + + if (have_bus_error) + goodix_strncat(ts_test->test_info, "0F-", TS_RAWDATA_RESULT_MAX); + else + goodix_strncat(ts_test->test_info, "0P-", TS_RAWDATA_RESULT_MAX); + + for (i = 0; i < MAX_TEST_ITEMS; i++) { + /* if have tested, show result */ + if (ts_test->test_result[i]) { + if (GTP_TEST_PASS == ts_test->test_result[i]) + goodix_strncatint(ts_test->test_info, i, "%dP-", + TS_RAWDATA_RESULT_MAX); + else + goodix_strncatint(ts_test->test_info, i, "%dF-", + TS_RAWDATA_RESULT_MAX); + } + } + + /* calculate rawdata min avg max value*/ + if (ts_test->rawdata[0].size) { + goodix_data_statistics( + ts_test->rawdata[0].data, + ts_test->rawdata[0].size, + statistics_data, + STATISTICS_DATA_LEN); + goodix_strncat(ts_test->test_info, statistics_data, + TS_RAWDATA_RESULT_MAX); + } else { + ts_err("NO valiable rawdata"); + goodix_strncat(ts_test->test_info, "[0,0,0]", + TS_RAWDATA_RESULT_MAX); + } + + /* calculate noisedata min avg max value*/ + if (ts_test->test_params.test_items[GTP_NOISE_TEST]) { + if (ts_test->noisedata[0].size) { + goodix_data_statistics( + ts_test->noisedata[0].data, + ts_test->noisedata[0].size, + statistics_data, + STATISTICS_DATA_LEN); + goodix_strncat(ts_test->test_info, statistics_data, + TS_RAWDATA_RESULT_MAX); + } else { + ts_err("NO valiable noisedata"); + goodix_strncat(ts_test->test_info, "[0,0,0]", + TS_RAWDATA_RESULT_MAX); + } + } + + /* calculate self_rawdata min avg max value*/ + if (ts_test->test_params.test_items[GTP_SELFCAP_TEST]) { + if (ts_test->self_rawdata.size) { + goodix_data_statistics( + ts_test->self_rawdata.data, + ts_test->self_rawdata.size, + statistics_data, + STATISTICS_DATA_LEN); + goodix_strncat(ts_test->test_info, statistics_data, + TS_RAWDATA_RESULT_MAX); + } else { + ts_err("NO valiable self_rawdata"); + goodix_strncat(ts_test->test_info, "[0,0,0]", + TS_RAWDATA_RESULT_MAX); + } + } + + /* calculate self_noisedata min avg max value*/ + if (ts_test->test_params.test_items[GTP_SELFNOISE_TEST]) { + if (ts_test->self_noisedata.size) { + goodix_data_statistics( + ts_test->self_noisedata.data, + ts_test->self_noisedata.size, + statistics_data, + STATISTICS_DATA_LEN); + goodix_strncat(ts_test->test_info, statistics_data, + TS_RAWDATA_RESULT_MAX); + } else { + ts_err("NO valiable self_noisedata"); + goodix_strncat(ts_test->test_info, "[0,0,0]", + TS_RAWDATA_RESULT_MAX); + } + } + + goodix_strncat(ts_test->test_info, "-GT", + TS_RAWDATA_RESULT_MAX); + goodix_strncat(ts_test->test_info, ts->fw_version.patch_pid, + TS_RAWDATA_RESULT_MAX); + goodix_strncat(ts_test->test_info, "\n", + TS_RAWDATA_RESULT_MAX); + strncpy(info->result, ts_test->test_info, TS_RAWDATA_RESULT_MAX - 1); + +#ifdef SAVE_IN_CSV + /* save result to file */ + goodix_save_result_data(ts_test); +#endif +} + +static int goodix_do_inspect(struct goodix_ts_core *cd, struct ts_rawdata_info *info) +{ + int ret; + struct goodix_ts_test *ts_test = NULL; + + if (!cd || !info) { + ts_err("core_data or info is NULL"); + return -ENODEV; + } + + ts_test = kzalloc(sizeof(*ts_test), GFP_KERNEL); + if (!ts_test) { + ts_err("Failed to alloc mem"); + return -ENOMEM; + } + + ts_test->ts = cd; + ret = goodix_tptest_prepare(ts_test); + if (ret < 0) { + ts_err("Failed to prepare TP test, exit"); + strncpy(info->result, "[FAIL]-0F-software reason\n", + TS_RAWDATA_RESULT_MAX - 1); + goto exit_finish; + } + ts_info("TP test prepare OK"); + + goodix_capacitance_test(ts_test); /* 1F 3F 6F 7F test */ + if (ts_test->test_params.test_items[GTP_SHORT_TEST]) + goodix_shortcircut_test(ts_test); /* 5F test */ + goodix_put_test_result(ts_test, info); + goodix_tptest_finish(ts_test); + +exit_finish: + kfree(ts_test); + return ret; +} + +/* show rawdata */ +static ssize_t goodix_ts_get_rawdata_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + struct ts_rawdata_info *info = NULL; + struct goodix_ts_core *cd = dev_get_drvdata(dev); + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ts_err("Failed to alloc rawdata info memory"); + return -ENOMEM; + } + + goodix_do_inspect(cd, info); + + ret = snprintf(buf, PAGE_SIZE, "resultInfo: %s", info->result); + + kfree(info); + return ret; +} + +static DEVICE_ATTR(get_rawdata, S_IRUGO, goodix_ts_get_rawdata_show, NULL); + +int inspect_module_init(void) +{ + int ret; + struct kobject *def_kobj = goodix_get_default_kobj(); + + /* create sysfs */ + ret = sysfs_create_file(def_kobj, &dev_attr_get_rawdata.attr); + if (ret < 0) { + ts_err("create sysfs of get_rawdata failed"); + goto err_out; + } + + module_initialized = true; + ts_info("inspect module init success"); + return 0; + +err_out: + ts_err("inspect module init failed!"); + return ret; +} + +void inspect_module_exit(void) +{ + struct kobject *def_kobj = goodix_get_default_kobj(); + + ts_info("inspect module exit"); + if (!module_initialized) + return; + + sysfs_remove_file(def_kobj, &dev_attr_get_rawdata.attr); + module_initialized = false; +} diff --git a/goodix_ts_tools.c b/goodix_ts_tools.c new file mode 100644 index 0000000000..9a69feeb6d --- /dev/null +++ b/goodix_ts_tools.c @@ -0,0 +1,503 @@ + /* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "goodix_ts_core.h" + +#define GOODIX_TOOLS_NAME "gtp_tools" +#define GOODIX_TOOLS_VER_MAJOR 1 +#define GOODIX_TOOLS_VER_MINOR 0 +static const u16 goodix_tools_ver = ((GOODIX_TOOLS_VER_MAJOR << 8) + + (GOODIX_TOOLS_VER_MINOR)); + +#define GOODIX_TS_IOC_MAGIC 'G' +#define NEGLECT_SIZE_MASK (~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) + +#define GTP_IRQ_ENABLE _IO(GOODIX_TS_IOC_MAGIC, 0) +#define GTP_DEV_RESET _IO(GOODIX_TS_IOC_MAGIC, 1) +#define GTP_SEND_COMMAND (_IOW(GOODIX_TS_IOC_MAGIC, 2, u8) & NEGLECT_SIZE_MASK) +#define GTP_SEND_CONFIG (_IOW(GOODIX_TS_IOC_MAGIC, 3, u8) & NEGLECT_SIZE_MASK) +#define GTP_ASYNC_READ (_IOR(GOODIX_TS_IOC_MAGIC, 4, u8) & NEGLECT_SIZE_MASK) +#define GTP_SYNC_READ (_IOR(GOODIX_TS_IOC_MAGIC, 5, u8) & NEGLECT_SIZE_MASK) +#define GTP_ASYNC_WRITE (_IOW(GOODIX_TS_IOC_MAGIC, 6, u8) & NEGLECT_SIZE_MASK) +#define GTP_READ_CONFIG (_IOW(GOODIX_TS_IOC_MAGIC, 7, u8) & NEGLECT_SIZE_MASK) +#define GTP_ESD_ENABLE _IO(GOODIX_TS_IOC_MAGIC, 8) +#define GTP_TOOLS_VER (_IOR(GOODIX_TS_IOC_MAGIC, 9, u8) & NEGLECT_SIZE_MASK) +#define GTP_TOOLS_CTRL_SYNC (_IOW(GOODIX_TS_IOC_MAGIC, 10, u8) & NEGLECT_SIZE_MASK) + +#define MAX_BUF_LENGTH (16*1024) +#define IRQ_FALG (0x01 << 2) + +#define I2C_MSG_HEAD_LEN 20 + +/* + * struct goodix_tools_dev - goodix tools device struct + * @ts_core: The core data struct of ts driver + * @ops_mode: represent device work mode + * @rawdiffcmd: Set slave device into rawdata mode + * @normalcmd: Set slave device into normal mode + * @wq: Wait queue struct use in synchronous data read + * @mutex: Protect goodix_tools_dev + * @in_use: device in use + */ +struct goodix_tools_dev { + struct goodix_ts_core *ts_core; + struct list_head head; + unsigned int ops_mode; + struct goodix_ts_cmd rawdiffcmd, normalcmd; + wait_queue_head_t wq; + struct mutex mutex; + atomic_t in_use; + struct goodix_ext_module module; +} *goodix_tools_dev; + + +/* read data asynchronous, + * success return data length, otherwise return < 0 + */ +static int async_read(struct goodix_tools_dev *dev, void __user *arg) +{ + u8 *databuf = NULL; + int ret = 0; + u32 reg_addr, length; + u8 i2c_msg_head[I2C_MSG_HEAD_LEN]; + const struct goodix_ts_hw_ops *hw_ops = dev->ts_core->hw_ops; + + ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN); + if (ret) + return -EFAULT; + + reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8) + + (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24); + length = i2c_msg_head[4] + (i2c_msg_head[5] << 8) + + (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24); + if (length > MAX_BUF_LENGTH) { + ts_err("buffer too long:%d > %d", length, MAX_BUF_LENGTH); + return -EINVAL; + } + databuf = kzalloc(length, GFP_KERNEL); + if (!databuf) { + ts_err("Alloc memory failed"); + return -ENOMEM; + } + + if (hw_ops->read(dev->ts_core, reg_addr, databuf, length)) { + ret = -EBUSY; + ts_err("Read i2c failed"); + goto err_out; + } + ret = copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, databuf, length); + if (ret) { + ret = -EFAULT; + ts_err("Copy_to_user failed"); + goto err_out; + } + ret = length; +err_out: + kfree(databuf); + return ret; +} + +/* if success return config data length */ +static int read_config_data(struct goodix_ts_core *ts_core, void __user *arg) +{ + int ret = 0; + u32 reg_addr, length; + u8 i2c_msg_head[I2C_MSG_HEAD_LEN]; + u8 *tmp_buf; + + ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN); + if (ret) { + ts_err("Copy data from user failed"); + return -EFAULT; + } + reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8) + + (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24); + length = i2c_msg_head[4] + (i2c_msg_head[5] << 8) + + (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24); + ts_info("read config,reg_addr=0x%x, length=%d", reg_addr, length); + if (length > MAX_BUF_LENGTH) { + ts_err("buffer too long:%d > %d", length, MAX_BUF_LENGTH); + return -EINVAL; + } + tmp_buf = kzalloc(length, GFP_KERNEL); + if (!tmp_buf) { + ts_err("failed alloc memory"); + return -ENOMEM; + } + /* if reg_addr == 0, read config data with specific flow */ + if (!reg_addr) { + if (ts_core->hw_ops->read_config) + ret = ts_core->hw_ops->read_config(ts_core, tmp_buf, length); + else + ret = -EINVAL; + } else { + ret = ts_core->hw_ops->read(ts_core, reg_addr, tmp_buf, length); + if (!ret) + ret = length; + } + if (ret <= 0) + goto err_out; + + if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, tmp_buf, ret)) { + ret = -EFAULT; + ts_err("Copy_to_user failed"); + } + +err_out: + kfree(tmp_buf); + return ret; +} + +/* write data to i2c asynchronous, + * success return bytes write, else return <= 0 + */ +static int async_write(struct goodix_tools_dev *dev, void __user *arg) +{ + u8 *databuf; + int ret = 0; + u32 reg_addr, length; + u8 i2c_msg_head[I2C_MSG_HEAD_LEN]; + struct goodix_ts_core *ts_core = dev->ts_core; + const struct goodix_ts_hw_ops *hw_ops = ts_core->hw_ops; + + ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN); + if (ret) { + ts_err("Copy data from user failed"); + return -EFAULT; + } + reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8) + + (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24); + length = i2c_msg_head[4] + (i2c_msg_head[5] << 8) + + (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24); + if (length > MAX_BUF_LENGTH) { + ts_err("buffer too long:%d > %d", length, MAX_BUF_LENGTH); + return -EINVAL; + } + + databuf = kzalloc(length, GFP_KERNEL); + if (!databuf) { + ts_err("Alloc memory failed"); + return -ENOMEM; + } + ret = copy_from_user(databuf, (u8 *)arg + I2C_MSG_HEAD_LEN, length); + if (ret) { + ret = -EFAULT; + ts_err("Copy data from user failed"); + goto err_out; + } + + if (hw_ops->write(ts_core, reg_addr, databuf, length)) { + ret = -EBUSY; + ts_err("Write data to device failed"); + } else { + ret = length; + } + +err_out: + kfree(databuf); + return ret; +} + +static int init_cfg_data(struct goodix_ic_config *cfg, void __user *arg) +{ + int ret = 0; + u32 length; + u8 i2c_msg_head[I2C_MSG_HEAD_LEN] = {0}; + + ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN); + if (ret) { + ts_err("Copy data from user failed"); + return -EFAULT; + } + + length = i2c_msg_head[4] + (i2c_msg_head[5] << 8) + + (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24); + if (length > GOODIX_CFG_MAX_SIZE) { + ts_err("buffer too long:%d > %d", length, MAX_BUF_LENGTH); + return -EINVAL; + } + ret = copy_from_user(cfg->data, (u8 *)arg + I2C_MSG_HEAD_LEN, length); + if (ret) { + ts_err("Copy data from user failed"); + return -EFAULT; + } + cfg->len = length; + return 0; +} + +/** + * goodix_tools_ioctl - ioctl implementation + * + * @filp: Pointer to file opened + * @cmd: Ioctl opertion command + * @arg: Command data + * Returns >=0 - succeed, else failed + */ +static long goodix_tools_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + struct goodix_tools_dev *dev = filp->private_data; + struct goodix_ts_core *ts_core; + const struct goodix_ts_hw_ops *hw_ops; + struct goodix_ic_config *temp_cfg = NULL; + + if (dev->ts_core == NULL) { + ts_err("Tools module not register"); + return -EINVAL; + } + ts_core = dev->ts_core; + hw_ops = ts_core->hw_ops; + + if (_IOC_TYPE(cmd) != GOODIX_TS_IOC_MAGIC) { + ts_err("Bad magic num:%c", _IOC_TYPE(cmd)); + return -ENOTTY; + } + + switch (cmd & NEGLECT_SIZE_MASK) { + case GTP_IRQ_ENABLE: + if (arg == 1) { + hw_ops->irq_enable(ts_core, true); + mutex_lock(&dev->mutex); + dev->ops_mode |= IRQ_FALG; + mutex_unlock(&dev->mutex); + ts_info("IRQ enabled"); + } else if (arg == 0) { + hw_ops->irq_enable(ts_core, false); + mutex_lock(&dev->mutex); + dev->ops_mode &= ~IRQ_FALG; + mutex_unlock(&dev->mutex); + ts_info("IRQ disabled"); + } else { + ts_info("Irq aready set with, arg = %ld", arg); + } + ret = 0; + break; + case GTP_ESD_ENABLE: + if (arg == 0) + goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); + else + goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); + break; + case GTP_DEV_RESET: + hw_ops->reset(ts_core, GOODIX_NORMAL_RESET_DELAY_MS); + break; + case GTP_SEND_COMMAND: + /* deprecated command */ + ts_err("the GTP_SEND_COMMAND function has been removed"); + ret = -EINVAL; + break; + case GTP_SEND_CONFIG: + temp_cfg = kzalloc(sizeof(struct goodix_ic_config), GFP_KERNEL); + if (temp_cfg == NULL) { + ts_err("Memory allco err"); + ret = -ENOMEM; + goto err_out; + } + + ret = init_cfg_data(temp_cfg, (void __user *)arg); + if (!ret && hw_ops->send_config) { + ret = hw_ops->send_config(ts_core, temp_cfg->data, temp_cfg->len); + if (ret) { + ts_err("Failed send config"); + ret = -EAGAIN; + } else { + ts_info("Send config success"); + ret = 0; + } + } + kfree(temp_cfg); + temp_cfg = NULL; + break; + case GTP_READ_CONFIG: + ret = read_config_data(ts_core, (void __user *)arg); + if (ret > 0) + ts_info("success read config:len=%d", ret); + else + ts_err("failed read config:ret=0x%x", ret); + break; + case GTP_ASYNC_READ: + ret = async_read(dev, (void __user *)arg); + if (ret < 0) + ts_err("Async data read failed"); + break; + case GTP_SYNC_READ: + ts_info("unsupport sync read"); + break; + case GTP_ASYNC_WRITE: + ret = async_write(dev, (void __user *)arg); + if (ret < 0) + ts_err("Async data write failed"); + break; + case GTP_TOOLS_VER: + ret = copy_to_user((u8 *)arg, &goodix_tools_ver, + sizeof(u16)); + if (ret) + ts_err("failed copy driver version info to user"); + break; + case GTP_TOOLS_CTRL_SYNC: + ts_core->tools_ctrl_sync = !!arg; + ts_info("set tools ctrl sync %d", ts_core->tools_ctrl_sync); + break; + default: + ts_info("Invalid cmd"); + ret = -ENOTTY; + break; + } + +err_out: + return ret; +} + +#ifdef CONFIG_COMPAT +static long goodix_tools_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *arg32 = compat_ptr(arg); + + if (!file->f_op || !file->f_op->unlocked_ioctl) + return -ENOTTY; + return file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32); +} +#endif + +static int goodix_tools_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + + ts_info("try open tool"); + /* Only the first time open device need to register module */ + ret = goodix_register_ext_module_no_wait(&goodix_tools_dev->module); + if (ret) { + ts_info("failed register to core module"); + return -EFAULT; + } + ts_info("success open tools"); + goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); + filp->private_data = goodix_tools_dev; + atomic_set(&goodix_tools_dev->in_use, 1); + return 0; +} + +static int goodix_tools_release(struct inode *inode, struct file *filp) +{ + int ret = 0; + /* when the last close this dev node unregister the module */ + goodix_tools_dev->ts_core->tools_ctrl_sync = false; + atomic_set(&goodix_tools_dev->in_use, 0); + goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); + ret = goodix_unregister_ext_module(&goodix_tools_dev->module); + return ret; +} + +static int goodix_tools_module_init(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + struct goodix_tools_dev *tools_dev = module->priv_data; + + if (core_data) + tools_dev->ts_core = core_data; + else + return -ENODEV; + + return 0; +} + +static int goodix_tools_module_exit(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + struct goodix_tools_dev *tools_dev = module->priv_data; + ts_debug("tools module unregister"); + if (atomic_read(&tools_dev->in_use)) { + ts_err("tools module busy, please close it then retry"); + return -EBUSY; + } + return 0; +} + +static const struct file_operations goodix_tools_fops = { + .owner = THIS_MODULE, + .open = goodix_tools_open, + .release = goodix_tools_release, + .unlocked_ioctl = goodix_tools_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = goodix_tools_compat_ioctl, +#endif +}; + +static struct miscdevice goodix_tools_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = GOODIX_TOOLS_NAME, + .fops = &goodix_tools_fops, +}; + +static struct goodix_ext_module_funcs goodix_tools_module_funcs = { + .init = goodix_tools_module_init, + .exit = goodix_tools_module_exit, +}; + +/** + * goodix_tools_init - init goodix tools device and register a miscdevice + * + * return: 0 success, else failed + */ +int goodix_tools_init(void) +{ + int ret; + + goodix_tools_dev = kzalloc(sizeof(struct goodix_tools_dev), GFP_KERNEL); + if (goodix_tools_dev == NULL) { + ts_err("Memory allco err"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&goodix_tools_dev->head); + goodix_tools_dev->ops_mode = 0; + goodix_tools_dev->ops_mode |= IRQ_FALG; + init_waitqueue_head(&goodix_tools_dev->wq); + mutex_init(&goodix_tools_dev->mutex); + atomic_set(&goodix_tools_dev->in_use, 0); + + goodix_tools_dev->module.funcs = &goodix_tools_module_funcs; + goodix_tools_dev->module.name = GOODIX_TOOLS_NAME; + goodix_tools_dev->module.priv_data = goodix_tools_dev; + goodix_tools_dev->module.priority = EXTMOD_PRIO_DBGTOOL; + + ret = misc_register(&goodix_tools_miscdev); + if (ret) + ts_err("Debug tools miscdev register failed"); + else + ts_info("Debug tools miscdev register success"); + + return ret; +} + +void goodix_tools_exit(void) +{ + misc_deregister(&goodix_tools_miscdev); + kfree(goodix_tools_dev); + ts_info("Debug tools miscdev exit"); +} diff --git a/goodix_ts_utils.c b/goodix_ts_utils.c new file mode 100644 index 0000000000..6d55eb3d9e --- /dev/null +++ b/goodix_ts_utils.c @@ -0,0 +1,179 @@ + /* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ +#include "goodix_ts_core.h" + +bool debug_log_flag = false; + +/***************************************************************************** +* goodix_append_checksum +* @summary +* Calcualte data checksum with the specified mode. +* +* @param data +* data need to be calculate +* @param len +* data length +* @param mode +* calculate for u8 or u16 checksum +* @return +* return the data checksum value. +* +*****************************************************************************/ +u32 goodix_append_checksum(u8 *data, int len, int mode) +{ + u32 checksum = 0; + int i; + + checksum = 0; + if (mode == CHECKSUM_MODE_U8_LE) { + for (i = 0; i < len; i++) + checksum += data[i]; + } else { + for (i = 0; i < len; i+=2) + checksum += (data[i] + (data[i+1] << 8)); + } + + if (mode == CHECKSUM_MODE_U8_LE) { + data[len] = checksum & 0xff; + data[len + 1] = (checksum >> 8) & 0xff; + return 0xFFFF & checksum; + } + data[len] = checksum & 0xff; + data[len + 1] = (checksum >> 8) & 0xff; + data[len + 2] = (checksum >> 16) & 0xff; + data[len + 3] = (checksum >> 24) & 0xff; + return checksum; +} + +/* checksum_cmp: check data valid or not + * @data: data need to be check + * @size: data length need to be check(include the checksum bytes) + * @mode: compare with U8 or U16 mode + * */ +int checksum_cmp(const u8 *data, int size, int mode) +{ + u32 cal_checksum = 0; + u32 r_checksum = 0; + u32 i; + + if (mode == CHECKSUM_MODE_U8_LE) { + if (size < 2) + return 1; + for (i = 0; i < size - 2; i++) + cal_checksum += data[i]; + + r_checksum = data[size - 2] + (data[size - 1] << 8); + return (cal_checksum & 0xFFFF) == r_checksum ? 0 : 1; + } + + if (size < 4) + return 1; + for (i = 0; i < size - 4; i += 2) + cal_checksum += data[i] + (data[i + 1] << 8); + r_checksum = data[size - 4] + (data[size - 3] << 8) + + (data[size - 2] << 16) + (data[size - 1] << 24); + return cal_checksum == r_checksum ? 0 : 1; +} + +/* return 1 if all data is zero or ff + * else return 0 + */ +int is_risk_data(const u8 *data, int size) +{ + int i; + int zero_count = 0; + int ff_count = 0; + + for (i = 0; i < size; i++) { + if (data[i] == 0) + zero_count++; + else if (data[i] == 0xff) + ff_count++; + } + if (zero_count == size || ff_count == size) { + ts_info("warning data is all %s\n", + zero_count == size ? "zero" : "0xff"); + return 1; + } + + return 0; +} + +/* get config id form config file */ +#define CONFIG_ID_OFFSET 30 +u32 goodix_get_file_config_id(u8 *ic_config) +{ + if (!ic_config) + return 0; + return le32_to_cpup((__le32 *)&ic_config[CONFIG_ID_OFFSET]); +} + +/* matrix transpose */ +void goodix_rotate_abcd2cbad(int tx, int rx, s16 *data) +{ + s16 *temp_buf = NULL; + int size = tx * rx; + int i; + int j; + int col; + + temp_buf = kcalloc(size, sizeof(s16), GFP_KERNEL); + if (!temp_buf) { + ts_err("malloc failed"); + return; + } + + for (i = 0, j = 0, col = 0; i < size; i++) { + temp_buf[i] = data[j++ * rx + col]; + if (j == tx) { + j = 0; + col++; + } + } + + memcpy(data, temp_buf, size * sizeof(s16)); + kfree(temp_buf); +} + +/* get ic type */ +int goodix_get_ic_type(struct device_node *node) +{ + const char *name_tmp; + int ret; + + ret = of_property_read_string(node, "compatible", &name_tmp); + if (ret < 0) { + ts_err("get compatible failed"); + return ret; + } + + if (strstr(name_tmp, "9897")) { + ts_info("ic type is BerlinA"); + ret = IC_TYPE_BERLIN_A; + } else if (strstr(name_tmp, "9966") || strstr(name_tmp, "7986")) { + ts_info("ic type is BerlinB"); + ret = IC_TYPE_BERLIN_B; + } else if (strstr(name_tmp, "9916")) { + ts_info("ic type is BerlinD"); + ret = IC_TYPE_BERLIN_D; + } else { + ts_info("can't find valid ic_type"); + ret = -EINVAL; + } + + return ret; +} From 573d007b518b2d5e749a36e93fd1074f15471ded Mon Sep 17 00:00:00 2001 From: xulinkun Date: Mon, 24 Jan 2022 15:43:18 +0800 Subject: [PATCH 006/170] touch: goodix: v1.2.4 1. add LICENSE Change-Id: Ic6175bc334599d135cb85caa9066971dbb089be2 Signed-off-by: xulinkun Git-commit: 428deaecc357693de40f8bbce5cddb5b236232f6 Git-repo: https://github.com/goodix/goodix_ts_berlin Signed-off-by: Fei Mao --- LICENSE | 16 + driver/Kconfig | 20 + driver/Makefile | 12 + .../goodix_brl_fwupdate.c | 164 +++---- goodix_brl_hw.c => driver/goodix_brl_hw.c | 57 ++- goodix_brl_i2c.c => driver/goodix_brl_i2c.c | 0 goodix_brl_spi.c => driver/goodix_brl_spi.c | 14 +- goodix_cfg_bin.c => driver/goodix_cfg_bin.c | 70 +-- goodix_ts_core.c => driver/goodix_ts_core.c | 259 +++++----- goodix_ts_core.h => driver/goodix_ts_core.h | 93 +++- driver/goodix_ts_gesture.c | 446 ++++++++++++++++++ .../goodix_ts_inspect.c | 243 +++++----- goodix_ts_tools.c => driver/goodix_ts_tools.c | 0 goodix_ts_utils.c => driver/goodix_ts_utils.c | 0 goodix_ts_gesture.c | 384 --------------- 15 files changed, 1000 insertions(+), 778 deletions(-) create mode 100644 LICENSE create mode 100644 driver/Kconfig create mode 100644 driver/Makefile rename goodix_brl_fwupdate.c => driver/goodix_brl_fwupdate.c (92%) rename goodix_brl_hw.c => driver/goodix_brl_hw.c (96%) rename goodix_brl_i2c.c => driver/goodix_brl_i2c.c (100%) rename goodix_brl_spi.c => driver/goodix_brl_spi.c (96%) rename goodix_cfg_bin.c => driver/goodix_cfg_bin.c (84%) rename goodix_ts_core.c => driver/goodix_ts_core.c (91%) rename goodix_ts_core.h => driver/goodix_ts_core.h (85%) create mode 100644 driver/goodix_ts_gesture.c rename goodix_ts_inspect.c => driver/goodix_ts_inspect.c (94%) rename goodix_ts_tools.c => driver/goodix_ts_tools.c (100%) rename goodix_ts_utils.c => driver/goodix_ts_utils.c (100%) delete mode 100644 goodix_ts_gesture.c diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..ee447585ed --- /dev/null +++ b/LICENSE @@ -0,0 +1,16 @@ + Copyright (c) 2003-2006, Goodix Technology Co, Ltd. + All Rights Reserved + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + 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. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + diff --git a/driver/Kconfig b/driver/Kconfig new file mode 100644 index 0000000000..375b5473a1 --- /dev/null +++ b/driver/Kconfig @@ -0,0 +1,20 @@ +# +# Goodix touchscreen driver configuration +# +menuconfig TOUCHSCREEN_GOODIX_BRL + tristate "Goodix berlin touchscreen" + help + Say Y here if you have a Goodix berlin series touch controller + to your system. + + If build module, say M. + If unsure, say N. + +if TOUCHSCREEN_GOODIX_BRL + +config TOUCHSCREEN_GOODIX_BRL_SPI + bool "support SPI bus connection" + help + Say Y here if the touchscreen is connected via SPI bus. + +endif diff --git a/driver/Makefile b/driver/Makefile new file mode 100644 index 0000000000..903a1cccb5 --- /dev/null +++ b/driver/Makefile @@ -0,0 +1,12 @@ +obj-$(CONFIG_TOUCHSCREEN_GOODIX_BRL) += goodix_core.o +goodix_core-y := \ + goodix_brl_i2c.o \ + goodix_brl_spi.o \ + goodix_ts_core.o \ + goodix_brl_hw.o \ + goodix_cfg_bin.o \ + goodix_ts_utils.o \ + goodix_brl_fwupdate.o \ + goodix_ts_gesture.o \ + goodix_ts_inspect.o \ + goodix_ts_tools.o diff --git a/goodix_brl_fwupdate.c b/driver/goodix_brl_fwupdate.c similarity index 92% rename from goodix_brl_fwupdate.c rename to driver/goodix_brl_fwupdate.c index 726ea76d00..44400bd428 100644 --- a/goodix_brl_fwupdate.c +++ b/driver/goodix_brl_fwupdate.c @@ -1,19 +1,19 @@ - /* - * Goodix Touchscreen Driver - * Copyright (C) 2020 - 2021 Goodix, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be a reference - * to you, when you are integrating the GOODiX's CTP IC into your system, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - */ +/* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ #include "goodix_ts_core.h" #define BUS_TYPE_SPI 1 @@ -191,8 +191,9 @@ struct firmware_summary { * @firmware: firmware data structure */ struct firmware_data { - struct firmware_summary fw_summary; + struct firmware_summary fw_summary; const struct firmware *firmware; + struct firmware *fw_sysfs; }; struct config_data { @@ -330,7 +331,11 @@ static int goodix_parse_firmware(struct firmware_data *fw_data) fw_summary = &fw_data->fw_summary; /* copy firmware head info */ - firmware = fw_data->firmware; + if (goodix_fw_update_ctrl.mode & UPDATE_MODE_SRC_SYSFS) + firmware = fw_data->fw_sysfs; + else + firmware = fw_data->firmware; + if (firmware->size < subsys_info_offset) { ts_err("Invalid firmware size:%zu", firmware->size); r = -EINVAL; @@ -342,7 +347,8 @@ static int goodix_parse_firmware(struct firmware_data *fw_data) fw_summary->size = le32_to_cpu(fw_summary->size); if (firmware->size != fw_summary->size + FW_FILE_CHECKSUM_OFFSET) { ts_err("Bad firmware, size not match, %zu != %d", - firmware->size, fw_summary->size + 6); + firmware->size, + fw_summary->size + FW_FILE_CHECKSUM_OFFSET); r = -EINVAL; goto err_size; } @@ -709,7 +715,7 @@ static int goodix_send_flash_cmd(struct goodix_flash_cmd *flash_cmd) } ts_info("flash cmd ack check pass"); - msleep(80); + msleep(50); retry = 20; for (i = 0; i < retry; i++) { ret = goodix_reg_read(flash_cmd_reg, @@ -721,8 +727,8 @@ static int goodix_send_flash_cmd(struct goodix_flash_cmd *flash_cmd) } ts_info("flash cmd status not ready, retry %d, ack 0x%x, status 0x%x, ret %d", - i, tmp_cmd.ack, tmp_cmd.status, ret); - msleep(20); + i, tmp_cmd.ack, tmp_cmd.status, ret); + usleep_range(10000, 11000); } ts_err("flash cmd status error %d, ack 0x%x, status 0x%x, ret %d", @@ -944,10 +950,10 @@ int goodix_fw_update_proc(struct fw_update_ctrl *fwu_ctrl) if (!(fwu_ctrl->mode & UPDATE_MODE_FORCE)) { ret = goodix_fw_version_compare(fwu_ctrl); if (!ret) { - ts_info("firmware upgraded"); + ts_info("no need to upgrade"); return 0; - } else - ts_info("need to upgrade"); + } + ts_info("need to upgrade"); } start_update: @@ -1036,75 +1042,29 @@ static ssize_t goodix_sysfs_update_en_store( return -EINVAL; } -static ssize_t goodix_sysfs_fwsize_show( - struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; - int r = -EINVAL; - - if (fw_ctrl && fw_ctrl->fw_data.firmware) - r = snprintf(buf, PAGE_SIZE, "%zu\n", - fw_ctrl->fw_data.firmware->size); - return r; -} - -static ssize_t goodix_sysfs_fwsize_store( - struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; - struct firmware *fw; - u8 **data; - size_t size = 0; - - if (!fw_ctrl) - return -EINVAL; - - if (sscanf(buf, "%zu", &size) < 0 || !size) { - ts_err("Failed to get fwsize"); - return -EFAULT; - } - - /* use vmalloc to alloc huge memory */ - fw = vmalloc(sizeof(*fw) + size); - if (!fw) - return -ENOMEM; - mutex_lock(&fw_ctrl->mutex); - memset(fw, 0x00, sizeof(*fw) + size); - data = (u8 **)&fw->data; - *data = (u8 *)fw + sizeof(struct firmware); - fw->size = size; - fw_ctrl->fw_data.firmware = fw; - fw_ctrl->mode = UPDATE_MODE_SRC_SYSFS; - mutex_unlock(&fw_ctrl->mutex); - return count; -} - static ssize_t goodix_sysfs_fwimage_store(struct file *file, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t pos, size_t count) { - struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; - struct firmware_data *fw_data; + struct firmware **fw = &goodix_fw_update_ctrl.fw_data.fw_sysfs; - fw_data = &fw_ctrl->fw_data; - - if (!fw_data->firmware) { - ts_err("Need set fw image size first"); - return -ENOMEM; + if (*fw == NULL) { + *fw = kzalloc(sizeof(**fw), GFP_KERNEL); + if (*fw == NULL) + return -ENOMEM; + (*fw)->data = vmalloc(GOODIX_FW_MAX_SIEZE); + if ((*fw)->data == NULL) { + kfree(*fw); + *fw = NULL; + return -ENOMEM; + } } - if (fw_data->firmware->size == 0) { - ts_err("Invalid firmware size"); - return -EINVAL; - } - - if (pos + count > fw_data->firmware->size) + if (pos + count > GOODIX_FW_MAX_SIEZE) return -EFAULT; - mutex_lock(&fw_ctrl->mutex); - memcpy((u8 *)&fw_data->firmware->data[pos], buf, count); - mutex_unlock(&fw_ctrl->mutex); + memcpy((u8 *)&(*fw)->data[pos], buf, count); + (*fw)->size = pos + count; + return count; } @@ -1146,13 +1106,10 @@ static ssize_t goodix_sysfs_result_show( } static DEVICE_ATTR(update_en, 0220, NULL, goodix_sysfs_update_en_store); -static DEVICE_ATTR(fwsize, 0664, goodix_sysfs_fwsize_show, - goodix_sysfs_fwsize_store); static DEVICE_ATTR(result, 0664, goodix_sysfs_result_show, NULL); static struct attribute *goodix_fwu_attrs[] = { &dev_attr_update_en.attr, - &dev_attr_fwsize.attr, &dev_attr_result.attr }; @@ -1181,7 +1138,7 @@ static int goodix_fw_sysfs_init(struct goodix_ts_core *core_data, } fw_ctrl->attr_fwimage.attr.name = "fwimage"; - fw_ctrl->attr_fwimage.attr.mode = 0666; + fw_ctrl->attr_fwimage.attr.mode = 0664; fw_ctrl->attr_fwimage.size = 0; fw_ctrl->attr_fwimage.write = goodix_sysfs_fwimage_store; ret = sysfs_create_bin_file(fw_ctrl->kobj, &fw_ctrl->attr_fwimage); @@ -1259,7 +1216,6 @@ static inline void goodix_release_firmware(struct firmware_data *fw_data) static int goodix_fw_update_thread(void *data) { struct fw_update_ctrl *fwu_ctrl = data; - struct firmware *temp_firmware = NULL; ktime_t start, end; int r = -EINVAL; @@ -1268,37 +1224,45 @@ static int goodix_fw_update_thread(void *data) fwu_ctrl->status = UPSTA_NOTWORK; mutex_lock(&fwu_ctrl->mutex); + ts_debug("notify update start"); + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_START, NULL); + if (fwu_ctrl->mode & UPDATE_MODE_SRC_REQUEST) { ts_info("Firmware request update starts"); r = goodix_request_firmware(&fwu_ctrl->fw_data, fwu_ctrl->fw_name); if (r < 0) goto out; - } else if (fwu_ctrl->mode & UPDATE_MODE_SRC_SYSFS) { - if (!fwu_ctrl->fw_data.firmware) { + if (!fwu_ctrl->fw_data.fw_sysfs) { ts_err("Invalid firmware from sysfs"); r = -EINVAL; goto out; } + if (fwu_ctrl->fw_data.fw_sysfs->size < 4096) { + ts_err("Invalid firmware size[%ld] from sysfs", + fwu_ctrl->fw_data.fw_sysfs->size); + vfree(fwu_ctrl->fw_data.fw_sysfs->data); + kfree(fwu_ctrl->fw_data.fw_sysfs); + fwu_ctrl->fw_data.fw_sysfs = NULL; + r = -EINVAL; + goto out; + } } else { ts_err("unknown update mode 0x%x", fwu_ctrl->mode); r = -EINVAL; goto out; } - ts_debug("notify update start"); - goodix_ts_blocking_notify(NOTIFY_FWUPDATE_START, NULL); - /* ready to update */ ts_debug("start update proc"); r = goodix_fw_update_proc(fwu_ctrl); /* clean */ - if (fwu_ctrl->mode & UPDATE_MODE_SRC_HEAD) { - kfree(fwu_ctrl->fw_data.firmware); - fwu_ctrl->fw_data.firmware = NULL; - temp_firmware = NULL; + if (fwu_ctrl->mode & UPDATE_MODE_SRC_SYSFS) { + vfree(fwu_ctrl->fw_data.fw_sysfs->data); + kfree(fwu_ctrl->fw_data.fw_sysfs); + fwu_ctrl->fw_data.fw_sysfs = NULL; } else if (fwu_ctrl->mode & UPDATE_MODE_SRC_REQUEST) { goodix_release_firmware(&fwu_ctrl->fw_data); } diff --git a/goodix_brl_hw.c b/driver/goodix_brl_hw.c similarity index 96% rename from goodix_brl_hw.c rename to driver/goodix_brl_hw.c index 49a03f9719..3c5b22d424 100644 --- a/goodix_brl_hw.c +++ b/driver/goodix_brl_hw.c @@ -68,7 +68,7 @@ static int brl_select_spi_mode(struct goodix_ts_core *cd) if (!ret && r_value == w_value) return 0; } - ts_err("failed switch SPI mode after reset, ret:%d r_value:%02x", ret, r_value); + ts_err("failed switch SPI mode, ret:%d r_value:%02x", ret, r_value); return -EINVAL; } @@ -151,7 +151,8 @@ static int brl_reset_after(struct goodix_ts_core *cd) break; } if (retry < 0) { - ts_err("failed to enable group0 clock, ret:%d status:%02x", ret, temp_buf[0]); + ts_err("failed to enable group0 clock, ret:%d status:%02x", + ret, temp_buf[0]); return -EINVAL; } @@ -165,7 +166,8 @@ static int brl_reset_after(struct goodix_ts_core *cd) break; } if (retry < 0) { - ts_err("failed to enable group1 clock, ret:%d status:%02x", ret, temp_buf[0]); + ts_err("failed to enable group1 clock, ret:%d status:%02x", + ret, temp_buf[0]); return -EINVAL; } @@ -179,7 +181,8 @@ static int brl_reset_after(struct goodix_ts_core *cd) break; } if (retry < 0) { - ts_err("failed to set D12, ret:%d status:%02x", ret, temp_buf[0]); + ts_err("failed to set D12, ret:%d status:%02x", + ret, temp_buf[0]); return -EINVAL; } @@ -272,12 +275,16 @@ int brl_resume(struct goodix_ts_core *cd) return cd->hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); } -#define GOODIX_GESTURE_CMD 0x12 +#define GOODIX_GESTURE_CMD_BA 0x12 +#define GOODIX_GESTURE_CMD 0xA6 int brl_gesture(struct goodix_ts_core *cd, int gesture_type) { struct goodix_ts_cmd cmd; - cmd.cmd = GOODIX_GESTURE_CMD; + if (cd->bus->ic_type == IC_TYPE_BERLIN_A) + cmd.cmd = GOODIX_GESTURE_CMD_BA; + else + cmd.cmd = GOODIX_GESTURE_CMD; cmd.len = 5; cmd.data[0] = gesture_type; if (cd->hw_ops->send_cmd(cd, &cmd)) @@ -375,7 +382,7 @@ static int brl_send_cmd(struct goodix_ts_core *cd, ts_debug("cmd ack data %*ph", (int)sizeof(cmd_ack), cmd_ack.buf); if (cmd_ack.ack == CMD_ACK_OK) { - usleep_range(2000, 2100); + msleep(40); // wait for cmd response return 0; } if (cmd_ack.ack == CMD_ACK_BUSY || @@ -1083,12 +1090,16 @@ static int goodix_touch_handler(struct goodix_ts_core *cd, } } + /* read done */ + hw_ops->after_event_handler(cd); + if (touch_num > 0) { point_type = buffer[IRQ_EVENT_HEAD_LEN] & 0x0F; if (point_type == POINT_TYPE_STYLUS || point_type == POINT_TYPE_STYLUS_HOVER) { ret = checksum_cmp(&buffer[IRQ_EVENT_HEAD_LEN], - BYTES_PER_POINT * 2 + 2, CHECKSUM_MODE_U8_LE); + BYTES_PER_POINT * 2 + 2, + CHECKSUM_MODE_U8_LE); if (ret) { ts_debug("touch data checksum error"); ts_debug("data:%*ph", BYTES_PER_POINT * 2 + 2, @@ -1097,10 +1108,12 @@ static int goodix_touch_handler(struct goodix_ts_core *cd, } } else { ret = checksum_cmp(&buffer[IRQ_EVENT_HEAD_LEN], - touch_num * BYTES_PER_POINT + 2, CHECKSUM_MODE_U8_LE); + touch_num * BYTES_PER_POINT + 2, + CHECKSUM_MODE_U8_LE); if (ret) { ts_debug("touch data checksum error"); - ts_debug("data:%*ph", touch_num * BYTES_PER_POINT + 2, + ts_debug("data:%*ph", + touch_num * BYTES_PER_POINT + 2, &buffer[IRQ_EVENT_HEAD_LEN]); return -EINVAL; } @@ -1159,10 +1172,14 @@ static int brl_event_handler(struct goodix_ts_core *cd, return ret; } + if (pre_buf[0] == 0x00) { + ts_debug("invalid touch head"); + return -EINVAL; + } + if (checksum_cmp(pre_buf, IRQ_EVENT_HEAD_LEN, CHECKSUM_MODE_U8_LE)) { - ts_debug("touch head checksum err"); - ts_debug("touch_head %*ph", IRQ_EVENT_HEAD_LEN, pre_buf); - ts_event->retry = 1; + ts_debug("touch head checksum err[%*ph]", + IRQ_EVENT_HEAD_LEN, pre_buf); return -EINVAL; } @@ -1180,10 +1197,16 @@ static int brl_event_handler(struct goodix_ts_core *cd, else ts_debug("unsupported request code 0x%x", pre_buf[2]); } + if (event_status & GOODIX_GESTURE_EVENT) { ts_event->event_type = EVENT_GESTURE; ts_event->gesture_type = pre_buf[4]; + memcpy(ts_event->gesture_data, &pre_buf[8], + GOODIX_GESTURE_DATA_LEN); } + /* read done */ + hw_ops->after_event_handler(cd); + return 0; } @@ -1193,6 +1216,8 @@ static int brl_after_event_handler(struct goodix_ts_core *cd) struct goodix_ic_info_misc *misc = &cd->ic_info.misc; u8 sync_clean = 0; + if (cd->tools_ctrl_sync) + return 0; return hw_ops->write(cd, misc->touch_data_addr, &sync_clean, 1); } @@ -1233,13 +1258,15 @@ static int brld_get_framedata(struct goodix_ts_core *cd, return ret; } - if (checksum_cmp(frame_buf, cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) { + if (checksum_cmp(frame_buf, cd->ic_info.misc.frame_data_head_len, + CHECKSUM_MODE_U8_LE)) { ts_err("frame head checksum error"); return -EINVAL; } frame_head = (struct frame_head *)frame_buf; - if (checksum_cmp(frame_buf, frame_head->cur_frame_len, CHECKSUM_MODE_U16_LE)) { + if (checksum_cmp(frame_buf, frame_head->cur_frame_len, + CHECKSUM_MODE_U16_LE)) { ts_err("frame body checksum error"); return -EINVAL; } diff --git a/goodix_brl_i2c.c b/driver/goodix_brl_i2c.c similarity index 100% rename from goodix_brl_i2c.c rename to driver/goodix_brl_i2c.c diff --git a/goodix_brl_spi.c b/driver/goodix_brl_spi.c similarity index 96% rename from goodix_brl_spi.c rename to driver/goodix_brl_spi.c index f84961d814..ea1ef860a8 100644 --- a/goodix_brl_spi.c +++ b/driver/goodix_brl_spi.c @@ -24,7 +24,8 @@ #define SPI_TRANS_PREFIX_LEN 1 #define REGISTER_WIDTH 4 #define SPI_READ_DUMMY_LEN 4 -#define SPI_READ_PREFIX_LEN (SPI_TRANS_PREFIX_LEN + REGISTER_WIDTH + SPI_READ_DUMMY_LEN) +#define SPI_READ_PREFIX_LEN \ + (SPI_TRANS_PREFIX_LEN + REGISTER_WIDTH + SPI_READ_DUMMY_LEN) #define SPI_WRITE_PREFIX_LEN (SPI_TRANS_PREFIX_LEN + REGISTER_WIDTH) #define SPI_WRITE_FLAG 0xF0 @@ -80,7 +81,7 @@ static int goodix_spi_read_bra(struct device *dev, unsigned int addr, spi_message_add_tail(&xfers, &spi_msg); ret = spi_sync(spi, &spi_msg); if (ret < 0) { - ts_err("spi transfer error:%d",ret); + ts_err("spi transfer error:%d", ret); goto exit; } memcpy(data, &rx_buf[SPI_READ_PREFIX_LEN], len); @@ -129,7 +130,7 @@ static int goodix_spi_read(struct device *dev, unsigned int addr, spi_message_add_tail(&xfers, &spi_msg); ret = spi_sync(spi, &spi_msg); if (ret < 0) { - ts_err("spi transfer error:%d",ret); + ts_err("spi transfer error:%d", ret); goto exit; } memcpy(data, &rx_buf[SPI_READ_PREFIX_LEN - 1], len); @@ -158,11 +159,8 @@ static int goodix_spi_write(struct device *dev, unsigned int addr, int ret = 0; tx_buf = kzalloc(SPI_WRITE_PREFIX_LEN + len, GFP_KERNEL); - if (!tx_buf) { - ts_err("alloc tx_buf failed, size:%d", - SPI_WRITE_PREFIX_LEN + len); + if (!tx_buf) return -ENOMEM; - } spi_message_init(&spi_msg); memset(&xfers, 0, sizeof(xfers)); @@ -179,7 +177,7 @@ static int goodix_spi_write(struct device *dev, unsigned int addr, spi_message_add_tail(&xfers, &spi_msg); ret = spi_sync(spi, &spi_msg); if (ret < 0) - ts_err("spi transfer error:%d",ret); + ts_err("spi transfer error:%d", ret); kfree(tx_buf); return ret; diff --git a/goodix_cfg_bin.c b/driver/goodix_cfg_bin.c similarity index 84% rename from goodix_cfg_bin.c rename to driver/goodix_cfg_bin.c index 620c110114..fa27255f2c 100644 --- a/goodix_cfg_bin.c +++ b/driver/goodix_cfg_bin.c @@ -21,11 +21,15 @@ #define TS_CFG_BIN_HEAD_RESERVED_LEN 6 #define TS_CFG_OFFSET_LEN 2 #define TS_IC_TYPE_NAME_MAX_LEN 15 -#define TS_CFG_BIN_HEAD_LEN (sizeof(struct goodix_cfg_bin_head) + \ - TS_CFG_BIN_HEAD_RESERVED_LEN) -#define TS_PKG_CONST_INFO_LEN (sizeof(struct goodix_cfg_pkg_const_info)) -#define TS_PKG_REG_INFO_LEN (sizeof(struct goodix_cfg_pkg_reg_info)) -#define TS_PKG_HEAD_LEN (TS_PKG_CONST_INFO_LEN + TS_PKG_REG_INFO_LEN) +#define TS_CFG_BIN_HEAD_LEN \ + (sizeof(struct goodix_cfg_bin_head) + \ + TS_CFG_BIN_HEAD_RESERVED_LEN) +#define TS_PKG_CONST_INFO_LEN \ + (sizeof(struct goodix_cfg_pkg_const_info)) +#define TS_PKG_REG_INFO_LEN \ + (sizeof(struct goodix_cfg_pkg_reg_info)) +#define TS_PKG_HEAD_LEN \ + (TS_PKG_CONST_INFO_LEN + TS_PKG_REG_INFO_LEN) /*cfg block definitin*/ #define TS_CFG_BLOCK_PID_LEN 8 @@ -34,9 +38,9 @@ #define TS_CFG_BLOCK_FW_PATCH_LEN 4 #define TS_CFG_BLOCK_RESERVED_LEN 9 -#define TS_NORMAL_CFG 0x01 -#define TS_HIGH_SENSE_CFG 0x03 -#define TS_RQST_FW_RETRY_TIMES 2 +#define TS_NORMAL_CFG 0x01 +#define TS_HIGH_SENSE_CFG 0x03 +#define TS_RQST_FW_RETRY_TIMES 2 #pragma pack(1) struct goodix_cfg_pkg_reg { @@ -100,7 +104,7 @@ struct goodix_cfg_bin { struct goodix_cfg_package *cfg_pkgs; }; -static int goodix_read_cfg_bin(struct device *dev, const char *cfg_name, +static int goodix_read_cfg_bin(struct device *dev, const char *cfg_name, struct goodix_cfg_bin *cfg_bin) { const struct firmware *firmware = NULL; @@ -167,9 +171,9 @@ static int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin) /*check cfg_bin valid*/ checksum = 0; - for (i = TS_BIN_VERSION_START_INDEX; i < cfg_bin->bin_data_len; i++) { + for (i = TS_BIN_VERSION_START_INDEX; i < cfg_bin->bin_data_len; i++) checksum += cfg_bin->bin_data[i]; - } + if (checksum != cfg_bin->head.checksum) { ts_err("cfg_bin checksum check filed 0x%02x != 0x%02x", cfg_bin->head.checksum, checksum); @@ -179,28 +183,34 @@ static int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin) /*allocate memory for cfg packages*/ cfg_bin->cfg_pkgs = kzalloc(sizeof(struct goodix_cfg_package) * cfg_bin->head.pkg_num, GFP_KERNEL); - if (!cfg_bin->cfg_pkgs) { - ts_err("cfg_pkgs, allocate memory ERROR"); + if (!cfg_bin->cfg_pkgs) return -ENOMEM; - } /*get cfg_pkg's info*/ for (i = 0; i < cfg_bin->head.pkg_num; i++) { /*get cfg pkg length*/ if (i == cfg_bin->head.pkg_num - 1) { - offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN] + - (cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN + 1] << 8); + offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + + i * TS_CFG_OFFSET_LEN] + + (cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + + i * TS_CFG_OFFSET_LEN + 1] << 8); - cfg_bin->cfg_pkgs[i].pkg_len = cfg_bin->bin_data_len - offset1; + cfg_bin->cfg_pkgs[i].pkg_len = + cfg_bin->bin_data_len - offset1; } else { - offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN] + - (cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN + 1] << 8); + offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + + i * TS_CFG_OFFSET_LEN] + + (cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + + i * TS_CFG_OFFSET_LEN + 1] << 8); - offset2 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN + 2] + - (cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN + 3] << 8); + offset2 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + + i * TS_CFG_OFFSET_LEN + 2] + + (cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + + i * TS_CFG_OFFSET_LEN + 3] << 8); if (offset2 <= offset1) { - ts_err("offset error,pkg:%d, offset1:%d, offset2:%d", i, offset1, offset2); + ts_err("offset error,pkg:%d, offset1:%d, offset2:%d", + i, offset1, offset2); goto exit; } @@ -214,12 +224,15 @@ static int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin) TS_PKG_REG_INFO_LEN); /*get configuration data*/ - cfg_bin->cfg_pkgs[i].cfg = &cfg_bin->bin_data[offset1 + TS_PKG_HEAD_LEN]; + cfg_bin->cfg_pkgs[i].cfg = + &cfg_bin->bin_data[offset1 + TS_PKG_HEAD_LEN]; } /*debug, print pkg information*/ - ts_info("Driver bin info: ver %s, len %d, pkgs %d", cfg_bin->head.bin_version, - cfg_bin->head.bin_len, cfg_bin->head.pkg_num); + ts_info("Driver bin info: ver %s, len %d, pkgs %d", + cfg_bin->head.bin_version, + cfg_bin->head.bin_len, + cfg_bin->head.pkg_num); return 0; exit: @@ -268,7 +281,9 @@ static int goodix_get_reg_and_cfg(struct goodix_ts_core *cd, u8 sensor_id, sensor_id); continue; } - cd->ic_configs[cfg_type] = kzalloc(sizeof(struct goodix_ic_config), GFP_KERNEL); + cd->ic_configs[cfg_type] = + kzalloc(sizeof(struct goodix_ic_config), + GFP_KERNEL); if (!cd->ic_configs[cfg_type]) goto err_out; cd->ic_configs[cfg_type]->len = cfg_len; @@ -281,8 +296,7 @@ static int goodix_get_reg_and_cfg(struct goodix_ts_core *cd, u8 sensor_id, err_out: /* parse config enter error, release memory alloced */ for (i = 0; i < GOODIX_MAX_CONFIG_GROUP; i++) { - if (cd->ic_configs[i]) - kfree(cd->ic_configs[i]); + kfree(cd->ic_configs[i]); cd->ic_configs[i] = NULL; } return -EINVAL; diff --git a/goodix_ts_core.c b/driver/goodix_ts_core.c similarity index 91% rename from goodix_ts_core.c rename to driver/goodix_ts_core.c index c18ac85eb7..e241eca2da 100644 --- a/goodix_ts_core.c +++ b/driver/goodix_ts_core.c @@ -27,7 +27,7 @@ #include "goodix_ts_core.h" -#define GOODIX_DEFAULT_CFG_NAME "goodix_cfg_group.cfg" +#define GOODIX_DEFAULT_CFG_NAME "goodix_cfg_group.cfg" #define GOOIDX_INPUT_PHYS "goodix_ts/input0" struct goodix_module goodix_modules; @@ -91,7 +91,8 @@ static int __do_register_ext_module(struct goodix_ext_module *module) return 0; } -static void goodix_register_ext_module_work(struct work_struct *work) { +static void goodix_register_ext_module_work(struct work_struct *work) +{ struct goodix_ext_module *module = container_of(work, struct goodix_ext_module, work); @@ -132,13 +133,13 @@ int goodix_register_ext_module(struct goodix_ext_module *module) if (!module) return -EINVAL; - ts_info("goodix_register_ext_module IN"); + ts_info("IN"); goodix_core_module_init(); INIT_WORK(&module->work, goodix_register_ext_module_work); schedule_work(&module->work); - ts_info("goodix_register_ext_module OUT"); + ts_info("OUT"); return 0; } @@ -150,7 +151,8 @@ int goodix_register_ext_module_no_wait(struct goodix_ext_module *module) { if (!module) return -EINVAL; - ts_info("goodix_register_ext_module_no_wait IN"); + + ts_info("IN"); goodix_core_module_init(); /* driver probe failed */ if (core_module_prob_sate != CORE_MODULE_PROB_SUCCESS) { @@ -273,7 +275,7 @@ struct kobject *goodix_get_default_kobj(void) } /* show driver infomation */ -static ssize_t goodix_ts_driver_info_show(struct device *dev, +static ssize_t driver_info_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "DriverVersion:%s\n", @@ -281,20 +283,22 @@ static ssize_t goodix_ts_driver_info_show(struct device *dev, } /* show chip infoamtion */ -static ssize_t goodix_ts_chip_info_show(struct device *dev, +static ssize_t chip_info_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct goodix_ts_core *core_data = dev_get_drvdata(dev); - struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + struct goodix_ts_core *cd = dev_get_drvdata(dev); + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; struct goodix_fw_version chip_ver; + struct goodix_ic_info ic_info; u8 temp_pid[8] = {0}; int ret; int cnt = -EINVAL; if (hw_ops->read_version) { - ret = hw_ops->read_version(core_data, &chip_ver); + ret = hw_ops->read_version(cd, &chip_ver); if (!ret) { - memcpy(temp_pid, chip_ver.rom_pid, sizeof(chip_ver.rom_pid)); + memcpy(temp_pid, chip_ver.rom_pid, + sizeof(chip_ver.rom_pid)); cnt = snprintf(&buf[0], PAGE_SIZE, "rom_pid:%s\nrom_vid:%02x%02x%02x\n", temp_pid, chip_ver.rom_vid[0], @@ -310,12 +314,14 @@ static ssize_t goodix_ts_chip_info_show(struct device *dev, } if (hw_ops->get_ic_info) { - ret = hw_ops->get_ic_info(core_data, &core_data->ic_info); + ret = hw_ops->get_ic_info(cd, &ic_info); if (!ret) { cnt += snprintf(&buf[cnt], PAGE_SIZE, - "config_id:%x\n", core_data->ic_info.version.config_id); + "config_id:%x\n", + ic_info.version.config_id); cnt += snprintf(&buf[cnt], PAGE_SIZE, - "config_version:%x\n", core_data->ic_info.version.config_version); + "config_version:%x\n", + ic_info.version.config_version); } } @@ -339,7 +345,7 @@ static ssize_t goodix_ts_reset_store(struct device *dev, } /* read config */ -static ssize_t goodix_ts_read_cfg_show(struct device *dev, +static ssize_t read_cfg_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -415,9 +421,8 @@ static int goodix_ts_convert_0x_data(const u8 *buf, int buf_size, continue; if (temp_index >= m_size) { - ts_err("exchange cfg data error, overflow," - "temp_index:%d,m_size:%d", - temp_index, m_size); + ts_err("exchange cfg data error, overflow, temp_index:%d,m_size:%d", + temp_index, m_size); return -EINVAL; } high = ascii2hex(buf[i + 1]); @@ -441,13 +446,9 @@ static ssize_t goodix_ts_send_cfg_store(struct device *dev, struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; struct goodix_ic_config *config = NULL; const struct firmware *cfg_img = NULL; - int en; int ret; - if (sscanf(buf, "%d", &en) != 1) - return -EINVAL; - - if (en != 1) + if (buf[0] != '1') return -EINVAL; hw_ops->irq_enable(core_data, false); @@ -476,7 +477,7 @@ static ssize_t goodix_ts_send_cfg_store(struct device *dev, if (ret < 0) ts_err("send config failed"); } - + exit: hw_ops->irq_enable(core_data, true); kfree(config); @@ -513,7 +514,8 @@ static ssize_t goodix_ts_reg_rw_show(struct device *dev, ret = hw_ops->read(core_data, rw_addr, show_buf, rw_len); if (ret < 0) { ts_err("failed read addr(%x) length(%d)", rw_addr, rw_len); - return snprintf(buf, PAGE_SIZE, "failed read addr(%x), len(%d)\n", + return snprintf(buf, PAGE_SIZE, + "failed read addr(%x), len(%d)\n", rw_addr, rw_len); } @@ -692,7 +694,7 @@ static ssize_t goodix_ts_esd_info_show(struct device *dev, "enabled" : "disabled"); return r; -} +} /* enable/disable esd */ static ssize_t goodix_ts_esd_info_store(struct device *dev, @@ -736,17 +738,26 @@ static ssize_t goodix_ts_debug_log_store(struct device *dev, else debug_log_flag = false; return count; -} +} -static DEVICE_ATTR(driver_info, 0444, goodix_ts_driver_info_show, NULL); -static DEVICE_ATTR(chip_info, 0444, goodix_ts_chip_info_show, NULL); -static DEVICE_ATTR(reset, 0220, NULL, goodix_ts_reset_store); -static DEVICE_ATTR(send_cfg, 0220, NULL, goodix_ts_send_cfg_store); -static DEVICE_ATTR(read_cfg, 0444, goodix_ts_read_cfg_show, NULL); -static DEVICE_ATTR(reg_rw, 0664, goodix_ts_reg_rw_show, goodix_ts_reg_rw_store); -static DEVICE_ATTR(irq_info, 0664, goodix_ts_irq_info_show, goodix_ts_irq_info_store); -static DEVICE_ATTR(esd_info, 0664, goodix_ts_esd_info_show, goodix_ts_esd_info_store); -static DEVICE_ATTR(debug_log, 0664, goodix_ts_debug_log_show, goodix_ts_debug_log_store); +static DEVICE_ATTR(driver_info, 0440, + driver_info_show, NULL); +static DEVICE_ATTR(chip_info, 0440, + chip_info_show, NULL); +static DEVICE_ATTR(reset, 0220, + NULL, goodix_ts_reset_store); +static DEVICE_ATTR(send_cfg, 0220, + NULL, goodix_ts_send_cfg_store); +static DEVICE_ATTR(read_cfg, 0440, + read_cfg_show, NULL); +static DEVICE_ATTR(reg_rw, 0664, + goodix_ts_reg_rw_show, goodix_ts_reg_rw_store); +static DEVICE_ATTR(irq_info, 0664, + goodix_ts_irq_info_show, goodix_ts_irq_info_store); +static DEVICE_ATTR(esd_info, 0664, + goodix_ts_esd_info_show, goodix_ts_esd_info_store); +static DEVICE_ATTR(debug_log, 0664, + goodix_ts_debug_log_show, goodix_ts_debug_log_store); static struct attribute *sysfs_attrs[] = { &dev_attr_driver_info.attr, @@ -794,16 +805,12 @@ static int rawdata_proc_show(struct seq_file *m, void *v) int i; int index; - if (!m || !v || !cd) { - ts_err("rawdata_proc_show, input null ptr"); + if (!m || !v || !cd) return -EIO; - } info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) { - ts_err("Failed to alloc rawdata info memory"); + if (!info) return -ENOMEM; - } ret = cd->hw_ops->get_capacitance_data(cd, info); if (ret < 0) { @@ -814,19 +821,19 @@ static int rawdata_proc_show(struct seq_file *m, void *v) rx = info->buff[0]; tx = info->buff[1]; seq_printf(m, "TX:%d RX:%d\n", tx, rx); - seq_printf(m, "mutual_rawdata:\n"); + seq_puts(m, "mutual_rawdata:\n"); index = 2; for (i = 0; i < tx * rx; i++) { seq_printf(m, "%5d,", info->buff[index + i]); if ((i + 1) % tx == 0) - seq_printf(m, "\n"); + seq_puts(m, "\n"); } - seq_printf(m, "mutual_diffdata:\n"); + seq_puts(m, "mutual_diffdata:\n"); index += tx * rx; for (i = 0; i < tx * rx; i++) { seq_printf(m, "%3d,", info->buff[index + i]); if ((i + 1) % tx == 0) - seq_printf(m, "\n"); + seq_puts(m, "\n"); } exit: @@ -836,22 +843,36 @@ exit: static int rawdata_proc_open(struct inode *inode, struct file *file) { - return single_open_size(file, rawdata_proc_show, PDE_DATA(inode), PAGE_SIZE * 10); + return single_open_size(file, rawdata_proc_show, + PDE_DATA(inode), PAGE_SIZE * 10); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) +static const struct proc_ops rawdata_proc_fops = { + .proc_open = rawdata_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +#else static const struct file_operations rawdata_proc_fops = { .open = rawdata_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; +#endif static void goodix_ts_procfs_init(struct goodix_ts_core *core_data) { + struct proc_dir_entry *proc_entry; + if (!proc_mkdir("goodix_ts", NULL)) return; - proc_create_data("goodix_ts/tp_capacitance_data", - 0666, NULL, &rawdata_proc_fops, core_data); + proc_entry = proc_create_data("goodix_ts/tp_capacitance_data", + 0664, NULL, &rawdata_proc_fops, core_data); + if (!proc_entry) + ts_err("failed to create proc entry"); } static void goodix_ts_procfs_exit(struct goodix_ts_core *core_data) @@ -895,7 +916,7 @@ int goodix_ts_blocking_notify(enum ts_notify_event evt, void *v) return ret; } -#ifdef CONFIG_OF +#if IS_ENABLED(CONFIG_OF) /** * goodix_parse_dt_resolution - parse resolution from dt * @node: devicetree node @@ -1026,20 +1047,28 @@ static int goodix_parse_dt(struct device_node *node, r = of_property_read_string(node, "goodix,firmware-name", &name_tmp); if (!r) { ts_info("firmware name from dt: %s", name_tmp); - strncpy(board_data->fw_name, name_tmp, sizeof(board_data->fw_name)); + strncpy(board_data->fw_name, + name_tmp, sizeof(board_data->fw_name)); } else { - ts_info("can't find firmware name, use default: %s", TS_DEFAULT_FIRMWARE); - strncpy(board_data->fw_name, TS_DEFAULT_FIRMWARE, sizeof(board_data->fw_name)); + ts_info("can't find firmware name, use default: %s", + TS_DEFAULT_FIRMWARE); + strncpy(board_data->fw_name, + TS_DEFAULT_FIRMWARE, + sizeof(board_data->fw_name)); } /* get config file name */ r = of_property_read_string(node, "goodix,config-name", &name_tmp); if (!r) { ts_info("config name from dt: %s", name_tmp); - strncpy(board_data->cfg_bin_name, name_tmp, sizeof(board_data->cfg_bin_name)); + strncpy(board_data->cfg_bin_name, name_tmp, + sizeof(board_data->cfg_bin_name)); } else { - ts_info("can't find config name, use default: %s", TS_DEFAULT_CFG_BIN); - strncpy(board_data->cfg_bin_name, TS_DEFAULT_CFG_BIN, sizeof(board_data->cfg_bin_name)); + ts_info("can't find config name, use default: %s", + TS_DEFAULT_CFG_BIN); + strncpy(board_data->cfg_bin_name, + TS_DEFAULT_CFG_BIN, + sizeof(board_data->cfg_bin_name)); } /* get xyz resolutions */ @@ -1081,7 +1110,8 @@ static void goodix_ts_report_pen(struct input_dev *dev, ts_debug("pen_data:x %d, y %d, p %d, tilt_x %d tilt_y %d key[%d %d]", pen_data->coords.x, pen_data->coords.y, pen_data->coords.p, pen_data->coords.tilt_x, - pen_data->coords.tilt_y, pen_data->keys[0].status == TS_TOUCH ? 1 : 0, + pen_data->coords.tilt_y, + pen_data->keys[0].status == TS_TOUCH ? 1 : 0, pen_data->keys[1].status == TS_TOUCH ? 1 : 0); } else { input_report_key(dev, BTN_TOUCH, 0); @@ -1109,8 +1139,9 @@ static void goodix_ts_report_finger(struct input_dev *dev, for (i = 0; i < GOODIX_MAX_TOUCH; i++) { if (touch_data->coords[i].status == TS_TOUCH) { - ts_debug("report: id %d, x %d, y %d, w %d", i, - touch_data->coords[i].x, touch_data->coords[i].y, + ts_debug("report: id[%d], x %d, y %d, w %d", i, + touch_data->coords[i].x, + touch_data->coords[i].y, touch_data->coords[i].w); input_mt_slot(dev, i); input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); @@ -1200,15 +1231,10 @@ static irqreturn_t goodix_ts_threadirq_func(int irq, void *data) goodix_ts_report_pen(core_data->pen_dev, &ts_event->pen_data); } - if (ts_event->event_type == EVENT_REQUEST) { + if (ts_event->event_type == EVENT_REQUEST) goodix_ts_request_handle(core_data, ts_event); - } } - if (!core_data->tools_ctrl_sync && !ts_event->retry) - hw_ops->after_event_handler(core_data); - ts_event->retry = 0; - return IRQ_HANDLED; } @@ -1343,22 +1369,25 @@ static int goodix_ts_gpio_setup(struct goodix_ts_core *core_data) * after kenerl3.13, gpio_ api is deprecated, new * driver should use gpiod_ api. */ - r = devm_gpio_request_one(&core_data->pdev->dev, ts_bdata->reset_gpio, - GPIOF_OUT_INIT_LOW, "ts_reset_gpio"); + r = devm_gpio_request_one(&core_data->pdev->dev, + ts_bdata->reset_gpio, + GPIOF_OUT_INIT_LOW, "ts_reset_gpio"); if (r < 0) { ts_err("Failed to request reset gpio, r:%d", r); return r; } - r = devm_gpio_request_one(&core_data->pdev->dev, ts_bdata->irq_gpio, - GPIOF_IN, "ts_irq_gpio"); + r = devm_gpio_request_one(&core_data->pdev->dev, + ts_bdata->irq_gpio, + GPIOF_IN, "ts_irq_gpio"); if (r < 0) { ts_err("Failed to request irq gpio, r:%d", r); return r; } if (ts_bdata->avdd_gpio > 0) { - r = devm_gpio_request_one(&core_data->pdev->dev, ts_bdata->avdd_gpio, + r = devm_gpio_request_one(&core_data->pdev->dev, + ts_bdata->avdd_gpio, GPIOF_OUT_INIT_LOW, "ts_avdd_gpio"); if (r < 0) { ts_err("Failed to request avdd-gpio, r:%d", r); @@ -1367,7 +1396,8 @@ static int goodix_ts_gpio_setup(struct goodix_ts_core *core_data) } if (ts_bdata->iovdd_gpio > 0) { - r = devm_gpio_request_one(&core_data->pdev->dev, ts_bdata->iovdd_gpio, + r = devm_gpio_request_one(&core_data->pdev->dev, + ts_bdata->iovdd_gpio, GPIOF_OUT_INIT_LOW, "ts_iovdd_gpio"); if (r < 0) { ts_err("Failed to request iovdd-gpio, r:%d", r); @@ -1405,15 +1435,12 @@ static int goodix_ts_input_dev_config(struct goodix_ts_core *core_data) input_dev->id.vendor = 0xBEEF; input_dev->id.version = 10427; - __set_bit(EV_SYN, input_dev->evbit); - __set_bit(EV_KEY, input_dev->evbit); - __set_bit(EV_ABS, input_dev->evbit); - __set_bit(BTN_TOUCH, input_dev->keybit); - __set_bit(BTN_TOOL_FINGER, input_dev->keybit); - -#ifdef INPUT_PROP_DIRECT - __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); -#endif + set_bit(EV_SYN, input_dev->evbit); + set_bit(EV_KEY, input_dev->evbit); + set_bit(EV_ABS, input_dev->evbit); + set_bit(BTN_TOUCH, input_dev->keybit); + set_bit(BTN_TOOL_FINGER, input_dev->keybit); + set_bit(INPUT_PROP_DIRECT, input_dev->propbit); /* set input parameters */ input_set_abs_params(input_dev, ABS_MT_POSITION_X, @@ -1432,6 +1459,8 @@ static int goodix_ts_input_dev_config(struct goodix_ts_core *core_data) #endif input_set_capability(input_dev, EV_KEY, KEY_POWER); + input_set_capability(input_dev, EV_KEY, KEY_WAKEUP); + input_set_capability(input_dev, EV_KEY, KEY_GOTO); r = input_register_device(input_dev); if (r < 0) { @@ -1464,15 +1493,15 @@ static int goodix_ts_pen_dev_config(struct goodix_ts_core *core_data) pen_dev->id.version = 10427; pen_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); - __set_bit(ABS_X, pen_dev->absbit); - __set_bit(ABS_Y, pen_dev->absbit); - __set_bit(ABS_TILT_X, pen_dev->absbit); - __set_bit(ABS_TILT_Y, pen_dev->absbit); - __set_bit(BTN_STYLUS, pen_dev->keybit); - __set_bit(BTN_STYLUS2, pen_dev->keybit); - __set_bit(BTN_TOUCH, pen_dev->keybit); - __set_bit(BTN_TOOL_PEN, pen_dev->keybit); - __set_bit(INPUT_PROP_DIRECT, pen_dev->propbit); + set_bit(ABS_X, pen_dev->absbit); + set_bit(ABS_Y, pen_dev->absbit); + set_bit(ABS_TILT_X, pen_dev->absbit); + set_bit(ABS_TILT_Y, pen_dev->absbit); + set_bit(BTN_STYLUS, pen_dev->keybit); + set_bit(BTN_STYLUS2, pen_dev->keybit); + set_bit(BTN_TOUCH, pen_dev->keybit); + set_bit(BTN_TOOL_PEN, pen_dev->keybit); + set_bit(INPUT_PROP_DIRECT, pen_dev->propbit); input_set_abs_params(pen_dev, ABS_X, 0, ts_bdata->panel_max_x, 0, 0); input_set_abs_params(pen_dev, ABS_Y, 0, ts_bdata->panel_max_y, 0, 0); input_set_abs_params(pen_dev, ABS_PRESSURE, 0, @@ -1480,7 +1509,7 @@ static int goodix_ts_pen_dev_config(struct goodix_ts_core *core_data) input_set_abs_params(pen_dev, ABS_TILT_X, -GOODIX_PEN_MAX_TILT, GOODIX_PEN_MAX_TILT, 0, 0); input_set_abs_params(pen_dev, ABS_TILT_Y, - -GOODIX_PEN_MAX_TILT, GOODIX_PEN_MAX_TILT, 0, 0); + -GOODIX_PEN_MAX_TILT, GOODIX_PEN_MAX_TILT, 0, 0); r = input_register_device(pen_dev); if (r < 0) { @@ -1562,9 +1591,9 @@ static void goodix_ts_esd_on(struct goodix_ts_core *cd) return; atomic_set(&ts_esd->esd_on, 1); - if (!schedule_delayed_work(&ts_esd->esd_work, 2 * HZ)) { + if (!schedule_delayed_work(&ts_esd->esd_work, 2 * HZ)) ts_info("esd work already in workqueue"); - } + ts_info("esd on"); } @@ -1752,6 +1781,7 @@ static int goodix_ts_resume(struct goodix_ts_core *core_data) ts_info("Resume start"); atomic_set(&core_data->suspended, 0); + hw_ops->irq_enable(core_data, false); mutex_lock(&goodix_modules.mutex); if (!list_empty(&goodix_modules.head)) { @@ -1804,7 +1834,25 @@ out: return 0; } -#ifdef CONFIG_FB +/* goodix FB test */ +/* +void goodix_fb_ext_ctrl(int suspend) +{ + struct goodix_ts_core *cd = goodix_modules.core_data; + + if (!cd) + return; + + if (suspend) + goodix_ts_suspend(cd); + else + goodix_ts_resume(cd); +} +EXPORT_SYMBOL(goodix_fb_ext_ctrl); +*/ + + +#if IS_ENABLED(CONFIG_FB) /** * goodix_ts_fb_notifier_callback - Framebuffer notifier callback * Called by kernel during framebuffer blanck/unblank phrase @@ -1821,6 +1869,7 @@ int goodix_ts_fb_notifier_callback(struct notifier_block *self, /* before fb blank */ } else if (event == FB_EVENT_BLANK) { int *blank = fb_event->data; + if (*blank == FB_BLANK_UNBLANK) goodix_ts_resume(core_data); else if (*blank == FB_BLANK_POWERDOWN) @@ -1832,9 +1881,8 @@ int goodix_ts_fb_notifier_callback(struct notifier_block *self, } #endif - -#ifdef CONFIG_PM -#if !defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND) +#if IS_ENABLED(CONFIG_PM) +#if !IS_ENABLED(CONFIG_FB) && !IS_ENABLED(CONFIG_HAS_EARLYSUSPEND) /** * goodix_ts_pm_suspend - PM suspend function * Called by kernel during system suspend phrase @@ -1917,7 +1965,7 @@ int goodix_ts_stage2_init(struct goodix_ts_core *cd) } ts_info("success register irq"); -#ifdef CONFIG_FB +#if IS_ENABLED(CONFIG_FB) cd->fb_notifier.notifier_call = goodix_ts_fb_notifier_callback; if (fb_register_client(&cd->fb_notifier)) ts_err("Failed to register fb notifier client:%d", ret); @@ -2010,7 +2058,8 @@ upgrade: } ts_info("update flag: 0x%X", update_flag); - ret = goodix_do_fw_update(cd->ic_configs[CONFIG_TYPE_NORMAL], update_flag); + ret = goodix_do_fw_update(cd->ic_configs[CONFIG_TYPE_NORMAL], + update_flag); if (ret) ts_err("failed do fw update"); @@ -2051,8 +2100,7 @@ err_out: ts_err("stage2 init failed"); cd->init_stage = CORE_INIT_FAIL; for (i = 0; i < GOODIX_MAX_CONFIG_GROUP; i++) { - if (cd->ic_configs[i]) - kfree(cd->ic_configs[i]); + kfree(cd->ic_configs[i]); cd->ic_configs[i] = NULL; } return ret; @@ -2082,7 +2130,7 @@ static int goodix_ts_probe(struct platform_device *pdev) struct goodix_bus_interface *bus_interface; int ret; - ts_info("goodix_ts_probe IN"); + ts_info("IN"); bus_interface = pdev->dev.platform_data; if (!bus_interface) { @@ -2094,7 +2142,6 @@ static int goodix_ts_probe(struct platform_device *pdev) core_data = devm_kzalloc(&pdev->dev, sizeof(struct goodix_ts_core), GFP_KERNEL); if (!core_data) { - ts_err("Failed to allocate memory for core data"); core_module_prob_sate = CORE_MODULE_PROB_FAILED; return -ENOMEM; } @@ -2180,7 +2227,7 @@ static int goodix_ts_remove(struct platform_device *pdev) gesture_module_exit(); inspect_module_exit(); hw_ops->irq_enable(core_data, false); - #ifdef CONFIG_FB + #if IS_ENABLED(CONFIG_FB) fb_unregister_client(&core_data->fb_notifier); #endif core_module_prob_sate = CORE_MODULE_REMOVED; @@ -2199,9 +2246,9 @@ static int goodix_ts_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#if IS_ENABLED(CONFIG_PM) static const struct dev_pm_ops dev_pm_ops = { -#if !defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND) +#if !IS_ENABLED(CONFIG_FB) && !IS_ENABLED(CONFIG_HAS_EARLYSUSPEND) .suspend = goodix_ts_pm_suspend, .resume = goodix_ts_pm_resume, #endif @@ -2218,7 +2265,7 @@ static struct platform_driver goodix_ts_driver = { .driver = { .name = GOODIX_CORE_DRIVER_NAME, .owner = THIS_MODULE, -#ifdef CONFIG_PM +#if IS_ENABLED(CONFIG_PM) .pm = &dev_pm_ops, #endif }, diff --git a/goodix_ts_core.h b/driver/goodix_ts_core.h similarity index 85% rename from goodix_ts_core.h rename to driver/goodix_ts_core.h index 8cb0343605..0cfb5edfe7 100644 --- a/goodix_ts_core.h +++ b/driver/goodix_ts_core.h @@ -1,3 +1,19 @@ +/* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ #ifndef _GOODIX_TS_CORE_H_ #define _GOODIX_TS_CORE_H_ #include @@ -15,25 +31,27 @@ #include #include #include -#ifdef CONFIG_OF +#if IS_ENABLED(CONFIG_OF) #include #include #endif -#ifdef CONFIG_FB +#if IS_ENABLED(CONFIG_FB) #include #include #endif #define GOODIX_CORE_DRIVER_NAME "goodix_ts" #define GOODIX_PEN_DRIVER_NAME "goodix_ts,pen" -#define GOODIX_DRIVER_VERSION "v1.2.3" +#define GOODIX_DRIVER_VERSION "v1.2.4" #define GOODIX_MAX_TOUCH 10 #define GOODIX_PEN_MAX_PRESSURE 4096 -#define GOODIX_MAX_PEN_KEY 2 +#define GOODIX_MAX_PEN_KEY 2 #define GOODIX_PEN_MAX_TILT 90 #define GOODIX_CFG_MAX_SIZE 4096 +#define GOODIX_FW_MAX_SIEZE (300 * 1024) #define GOODIX_MAX_STR_LABLE_LEN 32 #define GOODIX_MAX_FRAMEDATA_LEN 1700 +#define GOODIX_GESTURE_DATA_LEN 16 #define GOODIX_NORMAL_RESET_DELAY_MS 100 #define GOODIX_HOLD_CPU_RESET_DELAY_MS 5 @@ -43,7 +61,13 @@ #define GOODIX_RETRY_10 10 #define TS_DEFAULT_FIRMWARE "goodix_firmware.bin" -#define TS_DEFAULT_CFG_BIN "goodix_cfg_group.bin" +#define TS_DEFAULT_CFG_BIN "goodix_cfg_group.bin" + +enum GOODIX_GESTURE_TYP { + GESTURE_SINGLE_TAP = (1 << 0), + GESTURE_DOUBLE_TAP = (1 << 1), + GESTURE_FOD_PRESS = (1 << 2) +}; enum CORD_PROB_STA { CORE_MODULE_UNPROBED = 0, @@ -90,8 +114,8 @@ enum CHECKSUM_MODE { CHECKSUM_MODE_U16_LE, }; -#define MAX_SCAN_FREQ_NUM 5 -#define MAX_SCAN_RATE_NUM 5 +#define MAX_SCAN_FREQ_NUM 8 +#define MAX_SCAN_RATE_NUM 8 #define MAX_FREQ_NUM_STYLUS 8 #define MAX_STYLUS_SCAN_FREQ_NUM 6 #pragma pack(1) @@ -366,10 +390,10 @@ struct goodix_pen_data { * @event_data: event data */ struct goodix_ts_event { - int retry; enum ts_event_type event_type; u8 request_code; /* represent the request type */ u8 gesture_type; + u8 gesture_data[GOODIX_GESTURE_DATA_LEN]; struct goodix_touch_data touch_data; struct goodix_pen_data pen_data; }; @@ -387,7 +411,7 @@ struct goodix_bus_interface { int (*read)(struct device *dev, unsigned int addr, unsigned char *data, unsigned int len); int (*write)(struct device *dev, unsigned int addr, - unsigned char *data, unsigned int len); + unsigned char *data, unsigned int len); }; struct goodix_ts_hw_ops { @@ -401,15 +425,22 @@ struct goodix_ts_hw_ops { unsigned char *data, unsigned int len); int (*write)(struct goodix_ts_core *cd, unsigned int addr, unsigned char *data, unsigned int len); - int (*send_cmd)(struct goodix_ts_core *cd, struct goodix_ts_cmd *cmd); - int (*send_config)(struct goodix_ts_core *cd, u8 *config, int len); - int (*read_config)(struct goodix_ts_core *cd, u8 *config_data, int size); - int (*read_version)(struct goodix_ts_core *cd, struct goodix_fw_version *version); - int (*get_ic_info)(struct goodix_ts_core *cd, struct goodix_ic_info *ic_info); + int (*send_cmd)(struct goodix_ts_core *cd, + struct goodix_ts_cmd *cmd); + int (*send_config)(struct goodix_ts_core *cd, + u8 *config, int len); + int (*read_config)(struct goodix_ts_core *cd, + u8 *config_data, int size); + int (*read_version)(struct goodix_ts_core *cd, + struct goodix_fw_version *version); + int (*get_ic_info)(struct goodix_ts_core *cd, + struct goodix_ic_info *ic_info); int (*esd_check)(struct goodix_ts_core *cd); - int (*event_handler)(struct goodix_ts_core *cd, struct goodix_ts_event *ts_event); - int (*after_event_handler)(struct goodix_ts_core *cd); /* clean sync flag */ - int (*get_capacitance_data)(struct goodix_ts_core *cd, struct ts_rawdata_info *info); + int (*event_handler)(struct goodix_ts_core *cd, + struct goodix_ts_event *ts_event); + int (*after_event_handler)(struct goodix_ts_core *cd); + int (*get_capacitance_data)(struct goodix_ts_core *cd, + struct ts_rawdata_info *info); }; /* @@ -448,13 +479,14 @@ struct goodix_ts_core { struct goodix_ts_hw_ops *hw_ops; struct input_dev *input_dev; struct input_dev *pen_dev; - /* TODO counld we remove this from core data? */ + /* TODO counld we remove this from core data? */ struct goodix_ts_event ts_event; /* every pointer of this array represent a kind of config */ struct goodix_ic_config *ic_configs[GOODIX_MAX_CONFIG_GROUP]; struct regulator *avdd; struct regulator *iovdd; + unsigned char gesture_type; int power_on; int irq; @@ -468,7 +500,7 @@ struct goodix_ts_core { struct notifier_block ts_notifier; struct goodix_ts_esd ts_esd; -#ifdef CONFIG_FB +#if IS_ENABLED(CONFIG_FB) struct notifier_block fb_notifier; #endif }; @@ -542,8 +574,9 @@ struct goodix_ext_module { */ struct goodix_ext_attribute { struct attribute attr; - ssize_t (*show)(struct goodix_ext_module *, char *); - ssize_t (*store)(struct goodix_ext_module *, const char *, size_t); + ssize_t (*show)(struct goodix_ext_module *module, char *buf); + ssize_t (*store)(struct goodix_ext_module *module, + const char *buf, size_t len); }; /* external attrs helper macro */ @@ -556,13 +589,17 @@ struct goodix_ext_attribute { /* external attrs helper macro, used to define external attrs */ #define DEFINE_EXTMOD_ATTR(_name, _mode, _show, _store) \ static struct goodix_ext_attribute ext_attr_##_name = \ - __EXTMOD_ATTR(_name, _mode, _show, _store); + __EXTMOD_ATTR(_name, _mode, _show, _store) /* log macro */ extern bool debug_log_flag; -#define ts_info(fmt, arg...) pr_info("[GTP-INF][%s:%d] "fmt"\n", __func__, __LINE__, ##arg) -#define ts_err(fmt, arg...) pr_err("[GTP-ERR][%s:%d] "fmt"\n", __func__, __LINE__, ##arg) -#define ts_debug(fmt, arg...) {if (debug_log_flag) pr_info("[GTP-DBG][%s:%d] "fmt"\n", __func__, __LINE__, ##arg);} +#define ts_info(fmt, arg...) \ + pr_info("[GTP-INF][%s:%d] "fmt"\n", __func__, __LINE__, ##arg) +#define ts_err(fmt, arg...) \ + pr_err("[GTP-ERR][%s:%d] "fmt"\n", __func__, __LINE__, ##arg) +#define ts_debug(fmt, arg...) \ + {if (debug_log_flag) \ + pr_info("[GTP-DBG][%s:%d] "fmt"\n", __func__, __LINE__, ##arg);} /* * get board data pointer @@ -613,7 +650,6 @@ int checksum_cmp(const u8 *data, int size, int mode); int is_risk_data(const u8 *data, int size); u32 goodix_get_file_config_id(u8 *ic_config); void goodix_rotate_abcd2cbad(int tx, int rx, s16 *data); -int goodix_gesture_enable(int enable); int goodix_fw_update_init(struct goodix_ts_core *core_data); void goodix_fw_update_uninit(void); @@ -627,4 +663,9 @@ void inspect_module_exit(void); int goodix_tools_init(void); void goodix_tools_exit(void); +/* goodix FB test */ +/* +void goodix_fb_ext_ctrl(int suspend); +*/ + #endif diff --git a/driver/goodix_ts_gesture.c b/driver/goodix_ts_gesture.c new file mode 100644 index 0000000000..07fd530c1b --- /dev/null +++ b/driver/goodix_ts_gesture.c @@ -0,0 +1,446 @@ +/* + * Goodix Gesture Module + * + * Copyright (C) 2019 - 2020 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "goodix_ts_core.h" + + +#define GOODIX_GESTURE_DOUBLE_TAP 0xCC +#define GOODIX_GESTURE_SINGLE_TAP 0x4C +#define GOODIX_GESTURE_FOD_DOWN 0x46 +#define GOODIX_GESTURE_FOD_UP 0x55 + +/* + * struct gesture_module - gesture module data + * @registered: module register state + * @sysfs_node_created: sysfs node state + * @gesture_type: valid gesture type, each bit represent one gesture type + * @gesture_data: store latest gesture code get from irq event + * @gesture_ts_cmd: gesture command data + */ +struct gesture_module { + atomic_t registered; + struct goodix_ts_core *ts_core; + struct goodix_ext_module module; +}; + +static struct gesture_module *gsx_gesture; /*allocated in gesture init module*/ +static bool module_initialized; + +static ssize_t gsx_double_type_show(struct goodix_ext_module *module, + char *buf) +{ + struct gesture_module *gsx = module->priv_data; + unsigned char type = gsx->ts_core->gesture_type; + + if (!gsx) + return -EIO; + + if (atomic_read(&gsx->registered) == 0) { + ts_err("gesture module is not registered"); + return 0; + } + + return sprintf(buf, "%s\n", + (type & GESTURE_DOUBLE_TAP) ? "enable" : "disable"); +} + +static ssize_t gsx_double_type_store(struct goodix_ext_module *module, + const char *buf, size_t count) +{ + struct gesture_module *gsx = module->priv_data; + + if (!gsx) + return -EIO; + + if (atomic_read(&gsx->registered) == 0) { + ts_err("gesture module is not registered"); + return 0; + } + + if (buf[0] == '1') { + ts_info("enable double tap"); + gsx->ts_core->gesture_type |= GESTURE_DOUBLE_TAP; + } else if (buf[0] == '0') { + ts_info("disable double tap"); + gsx->ts_core->gesture_type &= ~GESTURE_DOUBLE_TAP; + } else + ts_err("invalid cmd[%d]", buf[0]); + + return count; +} + +static ssize_t gsx_single_type_show(struct goodix_ext_module *module, + char *buf) +{ + struct gesture_module *gsx = module->priv_data; + unsigned char type = gsx->ts_core->gesture_type; + + if (!gsx) + return -EIO; + + if (atomic_read(&gsx->registered) == 0) { + ts_err("gesture module is not registered"); + return 0; + } + + return sprintf(buf, "%s\n", + (type & GESTURE_SINGLE_TAP) ? "enable" : "disable"); +} + +static ssize_t gsx_single_type_store(struct goodix_ext_module *module, + const char *buf, size_t count) +{ + struct gesture_module *gsx = module->priv_data; + + if (!gsx) + return -EIO; + + if (atomic_read(&gsx->registered) == 0) { + ts_err("gesture module is not registered"); + return 0; + } + + if (buf[0] == '1') { + ts_info("enable single tap"); + gsx->ts_core->gesture_type |= GESTURE_SINGLE_TAP; + } else if (buf[0] == '0') { + ts_info("disable single tap"); + gsx->ts_core->gesture_type &= ~GESTURE_SINGLE_TAP; + } else + ts_err("invalid cmd[%d]", buf[0]); + + return count; +} + +static ssize_t gsx_fod_type_show(struct goodix_ext_module *module, + char *buf) +{ + struct gesture_module *gsx = module->priv_data; + unsigned char type = gsx->ts_core->gesture_type; + + if (!gsx) + return -EIO; + + if (atomic_read(&gsx->registered) == 0) { + ts_err("gesture module is not registered"); + return 0; + } + + return sprintf(buf, "%s\n", + (type & GESTURE_FOD_PRESS) ? "enable" : "disable"); +} + +static ssize_t gsx_fod_type_store(struct goodix_ext_module *module, + const char *buf, size_t count) +{ + struct gesture_module *gsx = module->priv_data; + + if (!gsx) + return -EIO; + + if (atomic_read(&gsx->registered) == 0) { + ts_err("gesture module is not registered"); + return 0; + } + + if (buf[0] == '1') { + ts_info("enable fod"); + gsx->ts_core->gesture_type |= GESTURE_FOD_PRESS; + } else if (buf[0] == '0') { + ts_info("disable fod"); + gsx->ts_core->gesture_type &= ~GESTURE_FOD_PRESS; + } else + ts_err("invalid cmd[%d]", buf[0]); + + return count; +} + + +const struct goodix_ext_attribute gesture_attrs[] = { + __EXTMOD_ATTR(double_en, 0664, + gsx_double_type_show, gsx_double_type_store), + __EXTMOD_ATTR(single_en, 0664, + gsx_single_type_show, gsx_single_type_store), + __EXTMOD_ATTR(fod_en, 0664, + gsx_fod_type_show, gsx_fod_type_store), +}; + +static int gsx_gesture_init(struct goodix_ts_core *cd, + struct goodix_ext_module *module) +{ + struct gesture_module *gsx = module->priv_data; + + if (!cd || !cd->hw_ops->gesture) { + ts_err("gesture unsupported"); + return -EINVAL; + } + + gsx->ts_core = cd; + gsx->ts_core->gesture_type = 0; + atomic_set(&gsx->registered, 1); + + return 0; +} + +static int gsx_gesture_exit(struct goodix_ts_core *cd, + struct goodix_ext_module *module) +{ + struct gesture_module *gsx = module->priv_data; + + if (!cd || !cd->hw_ops->gesture) { + ts_err("gesture unsupported"); + return -EINVAL; + } + + atomic_set(&gsx->registered, 0); + + return 0; +} + +/** + * gsx_gesture_ist - Gesture Irq handle + * This functions is excuted when interrupt happended and + * ic in doze mode. + * + * @cd: pointer to touch core data + * @module: pointer to goodix_ext_module struct + * return: 0 goon execute, EVT_CANCEL_IRQEVT stop execute + */ +static int gsx_gesture_ist(struct goodix_ts_core *cd, + struct goodix_ext_module *module) +{ + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + struct goodix_ts_event gs_event = {0}; + int fodx, fody, overlay_area; + int ret; + + if (atomic_read(&cd->suspended) == 0 || cd->gesture_type == 0) + return EVT_CONTINUE; + + ret = hw_ops->event_handler(cd, &gs_event); + if (ret) { + ts_err("failed get gesture data"); + goto re_send_ges_cmd; + } + + if (!(gs_event.event_type & EVENT_GESTURE)) { + ts_err("invalid event type: 0x%x", + cd->ts_event.event_type); + goto re_send_ges_cmd; + } + + switch (gs_event.gesture_type) { + case GOODIX_GESTURE_SINGLE_TAP: + if (cd->gesture_type & GESTURE_SINGLE_TAP) { + ts_info("get SINGLE-TAP gesture"); + input_report_key(cd->input_dev, KEY_WAKEUP, 1); + // input_report_key(cd->input_dev, KEY_GOTO, 1); + input_sync(cd->input_dev); + input_report_key(cd->input_dev, KEY_WAKEUP, 0); + // input_report_key(cd->input_dev, KEY_GOTO, 0); + input_sync(cd->input_dev); + } else { + ts_debug("not enable SINGLE-TAP"); + } + break; + case GOODIX_GESTURE_DOUBLE_TAP: + if (cd->gesture_type & GESTURE_DOUBLE_TAP) { + ts_info("get DOUBLE-TAP gesture"); + input_report_key(cd->input_dev, KEY_WAKEUP, 1); + input_sync(cd->input_dev); + input_report_key(cd->input_dev, KEY_WAKEUP, 0); + input_sync(cd->input_dev); + } else { + ts_debug("not enable DOUBLE-TAP"); + } + break; + case GOODIX_GESTURE_FOD_DOWN: + if (cd->gesture_type & GESTURE_FOD_PRESS) { + ts_info("get FOD-DOWN gesture"); + fodx = le16_to_cpup((__le16 *)gs_event.gesture_data); + fody = le16_to_cpup((__le16 *)(gs_event.gesture_data + 2)); + overlay_area = gs_event.gesture_data[4]; + ts_debug("fodx:%d fody:%d overlay_area:%d", fodx, fody, overlay_area); + input_report_key(cd->input_dev, BTN_TOUCH, 1); + input_mt_slot(cd->input_dev, 0); + input_mt_report_slot_state(cd->input_dev, MT_TOOL_FINGER, 1); + input_report_abs(cd->input_dev, ABS_MT_POSITION_X, fodx); + input_report_abs(cd->input_dev, ABS_MT_POSITION_Y, fody); + input_report_abs(cd->input_dev, ABS_MT_WIDTH_MAJOR, overlay_area); + input_sync(cd->input_dev); + } else { + ts_debug("not enable FOD-DOWN"); + } + break; + case GOODIX_GESTURE_FOD_UP: + if (cd->gesture_type & GESTURE_FOD_PRESS) { + ts_info("get FOD-UP gesture"); + fodx = le16_to_cpup((__le16 *)gs_event.gesture_data); + fody = le16_to_cpup((__le16 *)(gs_event.gesture_data + 2)); + overlay_area = gs_event.gesture_data[4]; + input_report_key(cd->input_dev, BTN_TOUCH, 0); + input_mt_slot(cd->input_dev, 0); + input_mt_report_slot_state(cd->input_dev, + MT_TOOL_FINGER, 0); + input_sync(cd->input_dev); + } else { + ts_debug("not enable FOD-UP"); + } + break; + default: + ts_err("not support gesture type[%02X]", gs_event.gesture_type); + break; + } + +re_send_ges_cmd: + if (hw_ops->gesture(cd, 0)) + ts_info("warning: failed re_send gesture cmd"); + return EVT_CANCEL_IRQEVT; +} + +/** + * gsx_gesture_before_suspend - execute gesture suspend routine + * This functions is excuted to set ic into doze mode + * + * @cd: pointer to touch core data + * @module: pointer to goodix_ext_module struct + * return: 0 goon execute, EVT_IRQCANCLED stop execute + */ +static int gsx_gesture_before_suspend(struct goodix_ts_core *cd, + struct goodix_ext_module *module) +{ + int ret; + const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + + if (cd->gesture_type == 0) + return EVT_CONTINUE; + + ret = hw_ops->gesture(cd, 0); + if (ret) + ts_err("failed enter gesture mode"); + else + ts_info("enter gesture mode, type[0x%02X]", cd->gesture_type); + + hw_ops->irq_enable(cd, true); + enable_irq_wake(cd->irq); + + return EVT_CANCEL_SUSPEND; +} + +static int gsx_gesture_before_resume(struct goodix_ts_core *cd, + struct goodix_ext_module *module) +{ + const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + + if (cd->gesture_type == 0) + return EVT_CONTINUE; + + disable_irq_wake(cd->irq); + hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); + + return EVT_CANCEL_RESUME; +} + +static struct goodix_ext_module_funcs gsx_gesture_funcs = { + .irq_event = gsx_gesture_ist, + .init = gsx_gesture_init, + .exit = gsx_gesture_exit, + .before_suspend = gsx_gesture_before_suspend, + .before_resume = gsx_gesture_before_resume, +}; + +int gesture_module_init(void) +{ + int ret; + int i; + struct kobject *def_kobj = goodix_get_default_kobj(); + struct kobj_type *def_kobj_type = goodix_get_default_ktype(); + + gsx_gesture = kzalloc(sizeof(struct gesture_module), GFP_KERNEL); + if (!gsx_gesture) + return -ENOMEM; + + gsx_gesture->module.funcs = &gsx_gesture_funcs; + gsx_gesture->module.priority = EXTMOD_PRIO_GESTURE; + gsx_gesture->module.name = "Goodix_gsx_gesture"; + gsx_gesture->module.priv_data = gsx_gesture; + + atomic_set(&gsx_gesture->registered, 0); + + /* gesture sysfs init */ + ret = kobject_init_and_add(&gsx_gesture->module.kobj, + def_kobj_type, def_kobj, "gesture"); + if (ret) { + ts_err("failed create gesture sysfs node!"); + goto err_out; + } + + for (i = 0; i < ARRAY_SIZE(gesture_attrs) && !ret; i++) + ret = sysfs_create_file(&gsx_gesture->module.kobj, + &gesture_attrs[i].attr); + if (ret) { + ts_err("failed create gst sysfs files"); + while (--i >= 0) + sysfs_remove_file(&gsx_gesture->module.kobj, + &gesture_attrs[i].attr); + + kobject_put(&gsx_gesture->module.kobj); + goto err_out; + } + + module_initialized = true; + goodix_register_ext_module_no_wait(&gsx_gesture->module); + ts_info("gesture module init success"); + + return 0; + +err_out: + ts_err("gesture module init failed!"); + kfree(gsx_gesture); + return ret; +} + +void gesture_module_exit(void) +{ + int i; + + ts_info("gesture module exit"); + if (!module_initialized) + return; + + goodix_unregister_ext_module(&gsx_gesture->module); + + /* deinit sysfs */ + for (i = 0; i < ARRAY_SIZE(gesture_attrs); i++) + sysfs_remove_file(&gsx_gesture->module.kobj, + &gesture_attrs[i].attr); + + kobject_put(&gsx_gesture->module.kobj); + kfree(gsx_gesture); + module_initialized = false; +} diff --git a/goodix_ts_inspect.c b/driver/goodix_ts_inspect.c similarity index 94% rename from goodix_ts_inspect.c rename to driver/goodix_ts_inspect.c index a96eceb169..ccc69954d6 100644 --- a/goodix_ts_inspect.c +++ b/driver/goodix_ts_inspect.c @@ -26,7 +26,7 @@ /* test config */ #define TOTAL_FRAME_NUM 1 /* rawdata test frames */ #define NOISEDATA_TEST_TIMES 1 /* noise test frames */ -#define SAVE_IN_CSV +// #define SAVE_IN_CSV #define GOODIX_RESULT_SAVE_PATH "/vendor/etc/Test_Data.csv" #define GOODIX_TEST_FILE_NAME "goodix" @@ -329,11 +329,11 @@ static int cal_cha_to_cha_res(struct goodix_ts_test *ts_test, int v1, int v2) static int cal_cha_to_avdd_res(struct goodix_ts_test *ts_test, int v1, int v2) { if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) - return 125 * 1024 * (100 * v2 - 125) * 40 / (10000 * v1) - 40; + return 64 * (2 * v2 - 25) * 40 / v1 - 40; else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B) - return 125 * 1024 * (100 * v2 - 125) * 99 / (10000 * v1) - 60; + return 64 * (2 * v2 - 25) * 99 / v1 - 60; else - return 125 * 1024 * (100 * v2 - 125) * 93 / (10000 * v1) - 20; + return 64 * (2 * v2 - 25) * 93 / v1 - 20; } static int cal_cha_to_gnd_res(struct goodix_ts_test *ts_test, int v) @@ -614,7 +614,7 @@ static int goodix_init_testlimits(struct goodix_ts_test *ts_test) ts_test->test_config.data[i] = (u8)test_params->cfg_buf[i]; ts_test->test_config.len = ret; } - + /* obtain mutual_raw min */ ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_RAW_MIN, test_params->min_limits, rx, tx); @@ -770,7 +770,8 @@ static int goodix_short_test_prepare(struct goodix_ts_test *ts_test) struct goodix_ts_cmd tmp_cmd; struct goodix_fw_version fw_ver; int ret; - int retry = 3; + int retry; + int resend = 3; u8 status; ts_info("short test prepare IN"); @@ -778,12 +779,14 @@ static int goodix_short_test_prepare(struct goodix_ts_test *ts_test) tmp_cmd.len = 4; tmp_cmd.cmd = INSPECT_FW_SWITCH_CMD; +resend_cmd: ret = ts_test_send_cmd(ts_test, &tmp_cmd); if (ret < 0) { ts_err("send test mode failed"); return ret; } + retry = 3; while (retry--) { msleep(40); if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) { @@ -805,6 +808,11 @@ static int goodix_short_test_prepare(struct goodix_ts_test *ts_test) } } + if (resend--) { + ts_test_reset(ts_test, 100); + goto resend_cmd; + } + return -EINVAL; } @@ -1140,21 +1148,24 @@ static int gdix_check_resistance_to_gnd(struct ts_test_params *test_params, u16 r_th = 0, avdd_value = 0; u16 chn_id_tmp = 0; u8 pin_num = 0; + unsigned short short_type; struct goodix_ts_test *ts_test = container_of(test_params, struct goodix_ts_test, test_params); int max_drv_num = test_params->params_info->max_drv_num; int max_sen_num = test_params->params_info->max_sen_num; avdd_value = test_params->avdd_value; - if (adc_signal == 0 || adc_signal == 0x8000) - adc_signal |= 1; + short_type = adc_signal & 0x8000; + adc_signal &= ~0x8000; + if (adc_signal == 0) + adc_signal = 1; - if ((adc_signal & 0x8000) == 0) { + if (short_type == 0) { /* short to GND */ r = cal_cha_to_gnd_res(ts_test, adc_signal); } else { /* short to VDD */ - r = cal_cha_to_avdd_res(ts_test, adc_signal & ~0x8000, avdd_value); + r = cal_cha_to_avdd_res(ts_test, adc_signal, avdd_value); } if (pos < max_drv_num) @@ -1171,11 +1182,11 @@ static int gdix_check_resistance_to_gnd(struct ts_test_params *test_params, if (r < r_th) { pin_num = map_die2pin(test_params, chn_id_tmp); goodix_save_short_res(test_params, pin_num, - (adc_signal & 0x8000)? CHN_VDD : CHN_GND, r); + short_type ? CHN_VDD : CHN_GND, r); ts_err("%s%d shortcircut to %s,R=%ldK,R_Threshold=%dK", (pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", (pin_num & ~DRV_CHANNEL_FLAG), - (adc_signal & 0x8000) ? "VDD" : "GND", + short_type ? "VDD" : "GND", r, r_th); return -EINVAL; @@ -1253,7 +1264,7 @@ static int goodix_shortcircut_analysis(struct goodix_ts_test *ts_test) if (!(test_result.result & 0x0F)) { ts_info(">>>>> No shortcircut"); return 0; - } + } ts_info("short flag 0x%02x, drv&drv:%d, sen&sen:%d, drv&sen:%d, drv/GNDVDD:%d, sen/GNDVDD:%d", test_result.result, test_result.drv_drv_num, test_result.sen_sen_num, test_result.drv_sen_num, test_result.drv_gnd_avdd_num, test_result.sen_gnd_avdd_num); @@ -2051,10 +2062,10 @@ static int goodix_save_test_config(struct goodix_ts_test *ts_test, ts_err("test config write failed"); goto save_end; } - + save_end: kfree(data); - return ret; + return ret; } static int goodix_save_header(struct goodix_ts_test *ts_test, @@ -2066,7 +2077,7 @@ static int goodix_save_header(struct goodix_ts_test *ts_test, bool result = false; char *data = NULL; struct goodix_ts_core *ts = ts_test->ts; - + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); if (!data) { ts_err("alloc memory failed"); @@ -2117,10 +2128,10 @@ static int goodix_save_header(struct goodix_ts_test *ts_test, bytes += sprintf(&data[bytes], "\n"); if (ts_test->test_result[GTP_CAP_TEST]) { if (GTP_TEST_PASS == ts_test->test_result[GTP_CAP_TEST]) - bytes += sprintf(&data[bytes], + bytes += sprintf(&data[bytes], "\n"); else - bytes += sprintf(&data[bytes], + bytes += sprintf(&data[bytes], "\n"); } @@ -2135,37 +2146,37 @@ static int goodix_save_header(struct goodix_ts_test *ts_test, if (ts_test->test_result[GTP_NOISE_TEST]) { if (GTP_TEST_PASS == ts_test->test_result[GTP_NOISE_TEST]) - bytes += sprintf(&data[bytes], + bytes += sprintf(&data[bytes], "\n"); else - bytes += sprintf(&data[bytes], + bytes += sprintf(&data[bytes], "\n"); } if (ts_test->test_result[GTP_SELFNOISE_TEST]) { if (GTP_TEST_PASS == ts_test->test_result[GTP_SELFNOISE_TEST]) - bytes += sprintf(&data[bytes], + bytes += sprintf(&data[bytes], "\n"); else - bytes += sprintf(&data[bytes], + bytes += sprintf(&data[bytes], "\n"); } if (ts_test->test_result[GTP_SELFCAP_TEST]) { if (GTP_TEST_PASS == ts_test->test_result[GTP_SELFCAP_TEST]) - bytes += sprintf(&data[bytes], + bytes += sprintf(&data[bytes], "\n"); else - bytes += sprintf(&data[bytes], + bytes += sprintf(&data[bytes], "\n"); } if (ts_test->test_result[GTP_SHORT_TEST]) { if (GTP_TEST_PASS == ts_test->test_result[GTP_SHORT_TEST]) - bytes += sprintf(&data[bytes], + bytes += sprintf(&data[bytes], "\n"); else - bytes += sprintf(&data[bytes], + bytes += sprintf(&data[bytes], "\n"); } @@ -2178,7 +2189,7 @@ static int goodix_save_header(struct goodix_ts_test *ts_test, save_end: kfree(data); - return ret; + return ret; } static int goodix_save_limits(struct goodix_ts_test *ts_test, @@ -2196,9 +2207,7 @@ static int goodix_save_limits(struct goodix_ts_test *ts_test, data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); if (!data) { - ts_err("alloc memory failed for "); return -ENOMEM; - } bytes += sprintf(&data[bytes], "\n"); @@ -2223,7 +2232,7 @@ static int goodix_save_limits(struct goodix_ts_test *ts_test, bytes += sprintf(&data[bytes], "\n", r); else if (chn2 == CHN_GND) bytes += sprintf(&data[bytes], @@ -2306,7 +2315,7 @@ static int goodix_save_limits(struct goodix_ts_test *ts_test, ts_test->test_params.deviation_limits[i]); if ((i + 1) % tx == 0) bytes += sprintf(&data[bytes], "\n"); - } + } bytes += sprintf(&data[bytes], "\n"); /* BeyondAccordLimitCnt */ bytes += sprintf(&data[bytes], "\n"); @@ -2327,10 +2336,13 @@ static int goodix_save_limits(struct goodix_ts_test *ts_test, /* save noise limit */ if (ts_test->test_result[GTP_NOISE_TEST]) { - bytes += sprintf(&data[bytes], "\n"); - bytes += sprintf(&data[bytes], "%d\n", + bytes += sprintf(&data[bytes], + "\n"); + bytes += sprintf(&data[bytes], + "%d\n", NOISEDATA_TEST_TIMES); - bytes += sprintf(&data[bytes], "%d\n", + bytes += sprintf(&data[bytes], + "%d\n", ts_test->test_params.noise_threshold); bytes += sprintf(&data[bytes], "\n"); ret = fs_write(data, bytes, fp); @@ -2343,12 +2355,15 @@ static int goodix_save_limits(struct goodix_ts_test *ts_test, /* save self rawdata limit */ if (ts_test->test_result[GTP_SELFCAP_TEST]) { - bytes += sprintf(&data[bytes], "\n"); - bytes += sprintf(&data[bytes], "1\n"); - bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], + "\n"); + bytes += sprintf(&data[bytes], + "1\n"); + bytes += sprintf(&data[bytes], + "\n"); for (i = 0; i < tx + rx; i++) { bytes += sprintf(&data[bytes], "%d,", - ts_test->test_params.self_max_limits[i]); + ts_test->test_params.self_max_limits[i]); if ((i + 1) % tx == 0) bytes += sprintf(&data[bytes], "\n"); } @@ -2358,7 +2373,7 @@ static int goodix_save_limits(struct goodix_ts_test *ts_test, bytes += sprintf(&data[bytes], "\n"); for (i = 0; i < tx + rx; i++) { bytes += sprintf(&data[bytes], "%d,", - ts_test->test_params.self_min_limits[i]); + ts_test->test_params.self_min_limits[i]); if ((i + 1) % tx == 0) bytes += sprintf(&data[bytes], "\n"); } @@ -2376,9 +2391,12 @@ static int goodix_save_limits(struct goodix_ts_test *ts_test, /* save selfnoise limit */ if (ts_test->test_result[GTP_SELFNOISE_TEST]) { - bytes += sprintf(&data[bytes], "\n"); - bytes += sprintf(&data[bytes], "1\n"); - bytes += sprintf(&data[bytes], "%d\n", + bytes += sprintf(&data[bytes], + "\n"); + bytes += sprintf(&data[bytes], + "1\n"); + bytes += sprintf(&data[bytes], + "%d\n", ts_test->test_params.self_noise_threshold); bytes += sprintf(&data[bytes], "\n"); ret = fs_write(data, bytes, fp); @@ -2413,10 +2431,8 @@ static int goodix_save_rawdata(struct goodix_ts_test *ts_test, int len = tx * rx; data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); - if (!data) { - ts_err("alloc memory failed for "); + if (!data) return -ENOMEM; - } bytes += sprintf(&data[bytes], "\n"); for (i = 0; i < TOTAL_FRAME_NUM; i++) { @@ -2425,7 +2441,8 @@ static int goodix_save_rawdata(struct goodix_ts_test *ts_test, "\n", i, len, stat_result[1], stat_result[2], stat_result[0]); for (j = 0; j < len; j++) { - bytes += sprintf(&data[bytes], "%d,", ts_test->rawdata[i].data[j]); + bytes += sprintf(&data[bytes], "%d,", + ts_test->rawdata[i].data[j]); if ((j + 1) % tx == 0) bytes += sprintf(&data[bytes], "\n"); } @@ -2435,7 +2452,8 @@ static int goodix_save_rawdata(struct goodix_ts_test *ts_test, "\n", i, len, stat_result[1], stat_result[2], stat_result[0]); for (j = 0; j < len; j++) { - bytes += sprintf(&data[bytes], "%d,", ts_test->accord_arr[i].data[j]); + bytes += sprintf(&data[bytes], "%d,", + ts_test->accord_arr[i].data[j]); if ((j + 1) % tx == 0) bytes += sprintf(&data[bytes], "\n"); } @@ -2458,7 +2476,8 @@ save_end: return ret; } -static int goodix_save_noise_data(struct goodix_ts_test *ts_test, struct file *fp) +static int goodix_save_noise_data(struct goodix_ts_test *ts_test, + struct file *fp) { int i; int j; @@ -2471,10 +2490,8 @@ static int goodix_save_noise_data(struct goodix_ts_test *ts_test, struct file *f int len = tx * rx; data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); - if (!data) { - ts_err("alloc memory failed for "); + if (!data) return -ENOMEM; - } bytes += sprintf(&data[bytes], "\n"); for (i = 0; i < NOISEDATA_TEST_TIMES; i++) { @@ -2483,7 +2500,8 @@ static int goodix_save_noise_data(struct goodix_ts_test *ts_test, struct file *f "\n", i, len, stat_result[1], stat_result[2], stat_result[0]); for (j = 0; j < len; j++) { - bytes += sprintf(&data[bytes], "%d,", ts_test->noisedata[i].data[j]); + bytes += sprintf(&data[bytes], "%d,", + ts_test->noisedata[i].data[j]); if ((j + 1) % tx == 0) bytes += sprintf(&data[bytes], "\n"); } @@ -2503,7 +2521,7 @@ static int goodix_save_noise_data(struct goodix_ts_test *ts_test, struct file *f save_end: kfree(data); - return ret; + return ret; } static int goodix_save_self_data(struct goodix_ts_test *ts_test, @@ -2517,12 +2535,10 @@ static int goodix_save_self_data(struct goodix_ts_test *ts_test, int tx = ts_test->test_params.drv_num; data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); - if (!data) { - ts_err("alloc memory failed for "); + if (!data) return -ENOMEM; - } - bytes += sprintf(&data[bytes], "<%s>\n",title); + bytes += sprintf(&data[bytes], "<%s>\n", title); ret = fs_write(data, bytes, fp); if (ret < 0) { ts_err("rawdata write fail."); @@ -2542,7 +2558,7 @@ static int goodix_save_self_data(struct goodix_ts_test *ts_test, if (len % tx != 0) bytes += sprintf(&data[bytes], "\n"); bytes += sprintf(&data[bytes], "\n"); - bytes += sprintf(&data[bytes], "\n",title); + bytes += sprintf(&data[bytes], "\n", title); ret = fs_write(data, bytes, fp); if (ret < 0) ts_err("rawdata write fail."); @@ -2560,10 +2576,8 @@ static int goodix_save_data(struct goodix_ts_test *ts_test, char *data = NULL; data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); - if (!data) { - ts_err("alloc memory failed for "); + if (!data) return -ENOMEM; - } bytes += sprintf(&data[bytes], "\n"); ret = fs_write(data, bytes, fp); @@ -2584,15 +2598,19 @@ static int goodix_save_data(struct goodix_ts_test *ts_test, } if (ts_test->test_result[GTP_SELFCAP_TEST]) { - ret = goodix_save_self_data(ts_test, fp, ts_test->self_rawdata.data, - "selfDataRecord", ts_test->self_rawdata.size); + ret = goodix_save_self_data(ts_test, fp, + ts_test->self_rawdata.data, + "selfDataRecord", + ts_test->self_rawdata.size); if (ret < 0) - goto save_end; + goto save_end; } if (ts_test->test_result[GTP_SELFNOISE_TEST]) { - ret = goodix_save_self_data(ts_test, fp, ts_test->self_noisedata.data, - "selfDiffDataRecord", ts_test->self_noisedata.size); + ret = goodix_save_self_data(ts_test, fp, + ts_test->self_noisedata.data, + "selfDiffDataRecord", + ts_test->self_noisedata.size); if (ret < 0) goto save_end; } @@ -2616,10 +2634,8 @@ static int goodix_save_tail(struct goodix_ts_test *ts_test, char *data = NULL; data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); - if (!data) { - ts_err("alloc memory failed for "); + if (!data) return -ENOMEM; - } bytes += sprintf(&data[bytes], "\n"); ret = fs_write(data, bytes, fp); @@ -2689,28 +2705,32 @@ static void goodix_put_test_result(struct goodix_ts_test *ts_test, /* save rawdata to info->buff, only one frame */ if (ts_test->rawdata[0].size) { for (i = 0; i < ts_test->rawdata[0].size; i++) - info->buff[info->used_size + i] = ts_test->rawdata[0].data[i]; + info->buff[info->used_size + i] = + ts_test->rawdata[0].data[i]; info->used_size += ts_test->rawdata[0].size; } /* save noisedata to info->buff */ if (ts_test->noisedata[0].size) { for (i = 0; i < ts_test->noisedata[0].size; i++) - info->buff[info->used_size + i] = ts_test->noisedata[0].data[i]; + info->buff[info->used_size + i] = + ts_test->noisedata[0].data[i]; info->used_size += ts_test->noisedata[0].size; } /* save self_noisedata to info->buff */ if (ts_test->self_noisedata.size) { for (i = 0; i < ts_test->self_noisedata.size; i++) - info->buff[info->used_size + i] = ts_test->self_noisedata.data[i]; + info->buff[info->used_size + i] = + ts_test->self_noisedata.data[i]; info->used_size += ts_test->self_noisedata.size; } /* save self_rawdata to info->buff */ if (ts_test->self_rawdata.size) { for (i = 0; i < ts_test->self_rawdata.size; i++) - info->buff[info->used_size + i] = ts_test->self_rawdata.data[i]; + info->buff[info->used_size + i] = + ts_test->self_rawdata.data[i]; info->used_size += ts_test->self_rawdata.size; } @@ -2723,19 +2743,23 @@ static void goodix_put_test_result(struct goodix_ts_test *ts_test, } ts_info("Have bus error:%d", have_bus_error); if (have_bus_error || have_panel_error) - goodix_strncat(ts_test->test_info, "[FAIL]-", TS_RAWDATA_RESULT_MAX); + goodix_strncat(ts_test->test_info, "[FAIL]-", + TS_RAWDATA_RESULT_MAX); else - goodix_strncat(ts_test->test_info, "[PASS]-", TS_RAWDATA_RESULT_MAX); + goodix_strncat(ts_test->test_info, "[PASS]-", + TS_RAWDATA_RESULT_MAX); if (have_bus_error) - goodix_strncat(ts_test->test_info, "0F-", TS_RAWDATA_RESULT_MAX); + goodix_strncat(ts_test->test_info, "0F-", + TS_RAWDATA_RESULT_MAX); else - goodix_strncat(ts_test->test_info, "0P-", TS_RAWDATA_RESULT_MAX); + goodix_strncat(ts_test->test_info, "0P-", + TS_RAWDATA_RESULT_MAX); for (i = 0; i < MAX_TEST_ITEMS; i++) { /* if have tested, show result */ if (ts_test->test_result[i]) { - if (GTP_TEST_PASS == ts_test->test_result[i]) + if (ts_test->test_result[i] == GTP_TEST_PASS) goodix_strncatint(ts_test->test_info, i, "%dP-", TS_RAWDATA_RESULT_MAX); else @@ -2821,48 +2845,47 @@ static void goodix_put_test_result(struct goodix_ts_test *ts_test, #ifdef SAVE_IN_CSV /* save result to file */ goodix_save_result_data(ts_test); -#endif +#endif } -static int goodix_do_inspect(struct goodix_ts_core *cd, struct ts_rawdata_info *info) +static int goodix_do_inspect(struct goodix_ts_core *cd, + struct ts_rawdata_info *info) { - int ret; - struct goodix_ts_test *ts_test = NULL; + int ret; + struct goodix_ts_test *ts_test = NULL; - if (!cd || !info) { - ts_err("core_data or info is NULL"); - return -ENODEV; - } - - ts_test = kzalloc(sizeof(*ts_test), GFP_KERNEL); - if (!ts_test) { - ts_err("Failed to alloc mem"); - return -ENOMEM; + if (!cd || !info) { + ts_err("core_data or info is NULL"); + return -ENODEV; } - ts_test->ts = cd; - ret = goodix_tptest_prepare(ts_test); - if (ret < 0) { - ts_err("Failed to prepare TP test, exit"); - strncpy(info->result, "[FAIL]-0F-software reason\n", - TS_RAWDATA_RESULT_MAX - 1); - goto exit_finish; - } - ts_info("TP test prepare OK"); + ts_test = kzalloc(sizeof(*ts_test), GFP_KERNEL); + if (!ts_test) + return -ENOMEM; - goodix_capacitance_test(ts_test); /* 1F 3F 6F 7F test */ + ts_test->ts = cd; + ret = goodix_tptest_prepare(ts_test); + if (ret < 0) { + ts_err("Failed to prepare TP test, exit"); + strncpy(info->result, "[FAIL]-0F-software reason\n", + TS_RAWDATA_RESULT_MAX - 1); + goto exit_finish; + } + ts_info("TP test prepare OK"); + + goodix_capacitance_test(ts_test); /* 1F 3F 6F 7F test */ if (ts_test->test_params.test_items[GTP_SHORT_TEST]) - goodix_shortcircut_test(ts_test); /* 5F test */ - goodix_put_test_result(ts_test, info); - goodix_tptest_finish(ts_test); + goodix_shortcircut_test(ts_test); /* 5F test */ + goodix_put_test_result(ts_test, info); + goodix_tptest_finish(ts_test); exit_finish: kfree(ts_test); - return ret; + return ret; } /* show rawdata */ -static ssize_t goodix_ts_get_rawdata_show(struct device *dev, +static ssize_t get_rawdata_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = 0; @@ -2870,10 +2893,8 @@ static ssize_t goodix_ts_get_rawdata_show(struct device *dev, struct goodix_ts_core *cd = dev_get_drvdata(dev); info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) { - ts_err("Failed to alloc rawdata info memory"); + if (!info) return -ENOMEM; - } goodix_do_inspect(cd, info); @@ -2883,7 +2904,7 @@ static ssize_t goodix_ts_get_rawdata_show(struct device *dev, return ret; } -static DEVICE_ATTR(get_rawdata, S_IRUGO, goodix_ts_get_rawdata_show, NULL); +static DEVICE_ATTR(get_rawdata, 0444, get_rawdata_show, NULL); int inspect_module_init(void) { @@ -2903,7 +2924,7 @@ int inspect_module_init(void) err_out: ts_err("inspect module init failed!"); - return ret; + return ret; } void inspect_module_exit(void) diff --git a/goodix_ts_tools.c b/driver/goodix_ts_tools.c similarity index 100% rename from goodix_ts_tools.c rename to driver/goodix_ts_tools.c diff --git a/goodix_ts_utils.c b/driver/goodix_ts_utils.c similarity index 100% rename from goodix_ts_utils.c rename to driver/goodix_ts_utils.c diff --git a/goodix_ts_gesture.c b/goodix_ts_gesture.c deleted file mode 100644 index 1687a57086..0000000000 --- a/goodix_ts_gesture.c +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Goodix Gesture Module - * - * Copyright (C) 2019 - 2020 Goodix, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be a reference - * to you, when you are integrating the GOODiX's CTP IC into your system, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "goodix_ts_core.h" - -#define QUERYBIT(longlong, bit) (!!(longlong[bit/8] & (1 << bit%8))) - -#define GSX_GESTURE_TYPE_LEN 32 - -/* - * struct gesture_module - gesture module data - * @registered: module register state - * @sysfs_node_created: sysfs node state - * @gesture_type: valid gesture type, each bit represent one gesture type - * @gesture_data: store latest gesture code get from irq event - * @gesture_ts_cmd: gesture command data - */ -struct gesture_module { - atomic_t registered; - rwlock_t rwlock; - u8 gesture_type[GSX_GESTURE_TYPE_LEN]; - u8 gesture_data; - struct goodix_ext_module module; -}; - -static struct gesture_module *gsx_gesture; /*allocated in gesture init module*/ -static bool module_initialized; - - -int goodix_gesture_enable(int enable) -{ - int ret = 0; - - if (!module_initialized) - return 0; - - if (enable) { - if (atomic_read(&gsx_gesture->registered)) - ts_info("gesture module has been already registered"); - else - ret = goodix_register_ext_module_no_wait(&gsx_gesture->module); - } else { - if (!atomic_read(&gsx_gesture->registered)) - ts_info("gesture module has been already unregistered"); - else - ret = goodix_unregister_ext_module(&gsx_gesture->module); - } - - return ret; -} - -/** - * gsx_gesture_type_show - show valid gesture type - * - * @module: pointer to goodix_ext_module struct - * @buf: pointer to output buffer - * Returns >=0 - succeed,< 0 - failed - */ -static ssize_t gsx_gesture_type_show(struct goodix_ext_module *module, - char *buf) -{ - int count = 0, i, ret = 0; - unsigned char *type; - - type = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (!type) - return -ENOMEM; - read_lock(&gsx_gesture->rwlock); - for (i = 0; i < 256; i++) { - if (QUERYBIT(gsx_gesture->gesture_type, i)) { - count += scnprintf(type + count, - PAGE_SIZE, "%02x,", i); - } - } - if (count > 0) - ret = scnprintf(buf, PAGE_SIZE, "%s\n", type); - read_unlock(&gsx_gesture->rwlock); - - kfree(type); - return ret; -} - -/** - * gsx_gesture_type_store - set vailed gesture - * - * @module: pointer to goodix_ext_module struct - * @buf: pointer to valid gesture type - * @count: length of buf - * Returns >0 - valid gestures, < 0 - failed - */ -static ssize_t gsx_gesture_type_store(struct goodix_ext_module *module, - const char *buf, size_t count) -{ - int i; - - if (count <= 0 || count > 256 || buf == NULL) { - ts_err("Parameter error"); - return -EINVAL; - } - - write_lock(&gsx_gesture->rwlock); - memset(gsx_gesture->gesture_type, 0, GSX_GESTURE_TYPE_LEN); - for (i = 0; i < count; i++) - gsx_gesture->gesture_type[buf[i]/8] |= (0x1 << buf[i]%8); - write_unlock(&gsx_gesture->rwlock); - - return count; -} - -static ssize_t gsx_gesture_enable_show(struct goodix_ext_module *module, - char *buf) -{ - return scnprintf(buf, PAGE_SIZE, "%d\n", - atomic_read(&gsx_gesture->registered)); -} - -static ssize_t gsx_gesture_enable_store(struct goodix_ext_module *module, - const char *buf, size_t count) -{ - bool val; - int ret; - - ret = strtobool(buf, &val); - if (ret < 0) - return ret; - - if (val) { - ret = goodix_gesture_enable(1); - return ret ? ret : count; - } else { - ret = goodix_gesture_enable(0); - return ret ? ret : count; - } -} - -static ssize_t gsx_gesture_data_show(struct goodix_ext_module *module, - char *buf) -{ - ssize_t count; - - read_lock(&gsx_gesture->rwlock); - count = scnprintf(buf, PAGE_SIZE, "gesture type code:0x%x\n", - gsx_gesture->gesture_data); - read_unlock(&gsx_gesture->rwlock); - - return count; -} - -const struct goodix_ext_attribute gesture_attrs[] = { - __EXTMOD_ATTR(type, 0666, gsx_gesture_type_show, - gsx_gesture_type_store), - __EXTMOD_ATTR(enable, 0666, gsx_gesture_enable_show, - gsx_gesture_enable_store), - __EXTMOD_ATTR(data, 0444, gsx_gesture_data_show, NULL) -}; - -static int gsx_gesture_init(struct goodix_ts_core *cd, - struct goodix_ext_module *module) -{ - if (!cd || !cd->hw_ops->gesture) { - ts_err("gesture unsupported"); - return -EINVAL; - } - - ts_info("gesture switch: ON"); - ts_debug("enable all gesture type"); - /* set all bit to 1 to enable all gesture wakeup */ - memset(gsx_gesture->gesture_type, 0xff, GSX_GESTURE_TYPE_LEN); - atomic_set(&gsx_gesture->registered, 1); - - return 0; -} - -static int gsx_gesture_exit(struct goodix_ts_core *cd, - struct goodix_ext_module *module) -{ - if (!cd || !cd->hw_ops->gesture) { - ts_err("gesture unsupported"); - return -EINVAL; - } - - ts_info("gesture switch: OFF"); - ts_debug("disable all gesture type"); - memset(gsx_gesture->gesture_type, 0x00, GSX_GESTURE_TYPE_LEN); - atomic_set(&gsx_gesture->registered, 0); - - return 0; -} - -/** - * gsx_gesture_ist - Gesture Irq handle - * This functions is excuted when interrupt happended and - * ic in doze mode. - * - * @cd: pointer to touch core data - * @module: pointer to goodix_ext_module struct - * return: 0 goon execute, EVT_CANCEL_IRQEVT stop execute - */ -static int gsx_gesture_ist(struct goodix_ts_core *cd, - struct goodix_ext_module *module) -{ - struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; - struct goodix_ts_event gs_event = {0}; - int ret; - - if (atomic_read(&cd->suspended) == 0) - return EVT_CONTINUE; - - ret = hw_ops->event_handler(cd, &gs_event); - if (ret) { - ts_err("failed get gesture data"); - goto re_send_ges_cmd; - } - - if (!(gs_event.event_type & EVENT_GESTURE)) { - ts_err("invalid event type: 0x%x", - cd->ts_event.event_type); - goto re_send_ges_cmd; - } - - if (QUERYBIT(gsx_gesture->gesture_type, - gs_event.gesture_type)) { - gsx_gesture->gesture_data = gs_event.gesture_type; - /* do resume routine */ - ts_info("got valid gesture type 0x%x", - gs_event.gesture_type); - input_report_key(cd->input_dev, KEY_POWER, 1); - input_sync(cd->input_dev); - input_report_key(cd->input_dev, KEY_POWER, 0); - input_sync(cd->input_dev); - goto gesture_ist_exit; - } else { - ts_info("unsupported gesture:%x", gs_event.gesture_type); - } - -re_send_ges_cmd: - if (hw_ops->gesture(cd, 0)) - ts_info("warning: failed re_send gesture cmd"); -gesture_ist_exit: - if (!cd->tools_ctrl_sync) - hw_ops->after_event_handler(cd); - return EVT_CANCEL_IRQEVT; -} - -/** - * gsx_gesture_before_suspend - execute gesture suspend routine - * This functions is excuted to set ic into doze mode - * - * @cd: pointer to touch core data - * @module: pointer to goodix_ext_module struct - * return: 0 goon execute, EVT_IRQCANCLED stop execute - */ -static int gsx_gesture_before_suspend(struct goodix_ts_core *cd, - struct goodix_ext_module *module) -{ - int ret; - const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; - - ret = hw_ops->gesture(cd, 0); - if (ret) - ts_err("failed enter gesture mode"); - else - ts_info("enter gesture mode"); - - hw_ops->irq_enable(cd, true); - enable_irq_wake(cd->irq); - - return EVT_CANCEL_SUSPEND; -} - -static int gsx_gesture_before_resume(struct goodix_ts_core *cd, - struct goodix_ext_module *module) -{ - const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; - - hw_ops->irq_enable(cd, false); - disable_irq_wake(cd->irq); - hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); - - return EVT_CANCEL_RESUME; -} - -static struct goodix_ext_module_funcs gsx_gesture_funcs = { - .irq_event = gsx_gesture_ist, - .init = gsx_gesture_init, - .exit = gsx_gesture_exit, - .before_suspend = gsx_gesture_before_suspend, - .before_resume = gsx_gesture_before_resume, -}; - -int gesture_module_init(void) -{ - int ret; - int i; - struct kobject *def_kobj = goodix_get_default_kobj(); - struct kobj_type *def_kobj_type = goodix_get_default_ktype(); - - gsx_gesture = kzalloc(sizeof(struct gesture_module), GFP_KERNEL); - if (!gsx_gesture) - return -ENOMEM; - - gsx_gesture->module.funcs = &gsx_gesture_funcs; - gsx_gesture->module.priority = EXTMOD_PRIO_GESTURE; - gsx_gesture->module.name = "Goodix_gsx_gesture"; - gsx_gesture->module.priv_data = gsx_gesture; - - atomic_set(&gsx_gesture->registered, 0); - rwlock_init(&gsx_gesture->rwlock); - - /* gesture sysfs init */ - ret = kobject_init_and_add(&gsx_gesture->module.kobj, - def_kobj_type, def_kobj, "gesture"); - if (ret) { - ts_err("failed create gesture sysfs node!"); - goto err_out; - } - - for (i = 0; i < ARRAY_SIZE(gesture_attrs) && !ret; i++) - ret = sysfs_create_file(&gsx_gesture->module.kobj, - &gesture_attrs[i].attr); - if (ret) { - ts_err("failed create gst sysfs files"); - while (--i >= 0) - sysfs_remove_file(&gsx_gesture->module.kobj, - &gesture_attrs[i].attr); - - kobject_put(&gsx_gesture->module.kobj); - goto err_out; - } - - module_initialized = true; - ts_info("gesture module init success"); - - return 0; - -err_out: - ts_err("gesture module init failed!"); - kfree(gsx_gesture); - return ret; -} - -void gesture_module_exit(void) -{ - int i; - - ts_info("gesture module exit"); - if (!module_initialized) - return; - - goodix_gesture_enable(0); - - /* deinit sysfs */ - for (i = 0; i < ARRAY_SIZE(gesture_attrs); i++) - sysfs_remove_file(&gsx_gesture->module.kobj, - &gesture_attrs[i].attr); - - kobject_put(&gsx_gesture->module.kobj); - kfree(gsx_gesture); - module_initialized = false; -} \ No newline at end of file From 875ea6b0ee4184d45be69126bafdf902aa7af0f3 Mon Sep 17 00:00:00 2001 From: xulinkun Date: Mon, 24 Jan 2022 15:55:09 +0800 Subject: [PATCH 007/170] touch: goodix: update LICENSE Rename directory name to be "goodix_berlin_driver"; Update LICENSE. Change-Id: I79868637950245d42ef5ab561292b7893d1b1f75 Signed-off-by: xulinkun Git-commit: 0770c32b8c0f7d58e1761ded2347fc249f10b347 Git-repo: https://github.com/goodix/goodix_ts_berlin Signed-off-by: Fei Mao --- LICENSE | 347 +++++++++++++++++- {driver => goodix_berlin_driver}/Kconfig | 0 {driver => goodix_berlin_driver}/Makefile | 0 .../goodix_brl_fwupdate.c | 0 .../goodix_brl_hw.c | 0 .../goodix_brl_i2c.c | 0 .../goodix_brl_spi.c | 0 .../goodix_cfg_bin.c | 0 .../goodix_ts_core.c | 0 .../goodix_ts_core.h | 0 .../goodix_ts_gesture.c | 0 .../goodix_ts_inspect.c | 0 .../goodix_ts_tools.c | 0 .../goodix_ts_utils.c | 0 14 files changed, 335 insertions(+), 12 deletions(-) rename {driver => goodix_berlin_driver}/Kconfig (100%) rename {driver => goodix_berlin_driver}/Makefile (100%) rename {driver => goodix_berlin_driver}/goodix_brl_fwupdate.c (100%) rename {driver => goodix_berlin_driver}/goodix_brl_hw.c (100%) rename {driver => goodix_berlin_driver}/goodix_brl_i2c.c (100%) rename {driver => goodix_berlin_driver}/goodix_brl_spi.c (100%) rename {driver => goodix_berlin_driver}/goodix_cfg_bin.c (100%) rename {driver => goodix_berlin_driver}/goodix_ts_core.c (100%) rename {driver => goodix_berlin_driver}/goodix_ts_core.h (100%) rename {driver => goodix_berlin_driver}/goodix_ts_gesture.c (100%) rename {driver => goodix_berlin_driver}/goodix_ts_inspect.c (100%) rename {driver => goodix_berlin_driver}/goodix_ts_tools.c (100%) rename {driver => goodix_berlin_driver}/goodix_ts_utils.c (100%) diff --git a/LICENSE b/LICENSE index ee447585ed..d159169d10 100644 --- a/LICENSE +++ b/LICENSE @@ -1,16 +1,339 @@ - Copyright (c) 2003-2006, Goodix Technology Co, Ltd. - All Rights Reserved + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 - This program is free software; you can redistribute it and/or modify it - under the terms of version 2 of the GNU General Public License as - published by the Free Software Foundation. + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. - 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. + Preamble - You should have received a copy of the GNU General Public License along with - this program; if not, write to the Free Software Foundation, Inc., 59 - Temple Place - Suite 330, Boston, MA 02111-1307, USA. + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/driver/Kconfig b/goodix_berlin_driver/Kconfig similarity index 100% rename from driver/Kconfig rename to goodix_berlin_driver/Kconfig diff --git a/driver/Makefile b/goodix_berlin_driver/Makefile similarity index 100% rename from driver/Makefile rename to goodix_berlin_driver/Makefile diff --git a/driver/goodix_brl_fwupdate.c b/goodix_berlin_driver/goodix_brl_fwupdate.c similarity index 100% rename from driver/goodix_brl_fwupdate.c rename to goodix_berlin_driver/goodix_brl_fwupdate.c diff --git a/driver/goodix_brl_hw.c b/goodix_berlin_driver/goodix_brl_hw.c similarity index 100% rename from driver/goodix_brl_hw.c rename to goodix_berlin_driver/goodix_brl_hw.c diff --git a/driver/goodix_brl_i2c.c b/goodix_berlin_driver/goodix_brl_i2c.c similarity index 100% rename from driver/goodix_brl_i2c.c rename to goodix_berlin_driver/goodix_brl_i2c.c diff --git a/driver/goodix_brl_spi.c b/goodix_berlin_driver/goodix_brl_spi.c similarity index 100% rename from driver/goodix_brl_spi.c rename to goodix_berlin_driver/goodix_brl_spi.c diff --git a/driver/goodix_cfg_bin.c b/goodix_berlin_driver/goodix_cfg_bin.c similarity index 100% rename from driver/goodix_cfg_bin.c rename to goodix_berlin_driver/goodix_cfg_bin.c diff --git a/driver/goodix_ts_core.c b/goodix_berlin_driver/goodix_ts_core.c similarity index 100% rename from driver/goodix_ts_core.c rename to goodix_berlin_driver/goodix_ts_core.c diff --git a/driver/goodix_ts_core.h b/goodix_berlin_driver/goodix_ts_core.h similarity index 100% rename from driver/goodix_ts_core.h rename to goodix_berlin_driver/goodix_ts_core.h diff --git a/driver/goodix_ts_gesture.c b/goodix_berlin_driver/goodix_ts_gesture.c similarity index 100% rename from driver/goodix_ts_gesture.c rename to goodix_berlin_driver/goodix_ts_gesture.c diff --git a/driver/goodix_ts_inspect.c b/goodix_berlin_driver/goodix_ts_inspect.c similarity index 100% rename from driver/goodix_ts_inspect.c rename to goodix_berlin_driver/goodix_ts_inspect.c diff --git a/driver/goodix_ts_tools.c b/goodix_berlin_driver/goodix_ts_tools.c similarity index 100% rename from driver/goodix_ts_tools.c rename to goodix_berlin_driver/goodix_ts_tools.c diff --git a/driver/goodix_ts_utils.c b/goodix_berlin_driver/goodix_ts_utils.c similarity index 100% rename from driver/goodix_ts_utils.c rename to goodix_berlin_driver/goodix_ts_utils.c From ded7148715ad0f13dce91df92986fb70b587a3d7 Mon Sep 17 00:00:00 2001 From: Fei Mao Date: Mon, 24 Jan 2022 16:16:35 +0800 Subject: [PATCH 008/170] touch: goodix: adapt driver code Revert Makefile; Adapt driver code for this project. Change-Id: Ib8f66d1a86cf396d9e43cdb6e1f9ef66595bc4db Signed-off-by: Fei Mao --- Kconfig | 20 -- LICENSE | 339 ---------------------------------- Makefile | 27 +-- NOTICE | 17 ++ docs/Porting_Guide.md | 192 ------------------- goodix_berlin_driver/Kconfig | 20 -- goodix_berlin_driver/Makefile | 12 -- 7 files changed, 32 insertions(+), 595 deletions(-) delete mode 100644 Kconfig delete mode 100644 LICENSE delete mode 100644 docs/Porting_Guide.md delete mode 100644 goodix_berlin_driver/Kconfig delete mode 100644 goodix_berlin_driver/Makefile diff --git a/Kconfig b/Kconfig deleted file mode 100644 index 375b5473a1..0000000000 --- a/Kconfig +++ /dev/null @@ -1,20 +0,0 @@ -# -# Goodix touchscreen driver configuration -# -menuconfig TOUCHSCREEN_GOODIX_BRL - tristate "Goodix berlin touchscreen" - help - Say Y here if you have a Goodix berlin series touch controller - to your system. - - If build module, say M. - If unsure, say N. - -if TOUCHSCREEN_GOODIX_BRL - -config TOUCHSCREEN_GOODIX_BRL_SPI - bool "support SPI bus connection" - help - Say Y here if the touchscreen is connected via SPI bus. - -endif diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d159169d10..0000000000 --- a/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - 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. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/Makefile b/Makefile index 903a1cccb5..05c3bcc66c 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,15 @@ -obj-$(CONFIG_TOUCHSCREEN_GOODIX_BRL) += goodix_core.o -goodix_core-y := \ - goodix_brl_i2c.o \ - goodix_brl_spi.o \ - goodix_ts_core.o \ - goodix_brl_hw.o \ - goodix_cfg_bin.o \ - goodix_ts_utils.o \ - goodix_brl_fwupdate.o \ - goodix_ts_gesture.o \ - goodix_ts_inspect.o \ - goodix_ts_tools.o + +KBUILD_OPTIONS+= TOUCH_ROOT=$(KERNEL_SRC)/$(M) + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install + +%: + $(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS) + +clean: + rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers + rm -rf .tmp_versions diff --git a/NOTICE b/NOTICE index 35e497ec37..e4c914eac5 100644 --- a/NOTICE +++ b/NOTICE @@ -93,3 +93,20 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ + +/* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ \ No newline at end of file diff --git a/docs/Porting_Guide.md b/docs/Porting_Guide.md deleted file mode 100644 index 83a45ecc19..0000000000 --- a/docs/Porting_Guide.md +++ /dev/null @@ -1,192 +0,0 @@ -# **Berlin Driver Porting Guide** - -## **Introduce** -Berlin series driver is currently support BerlinA(GT9897), BerlinB(GT9966, GT7986). And support I2C or SPI connection. - -## **Driver source file prepare** -1. Move driver source code to $KERNEL_SRC/drivers/input/touchscree/ -2. Change $KERNEL_SRC/drivers/input/touchscree/Makefile - Add this line to the Makefile - ``` - obj-y += goodix_berlin_driver/ - ``` -3. Change $KERNEL_SRC/drivers/input/touchscree/Kconfg - Add this line to the Kconfig - ``` - source "drivers/input/touchscreen/goodix_berlin_driver/Kconfig" - ``` - -## **Add device declaration in the board devicetree** -Please add Goodix touch device declaration info in the board devicetree, you can refer the Appendix goodix-ts-i2c-dtsi or goodix-ts-spi-dtsi to see how to set deivce properties. - -## **Build driver** -When build kernel you will see the following promt to let you confirm how to build the driver. This driver support built-in kernel or build as modules. - -**Setting up in the menu:** -1. In the $KERNEL_SRC directory, exec `make menuconfig`, then select -`Device Drivers ---> Input device support ---> Touchscreens --->` -2. Find `Goodix berlin touchscreen` menu, you can select `<*>`(build in kernel) or ``(build a module). -3. Enter `Goodix berlin touchscreen`, you can see `support SPI bus connection` item. If -you are on SPI connection, select `<*>`, or on I2C connection. - -**Setting up in the defconfig file:** -1. Add the following in you defconfig file. - ``` - CONFIG_TOUCHSCREEN_GOODIX_BRL=y - ``` - or - ``` - CONFIG_TOUCHSCREEN_GOODIX_BRL=m - ``` -2. If you are on SPI connection, add the following. - ``` - CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI=y - ``` - -## **Appendix** - -**goodix-ts-i2c-dtsi** - -```dts -devicetree binding for Goodix i2c touchdriver -Required properties: -- compatible: device & driver matching. - * for berlin series touch device, souch as "goodix,gt9897", "goodix,gt9966" - -- reg: i2c client address, value can be 0x14 or 0x5d. please refer to datasheet. -- goodix,reset-gpio: reset gpio. -- goodix,irq-gpio: interrupt gpio. -- goodix,irq-flags: irq trigger type config, value should be: - 1 - rising edge, - 2 - falling edge, - 4 - high level, - 5 - low level. -- goodix,panel-max-x: max resolution of x direction. -- goodix,panel-max-y: max resolution of y direction. -- goodix,panel-max-w: panel max width value. -- goodix,panel-max-p: pen device max pressure value. - -Optional properties: -- goodix,avdd-name: set name of regulator. -- avdd-supply: power supply for the touch device. - example of regulator: - goodix,avdd-name = "avdd"; - avdd-supply = <&pm8916_l15>; -- iovdd-supply: power supply for digital io circuit - example of regulator: - goodix,iovdd-name = "iovdd"; - iovdd-supply = <&pm8916_l16>; -- goodix,pen-enable: set this property if you want support stylus. - goodix,pen-enable; -- goodix,firmware-name: set firmware file name, if not configured, use the default name. -- goodix,config-name: set config file name, if not configured, use the default name. -Example 1: -goodix-berlin@5d { - compatible = "goodix,gt9897"; - reg = <0x5d>; - goodix,reset-gpio = <&msm_gpio 12 0x0>; - goodix,irq-gpio = <&msm_gpio 13 0x2800>; - goodix,irq-flags = <2>; /* 1:trigger rising, 2:trigger falling;*/ - goodix,panel-max-x = <720>; - goodix,panel-max-y = <1280>; - goodix,panel-max-w = <255>; -}; - -Example 2: -goodix-berlin@5d { - compatible = "goodix,gt9966"; - goodix,avdd-name = "avdd"; - avdd-supply = <&pm8916_l15>; - goodix,iovdd-name = "iovdd"; - iovdd-supply = <&pm8916_l16>; - - reg = <0x5d>; - goodix,reset-gpio = <&msm_gpio 12 0x0>; - goodix,irq-gpio = <&msm_gpio 13 0x2800>; - goodix,irq-flags = <2>; /* 1:trigger rising, 2:trigger falling;*/ - goodix,panel-max-x = <720>; - goodix,panel-max-y = <1280>; - goodix,panel-max-w = <255>; - goodix,panel-max-p = <4096>; /* max pressure that pen device supported */ - goodix,pen-enable; /* support active stylus device*/ - - goodix,firmware-name = "goodix_firmware.bin"; - goodix,config-name = "goodix_cfg_group.bin"; -}; -``` - -**goodix-ts-spi-dtsi** - -```dts -devicetree binding for Goodix spi touchdriver - -Required properties: -- compatible: device & driver matching. - * for berlin series touch device, souch as "goodix,gt9897T" - -- spi-max-frequency: set spi transfer speed. -- reg: depend on CS gpio. -- goodix,reset-gpio: reset gpio. -- goodix,irq-gpio: interrupt gpio. -- goodix,irq-flags: irq trigger type config, value should be: - 1 - rising edge, - 2 - falling edge, - 4 - high level, - 5 - low level. -- goodix,panel-max-x: max resolution of x direction. -- goodix,panel-max-y: max resolution of y direction. -- goodix,panel-max-w: panel max width value. -- goodix,panel-max-p: pen device max pressure value. - -Optional properties: -- goodix,avdd-name: set name of regulator. -- avdd-supply: power supply for the touch device. - example of regulator: - goodix,avdd-name = "avdd"; - avdd-supply = <&pm8916_l15>; -- iovdd-supply: power supply for digital io circuit - example of regulator: - goodix,iovdd-name = "iovdd"; - iovdd-supply = <&pm8916_l16>; -- goodix,pen-enable: set this property if you want support stylus. - goodix,pen-enable; -- goodix,firmware-name: set firmware file name, if not configured, use the default name. -- goodix,config-name: set config file name, if not configured, use the default name. -Example 1: -goodix-berlin@0 { - compatible = "goodix,gt9897"; - reg = <0>; - spi-max-frequency = <1000000>; - goodix,reset-gpio = <&msm_gpio 12 0x0>; - goodix,irq-gpio = <&msm_gpio 13 0x2800>; - goodix,irq-flags = <2>; /* 1:trigger rising, 2:trigger falling;*/ - goodix,panel-max-x = <720>; - goodix,panel-max-y = <1280>; - goodix,panel-max-w = <255>; -}; - -Example 2: -goodix-berlin@0 { - compatible = "goodix,gt9966S"; - reg = <0>; - spi-max-frequency = <1000000>; - - goodix,avdd-name = "avdd"; - avdd-supply = <&pm8916_l15>; - goodix,iovdd-name = "iovdd"; - iovdd-supply = <&pm8916_l16>; - - goodix,reset-gpio = <&msm_gpio 12 0x0>; - goodix,irq-gpio = <&msm_gpio 13 0x2800>; - goodix,irq-flags = <2>; /* 1:trigger rising, 2:trigger falling;*/ - goodix,panel-max-x = <720>; - goodix,panel-max-y = <1280>; - goodix,panel-max-w = <255>; - goodix,panel-max-p = <4096>; /* max pressure that pen device supported */ - goodix,pen-enable; /* support active stylus device*/ - - goodix,firmware-name = "goodix_firmware.bin"; - goodix,config-name = "goodix_cfg_group.bin"; -}; -``` - diff --git a/goodix_berlin_driver/Kconfig b/goodix_berlin_driver/Kconfig deleted file mode 100644 index 375b5473a1..0000000000 --- a/goodix_berlin_driver/Kconfig +++ /dev/null @@ -1,20 +0,0 @@ -# -# Goodix touchscreen driver configuration -# -menuconfig TOUCHSCREEN_GOODIX_BRL - tristate "Goodix berlin touchscreen" - help - Say Y here if you have a Goodix berlin series touch controller - to your system. - - If build module, say M. - If unsure, say N. - -if TOUCHSCREEN_GOODIX_BRL - -config TOUCHSCREEN_GOODIX_BRL_SPI - bool "support SPI bus connection" - help - Say Y here if the touchscreen is connected via SPI bus. - -endif diff --git a/goodix_berlin_driver/Makefile b/goodix_berlin_driver/Makefile deleted file mode 100644 index 903a1cccb5..0000000000 --- a/goodix_berlin_driver/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -obj-$(CONFIG_TOUCHSCREEN_GOODIX_BRL) += goodix_core.o -goodix_core-y := \ - goodix_brl_i2c.o \ - goodix_brl_spi.o \ - goodix_ts_core.o \ - goodix_brl_hw.o \ - goodix_cfg_bin.o \ - goodix_ts_utils.o \ - goodix_brl_fwupdate.o \ - goodix_ts_gesture.o \ - goodix_ts_inspect.o \ - goodix_ts_tools.o From baa15707f7661c3f7e8dce1d86318f0c30281405 Mon Sep 17 00:00:00 2001 From: Fei Mao Date: Thu, 30 Dec 2021 15:28:37 +0800 Subject: [PATCH 009/170] touch: goodix: fix cybrog errors fix all cybrog errors for this new driver code. Change-Id: I4f4afaca16c830cfa5d89c8f7bfa62ca0742a496 --- goodix_berlin_driver/goodix_brl_fwupdate.c | 16 +- goodix_berlin_driver/goodix_brl_hw.c | 17 +- goodix_berlin_driver/goodix_brl_i2c.c | 19 +- goodix_berlin_driver/goodix_brl_spi.c | 7 +- goodix_berlin_driver/goodix_cfg_bin.c | 22 +- goodix_berlin_driver/goodix_ts_core.c | 101 +- goodix_berlin_driver/goodix_ts_core.h | 28 +- goodix_berlin_driver/goodix_ts_gesture.c | 6 +- goodix_berlin_driver/goodix_ts_inspect.c | 1050 ++++++++++---------- goodix_berlin_driver/goodix_ts_tools.c | 8 +- 10 files changed, 647 insertions(+), 627 deletions(-) diff --git a/goodix_berlin_driver/goodix_brl_fwupdate.c b/goodix_berlin_driver/goodix_brl_fwupdate.c index 44400bd428..dbd9c63912 100644 --- a/goodix_berlin_driver/goodix_brl_fwupdate.c +++ b/goodix_berlin_driver/goodix_brl_fwupdate.c @@ -367,7 +367,7 @@ static int goodix_parse_firmware(struct firmware_data *fw_data) if (fw_summary->subsys_num > FW_SUBSYS_MAX_NUM) { ts_err("Bad firmware, invalid subsys num: %d", - fw_summary->subsys_num); + fw_summary->subsys_num); r = -EINVAL; goto err_size; } @@ -380,10 +380,10 @@ static int goodix_parse_firmware(struct firmware_data *fw_data) fw_summary->subsys[i].type = firmware->data[info_offset]; fw_summary->subsys[i].size = - le32_to_cpup((__le32 *)&firmware->data[info_offset + 1]); + le32_to_cpup((__le32 *)&firmware->data[info_offset + 1]); fw_summary->subsys[i].flash_addr = - le32_to_cpup((__le32 *)&firmware->data[info_offset + 5]); + le32_to_cpup((__le32 *)&firmware->data[info_offset + 5]); if (fw_offset > firmware->size) { ts_err("Sybsys offset exceed Firmware size"); goto err_size; @@ -1082,20 +1082,20 @@ static ssize_t goodix_sysfs_result_show( switch (fw_ctrl->status) { case UPSTA_PREPARING: - sprintf(str, "preparing"); + scnprintf(str, ARRAY_SIZE(str), "preparing"); break; case UPSTA_UPDATING: - sprintf(str, "updating"); + scnprintf(str, ARRAY_SIZE(str), "updating"); break; case UPSTA_SUCCESS: - sprintf(str, "success"); + scnprintf(str, ARRAY_SIZE(str), "success"); break; case UPSTA_FAILED: - sprintf(str, "failed"); + scnprintf(str, ARRAY_SIZE(str), "failed"); break; case UPSTA_NOTWORK: default: - sprintf(str, "notwork"); + scnprintf(str, ARRAY_SIZE(str), "notwork"); break; } diff --git a/goodix_berlin_driver/goodix_brl_hw.c b/goodix_berlin_driver/goodix_brl_hw.c index 3c5b22d424..3cb79b7ba8 100644 --- a/goodix_berlin_driver/goodix_brl_hw.c +++ b/goodix_berlin_driver/goodix_brl_hw.c @@ -382,11 +382,11 @@ static int brl_send_cmd(struct goodix_ts_core *cd, ts_debug("cmd ack data %*ph", (int)sizeof(cmd_ack), cmd_ack.buf); if (cmd_ack.ack == CMD_ACK_OK) { - msleep(40); // wait for cmd response + msleep(40); // wait for cmd response return 0; } if (cmd_ack.ack == CMD_ACK_BUSY || - cmd_ack.ack == 0x00) { + cmd_ack.ack == 0x00) { usleep_range(1000, 1100); continue; } @@ -568,7 +568,7 @@ static int brl_read_config(struct goodix_ts_core *cd, u8 *cfg, int size) } ret = hw_ops->read(cd, misc->fw_buffer_addr, - cfg_head.buf, sizeof(cfg_head)); + cfg_head.buf, sizeof(cfg_head)); if (ret) { ts_err("failed read config head %d", ret); goto exit; @@ -582,9 +582,9 @@ static int brl_read_config(struct goodix_ts_core *cd, u8 *cfg, int size) cfg_head.cfg_len = le16_to_cpu(cfg_head.cfg_len); if (cfg_head.cfg_len > misc->fw_buffer_max_len || - cfg_head.cfg_len > size) { + cfg_head.cfg_len > size) { ts_err("cfg len exceed buffer size %d > %d", cfg_head.cfg_len, - misc->fw_buffer_max_len); + misc->fw_buffer_max_len); ret = -EINVAL; goto exit; } @@ -917,7 +917,7 @@ static int brl_get_ic_info(struct goodix_ts_core *cd, continue; } if (checksum_cmp((const uint8_t *)afe_data, - length, CHECKSUM_MODE_U8_LE)) { + length, CHECKSUM_MODE_U8_LE)) { ts_info("fw info checksum error!"); usleep_range(5000, 5100); continue; @@ -1140,8 +1140,7 @@ static int goodix_touch_handler(struct goodix_ts_core *cd, pre_pen_num = 0; } else { ts_event->event_type = EVENT_TOUCH; - goodix_parse_finger(touch_data, - buffer, touch_num); + goodix_parse_finger(touch_data, buffer, touch_num); pre_finger_num = touch_num; } } @@ -1370,7 +1369,7 @@ static int brl_get_capacitance_data(struct goodix_ts_core *cd, brl_irq_enbale(cd, false); goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); - /* switch rawdata mode */ + /* switch rawdata mode */ temp_cmd.cmd = GOODIX_CMD_RAWDATA; temp_cmd.len = 4; ret = brl_send_cmd(cd, &temp_cmd); diff --git a/goodix_berlin_driver/goodix_brl_i2c.c b/goodix_berlin_driver/goodix_brl_i2c.c index a0238800ca..3767c95e99 100644 --- a/goodix_berlin_driver/goodix_brl_i2c.c +++ b/goodix_berlin_driver/goodix_brl_i2c.c @@ -70,10 +70,8 @@ static int goodix_i2c_read(struct device *dev, unsigned int reg, msgs[1].len = transfer_length; for (retry = 0; retry < GOODIX_BUS_RETRY_TIMES; retry++) { - if (likely(i2c_transfer(client->adapter, - msgs, 2) == 2)) { - memcpy(&data[pos], msgs[1].buf, - transfer_length); + if (likely(i2c_transfer(client->adapter, msgs, 2) == 2)) { + memcpy(&data[pos], msgs[1].buf, transfer_length); pos += transfer_length; address += transfer_length; break; @@ -83,7 +81,7 @@ static int goodix_i2c_read(struct device *dev, unsigned int reg, } if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) { ts_err("I2c read failed,dev:%02x,reg:%04x,size:%u", - client->addr, reg, len); + client->addr, reg, len); r = -EAGAIN; goto read_exit; } @@ -104,8 +102,8 @@ static int goodix_i2c_write(struct device *dev, unsigned int reg, unsigned char put_buf[128]; int retry, r = 0; struct i2c_msg msg = { - .addr = client->addr, - .flags = !I2C_M_RD, + .addr = client->addr, + .flags = !I2C_M_RD, }; if (likely(len + GOODIX_REG_ADDR_SIZE < sizeof(put_buf))) { @@ -119,9 +117,9 @@ static int goodix_i2c_write(struct device *dev, unsigned int reg, while (pos != len) { if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE - - GOODIX_REG_ADDR_SIZE)) + GOODIX_REG_ADDR_SIZE)) transfer_length = I2C_MAX_TRANSFER_SIZE - - GOODIX_REG_ADDR_SIZE; + GOODIX_REG_ADDR_SIZE; else transfer_length = len - pos; msg.buf[0] = (address >> 24) & 0xFF; @@ -169,8 +167,7 @@ static int goodix_i2c_probe(struct i2c_client *client, int ret = 0; ts_info("goodix i2c probe in"); - ret = i2c_check_functionality(client->adapter, - I2C_FUNC_I2C); + ret = i2c_check_functionality(client->adapter, I2C_FUNC_I2C); if (!ret) return -EIO; diff --git a/goodix_berlin_driver/goodix_brl_spi.c b/goodix_berlin_driver/goodix_brl_spi.c index ea1ef860a8..7dbe405ad1 100644 --- a/goodix_berlin_driver/goodix_brl_spi.c +++ b/goodix_berlin_driver/goodix_brl_spi.c @@ -196,8 +196,8 @@ static int goodix_spi_probe(struct spi_device *spi) ts_info("goodix spi probe in"); /* init spi_device */ - spi->mode = SPI_MODE_0; - spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; ret = spi_setup(spi); if (ret) { @@ -234,7 +234,8 @@ static int goodix_spi_probe(struct spi_device *spi) goodix_pdev->dev.platform_data = &goodix_spi_bus; goodix_pdev->dev.release = goodix_pdev_release; - /* register platform device, then the goodix_ts_core + /* + * register platform device, then the goodix_ts_core * module will probe the touch deivce. */ ret = platform_device_register(goodix_pdev); diff --git a/goodix_berlin_driver/goodix_cfg_bin.c b/goodix_berlin_driver/goodix_cfg_bin.c index fa27255f2c..99670f6e58 100644 --- a/goodix_berlin_driver/goodix_cfg_bin.c +++ b/goodix_berlin_driver/goodix_cfg_bin.c @@ -127,7 +127,7 @@ static int goodix_read_cfg_bin(struct device *dev, const char *cfg_name, if (firmware->size <= 0) { ts_err("request_firmware, cfg_bin length ERROR,len:%zu", - firmware->size); + firmware->size); ret = -EINVAL; goto exit; } @@ -159,13 +159,13 @@ static int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin) } memcpy(&cfg_bin->head, cfg_bin->bin_data, - sizeof(struct goodix_cfg_bin_head)); + sizeof(struct goodix_cfg_bin_head)); cfg_bin->head.bin_len = le32_to_cpu(cfg_bin->head.bin_len); /*check length*/ if (cfg_bin->bin_data_len != cfg_bin->head.bin_len) { ts_err("cfg_bin len check failed,%d != %d", - cfg_bin->head.bin_len, cfg_bin->bin_data_len); + cfg_bin->head.bin_len, cfg_bin->bin_data_len); return -EINVAL; } @@ -176,13 +176,13 @@ static int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin) if (checksum != cfg_bin->head.checksum) { ts_err("cfg_bin checksum check filed 0x%02x != 0x%02x", - cfg_bin->head.checksum, checksum); + cfg_bin->head.checksum, checksum); return -EINVAL; } /*allocate memory for cfg packages*/ cfg_bin->cfg_pkgs = kzalloc(sizeof(struct goodix_cfg_package) * - cfg_bin->head.pkg_num, GFP_KERNEL); + cfg_bin->head.pkg_num, GFP_KERNEL); if (!cfg_bin->cfg_pkgs) return -ENOMEM; @@ -218,10 +218,10 @@ static int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin) } /*get cfg pkg head*/ memcpy(&cfg_bin->cfg_pkgs[i].cnst_info, - &cfg_bin->bin_data[offset1], TS_PKG_CONST_INFO_LEN); + &cfg_bin->bin_data[offset1], TS_PKG_CONST_INFO_LEN); memcpy(&cfg_bin->cfg_pkgs[i].reg_info, - &cfg_bin->bin_data[offset1 + TS_PKG_CONST_INFO_LEN], - TS_PKG_REG_INFO_LEN); + &cfg_bin->bin_data[offset1 + TS_PKG_CONST_INFO_LEN], + TS_PKG_REG_INFO_LEN); /*get configuration data*/ cfg_bin->cfg_pkgs[i].cfg = @@ -241,7 +241,7 @@ exit: } static int goodix_get_reg_and_cfg(struct goodix_ts_core *cd, u8 sensor_id, - struct goodix_cfg_bin *cfg_bin) + struct goodix_cfg_bin *cfg_bin) { int i; u8 cfg_type; @@ -259,7 +259,7 @@ static int goodix_get_reg_and_cfg(struct goodix_ts_core *cd, u8 sensor_id, cfg_pkg = &cfg_bin->cfg_pkgs[i]; if (sensor_id != cfg_pkg->cnst_info.sensor_id) { ts_info("pkg:%d, sensor id contrast FAILED, bin %d != %d", - i, cfg_pkg->cnst_info.sensor_id, sensor_id); + i, cfg_pkg->cnst_info.sensor_id, sensor_id); continue; } cfg_type = cfg_pkg->cnst_info.cfg_type; @@ -270,7 +270,7 @@ static int goodix_get_reg_and_cfg(struct goodix_ts_core *cd, u8 sensor_id, } cfg_len = cfg_pkg->pkg_len - TS_PKG_CONST_INFO_LEN - - TS_PKG_REG_INFO_LEN; + TS_PKG_REG_INFO_LEN; if (cfg_len > GOODIX_CFG_MAX_SIZE) { ts_err("config len exceed limit %d > %d", cfg_len, GOODIX_CFG_MAX_SIZE); diff --git a/goodix_berlin_driver/goodix_ts_core.c b/goodix_berlin_driver/goodix_ts_core.c index e241eca2da..8b64e13521 100644 --- a/goodix_berlin_driver/goodix_ts_core.c +++ b/goodix_berlin_driver/goodix_ts_core.c @@ -47,7 +47,7 @@ static int __do_register_ext_module(struct goodix_ext_module *module) /* prority level *must* be set */ if (module->priority == EXTMOD_PRIO_RESERVED) { ts_err("Priority of module [%s] needs to be set", - module->name); + module->name); return -EINVAL; } mutex_lock(&goodix_modules.mutex); @@ -77,7 +77,7 @@ static int __do_register_ext_module(struct goodix_ext_module *module) if (module->funcs->init(goodix_modules.core_data, module) < 0) { ts_err("Module [%s] init error", - module->name ? module->name : " "); + module->name ? module->name : " "); mutex_unlock(&goodix_modules.mutex); return -EFAULT; } @@ -439,8 +439,8 @@ static int goodix_ts_convert_0x_data(const u8 *buf, int buf_size, /* send config */ static ssize_t goodix_ts_send_cfg_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) + struct device_attribute *attr, + const char *buf, size_t count) { struct goodix_ts_core *core_data = dev_get_drvdata(dev); struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; @@ -458,9 +458,8 @@ static ssize_t goodix_ts_send_cfg_store(struct device *dev, ts_err("cfg file [%s] not available,errno:%d", GOODIX_DEFAULT_CFG_NAME, ret); goto exit; - } else { + } else ts_info("cfg file [%s] is ready", GOODIX_DEFAULT_CFG_NAME); - } config = kzalloc(sizeof(*config), GFP_KERNEL); if (!config) @@ -540,11 +539,11 @@ static ssize_t goodix_ts_reg_rw_store(struct device *dev, goto err_out; } - if (buf[0] == 'r') { + if (buf[0] == 'r') rw_flag = 1; - } else if (buf[0] == 'w') { + else if (buf[0] == 'w') rw_flag = 2; - } else { + else { ts_err("string must start with 'r/w'"); goto err_out; } @@ -634,15 +633,13 @@ static ssize_t goodix_ts_irq_info_show(struct device *dev, offset += r; r = snprintf(&buf[offset], PAGE_SIZE - offset, "state:%s\n", - atomic_read(&core_data->irq_enabled) ? - "enabled" : "disabled"); + atomic_read(&core_data->irq_enabled) ? "enabled" : "disabled"); if (r < 0) return -EINVAL; desc = irq_to_desc(core_data->irq); offset += r; - r = snprintf(&buf[offset], PAGE_SIZE - offset, "disable-depth:%d\n", - desc->depth); + r = snprintf(&buf[offset], PAGE_SIZE - offset, "disable-depth:%d\n", desc->depth); if (r < 0) return -EINVAL; @@ -690,8 +687,7 @@ static ssize_t goodix_ts_esd_info_show(struct device *dev, int r = 0; r = snprintf(buf, PAGE_SIZE, "state:%s\n", - atomic_read(&ts_esd->esd_on) ? - "enabled" : "disabled"); + atomic_read(&ts_esd->esd_on) ? "enabled" : "disabled"); return r; } @@ -713,14 +709,13 @@ static ssize_t goodix_ts_esd_info_store(struct device *dev, /* debug level show */ static ssize_t goodix_ts_debug_log_show(struct device *dev, - struct device_attribute *attr, - char *buf) + struct device_attribute *attr, + char *buf) { int r = 0; r = snprintf(buf, PAGE_SIZE, "state:%s\n", - debug_log_flag ? - "enabled" : "disabled"); + debug_log_flag ? "enabled" : "disabled"); return r; } @@ -929,28 +924,28 @@ static int goodix_parse_dt_resolution(struct device_node *node, int ret; ret = of_property_read_u32(node, "goodix,panel-max-x", - &board_data->panel_max_x); + &board_data->panel_max_x); if (ret) { ts_err("failed get panel-max-x"); return ret; } ret = of_property_read_u32(node, "goodix,panel-max-y", - &board_data->panel_max_y); + &board_data->panel_max_y); if (ret) { ts_err("failed get panel-max-y"); return ret; } ret = of_property_read_u32(node, "goodix,panel-max-w", - &board_data->panel_max_w); + &board_data->panel_max_w); if (ret) { ts_err("failed get panel-max-w"); return ret; } ret = of_property_read_u32(node, "goodix,panel-max-p", - &board_data->panel_max_p); + &board_data->panel_max_p); if (ret) { ts_err("failed get panel-max-p, use default"); board_data->panel_max_p = GOODIX_PEN_MAX_PRESSURE; @@ -1022,7 +1017,7 @@ static int goodix_parse_dt(struct device_node *node, if (!r) { ts_info("avdd name from dt: %s", name_tmp); if (strlen(name_tmp) < sizeof(board_data->avdd_name)) - strncpy(board_data->avdd_name, + strlcpy(board_data->avdd_name, name_tmp, sizeof(board_data->avdd_name)); else ts_info("invalied avdd name length: %ld > %ld", @@ -1035,7 +1030,7 @@ static int goodix_parse_dt(struct device_node *node, if (!r) { ts_info("iovdd name from dt: %s", name_tmp); if (strlen(name_tmp) < sizeof(board_data->iovdd_name)) - strncpy(board_data->iovdd_name, + strlcpy(board_data->iovdd_name, name_tmp, sizeof(board_data->iovdd_name)); else ts_info("invalied iovdd name length: %ld > %ld", @@ -1047,12 +1042,12 @@ static int goodix_parse_dt(struct device_node *node, r = of_property_read_string(node, "goodix,firmware-name", &name_tmp); if (!r) { ts_info("firmware name from dt: %s", name_tmp); - strncpy(board_data->fw_name, + strlcpy(board_data->fw_name, name_tmp, sizeof(board_data->fw_name)); } else { ts_info("can't find firmware name, use default: %s", TS_DEFAULT_FIRMWARE); - strncpy(board_data->fw_name, + strlcpy(board_data->fw_name, TS_DEFAULT_FIRMWARE, sizeof(board_data->fw_name)); } @@ -1061,12 +1056,12 @@ static int goodix_parse_dt(struct device_node *node, r = of_property_read_string(node, "goodix,config-name", &name_tmp); if (!r) { ts_info("config name from dt: %s", name_tmp); - strncpy(board_data->cfg_bin_name, name_tmp, + strlcpy(board_data->cfg_bin_name, name_tmp, sizeof(board_data->cfg_bin_name)); } else { ts_info("can't find config name, use default: %s", TS_DEFAULT_CFG_BIN); - strncpy(board_data->cfg_bin_name, + strlcpy(board_data->cfg_bin_name, TS_DEFAULT_CFG_BIN, sizeof(board_data->cfg_bin_name)); } @@ -1175,13 +1170,13 @@ static int goodix_ts_request_handle(struct goodix_ts_core *cd, ret = hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); else ts_info("can not handle request type 0x%x", - ts_event->request_code); + ts_event->request_code); if (ret) ts_err("failed handle request 0x%x", - ts_event->request_code); + ts_event->request_code); else ts_info("success handle ic request 0x%x", - ts_event->request_code); + ts_event->request_code); return ret; } @@ -1257,11 +1252,11 @@ static int goodix_ts_irq_setup(struct goodix_ts_core *core_data) ts_info("IRQ:%u,flags:%d", core_data->irq, (int)ts_bdata->irq_flags); ret = devm_request_threaded_irq(&core_data->pdev->dev, - core_data->irq, NULL, - goodix_ts_threadirq_func, - ts_bdata->irq_flags | IRQF_ONESHOT, - GOODIX_CORE_DRIVER_NAME, - core_data); + core_data->irq, NULL, + goodix_ts_threadirq_func, + ts_bdata->irq_flags | IRQF_ONESHOT, + GOODIX_CORE_DRIVER_NAME, + core_data); if (ret < 0) ts_err("Failed to requeset threaded irq:%d", ret); else @@ -1283,17 +1278,15 @@ static int goodix_ts_power_init(struct goodix_ts_core *core_data) ts_info("Power init"); if (strlen(ts_bdata->avdd_name)) { - core_data->avdd = devm_regulator_get(dev, - ts_bdata->avdd_name); + core_data->avdd = devm_regulator_get(dev, ts_bdata->avdd_name); if (IS_ERR_OR_NULL(core_data->avdd)) { ret = PTR_ERR(core_data->avdd); ts_err("Failed to get regulator avdd:%d", ret); core_data->avdd = NULL; return ret; } - } else { + } else ts_info("Avdd name is NULL"); - } if (strlen(ts_bdata->iovdd_name)) { core_data->iovdd = devm_regulator_get(dev, @@ -1303,9 +1296,8 @@ static int goodix_ts_power_init(struct goodix_ts_core *core_data) ts_err("Failed to get regulator iovdd:%d", ret); core_data->iovdd = NULL; } - } else { + } else ts_info("iovdd name is NULL"); - } return ret; } @@ -1505,7 +1497,7 @@ static int goodix_ts_pen_dev_config(struct goodix_ts_core *core_data) input_set_abs_params(pen_dev, ABS_X, 0, ts_bdata->panel_max_x, 0, 0); input_set_abs_params(pen_dev, ABS_Y, 0, ts_bdata->panel_max_y, 0, 0); input_set_abs_params(pen_dev, ABS_PRESSURE, 0, - ts_bdata->panel_max_p, 0, 0); + ts_bdata->panel_max_p, 0, 0); input_set_abs_params(pen_dev, ABS_TILT_X, -GOODIX_PEN_MAX_TILT, GOODIX_PEN_MAX_TILT, 0, 0); input_set_abs_params(pen_dev, ABS_TILT_Y, @@ -1723,8 +1715,7 @@ static int goodix_ts_suspend(struct goodix_ts_core *core_data) if (!ext_module->funcs->before_suspend) continue; - ret = ext_module->funcs->before_suspend(core_data, - ext_module); + ret = ext_module->funcs->before_suspend(core_data, ext_module); if (ret == EVT_CANCEL_SUSPEND) { mutex_unlock(&goodix_modules.mutex); ts_info("Canceled by module:%s", @@ -1747,8 +1738,7 @@ static int goodix_ts_suspend(struct goodix_ts_core *core_data) if (!ext_module->funcs->after_suspend) continue; - ret = ext_module->funcs->after_suspend(core_data, - ext_module); + ret = ext_module->funcs->after_suspend(core_data, ext_module); if (ret == EVT_CANCEL_SUSPEND) { mutex_unlock(&goodix_modules.mutex); ts_info("Canceled by module:%s", @@ -1786,16 +1776,14 @@ static int goodix_ts_resume(struct goodix_ts_core *core_data) mutex_lock(&goodix_modules.mutex); if (!list_empty(&goodix_modules.head)) { list_for_each_entry_safe(ext_module, next, - &goodix_modules.head, list) { + &goodix_modules.head, list) { if (!ext_module->funcs->before_resume) continue; - ret = ext_module->funcs->before_resume(core_data, - ext_module); + ret = ext_module->funcs->before_resume(core_data, ext_module); if (ret == EVT_CANCEL_RESUME) { mutex_unlock(&goodix_modules.mutex); - ts_info("Canceled by module:%s", - ext_module->name); + ts_info("Canceled by module:%s", ext_module->name); goto out; } } @@ -1809,12 +1797,11 @@ static int goodix_ts_resume(struct goodix_ts_core *core_data) mutex_lock(&goodix_modules.mutex); if (!list_empty(&goodix_modules.head)) { list_for_each_entry_safe(ext_module, next, - &goodix_modules.head, list) { + &goodix_modules.head, list) { if (!ext_module->funcs->after_resume) continue; - ret = ext_module->funcs->after_resume(core_data, - ext_module); + ret = ext_module->funcs->after_resume(core_data, ext_module); if (ret == EVT_CANCEL_RESUME) { mutex_unlock(&goodix_modules.mutex); ts_info("Canceled by module:%s", @@ -2114,7 +2101,7 @@ static int goodix_start_later_init(struct goodix_ts_core *ts_core) ts_core, "goodix_init_thread"); if (IS_ERR_OR_NULL(init_thrd)) { ts_err("Failed to create update thread:%ld", - PTR_ERR(init_thrd)); + PTR_ERR(init_thrd)); return -EFAULT; } return 0; diff --git a/goodix_berlin_driver/goodix_ts_core.h b/goodix_berlin_driver/goodix_ts_core.h index 0cfb5edfe7..b723dc5279 100644 --- a/goodix_berlin_driver/goodix_ts_core.h +++ b/goodix_berlin_driver/goodix_ts_core.h @@ -132,11 +132,11 @@ struct frame_head { }; struct goodix_fw_version { - u8 rom_pid[6]; /* rom PID */ - u8 rom_vid[3]; /* Mask VID */ + u8 rom_pid[6]; /* rom PID */ + u8 rom_vid[3]; /* Mask VID */ u8 rom_vid_reserved; - u8 patch_pid[8]; /* Patch PID */ - u8 patch_vid[4]; /* Patch VID */ + u8 patch_pid[8]; /* Patch PID */ + u8 patch_vid[4]; /* Patch VID */ u8 patch_vid_reserved; u8 sensor_id; u8 reserved[2]; @@ -295,13 +295,13 @@ struct goodix_ts_board_data { enum goodix_fw_update_mode { UPDATE_MODE_DEFAULT = 0, - UPDATE_MODE_FORCE = (1<<0), /* force update mode */ - UPDATE_MODE_BLOCK = (1<<1), /* update in block mode */ - UPDATE_MODE_FLASH_CFG = (1<<2), /* reflash config */ - UPDATE_MODE_SRC_SYSFS = (1<<4), /* firmware file from sysfs */ - UPDATE_MODE_SRC_HEAD = (1<<5), /* firmware file from head file */ - UPDATE_MODE_SRC_REQUEST = (1<<6), /* request firmware */ - UPDATE_MODE_SRC_ARGS = (1<<7), /* firmware data from function args */ + UPDATE_MODE_FORCE = (1 << 0), /* force update mode */ + UPDATE_MODE_BLOCK = (1 << 1), /* update in block mode */ + UPDATE_MODE_FLASH_CFG = (1 << 2), /* reflash config */ + UPDATE_MODE_SRC_SYSFS = (1 << 4), /* firmware file from sysfs */ + UPDATE_MODE_SRC_HEAD = (1 << 5), /* firmware file from head file */ + UPDATE_MODE_SRC_REQUEST = (1 << 6), /* request firmware */ + UPDATE_MODE_SRC_ARGS = (1 << 7), /* firmware data from function args */ }; #define MAX_CMD_DATA_LEN 10 @@ -409,7 +409,7 @@ struct goodix_bus_interface { int ic_type; struct device *dev; int (*read)(struct device *dev, unsigned int addr, - unsigned char *data, unsigned int len); + unsigned char *data, unsigned int len); int (*write)(struct device *dev, unsigned int addr, unsigned char *data, unsigned int len); }; @@ -582,8 +582,8 @@ struct goodix_ext_attribute { /* external attrs helper macro */ #define __EXTMOD_ATTR(_name, _mode, _show, _store) { \ .attr = {.name = __stringify(_name), .mode = _mode }, \ - .show = _show, \ - .store = _store, \ + .show = _show, \ + .store = _store, \ } /* external attrs helper macro, used to define external attrs */ diff --git a/goodix_berlin_driver/goodix_ts_gesture.c b/goodix_berlin_driver/goodix_ts_gesture.c index 07fd530c1b..f177a1db5d 100644 --- a/goodix_berlin_driver/goodix_ts_gesture.c +++ b/goodix_berlin_driver/goodix_ts_gesture.c @@ -65,7 +65,7 @@ static ssize_t gsx_double_type_show(struct goodix_ext_module *module, return 0; } - return sprintf(buf, "%s\n", + return scnprintf(buf, PAGE_SIZE, "%s\n", (type & GESTURE_DOUBLE_TAP) ? "enable" : "disable"); } @@ -108,7 +108,7 @@ static ssize_t gsx_single_type_show(struct goodix_ext_module *module, return 0; } - return sprintf(buf, "%s\n", + return scnprintf(buf, PAGE_SIZE, "%s\n", (type & GESTURE_SINGLE_TAP) ? "enable" : "disable"); } @@ -151,7 +151,7 @@ static ssize_t gsx_fod_type_show(struct goodix_ext_module *module, return 0; } - return sprintf(buf, "%s\n", + return scnprintf(buf, PAGE_SIZE, "%s\n", (type & GESTURE_FOD_PRESS) ? "enable" : "disable"); } diff --git a/goodix_berlin_driver/goodix_ts_inspect.c b/goodix_berlin_driver/goodix_ts_inspect.c index ccc69954d6..33d0a9b59c 100644 --- a/goodix_berlin_driver/goodix_ts_inspect.c +++ b/goodix_berlin_driver/goodix_ts_inspect.c @@ -1,19 +1,19 @@ - /* - * Goodix Touchscreen Driver - * Copyright (C) 2020 - 2021 Goodix, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be a reference - * to you, when you are integrating the GOODiX's CTP IC into your system, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - */ +/* + * Goodix Touchscreen Driver + * Copyright (C) 2020 - 2021 Goodix, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be a reference + * to you, when you are integrating the GOODiX's CTP IC into your system, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ #include "goodix_ts_core.h" #include @@ -117,13 +117,13 @@ static bool module_initialized; /* berlin A drv-sen map */ static u8 brl_a_drv_map[] = { - 42, 43, 44, 45, 46, 47, 48, 49, - 50, 51, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, 62 + 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62 }; static u8 brl_a_sen_map[] = { - 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, @@ -162,7 +162,7 @@ static u8 brl_d_drv_map[] = { }; static u8 brl_d_sen_map[] = { - 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, @@ -170,7 +170,7 @@ static u8 brl_d_sen_map[] = { }; typedef struct __attribute__((packed)) { - u8 result; + u8 result; u8 drv_drv_num; u8 sen_sen_num; u8 drv_sen_num; @@ -346,69 +346,67 @@ static int cal_cha_to_gnd_res(struct goodix_ts_test *ts_test, int v) return 145000 / v - 15; } -static int ts_test_reset(struct goodix_ts_test *ts_test, - u32 delay_ms) +static int ts_test_reset(struct goodix_ts_test *ts_test, u32 delay_ms) { - return ts_test->ts->hw_ops->reset(ts_test->ts, delay_ms); + return ts_test->ts->hw_ops->reset(ts_test->ts, delay_ms); } static int ts_test_read(struct goodix_ts_test *ts_test, - u32 addr, u8 *data, u32 len) + u32 addr, u8 *data, u32 len) { - return ts_test->ts->hw_ops->read(ts_test->ts, addr, data, len); + return ts_test->ts->hw_ops->read(ts_test->ts, addr, data, len); } static int ts_test_write(struct goodix_ts_test *ts_test, - u32 addr, u8 *data, u32 len) + u32 addr, u8 *data, u32 len) { - return ts_test->ts->hw_ops->write(ts_test->ts, addr, data, len); + return ts_test->ts->hw_ops->write(ts_test->ts, addr, data, len); } static int ts_test_send_cmd(struct goodix_ts_test *ts_test, - struct goodix_ts_cmd *cmd) + struct goodix_ts_cmd *cmd) { - return ts_test->ts->hw_ops->send_cmd(ts_test->ts, cmd); + return ts_test->ts->hw_ops->send_cmd(ts_test->ts, cmd); } -static int ts_test_irq_enable(struct goodix_ts_test *ts_test, - bool flag) +static int ts_test_irq_enable(struct goodix_ts_test *ts_test, bool flag) { return ts_test->ts->hw_ops->irq_enable(ts_test->ts, flag); } -static int ts_test_send_config(struct goodix_ts_test *ts_test, - int type) +static int ts_test_send_config(struct goodix_ts_test *ts_test, int type) { - struct goodix_ic_config *cfg; + struct goodix_ic_config *cfg; if (type >= GOODIX_MAX_CONFIG_GROUP) { ts_err("unsupproted config type %d", type); return -EINVAL; } - cfg = ts_test->ts->ic_configs[type]; + cfg = ts_test->ts->ic_configs[type]; if (!cfg || cfg->len <= 0) { ts_err("no valid normal config found"); return -EINVAL; } - - return ts_test->ts->hw_ops->send_config(ts_test->ts, cfg->data, cfg->len); + + return ts_test->ts->hw_ops->send_config(ts_test->ts, cfg->data, cfg->len); } static int ts_test_read_version(struct goodix_ts_test *ts_test, - struct goodix_fw_version *version) + struct goodix_fw_version *version) { - return ts_test->ts->hw_ops->read_version(ts_test->ts, version); + return ts_test->ts->hw_ops->read_version(ts_test->ts, version); } static void goto_next_line(char **ptr) { - do { - *ptr = *ptr + 1; - } while (**ptr != '\n' && **ptr != '\0'); - if (**ptr == '\0') { - return; - } - *ptr = *ptr + 1; + do { + *ptr = *ptr + 1; + } while (**ptr != '\n' && **ptr != '\0'); + + if (**ptr == '\0') + return; + + *ptr = *ptr + 1; } static void copy_this_line(char *dest, char *src) @@ -433,17 +431,15 @@ static int getrid_space(s8* data, s32 len) u32 count = 0; buf = (char*)kzalloc(len + 5, GFP_KERNEL); - if (buf == NULL){ + if (!buf) { ts_err("get space kzalloc error"); return -ESRCH; } - for (i = 0; i < len; i++) - { + for (i = 0; i < len; i++) { if (data[i] == ' ' || data[i] == '\r' || data[i] == '\n') - { continue; - } + buf[count++] = data[i]; } @@ -456,35 +452,35 @@ static int getrid_space(s8* data, s32 len) } static int parse_valid_data(char *buf_start, loff_t buf_size, - char *ptr, s32 *data, s32 rows) + char *ptr, s32 *data, s32 rows) { - int i = 0; - int j = 0; - char *token = NULL; - char *tok_ptr = NULL; - char *row_data = NULL; + int i = 0; + int j = 0; + char *token = NULL; + char *tok_ptr = NULL; + char *row_data = NULL; long temp_val; - if (!ptr) { - ts_err("ptr is NULL"); - return -EINVAL; - } + if (!ptr) { + ts_err("ptr is NULL"); + return -EINVAL; + } if (!data) { ts_err("data is NULL"); return -EINVAL; } - row_data = (char *)kzalloc(MAX_LINE_LEN, GFP_KERNEL); - if (!row_data) { - ts_err("alloc bytes %d failed.", MAX_LINE_LEN); - return -ENOMEM; - } + row_data = (char *)kzalloc(MAX_LINE_LEN, GFP_KERNEL); + if (!row_data) { + ts_err("alloc bytes %d failed.", MAX_LINE_LEN); + return -ENOMEM; + } - for (i = 0; i < rows; i++) { - memset(row_data, 0, MAX_LINE_LEN); - copy_this_line(row_data, ptr); - getrid_space(row_data, strlen(row_data)); - tok_ptr = row_data; + for (i = 0; i < rows; i++) { + memset(row_data, 0, MAX_LINE_LEN); + copy_this_line(row_data, ptr); + getrid_space(row_data, strlen(row_data)); + tok_ptr = row_data; while ((token = strsep(&tok_ptr,","))) { if (strlen(token) == 0) continue; @@ -496,55 +492,54 @@ static int parse_valid_data(char *buf_start, loff_t buf_size, } if (i == rows - 1) break; - goto_next_line(&ptr); //next row - if(!ptr || (0 == strlen(ptr)) || (ptr >= (buf_start + buf_size))) { + goto_next_line(&ptr); //next row + if(!ptr || (strlen(ptr) == 0) || (ptr >= (buf_start + buf_size))) { ts_info("invalid ptr, return"); kfree(row_data); row_data = NULL; return -EPERM; - } - } - kfree(row_data); - return j; + } + } + kfree(row_data); + return j; } static int parse_csvfile(char *buf, size_t size, char *target_name, - s32 *data, s32 rows, s32 col) + s32 *data, s32 rows, s32 col) { - int ret = 0; - char *ptr = NULL; - int read_ret; + int ret = 0; + char *ptr = NULL; + int read_ret; - read_ret = size; - if (read_ret > 0) { - ptr = buf; - ptr = strstr(ptr, target_name); - if (!ptr) { + read_ret = size; + if (read_ret > 0) { + ptr = buf; + ptr = strstr(ptr, target_name); + if (!ptr) { ts_info("load %s failed 1, maybe not this item", target_name); - return -EINTR; - } + return -EINTR; + } - goto_next_line(&ptr); - if (!ptr || (0 == strlen(ptr))) { - ts_err("load %s failed 2!", target_name); - return -EIO; - } - - if (data) { - ret = parse_valid_data(buf, size, ptr, data, rows); - } else { - ts_err("load %s failed 3!", target_name); - return -EINTR; - } - } else { - ts_err("ret=%d, read_ret=%d", ret, read_ret); - ret = -ENXIO; - } + goto_next_line(&ptr); + if (!ptr || (strlen(ptr) == 0 )) { + ts_err("load %s failed 2!", target_name); + return -EIO; + } - return ret; + if (data) { + ret = parse_valid_data(buf, size, ptr, data, rows); + } else { + ts_err("load %s failed 3!", target_name); + return -EINTR; + } + } else { + ts_err("ret=%d, read_ret=%d", ret, read_ret); + ret = -ENXIO; + } + + return ret; } - static void goodix_init_params(struct goodix_ts_test *ts_test) { struct goodix_ts_core *ts = ts_test->ts; @@ -568,40 +563,40 @@ static void goodix_init_params(struct goodix_ts_test *ts_test) static int goodix_init_testlimits(struct goodix_ts_test *ts_test) { - int ret; + int ret; int i; - u32 data_buf[10] = {0}; - char *temp_buf = NULL; - struct ts_test_params *test_params = &ts_test->test_params; - struct goodix_ts_core *ts_core = ts_test->ts; - const struct firmware *firmware = NULL; - struct device *dev = &ts_core->pdev->dev; - char limit_file[100] = {0}; - u32 tx = test_params->drv_num; - u32 rx = test_params->sen_num; + u32 data_buf[10] = {0}; + char *temp_buf = NULL; + struct ts_test_params *test_params = &ts_test->test_params; + struct goodix_ts_core *ts_core = ts_test->ts; + const struct firmware *firmware = NULL; + struct device *dev = &ts_core->pdev->dev; + char limit_file[100] = {0}; + u32 tx = test_params->drv_num; + u32 rx = test_params->sen_num; - sprintf(limit_file, "%s_test_limits_%d.csv", GOODIX_TEST_FILE_NAME, - ts_core->fw_version.sensor_id); - ts_info("limit_file_name:%s", limit_file); + scnprintf(limit_file, ARRAY_SIZE(limit_file), "%s_test_limits_%d.csv", + GOODIX_TEST_FILE_NAME, ts_core->fw_version.sensor_id); + ts_info("limit_file_name:%s", limit_file); - ret = request_firmware(&firmware, limit_file, dev); - if (ret < 0) { - ts_err("limits file [%s] not available", limit_file); - return -EINVAL; - } - if (firmware->size <= 0) { - ts_err("request_firmware, limits param length error,len:%zu", - firmware->size); - ret = -EINVAL; - goto exit_free; - } - temp_buf = kzalloc(firmware->size + 1, GFP_KERNEL); - if (!temp_buf) { - ts_err("kzalloc bytes failed."); - ret = -ENOMEM; - goto exit_free; - } - memcpy(temp_buf, firmware->data, firmware->size); + ret = request_firmware(&firmware, limit_file, dev); + if (ret < 0) { + ts_err("limits file [%s] not available", limit_file); + return -EINVAL; + } + if (firmware->size <= 0) { + ts_err("request_firmware, limits param length error,len:%zu", + firmware->size); + ret = -EINVAL; + goto exit_free; + } + temp_buf = kzalloc(firmware->size + 1, GFP_KERNEL); + if (!temp_buf) { + ts_err("kzalloc bytes failed."); + ret = -ENOMEM; + goto exit_free; + } + memcpy(temp_buf, firmware->data, firmware->size); /* obtain config data */ ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_TEST_CONFIG, @@ -615,24 +610,24 @@ static int goodix_init_testlimits(struct goodix_ts_test *ts_test) ts_test->test_config.len = ret; } - /* obtain mutual_raw min */ - ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_RAW_MIN, - test_params->min_limits, rx, tx); - if (ret < 0) { - ts_err("Failed get min_limits"); - goto exit_free; - } else { - ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_RAW_MIN); - } - /* obtain mutual_raw max */ - ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_RAW_MAX, - test_params->max_limits, rx, tx); - if (ret < 0) { - ts_err("Failed get max_limits"); - goto exit_free; - } else { - ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_RAW_MAX); - } + /* obtain mutual_raw min */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_RAW_MIN, + test_params->min_limits, rx, tx); + if (ret < 0) { + ts_err("Failed get min_limits"); + goto exit_free; + } else + ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_RAW_MIN); + + /* obtain mutual_raw max */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_RAW_MAX, + test_params->max_limits, rx, tx); + if (ret < 0) { + ts_err("Failed get max_limits"); + goto exit_free; + } else + ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_RAW_MAX); + /* obtain delta limit */ ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_RAW_DELTA, test_params->deviation_limits, rx, tx); @@ -643,93 +638,93 @@ static int goodix_init_testlimits(struct goodix_ts_test *ts_test) ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_RAW_DELTA); } - /* obtain self_raw min */ - ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_SELFRAW_MIN, - test_params->self_min_limits, 1, tx + rx); + /* obtain self_raw min */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_SELFRAW_MIN, + test_params->self_min_limits, 1, tx + rx); /* obtain self_raw max */ ret |= parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_SELFRAW_MAX, test_params->self_max_limits, 1, tx + rx); - if (ret < 0) { - ts_info("Can't find self_min_max_limits, ship this item"); + if (ret < 0) { + ts_info("Can't find self_min_max_limits, ship this item"); ret = 0; - test_params->test_items[GTP_SELFCAP_TEST] = false; - } else { - ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_SELFRAW_MIN); + test_params->test_items[GTP_SELFCAP_TEST] = false; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_SELFRAW_MIN); ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_SELFRAW_MAX); test_params->test_items[GTP_SELFCAP_TEST] = true; - } + } - /* obtain noise_threshold */ - ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_NOISE_LIMIT, - &test_params->noise_threshold, 1, 1); - if (ret < 0) { - ts_info("Can't find noise_threshold, skip this item"); + /* obtain noise_threshold */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_NOISE_LIMIT, + &test_params->noise_threshold, 1, 1); + if (ret < 0) { + ts_info("Can't find noise_threshold, skip this item"); ret = 0; - test_params->test_items[GTP_NOISE_TEST] = false; - } else { - ts_info("parse_csvfile %s OK", CSV_TP_NOISE_LIMIT); + test_params->test_items[GTP_NOISE_TEST] = false; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_NOISE_LIMIT); test_params->test_items[GTP_NOISE_TEST] = true; - } + } - /* obtain self_noise_threshold */ - ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SELFNOISE_LIMIT, - &test_params->self_noise_threshold, 1, 1); - if (ret < 0) { - ts_info("Can't find self_noise_threshold, skip this item"); + /* obtain self_noise_threshold */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SELFNOISE_LIMIT, + &test_params->self_noise_threshold, 1, 1); + if (ret < 0) { + ts_info("Can't find self_noise_threshold, skip this item"); ret = 0; - test_params->test_items[GTP_SELFNOISE_TEST] = false; - } else { - ts_info("parse_csvfile %s OK", CSV_TP_SELFNOISE_LIMIT); + test_params->test_items[GTP_SELFNOISE_TEST] = false; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_SELFNOISE_LIMIT); test_params->test_items[GTP_SELFNOISE_TEST] = true; - } + } /* obtain short_params */ - ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SHORT_THRESHOLD, - (s32 *)data_buf, 1, 7); - if (ret < 0) { + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SHORT_THRESHOLD, + (s32 *)data_buf, 1, 7); + if (ret < 0) { ts_info("Can't find short shortciurt_threshold, skip this item"); ret = 0; test_params->test_items[GTP_SHORT_TEST] = false; - } else { - ts_info("parse_csvfile %s OK", CSV_TP_SHORT_THRESHOLD); + } else { + ts_info("parse_csvfile %s OK", CSV_TP_SHORT_THRESHOLD); test_params->test_items[GTP_SHORT_TEST] = true; - test_params->short_threshold = data_buf[0]; - test_params->r_drv_drv_threshold = data_buf[1]; - test_params->r_drv_sen_threshold = data_buf[2]; - test_params->r_sen_sen_threshold = data_buf[3]; - test_params->r_drv_gnd_threshold = data_buf[4]; - test_params->r_sen_gnd_threshold = data_buf[5]; - test_params->avdd_value = data_buf[6]; - } + test_params->short_threshold = data_buf[0]; + test_params->r_drv_drv_threshold = data_buf[1]; + test_params->r_drv_sen_threshold = data_buf[2]; + test_params->r_sen_sen_threshold = data_buf[3]; + test_params->r_drv_gnd_threshold = data_buf[4]; + test_params->r_sen_gnd_threshold = data_buf[5]; + test_params->avdd_value = data_buf[6]; + } exit_free: - kfree(temp_buf); - if (firmware) - release_firmware(firmware); - return ret; + kfree(temp_buf); + if (firmware) + release_firmware(firmware); + return ret; } static int goodix_tptest_prepare(struct goodix_ts_test *ts_test) { - int ret; + int ret; struct goodix_ic_config *cfg = &ts_test->test_config; ts_info("TP test prepare IN"); - goodix_init_params(ts_test); - /* parse test limits from csv */ - ret = goodix_init_testlimits(ts_test); - if (ret < 0) { - ts_err("Failed to init testlimits from csv."); - return ret; - } + goodix_init_params(ts_test); + /* parse test limits from csv */ + ret = goodix_init_testlimits(ts_test); + if (ret < 0) { + ts_err("Failed to init testlimits from csv."); + return ret; + } - /* disable irq */ - ts_test_irq_enable(ts_test, false); - /* close esd */ - goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); + /* disable irq */ + ts_test_irq_enable(ts_test, false); + /* close esd */ + goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); - /* send test config if exist */ + /* send test config if exist */ if (cfg->len > 0) { ts_info("Test config exists and send it"); ret = ts_test->ts->hw_ops->send_config(ts_test->ts, cfg->data, cfg->len); @@ -741,24 +736,24 @@ static int goodix_tptest_prepare(struct goodix_ts_test *ts_test) } } - return 0; + return 0; } static void goodix_tptest_finish(struct goodix_ts_test *ts_test) { ts_info("TP test finish IN"); - /* reset chip */ - ts_test_reset(ts_test, 100); - /* send normal config */ + /* reset chip */ + ts_test_reset(ts_test, 100); + /* send normal config */ if (ts_test->test_config.len > 0) { if (ts_test_send_config(ts_test, CONFIG_TYPE_NORMAL)) ts_err("Send normal config failed"); } - /* open esd */ - goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); - /* enable irq */ - ts_test_irq_enable(ts_test, true); + /* open esd */ + goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); + /* enable irq */ + ts_test_irq_enable(ts_test, true); } #define SHORT_TEST_RUN_REG 0x10400 @@ -767,28 +762,28 @@ static void goodix_tptest_finish(struct goodix_ts_test *ts_test) #define TEST_FW_PID "OST" static int goodix_short_test_prepare(struct goodix_ts_test *ts_test) { - struct goodix_ts_cmd tmp_cmd; - struct goodix_fw_version fw_ver; - int ret; - int retry; + struct goodix_ts_cmd tmp_cmd; + struct goodix_fw_version fw_ver; + int ret; + int retry; int resend = 3; u8 status; ts_info("short test prepare IN"); ts_test->test_result[GTP_SHORT_TEST] = SYS_SOFTWARE_REASON; - tmp_cmd.len = 4; - tmp_cmd.cmd = INSPECT_FW_SWITCH_CMD; + tmp_cmd.len = 4; + tmp_cmd.cmd = INSPECT_FW_SWITCH_CMD; resend_cmd: - ret = ts_test_send_cmd(ts_test, &tmp_cmd); - if (ret < 0) { - ts_err("send test mode failed"); - return ret; - } + ret = ts_test_send_cmd(ts_test, &tmp_cmd); + if (ret < 0) { + ts_err("send test mode failed"); + return ret; + } retry = 3; - while (retry--) { - msleep(40); + while (retry--) { + msleep(40); if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) { ret = ts_test_read_version(ts_test, &fw_ver); if (ret < 0) { @@ -798,22 +793,21 @@ resend_cmd: ret = memcmp(&(fw_ver.patch_pid[3]), TEST_FW_PID, strlen(TEST_FW_PID)); if (ret == 0) return 0; - else - ts_info("patch ID dismatch %s != %s", fw_ver.patch_pid, TEST_FW_PID); + ts_info("patch ID dismatch %s != %s", fw_ver.patch_pid, TEST_FW_PID); } else { ret = ts_test_read(ts_test, SHORT_TEST_RUN_REG, &status, 1); if (!ret && status == SHORT_TEST_RUN_FLAG) return 0; ts_info("short_mode_status=0x%02x ret=%d", status, ret); } - } + } if (resend--) { ts_test_reset(ts_test, 100); goto resend_cmd; } - return -EINVAL; + return -EINVAL; } static u32 map_die2pin(struct ts_test_params *test_params, u32 chn_num) @@ -845,7 +839,7 @@ static u32 map_die2pin(struct ts_test_params *test_params, u32 chn_num) else res |= DRV_CHANNEL_FLAG; - return res; + return res; } static void goodix_save_short_res(struct ts_test_params *params, @@ -860,7 +854,7 @@ static void goodix_save_short_res(struct ts_test_params *params, if (chn1 == chn2 || short_res->short_num >= MAX_SHORT_NUM) return; - + for (i = 0; i < short_res->short_num; i++) { repeat_cnt = 0; if (short_res->short_msg[4 * i] == chn1) @@ -887,7 +881,7 @@ static void goodix_save_short_res(struct ts_test_params *params, } static int gdix_check_tx_tx_shortcircut(struct goodix_ts_test *ts_test, - u8 short_ch_num) + u8 short_ch_num) { int ret = 0, err = 0; u32 r_threshold = 0, short_r = 0; @@ -907,7 +901,7 @@ static int gdix_check_tx_tx_shortcircut(struct goodix_ts_test *ts_test, ts_err("Failed to alloc memory"); return -ENOMEM; } - /* drv&drv shortcircut check */ + /* drv&drv shortcircut check */ data_reg = test_params->params_info->drv_drv_selfcode_reg; for (i = 0; i < short_ch_num; i++) { ret = ts_test_read(ts_test, data_reg, data_buf, size); @@ -975,7 +969,7 @@ static int gdix_check_tx_tx_shortcircut(struct goodix_ts_test *ts_test, } static int gdix_check_rx_rx_shortcircut(struct goodix_ts_test *ts_test, - u8 short_ch_num) + u8 short_ch_num) { int ret = 0, err = 0; u32 r_threshold = 0, short_r = 0; @@ -996,7 +990,7 @@ static int gdix_check_rx_rx_shortcircut(struct goodix_ts_test *ts_test, } /* drv&drv shortcircut check */ data_reg = test_params->params_info->sen_sen_selfcode_reg; - for (i = 0; i < short_ch_num; i++) { + for (i = 0; i < short_ch_num; i++) { ret = ts_test_read(ts_test, data_reg, data_buf, size); if (ret) { ts_err("Failed read Sen-to-Sen short rawdata"); @@ -1005,15 +999,15 @@ static int gdix_check_rx_rx_shortcircut(struct goodix_ts_test *ts_test, } if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { - ts_err("Sen-to-Sen adc data checksum error"); + ts_err("Sen-to-Sen adc data checksum error"); err = -EINVAL; - break; + break; } r_threshold = test_params->r_sen_sen_threshold; short_die_num = le16_to_cpup((__le16 *)&data_buf[0]); if (short_die_num >= max_sen_num) { - ts_info("invalid short pad num:%d", short_die_num); + ts_info("invalid short pad num:%d", short_die_num); continue; } @@ -1054,17 +1048,17 @@ static int gdix_check_rx_rx_shortcircut(struct goodix_ts_test *ts_test, } kfree(data_buf); - return err; + return err; } static int gdix_check_tx_rx_shortcircut(struct goodix_ts_test *ts_test, - u8 short_ch_num) + u8 short_ch_num) { int ret = 0, err = 0; - u32 r_threshold = 0, short_r = 0; + u32 r_threshold = 0, short_r = 0; int size = 0, i = 0, j = 0; u16 adc_signal = 0; - u8 master_pin_num, slave_pin_num; + u8 master_pin_num, slave_pin_num; u8 *data_buf = NULL; u32 data_reg; struct ts_test_params *test_params = &ts_test->test_params; @@ -1097,7 +1091,7 @@ static int gdix_check_tx_rx_shortcircut(struct goodix_ts_test *ts_test, r_threshold = test_params->r_drv_sen_threshold; short_die_num = le16_to_cpup((__le16 *)&data_buf[0]); if (short_die_num >= max_sen_num) { - ts_info("invalid short pad num:%d", short_die_num); + ts_info("invalid short pad num:%d", short_die_num); continue; } @@ -1138,11 +1132,11 @@ static int gdix_check_tx_rx_shortcircut(struct goodix_ts_test *ts_test, } kfree(data_buf); - return err; + return err; } static int gdix_check_resistance_to_gnd(struct ts_test_params *test_params, - u16 adc_signal, u32 pos) + u16 adc_signal, u32 pos) { long r = 0; u16 r_th = 0, avdd_value = 0; @@ -1150,7 +1144,7 @@ static int gdix_check_resistance_to_gnd(struct ts_test_params *test_params, u8 pin_num = 0; unsigned short short_type; struct goodix_ts_test *ts_test = container_of(test_params, - struct goodix_ts_test, test_params); + struct goodix_ts_test, test_params); int max_drv_num = test_params->params_info->max_drv_num; int max_sen_num = test_params->params_info->max_sen_num; @@ -1200,7 +1194,7 @@ static int gdix_check_gndvdd_shortcircut(struct goodix_ts_test *ts_test) int ret = 0, err = 0; int size = 0, i = 0; u16 adc_signal = 0; - u32 data_reg; + u32 data_reg; u8 *data_buf = NULL; int max_drv_num = ts_test->test_params.params_info->max_drv_num; int max_sen_num = ts_test->test_params.params_info->max_sen_num; @@ -1224,7 +1218,7 @@ static int gdix_check_gndvdd_shortcircut(struct goodix_ts_test *ts_test) if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { ts_err("diff code checksum error"); err = -EINVAL; - goto err_out; + goto err_out; } for (i = 0; i < max_drv_num + max_sen_num; i++) { @@ -1244,16 +1238,17 @@ err_out: static int goodix_shortcircut_analysis(struct goodix_ts_test *ts_test) { - int ret; - int err = 0; - test_result_t test_result; + int ret; + int err = 0; + test_result_t test_result; - ret = ts_test_read(ts_test, ts_test->test_params.params_info->short_test_result_reg, - (u8 *)&test_result, sizeof(test_result)); - if (ret < 0) { - ts_err("Read TEST_RESULT_REG failed"); - return ret; - } + ret = ts_test_read(ts_test, + ts_test->test_params.params_info->short_test_result_reg, + (u8 *)&test_result, sizeof(test_result)); + if (ret < 0) { + ts_err("Read TEST_RESULT_REG failed"); + return ret; + } if (checksum_cmp((u8 *)&test_result, sizeof(test_result), CHECKSUM_MODE_U8_LE)) { @@ -1278,26 +1273,26 @@ static int goodix_shortcircut_analysis(struct goodix_ts_test *ts_test) if (test_result.drv_gnd_avdd_num || test_result.sen_gnd_avdd_num) err |= gdix_check_gndvdd_shortcircut(ts_test); - ts_info(">>>>> short check return 0x%x", err); + ts_info(">>>>> short check return 0x%x", err); - return err; + return err; } #define SHORT_FW_CMD_REG 0x10400 static int send_test_cmd(struct goodix_ts_test *ts_test, - struct goodix_ts_cmd *cmd) + struct goodix_ts_cmd *cmd) { - int ret; - u32 reg = SHORT_FW_CMD_REG; - cmd->state = 0; - cmd->ack = 0; - goodix_append_checksum(&(cmd->buf[2]), cmd->len - 2, - CHECKSUM_MODE_U8_LE); - ret = ts_test_write(ts_test, reg, cmd->buf, cmd->len + 2); - if (ret < 0) - return ret; - usleep_range(10000, 11000); - return ret; + int ret; + u32 reg = SHORT_FW_CMD_REG; + + cmd->state = 0; + cmd->ack = 0; + goodix_append_checksum(&(cmd->buf[2]), cmd->len - 2, CHECKSUM_MODE_U8_LE); + ret = ts_test_write(ts_test, reg, cmd->buf, cmd->len + 2); + if (ret < 0) + return ret; + usleep_range(10000, 11000); + return ret; } @@ -1306,47 +1301,55 @@ static int send_test_cmd(struct goodix_ts_test *ts_test, #define SHORT_TEST_THRESHOLD_REG 0x20402 static void goodix_shortcircut_test(struct goodix_ts_test *ts_test) { - int ret = 0; - int retry; - u16 test_time; + int ret = 0; + int retry; + u16 test_time; u8 status; int ic_type = ts_test->ts->bus->ic_type; - struct goodix_ts_cmd test_parm_cmd; + struct goodix_ts_cmd test_parm_cmd; // u8 test_param[6]; ts_info("---------------------- short_test begin ----------------------"); - ret = goodix_short_test_prepare(ts_test); - if (ret < 0) { - ts_err("Failed enter short test mode"); - return; - } + ret = goodix_short_test_prepare(ts_test); + if (ret < 0) { + ts_err("Failed enter short test mode"); + return; + } /* get short test time */ - ret = ts_test_read(ts_test, ts_test->test_params.params_info->short_test_time_reg, (u8 *)&test_time, 2); - if (ret < 0) { - ts_err("Failed to get test_time, default %dms", DEFAULT_TEST_TIME_MS); - test_time = DEFAULT_TEST_TIME_MS; - } else { + ret = ts_test_read(ts_test, + ts_test->test_params.params_info->short_test_time_reg, + (u8 *)&test_time, 2); + if (ret < 0) { + ts_err("Failed to get test_time, default %dms", DEFAULT_TEST_TIME_MS); + test_time = DEFAULT_TEST_TIME_MS; + } else { if (ic_type == IC_TYPE_BERLIN_A) - test_time /= 10; - if (test_time > MAX_TEST_TIME_MS) { - ts_info("test time too long %d > %d", - test_time, MAX_TEST_TIME_MS); - test_time = MAX_TEST_TIME_MS; - } - ts_info("get test time %dms", test_time); - } + test_time /= 10; + if (test_time > MAX_TEST_TIME_MS) { + ts_info("test time too long %d > %d", + test_time, MAX_TEST_TIME_MS); + test_time = MAX_TEST_TIME_MS; + } + ts_info("get test time %dms", test_time); + } /* start short test */ if (ic_type == IC_TYPE_BERLIN_A) { test_parm_cmd.len = 0x0A; test_parm_cmd.cmd = INSPECT_PARAM_CMD; - test_parm_cmd.data[0] = ts_test->test_params.params_info->dft_short_threshold & 0xFF; - test_parm_cmd.data[1] = (ts_test->test_params.params_info->dft_short_threshold >> 8) & 0xFF; - test_parm_cmd.data[2] = ts_test->test_params.params_info->short_diffcode_threshold & 0xFF; - test_parm_cmd.data[3] = (ts_test->test_params.params_info->short_diffcode_threshold >> 8) & 0xFF; - test_parm_cmd.data[4] = ts_test->test_params.params_info->short_test_dump_num & 0xFF; - test_parm_cmd.data[5] = (ts_test->test_params.params_info->short_test_dump_num >> 8) & 0xFF; + test_parm_cmd.data[0] = + ts_test->test_params.params_info->dft_short_threshold & 0xFF; + test_parm_cmd.data[1] = + (ts_test->test_params.params_info->dft_short_threshold >> 8) & 0xFF; + test_parm_cmd.data[2] = + ts_test->test_params.params_info->short_diffcode_threshold & 0xFF; + test_parm_cmd.data[3] = + (ts_test->test_params.params_info->short_diffcode_threshold >> 8) & 0xFF; + test_parm_cmd.data[4] = + ts_test->test_params.params_info->short_test_dump_num & 0xFF; + test_parm_cmd.data[5] = + (ts_test->test_params.params_info->short_test_dump_num >> 8) & 0xFF; ret = send_test_cmd(ts_test, &test_parm_cmd); if (ret < 0) { ts_err("send INSPECT_PARAM_CMD failed"); @@ -1354,21 +1357,28 @@ static void goodix_shortcircut_test(struct goodix_ts_test *ts_test) } } else { // test_param[0] = ts_test->test_params.params_info->dft_short_threshold & 0xFF; - // test_param[1] = (ts_test->test_params.params_info->dft_short_threshold >> 8) & 0xFF; - // test_param[2] = ts_test->test_params.params_info->short_diffcode_threshold & 0xFF; - // test_param[3] = (ts_test->test_params.params_info->short_diffcode_threshold >> 8) & 0xFF; - // test_param[4] = ts_test->test_params.params_info->short_test_dump_num & 0xFF; - // test_param[5] = (ts_test->test_params.params_info->short_test_dump_num >> 8) & 0xFF; - // ts_test_write(ts_test, SHORT_TEST_THRESHOLD_REG, test_param, sizeof(test_param)); + // test_param[1] = + // (ts_test->test_params.params_info->dft_short_threshold >> 8) & 0xFF; + // test_param[2] = + // ts_test->test_params.params_info->short_diffcode_threshold & 0xFF; + // test_param[3] = + // (ts_test->test_params.params_info->short_diffcode_threshold >> 8) & 0xFF; + // test_param[4] = + // ts_test->test_params.params_info->short_test_dump_num & 0xFF; + // test_param[5] = + // (ts_test->test_params.params_info->short_test_dump_num >> 8) & 0xFF; + // ts_test_write(ts_test, SHORT_TEST_THRESHOLD_REG, + // test_param, sizeof(test_param)); status = 0; ts_test_write(ts_test, SHORT_TEST_RUN_REG, &status, 1); } /* wait short test finish */ - msleep(test_time); - retry = 50; + msleep(test_time); + retry = 50; while (retry--) { - ret = ts_test_read(ts_test, ts_test->test_params.params_info->short_test_status_reg, &status, 1); + ret = ts_test_read(ts_test, + ts_test->test_params.params_info->short_test_status_reg, &status, 1); if (!ret && status == SHORT_TEST_FINISH_FLAG) break; msleep(50); @@ -1391,8 +1401,8 @@ static void goodix_shortcircut_test(struct goodix_ts_test *ts_test) #define GOODIX_TOUCH_EVENT 0x80 static int goodix_cap_test_prepare(struct goodix_ts_test *ts_test) { - int ret; - struct goodix_ts_cmd temp_cmd; + int ret; + struct goodix_ts_cmd temp_cmd; ts_info("cap test prepare IN"); ts_test->test_result[GTP_CAP_TEST] = SYS_SOFTWARE_REASON; @@ -1404,7 +1414,7 @@ static int goodix_cap_test_prepare(struct goodix_ts_test *ts_test) if (ts_test->test_params.test_items[GTP_SELFNOISE_TEST]) ts_test->test_result[GTP_SELFNOISE_TEST] = SYS_SOFTWARE_REASON; - /* switch rawdata mode */ + /* switch rawdata mode */ if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D) { temp_cmd.cmd = 0x90; temp_cmd.data[0] = 0x81; @@ -1414,16 +1424,16 @@ static int goodix_cap_test_prepare(struct goodix_ts_test *ts_test) temp_cmd.len = 4; } ret = ts_test_send_cmd(ts_test, &temp_cmd); - if (ret < 0) - ts_err("Enter rawdata mode failed"); + if (ret < 0) + ts_err("Enter rawdata mode failed"); - return ret; + return ret; } static int goodix_cap_test_finish(struct goodix_ts_test *ts_test) { ts_info("cap_test finished"); - /* switch coor mode */ + /* switch coor mode */ ts_test_reset(ts_test, 100); return 0; } @@ -1470,12 +1480,14 @@ static int goodix_cache_rawdata(struct goodix_ts_test *ts_test) ret = ts_test_read(ts_test, flag_addr, frame_buf, sizeof(frame_buf)); if (ret < 0) return ret; - if (checksum_cmp(frame_buf, cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) { + if (checksum_cmp(frame_buf, + cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) { ts_err("frame head checksum error"); return -EINVAL; } frame_head = (struct frame_head *)frame_buf; - if (checksum_cmp(frame_buf, frame_head->cur_frame_len, CHECKSUM_MODE_U16_LE)) { + if (checksum_cmp(frame_buf, + frame_head->cur_frame_len, CHECKSUM_MODE_U16_LE)) { ts_err("frame body checksum error"); return -EINVAL; } @@ -1513,6 +1525,7 @@ static void goodix_cache_deltadata(struct goodix_ts_test *ts_test) data_size = ts_test->rawdata[i].size; if (data_size == 0) continue; + for (j = 0; j < data_size; j++) { raw = ts_test->rawdata[i].data[j]; max_val = 0; @@ -1557,13 +1570,15 @@ static int goodix_cache_self_rawdata(struct goodix_ts_test *ts_test) struct frame_head *frame_head; struct goodix_ts_core *cd = ts_test->ts; unsigned char frame_buf[GOODIX_MAX_FRAMEDATA_LEN]; - unsigned char *cur_ptr; + unsigned char *cur_ptr; if (cd->bus->ic_type == IC_TYPE_BERLIN_D) { ret = ts_test_read(ts_test, flag_addr, frame_buf, sizeof(frame_buf)); if (ret < 0) return ret; - if (checksum_cmp(frame_buf, cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) { + + if (checksum_cmp(frame_buf, + cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) { ts_err("frame head checksum error"); return -EINVAL; } @@ -1581,7 +1596,8 @@ static int goodix_cache_self_rawdata(struct goodix_ts_test *ts_test) cd->ic_info.misc.self_struct_len - 10); } else { ret = ts_test_read(ts_test, data_addr, - (u8 *)ts_test->self_rawdata.data, data_size * sizeof(s16)); + (u8 *)ts_test->self_rawdata.data, + data_size * sizeof(s16)); if (ret < 0) return ret; } @@ -1601,7 +1617,7 @@ static int goodix_cache_noisedata(struct goodix_ts_test *ts_test) unsigned char *cur_ptr; struct frame_head *frame_head; struct goodix_ts_cmd temp_cmd; - struct goodix_ts_core *cd = ts_test->ts; + struct goodix_ts_core *cd = ts_test->ts; u32 sen_num = ts_test->test_params.sen_num; u32 drv_num = ts_test->test_params.drv_num; u32 data_size = sen_num * drv_num; @@ -1643,12 +1659,16 @@ static int goodix_cache_noisedata(struct goodix_ts_test *ts_test) ret = ts_test_read(ts_test, flag_addr, frame_buf, sizeof(frame_buf)); if (ret < 0) return ret; - if (checksum_cmp(frame_buf, cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) { + if (checksum_cmp(frame_buf, + cd->ic_info.misc.frame_data_head_len, + CHECKSUM_MODE_U8_LE)) { ts_err("frame head checksum error"); return -EINVAL; } frame_head = (struct frame_head *)frame_buf; - if (checksum_cmp(frame_buf, frame_head->cur_frame_len, CHECKSUM_MODE_U16_LE)) { + if (checksum_cmp(frame_buf, + frame_head->cur_frame_len, + CHECKSUM_MODE_U16_LE)) { ts_err("frame body checksum error"); return -EINVAL; } @@ -1692,7 +1712,8 @@ static int goodix_cache_self_noisedata(struct goodix_ts_test *ts_test) ret = ts_test_read(ts_test, flag_addr, frame_buf, sizeof(frame_buf)); if (ret < 0) return ret; - if (checksum_cmp(frame_buf, cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) { + if (checksum_cmp(frame_buf, + cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) { ts_err("frame head checksum error"); return -EINVAL; } @@ -1716,11 +1737,10 @@ static int goodix_cache_self_noisedata(struct goodix_ts_test *ts_test) } ts_test->self_noisedata.size = data_size; - for (i = 0; i < data_size; i++) { + for (i = 0; i < data_size; i++) ts_test->self_noisedata.data[i] = ABS(ts_test->self_noisedata.data[i]); - } - return ret; + return ret; } static int goodix_analysis_rawdata(struct goodix_ts_test *ts_test) @@ -1774,7 +1794,7 @@ static int goodix_analysis_deltadata(struct goodix_ts_test *ts_test) if (val > ts_test->test_params.deviation_limits[j]) { ts_test->open_res.beyond_accord_limit_cnt[j]++; ret = -EINVAL; - } + } } } @@ -1798,7 +1818,7 @@ static int goodix_analysis_self_rawdata(struct goodix_ts_test *ts_test) } } - return 0; + return 0; } static int goodix_analysis_noisedata(struct goodix_ts_test *ts_test) @@ -1847,7 +1867,7 @@ static int goodix_analysis_self_noisedata(struct goodix_ts_test *ts_test) } } - return 0; + return 0; } static void goodix_capacitance_test(struct goodix_ts_test *ts_test) @@ -1855,30 +1875,30 @@ static void goodix_capacitance_test(struct goodix_ts_test *ts_test) int ret; ts_info("---------------------- cap_test begin ----------------------"); - ret = goodix_cap_test_prepare(ts_test); - if (ret < 0) { + ret = goodix_cap_test_prepare(ts_test); + if (ret < 0) { ts_err("cap_test prepare failed, exit"); goto exit; - } + } ts_info("cap rawdata prepare OK"); - /* obtain rawdata */ - ret = goodix_cache_rawdata(ts_test); - if (ret < 0) { + /* obtain rawdata */ + ret = goodix_cache_rawdata(ts_test); + if (ret < 0) { if (ret == -EAGAIN) { ts_err("Capacitance exit"); goto exit; - } else { + } else ts_err("Failed to read capdata"); - } - } else { + + } else { ts_info("get rawdata finish, start analysis"); ret = goodix_analysis_rawdata(ts_test); if (ret < 0) ts_test->test_result[GTP_CAP_TEST] = GTP_PANEL_REASON; else ts_test->test_result[GTP_CAP_TEST] = GTP_TEST_PASS; - } + } /* obtain delta_data */ goodix_cache_deltadata(ts_test); @@ -1889,7 +1909,7 @@ static void goodix_capacitance_test(struct goodix_ts_test *ts_test) else ts_test->test_result[GTP_DELTA_TEST] = GTP_TEST_PASS; - /* obtain self_rawdata */ + /* obtain self_rawdata */ if (ts_test->test_params.test_items[GTP_SELFCAP_TEST]) { ret = goodix_cache_self_rawdata(ts_test); if (ret < 0) { @@ -1904,7 +1924,7 @@ static void goodix_capacitance_test(struct goodix_ts_test *ts_test) } } - /* obtain noisedata */ + /* obtain noisedata */ if (ts_test->test_params.test_items[GTP_NOISE_TEST]) { ret = goodix_cache_noisedata(ts_test); if (ret < 0) { @@ -1919,7 +1939,7 @@ static void goodix_capacitance_test(struct goodix_ts_test *ts_test) } } - /* obtain self_noisedata */ + /* obtain self_noisedata */ if (ts_test->test_params.test_items[GTP_SELFNOISE_TEST]) { ret = goodix_cache_self_noisedata(ts_test); if (ret < 0) { @@ -1943,7 +1963,8 @@ char *goodix_strncat(char *dest, char *src, size_t dest_size) size_t dest_len = 0; dest_len = strnlen(dest, dest_size); - return strncat(&dest[dest_len], src, dest_size - dest_len - 1); + strlcat(&dest[dest_len], src, dest_size - dest_len - 1); + return dest; } char *goodix_strncatint(char *dest, int src, char *format, size_t dest_size) @@ -2004,30 +2025,30 @@ static void goodix_data_statistics(s16 *data, size_t data_size, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) static ssize_t fs_write(const void* buf, size_t size, struct file* fp) { - loff_t pos; - ssize_t len; + loff_t pos; + ssize_t len; - pos = fp->f_pos; - len = kernel_write(fp, buf, size, &pos); + pos = fp->f_pos; + len = kernel_write(fp, buf, size, &pos); fp->f_pos = pos; - return len; + return len; } #else static ssize_t fs_write(const void* buf, size_t size, struct file* fp) { mm_segment_t old_fs; - loff_t pos; - ssize_t len; + loff_t pos; + ssize_t len; - pos = fp->f_pos; + pos = fp->f_pos; old_fs = get_fs(); set_fs(KERNEL_DS); - len = vfs_write(fp, buf, size, &pos); + len = vfs_write(fp, buf, size, &pos); set_fs(old_fs); fp->f_pos = pos; - return len; + return len; } #endif @@ -2051,12 +2072,13 @@ static int goodix_save_test_config(struct goodix_ts_test *ts_test, return -ENOMEM; } - bytes += sprintf(&data[bytes], "\n"); - for (i = 0; i < cfg->len; i++) { - bytes += sprintf(&data[bytes], "0x%02x,", cfg->data[i]); - } - bytes += sprintf(&data[bytes], "\n"); - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); + for (i = 0; i < cfg->len; i++) + bytes += scnprintf(&data[bytes], + MAX_DATA_BUFFER - bytes, "0x%02x,", cfg->data[i]); + + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); ret = fs_write(data, bytes, fp); if (ret < 0) { ts_err("test config write failed"); @@ -2084,9 +2106,11 @@ static int goodix_save_header(struct goodix_ts_test *ts_test, return -ENOMEM; } - bytes += sprintf(&data[bytes], "\n"); - bytes += sprintf(&data[bytes], "\n"); - bytes += sprintf(&data[bytes], "
\n"); + bytes += scnprintf(&data[bytes], + MAX_DATA_BUFFER - bytes, + "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "
\n"); /* sava test result */ for (i = 0; i < MAX_TEST_ITEMS; i++) { if ((ts_test->test_result[i] > 0) && @@ -2096,13 +2120,20 @@ static int goodix_save_header(struct goodix_ts_test *ts_test, } } if (result) - bytes += sprintf(&data[bytes], "NG\n"); + bytes += scnprintf(&data[bytes], + MAX_DATA_BUFFER - bytes, + "NG\n"); else - bytes += sprintf(&data[bytes], "OK\n"); - bytes += sprintf(&data[bytes], "GT%s\n", - ts->fw_version.patch_pid); - bytes += sprintf(&data[bytes], "%d\n", - ts_test->ts->fw_version.sensor_id); + bytes += scnprintf(&data[bytes], + MAX_DATA_BUFFER - bytes, + "OK\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "GT%s\n", + ts->fw_version.patch_pid); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "%d\n", + ts_test->ts->fw_version.sensor_id); + ret = fs_write(data, bytes, fp); if (ret < 0) { ts_err("header write failed"); @@ -2116,7 +2147,7 @@ static int goodix_save_header(struct goodix_ts_test *ts_test, goto save_end; } - bytes += sprintf(&data[bytes], "
\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "
\n"); ret = fs_write(data, bytes, fp); if (ret < 0) { ts_err("header write failed"); @@ -2125,62 +2156,62 @@ static int goodix_save_header(struct goodix_ts_test *ts_test, bytes = 0; /* item list */ - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); if (ts_test->test_result[GTP_CAP_TEST]) { if (GTP_TEST_PASS == ts_test->test_result[GTP_CAP_TEST]) - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); else - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); } if (ts_test->test_result[GTP_DELTA_TEST]) { if (GTP_TEST_PASS == ts_test->test_result[GTP_DELTA_TEST]) - bytes += sprintf(&data[bytes], - "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "\n"); else - bytes += sprintf(&data[bytes], - "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "\n"); } if (ts_test->test_result[GTP_NOISE_TEST]) { if (GTP_TEST_PASS == ts_test->test_result[GTP_NOISE_TEST]) - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); else - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); } if (ts_test->test_result[GTP_SELFNOISE_TEST]) { if (GTP_TEST_PASS == ts_test->test_result[GTP_SELFNOISE_TEST]) - bytes += sprintf(&data[bytes], - "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "\n"); else - bytes += sprintf(&data[bytes], - "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "\n"); } if (ts_test->test_result[GTP_SELFCAP_TEST]) { if (GTP_TEST_PASS == ts_test->test_result[GTP_SELFCAP_TEST]) - bytes += sprintf(&data[bytes], - "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "\n"); else - bytes += sprintf(&data[bytes], - "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "\n"); } if (ts_test->test_result[GTP_SHORT_TEST]) { if (GTP_TEST_PASS == ts_test->test_result[GTP_SHORT_TEST]) - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); else - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); } - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); ret = fs_write(data, bytes, fp); if (ret < 0) { ts_err("item list write failed"); @@ -2209,12 +2240,14 @@ static int goodix_save_limits(struct goodix_ts_test *ts_test, if (!data) { return -ENOMEM; - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); /* save short result */ if (ts_test->test_result[GTP_SHORT_TEST]) { - bytes += sprintf(&data[bytes], "\n"); - bytes += sprintf(&data[bytes], "%d\n", + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "%d\n", ts_test->short_res.short_num); for (i = 0; i < ts_test->short_res.short_num; i++) { chn1 = ts_test->short_res.short_msg[4 * i]; @@ -2222,31 +2255,35 @@ static int goodix_save_limits(struct goodix_ts_test *ts_test, r = (ts_test->short_res.short_msg[4 * i + 2] << 8) + ts_test->short_res.short_msg[4 * i + 3]; if (chn1 == CHN_VDD) - bytes += sprintf(&data[bytes], "\n", r); else if (chn2 == CHN_GND) - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "Chn2=\"GND\" ShortResistor= \"%dKom\"/>\n", r); else if (chn2 & DRV_CHANNEL_FLAG) - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "Chn2=\"Tx%d\" ShortResistor= \"%dKom\"/>\n", chn2 & 0x7f, r); else - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "Chn2=\"Rx%d\" ShortResistor= \"%dKom\"/>\n", chn2 & 0x7f, r); } - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); ret = fs_write(data, bytes, fp); if (ret < 0) { ts_err("short res write fail."); @@ -2256,26 +2293,29 @@ static int goodix_save_limits(struct goodix_ts_test *ts_test, } /* rawdata max limit */ - bytes += sprintf(&data[bytes], "\n"); - bytes += sprintf(&data[bytes], "%d\n", - TOTAL_FRAME_NUM); - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "%d\n", TOTAL_FRAME_NUM); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); for (i = 0; i < tx * rx; i++) { - bytes += sprintf(&data[bytes], "%d,", + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,", ts_test->test_params.max_limits[i]); if ((i + 1) % tx == 0) - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); } - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); /* BeyondRawdataUpperLimit */ - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "\n"); for (i = 0; i < tx * rx; i++) { - bytes += sprintf(&data[bytes], "%d,", + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,", ts_test->open_res.beyond_max_limit_cnt[i]); if ((i + 1) % tx == 0) - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); } - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "\n"); ret = fs_write(data, bytes, fp); if (ret < 0) { ts_err("rawdata limit write failed"); @@ -2284,23 +2324,25 @@ static int goodix_save_limits(struct goodix_ts_test *ts_test, bytes = 0; /* rawdata min limit */ - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); for (i = 0; i < tx * rx; i++) { - bytes += sprintf(&data[bytes], "%d,", + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,", ts_test->test_params.min_limits[i]); if ((i + 1) % tx == 0) - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); } - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); /* BeyondRawdataLower limit */ - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "\n"); for (i = 0; i < tx * rx; i++) { - bytes += sprintf(&data[bytes], "%d,", + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,", ts_test->open_res.beyond_min_limit_cnt[i]); if ((i + 1) % tx == 0) - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); } - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "\n"); ret = fs_write(data, bytes, fp); if (ret < 0) { ts_err("rawdata limit write failed"); @@ -2309,24 +2351,24 @@ static int goodix_save_limits(struct goodix_ts_test *ts_test, bytes = 0; /* Max Accord limit */ - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); for (i = 0; i < tx * rx; i++) { - bytes += sprintf(&data[bytes], "%d,", + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,", ts_test->test_params.deviation_limits[i]); if ((i + 1) % tx == 0) - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); } - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); /* BeyondAccordLimitCnt */ - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); for (i = 0; i < tx * rx; i++) { - bytes += sprintf(&data[bytes], "%d,", + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,", ts_test->open_res.beyond_accord_limit_cnt[i]); if ((i + 1) % tx == 0) - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); } - bytes += sprintf(&data[bytes], "\n"); - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "
\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "
\n"); ret = fs_write(data, bytes, fp); if (ret < 0) { ts_err("rawdata limit write failed"); @@ -2336,15 +2378,15 @@ static int goodix_save_limits(struct goodix_ts_test *ts_test, /* save noise limit */ if (ts_test->test_result[GTP_NOISE_TEST]) { - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d\n", NOISEDATA_TEST_TIMES); - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d\n", ts_test->test_params.noise_threshold); - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "
\n"); ret = fs_write(data, bytes, fp); if (ret < 0) { ts_err("noise limit write failed"); @@ -2355,32 +2397,31 @@ static int goodix_save_limits(struct goodix_ts_test *ts_test, /* save self rawdata limit */ if (ts_test->test_result[GTP_SELFCAP_TEST]) { - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "1\n"); - bytes += sprintf(&data[bytes], - "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); for (i = 0; i < tx + rx; i++) { - bytes += sprintf(&data[bytes], "%d,", - ts_test->test_params.self_max_limits[i]); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,", + ts_test->test_params.self_max_limits[i]); if ((i + 1) % tx == 0) - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); } if ((tx + rx) % tx != 0) - bytes += sprintf(&data[bytes], "\n"); - bytes += sprintf(&data[bytes], "\n"); - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); for (i = 0; i < tx + rx; i++) { - bytes += sprintf(&data[bytes], "%d,", - ts_test->test_params.self_min_limits[i]); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,", + ts_test->test_params.self_min_limits[i]); if ((i + 1) % tx == 0) - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); } if ((tx + rx) % tx != 0) - bytes += sprintf(&data[bytes], "\n"); - bytes += sprintf(&data[bytes], "\n"); - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "
\n"); ret = fs_write(data, bytes, fp); if (ret < 0) { ts_err("self rawdata limit write failed"); @@ -2391,14 +2432,14 @@ static int goodix_save_limits(struct goodix_ts_test *ts_test, /* save selfnoise limit */ if (ts_test->test_result[GTP_SELFNOISE_TEST]) { - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "1\n"); - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d\n", ts_test->test_params.self_noise_threshold); - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "
\n"); ret = fs_write(data, bytes, fp); if (ret < 0) { ts_err("raw limit write failed"); @@ -2407,7 +2448,7 @@ static int goodix_save_limits(struct goodix_ts_test *ts_test, bytes = 0; } - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); ret = fs_write(data, bytes, fp); if (ret < 0) ts_err("limit write fail."); @@ -2434,30 +2475,30 @@ static int goodix_save_rawdata(struct goodix_ts_test *ts_test, if (!data) return -ENOMEM; - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); for (i = 0; i < TOTAL_FRAME_NUM; i++) { goodix_data_cal(ts_test->rawdata[i].data, len, stat_result); - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n", i, len, stat_result[1], stat_result[2], stat_result[0]); for (j = 0; j < len; j++) { - bytes += sprintf(&data[bytes], "%d,", - ts_test->rawdata[i].data[j]); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "%d,", ts_test->rawdata[i].data[j]); if ((j + 1) % tx == 0) - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); } - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); goodix_data_cal(ts_test->accord_arr[i].data, len, stat_result); - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n", i, len, stat_result[1], stat_result[2], stat_result[0]); for (j = 0; j < len; j++) { - bytes += sprintf(&data[bytes], "%d,", - ts_test->accord_arr[i].data[j]); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "%d,", ts_test->accord_arr[i].data[j]); if ((j + 1) % tx == 0) - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); } - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); ret = fs_write(data, bytes, fp); if (ret < 0) { ts_err("rawdata write fail."); @@ -2466,7 +2507,7 @@ static int goodix_save_rawdata(struct goodix_ts_test *ts_test, bytes = 0; } - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); ret = fs_write(data, bytes, fp); if (ret < 0) ts_err("rawdata write fail."); @@ -2493,19 +2534,19 @@ static int goodix_save_noise_data(struct goodix_ts_test *ts_test, if (!data) return -ENOMEM; - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); for (i = 0; i < NOISEDATA_TEST_TIMES; i++) { goodix_data_cal(ts_test->noisedata[i].data, len, stat_result); - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n", i, len, stat_result[1], stat_result[2], stat_result[0]); for (j = 0; j < len; j++) { - bytes += sprintf(&data[bytes], "%d,", - ts_test->noisedata[i].data[j]); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, + "%d,", ts_test->noisedata[i].data[j]); if ((j + 1) % tx == 0) - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); } - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); ret = fs_write(data, bytes, fp); if (ret < 0) { ts_err("noisedata write fail."); @@ -2514,7 +2555,7 @@ static int goodix_save_noise_data(struct goodix_ts_test *ts_test, bytes = 0; } - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); ret = fs_write(data, bytes, fp); if (ret < 0) ts_err("noisedata write fail."); @@ -2538,7 +2579,7 @@ static int goodix_save_self_data(struct goodix_ts_test *ts_test, if (!data) return -ENOMEM; - bytes += sprintf(&data[bytes], "<%s>\n", title); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "<%s>\n",title); ret = fs_write(data, bytes, fp); if (ret < 0) { ts_err("rawdata write fail."); @@ -2547,18 +2588,18 @@ static int goodix_save_self_data(struct goodix_ts_test *ts_test, bytes = 0; goodix_data_cal(src_data, len, stat_result); - bytes += sprintf(&data[bytes], + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n", len, stat_result[1], stat_result[2], stat_result[0]); for (i = 0; i < len; i++) { - bytes += sprintf(&data[bytes], "%d,", src_data[i]); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "%d,", src_data[i]); if ((i + 1) % tx == 0) - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); } if (len % tx != 0) - bytes += sprintf(&data[bytes], "\n"); - bytes += sprintf(&data[bytes], "\n"); - bytes += sprintf(&data[bytes], "\n", title); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n",title); ret = fs_write(data, bytes, fp); if (ret < 0) ts_err("rawdata write fail."); @@ -2579,7 +2620,7 @@ static int goodix_save_data(struct goodix_ts_test *ts_test, if (!data) return -ENOMEM; - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); ret = fs_write(data, bytes, fp); if (ret < 0) { ts_err("rawdata record lable failed"); @@ -2615,7 +2656,7 @@ static int goodix_save_data(struct goodix_ts_test *ts_test, goto save_end; } - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER - bytes, "\n"); ret = fs_write(data, bytes, fp); if (ret < 0) ts_err("rawdata data record lable fail."); @@ -2637,7 +2678,7 @@ static int goodix_save_tail(struct goodix_ts_test *ts_test, if (!data) return -ENOMEM; - bytes += sprintf(&data[bytes], "\n"); + bytes += scnprintf(&data[bytes], MAX_DATA_BUFFER, "\n"); ret = fs_write(data, bytes, fp); if (ret < 0) ts_err("tail write failed"); @@ -2653,7 +2694,7 @@ static void goodix_save_result_data(struct goodix_ts_test *ts_test) struct file *fp = NULL; /* format result file */ - sprintf(save_path, GOODIX_RESULT_SAVE_PATH); + scnprintf(save_path, ARRAY_SIZE(save_path), GOODIX_RESULT_SAVE_PATH); ts_info("save result IN, file_name:%s", save_path); fp = filp_open(save_path, O_CREAT | O_WRONLY | O_TRUNC, 0666); @@ -2705,32 +2746,28 @@ static void goodix_put_test_result(struct goodix_ts_test *ts_test, /* save rawdata to info->buff, only one frame */ if (ts_test->rawdata[0].size) { for (i = 0; i < ts_test->rawdata[0].size; i++) - info->buff[info->used_size + i] = - ts_test->rawdata[0].data[i]; + info->buff[info->used_size + i] = ts_test->rawdata[0].data[i]; info->used_size += ts_test->rawdata[0].size; } /* save noisedata to info->buff */ if (ts_test->noisedata[0].size) { for (i = 0; i < ts_test->noisedata[0].size; i++) - info->buff[info->used_size + i] = - ts_test->noisedata[0].data[i]; + info->buff[info->used_size + i] = ts_test->noisedata[0].data[i]; info->used_size += ts_test->noisedata[0].size; } /* save self_noisedata to info->buff */ if (ts_test->self_noisedata.size) { for (i = 0; i < ts_test->self_noisedata.size; i++) - info->buff[info->used_size + i] = - ts_test->self_noisedata.data[i]; + info->buff[info->used_size + i] = ts_test->self_noisedata.data[i]; info->used_size += ts_test->self_noisedata.size; } /* save self_rawdata to info->buff */ if (ts_test->self_rawdata.size) { for (i = 0; i < ts_test->self_rawdata.size; i++) - info->buff[info->used_size + i] = - ts_test->self_rawdata.data[i]; + info->buff[info->used_size + i] = ts_test->self_rawdata.data[i]; info->used_size += ts_test->self_rawdata.size; } @@ -2840,7 +2877,7 @@ static void goodix_put_test_result(struct goodix_ts_test *ts_test, TS_RAWDATA_RESULT_MAX); goodix_strncat(ts_test->test_info, "\n", TS_RAWDATA_RESULT_MAX); - strncpy(info->result, ts_test->test_info, TS_RAWDATA_RESULT_MAX - 1); + strlcpy(info->result, ts_test->test_info, TS_RAWDATA_RESULT_MAX - 1); #ifdef SAVE_IN_CSV /* save result to file */ @@ -2848,8 +2885,7 @@ static void goodix_put_test_result(struct goodix_ts_test *ts_test, #endif } -static int goodix_do_inspect(struct goodix_ts_core *cd, - struct ts_rawdata_info *info) +static int goodix_do_inspect(struct goodix_ts_core *cd, struct ts_rawdata_info *info) { int ret; struct goodix_ts_test *ts_test = NULL; @@ -2867,7 +2903,7 @@ static int goodix_do_inspect(struct goodix_ts_core *cd, ret = goodix_tptest_prepare(ts_test); if (ret < 0) { ts_err("Failed to prepare TP test, exit"); - strncpy(info->result, "[FAIL]-0F-software reason\n", + strlcpy(info->result, "[FAIL]-0F-software reason\n", TS_RAWDATA_RESULT_MAX - 1); goto exit_finish; } diff --git a/goodix_berlin_driver/goodix_ts_tools.c b/goodix_berlin_driver/goodix_ts_tools.c index 9a69feeb6d..0807cb97ca 100644 --- a/goodix_berlin_driver/goodix_ts_tools.c +++ b/goodix_berlin_driver/goodix_ts_tools.c @@ -265,7 +265,7 @@ static long goodix_tools_ioctl(struct file *filp, unsigned int cmd, const struct goodix_ts_hw_ops *hw_ops; struct goodix_ic_config *temp_cfg = NULL; - if (dev->ts_core == NULL) { + if (!dev->ts_core) { ts_err("Tools module not register"); return -EINVAL; } @@ -312,7 +312,7 @@ static long goodix_tools_ioctl(struct file *filp, unsigned int cmd, break; case GTP_SEND_CONFIG: temp_cfg = kzalloc(sizeof(struct goodix_ic_config), GFP_KERNEL); - if (temp_cfg == NULL) { + if (!temp_cfg) { ts_err("Memory allco err"); ret = -ENOMEM; goto err_out; @@ -353,8 +353,7 @@ static long goodix_tools_ioctl(struct file *filp, unsigned int cmd, ts_err("Async data write failed"); break; case GTP_TOOLS_VER: - ret = copy_to_user((u8 *)arg, &goodix_tools_ver, - sizeof(u16)); + ret = copy_to_user((u8 *)arg, &goodix_tools_ver, sizeof(u16)); if (ret) ts_err("failed copy driver version info to user"); break; @@ -405,6 +404,7 @@ static int goodix_tools_open(struct inode *inode, struct file *filp) static int goodix_tools_release(struct inode *inode, struct file *filp) { int ret = 0; + /* when the last close this dev node unregister the module */ goodix_tools_dev->ts_core->tools_ctrl_sync = false; atomic_set(&goodix_tools_dev->in_use, 0); From 29c3b3659151c475bdc677258326d41145614004 Mon Sep 17 00:00:00 2001 From: Fei Mao Date: Wed, 5 Jan 2022 16:56:27 +0800 Subject: [PATCH 010/170] touch: enable goodix_brl driver Enable new Goodix touch driver; add notifier from drm display. Change-Id: Idffa17e7cb41b1685753d58184a0fe6c8f71d2ac --- Android.mk | 11 ++ Kbuild | 18 +++ config/gki_kalamatouch.conf | 1 + config/gki_kalamatouchconf.h | 1 + goodix_berlin_driver/goodix_ts_core.c | 206 ++++++++++++++++++++++---- goodix_berlin_driver/goodix_ts_core.h | 21 ++- touch_driver_board.mk | 3 +- touch_driver_product.mk | 3 +- 8 files changed, 231 insertions(+), 33 deletions(-) diff --git a/Android.mk b/Android.mk index f433727261..3654990a2b 100644 --- a/Android.mk +++ b/Android.mk @@ -35,4 +35,15 @@ LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### +########################################################### +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) +LOCAL_MODULE := goodix_ts.ko +LOCAL_MODULE_KBUILD_NAME := goodix_ts.ko +LOCAL_MODULE_TAGS := optional +#LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/Build_external_kernelmodule.mk +########################################################### + endif # DLKM check diff --git a/Kbuild b/Kbuild index ee962bdcb4..b55957af94 100644 --- a/Kbuild +++ b/Kbuild @@ -86,4 +86,22 @@ ifeq ($(CONFIG_TOUCHSCREEN_NT36XXX_I2C), y) obj-$(CONFIG_MSM_TOUCH) += nt36xxx-i2c.o endif +ifeq ($(CONFIG_TOUCHSCREEN_GOODIX_BRL), y) + LINUX_INC += -include $(TOUCH_ROOT)/goodix_berlin_driver/goodix_ts_core.h + + goodix_ts-y := \ + ./goodix_berlin_driver/goodix_ts_core.o \ + ./goodix_berlin_driver/goodix_brl_hw.o \ + ./goodix_berlin_driver/goodix_cfg_bin.o \ + ./goodix_berlin_driver/goodix_ts_utils.o \ + ./goodix_berlin_driver/goodix_brl_fwupdate.o \ + ./goodix_berlin_driver/goodix_ts_tools.o \ + ./goodix_berlin_driver/goodix_ts_gesture.o \ + ./goodix_berlin_driver/goodix_ts_inspect.o \ + ./goodix_berlin_driver/goodix_brl_spi.o \ + ./goodix_berlin_driver/goodix_brl_i2c.o + + obj-$(CONFIG_MSM_TOUCH) += goodix_ts.o +endif + CDEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/config/gki_kalamatouch.conf b/config/gki_kalamatouch.conf index 24e0f11348..4445b828df 100644 --- a/config/gki_kalamatouch.conf +++ b/config/gki_kalamatouch.conf @@ -1,2 +1,3 @@ export CONFIG_TOUCHSCREEN_NT36XXX_I2C=y +export CONFIG_TOUCHSCREEN_GOODIX_BRL=y export CONFIG_MSM_TOUCH=m diff --git a/config/gki_kalamatouchconf.h b/config/gki_kalamatouchconf.h index fdb5f87a31..4747411498 100644 --- a/config/gki_kalamatouchconf.h +++ b/config/gki_kalamatouchconf.h @@ -4,4 +4,5 @@ */ #define CONFIG_TOUCHSCREEN_NT36XXX_I2C 1 +#define CONFIG_TOUCHSCREEN_GOODIX_BRL 1 diff --git a/goodix_berlin_driver/goodix_ts_core.c b/goodix_berlin_driver/goodix_ts_core.c index 8b64e13521..afa44c80c4 100644 --- a/goodix_berlin_driver/goodix_ts_core.c +++ b/goodix_berlin_driver/goodix_ts_core.c @@ -19,6 +19,7 @@ #include #include #include +#include #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) #include @@ -30,6 +31,32 @@ #define GOODIX_DEFAULT_CFG_NAME "goodix_cfg_group.cfg" #define GOOIDX_INPUT_PHYS "goodix_ts/input0" +#if defined(CONFIG_DRM) +static struct drm_panel *active_panel; +static void goodix_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *event, void *client_data); + +static void goodix_register_for_panel_events(struct device_node *dp, + struct goodix_ts_core *cd) +{ + void *cookie; + + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, active_panel, + &goodix_panel_notifier_callback, cd); + if (!cookie) { + pr_err("Failed to register for panel events\n"); + return; + } + + ts_debug("registered for panel notifications panel: 0x%x\n", + active_panel); + + cd->notifier_cookie = cookie; +} + +#endif + struct goodix_module goodix_modules; int core_module_prob_sate = CORE_MODULE_UNPROBED; @@ -1821,25 +1848,51 @@ out: return 0; } -/* goodix FB test */ -/* -void goodix_fb_ext_ctrl(int suspend) +#if defined(CONFIG_DRM) + +static void goodix_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data) { - struct goodix_ts_core *cd = goodix_modules.core_data; + struct goodix_ts_core *core_data = client_data; - if (!cd) + if (!notification) { + pr_err("Invalid notification\n"); return; + } - if (suspend) - goodix_ts_suspend(cd); - else - goodix_ts_resume(cd); + ts_debug("Notification type:%d, early_trigger:%d", + notification->notif_type, + notification->notif_data.early_trigger); + switch (notification->notif_type) { + case DRM_PANEL_EVENT_UNBLANK: + if (!notification->notif_data.early_trigger) + goodix_ts_resume(core_data); + break; + + case DRM_PANEL_EVENT_BLANK: + if (notification->notif_data.early_trigger) + goodix_ts_suspend(core_data); + break; + + case DRM_PANEL_EVENT_BLANK_LP: + ts_debug("received lp event\n"); + break; + + case DRM_PANEL_EVENT_FPS_CHANGE: + ts_debug("Received fps change old fps:%d new fps:%d\n", + notification->notif_data.old_fps, + notification->notif_data.new_fps); + break; + + default: + ts_debug("notification serviced :%d\n", + notification->notif_type); + break; + } } -EXPORT_SYMBOL(goodix_fb_ext_ctrl); -*/ +#elif IS_ENABLED(CONFIG_FB) -#if IS_ENABLED(CONFIG_FB) /** * goodix_ts_fb_notifier_callback - Framebuffer notifier callback * Called by kernel during framebuffer blanck/unblank phrase @@ -1868,8 +1921,9 @@ int goodix_ts_fb_notifier_callback(struct notifier_block *self, } #endif -#if IS_ENABLED(CONFIG_PM) -#if !IS_ENABLED(CONFIG_FB) && !IS_ENABLED(CONFIG_HAS_EARLYSUSPEND) + +#if defined(CONFIG_PM) && !defined(CONFIG_DRM) +#if !defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND) /** * goodix_ts_pm_suspend - PM suspend function * Called by kernel during system suspend phrase @@ -1952,7 +2006,11 @@ int goodix_ts_stage2_init(struct goodix_ts_core *cd) } ts_info("success register irq"); -#if IS_ENABLED(CONFIG_FB) +#if defined(CONFIG_DRM) + if (cd->touch_environment && !strcmp(cd->touch_environment, "pvm")) + goodix_register_for_panel_events(cd->bus->dev->of_node, cd); + +#elif defined(CONFIG_FB) cd->fb_notifier.notifier_call = goodix_ts_fb_notifier_callback; if (fb_register_client(&cd->fb_notifier)) ts_err("Failed to register fb notifier client:%d", ret); @@ -2107,6 +2165,77 @@ static int goodix_start_later_init(struct goodix_ts_core *ts_core) return 0; } +#if defined(CONFIG_DRM) +static int goodix_check_dt(struct device_node *np) +{ + int i; + int count; + struct device_node *node; + struct drm_panel *panel; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) + return 0; + + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + panel = of_drm_find_panel(node); + of_node_put(node); + if (!IS_ERR(panel)) { + active_panel = panel; + return 0; + } + } + + return PTR_ERR(panel); +} + +static int goodix_check_default_tp(struct device_node *dt, const char *prop) +{ + const char **active_tp = NULL; + int count, tmp, score = 0; + const char *active; + int ret, i; + + count = of_property_count_strings(dt->parent, prop); + if (count <= 0 || count > 3) + return -ENODEV; + + active_tp = kcalloc(count, sizeof(char *), GFP_KERNEL); + if (!active_tp) { + ts_err("FTS alloc failed\n"); + return -ENOMEM; + } + + ret = of_property_read_string_array(dt->parent, prop, + active_tp, count); + if (ret < 0) { + ts_err("fail to read %s %d\n", prop, ret); + ret = -ENODEV; + goto out; + } + + for (i = 0; i < count; i++) { + active = active_tp[i]; + if (active != NULL) { + tmp = of_device_is_compatible(dt, active); + if (tmp > 0) + score++; + } + } + + if (score <= 0) { + ts_err("not match this driver\n"); + ret = -ENODEV; + goto out; + } + ret = 0; +out: + kfree(active_tp); + return ret; +} + +#endif /** * goodix_ts_probe - called by kernel when Goodix touch * platform driver is added. @@ -2116,8 +2245,9 @@ static int goodix_ts_probe(struct platform_device *pdev) struct goodix_ts_core *core_data = NULL; struct goodix_bus_interface *bus_interface; int ret; + struct device_node *node; - ts_info("IN"); + ts_info("goodix_ts_probe IN"); bus_interface = pdev->dev.platform_data; if (!bus_interface) { @@ -2125,6 +2255,22 @@ static int goodix_ts_probe(struct platform_device *pdev) core_module_prob_sate = CORE_MODULE_PROB_FAILED; return -ENODEV; } + node = bus_interface->dev->of_node; + +#if defined(CONFIG_DRM) + ret = goodix_check_dt(node); + if (ret == -EPROBE_DEFER) + return ret; + + if (ret) { + if (!goodix_check_default_tp(node, "qcom,touch-active")) + ret = -EPROBE_DEFER; + else + ret = -ENODEV; + + return ret; + } +#endif core_data = devm_kzalloc(&pdev->dev, sizeof(struct goodix_ts_core), GFP_KERNEL); @@ -2135,8 +2281,7 @@ static int goodix_ts_probe(struct platform_device *pdev) if (IS_ENABLED(CONFIG_OF) && bus_interface->dev->of_node) { /* parse devicetree property */ - ret = goodix_parse_dt(bus_interface->dev->of_node, - &core_data->board_data); + ret = goodix_parse_dt(node, &core_data->board_data); if (ret) { ts_err("failed parse device info form dts, %d", ret); return -EINVAL; @@ -2214,7 +2359,11 @@ static int goodix_ts_remove(struct platform_device *pdev) gesture_module_exit(); inspect_module_exit(); hw_ops->irq_enable(core_data, false); - #if IS_ENABLED(CONFIG_FB) + + #if defined(CONFIG_DRM) + if (core_data->notifier_cookie) + panel_event_notifier_unregister(core_data->notifier_cookie); + #elif IS_ENABLED(CONFIG_FB) fb_unregister_client(&core_data->fb_notifier); #endif core_module_prob_sate = CORE_MODULE_REMOVED; @@ -2233,9 +2382,9 @@ static int goodix_ts_remove(struct platform_device *pdev) return 0; } -#if IS_ENABLED(CONFIG_PM) +#if defined(CONFIG_PM) && !defined(CONFIG_DRM) static const struct dev_pm_ops dev_pm_ops = { -#if !IS_ENABLED(CONFIG_FB) && !IS_ENABLED(CONFIG_HAS_EARLYSUSPEND) +#if !defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND) .suspend = goodix_ts_pm_suspend, .resume = goodix_ts_pm_resume, #endif @@ -2252,7 +2401,7 @@ static struct platform_driver goodix_ts_driver = { .driver = { .name = GOODIX_CORE_DRIVER_NAME, .owner = THIS_MODULE, -#if IS_ENABLED(CONFIG_PM) +#if defined(CONFIG_PM) && !defined(CONFIG_DRM) .pm = &dev_pm_ops, #endif }, @@ -2263,14 +2412,12 @@ static struct platform_driver goodix_ts_driver = { static int __init goodix_ts_core_init(void) { - int ret; + int ret = 0; ts_info("Core layer init:%s", GOODIX_DRIVER_VERSION); -#ifdef CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI + ret = goodix_spi_bus_init(); -#else - ret = goodix_i2c_bus_init(); -#endif + ret |= goodix_i2c_bus_init(); if (ret) { ts_err("failed add bus driver"); return ret; @@ -2282,11 +2429,10 @@ static void __exit goodix_ts_core_exit(void) { ts_info("Core layer exit"); platform_driver_unregister(&goodix_ts_driver); -#ifdef CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI + goodix_spi_bus_exit(); -#else + goodix_i2c_bus_exit(); -#endif } late_initcall(goodix_ts_core_init); diff --git a/goodix_berlin_driver/goodix_ts_core.h b/goodix_berlin_driver/goodix_ts_core.h index b723dc5279..0c3b3bb145 100644 --- a/goodix_berlin_driver/goodix_ts_core.h +++ b/goodix_berlin_driver/goodix_ts_core.h @@ -500,8 +500,27 @@ struct goodix_ts_core { struct notifier_block ts_notifier; struct goodix_ts_esd ts_esd; -#if IS_ENABLED(CONFIG_FB) +#if defined(CONFIG_DRM) struct notifier_block fb_notifier; + void *notifier_cookie; + const char *touch_environment; +#elif defined(CONFIG_FB) + struct notifier_block fb_notifier; +#endif + +#ifdef CONFIG_GOODIX_TRUSTED_TOUCH + struct trusted_touch_vm_info *vm_info; + struct mutex fts_clk_io_ctrl_mutex; + struct completion trusted_touch_powerdown; + struct clk *core_clk; + struct clk *iface_clk; + atomic_t trusted_touch_initialized; + atomic_t trusted_touch_enabled; + atomic_t trusted_touch_transition; + atomic_t trusted_touch_event; + atomic_t trusted_touch_abort_status; + atomic_t delayed_vm_probe_pending; + atomic_t trusted_touch_mode; #endif }; diff --git a/touch_driver_board.mk b/touch_driver_board.mk index 92e64a0ea9..3f26729e74 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -1,6 +1,7 @@ ifneq ($(TARGET_BOARD_AUTO),true) ifeq ($(call is-board-platform-in-list,$(TARGET_BOARD_PLATFORM)),true) - BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko endif endif diff --git a/touch_driver_product.mk b/touch_driver_product.mk index 170700f977..28ee488175 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -1,2 +1,3 @@ -PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ No newline at end of file +PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko From b82040cf05a4c2bc369286f3aba32b6a6f1877ca Mon Sep 17 00:00:00 2001 From: Shashank Babu Chinta Venkata Date: Thu, 17 Feb 2022 15:39:34 -0800 Subject: [PATCH 011/170] touch: goodix: flip panel max x and panel max y Invert panel max x and panel max y for scenarios where IC is mounted inversely. Signed-off-by: Shashank Babu Chinta Venkata --- goodix_berlin_driver/goodix_ts_core.c | 18 ++++++++++++++++-- goodix_berlin_driver/goodix_ts_core.h | 2 ++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/goodix_berlin_driver/goodix_ts_core.c b/goodix_berlin_driver/goodix_ts_core.c index afa44c80c4..e67b253df0 100644 --- a/goodix_berlin_driver/goodix_ts_core.c +++ b/goodix_berlin_driver/goodix_ts_core.c @@ -964,6 +964,13 @@ static int goodix_parse_dt_resolution(struct device_node *node, return ret; } + board_data->invert_xy = of_property_read_bool(node, + "invert_xy"); + if (board_data->invert_xy) { + swap(board_data->panel_max_x, board_data->panel_max_y); + ts_info("Panel max x and max y inverted\n"); + } + ret = of_property_read_u32(node, "goodix,panel-max-w", &board_data->panel_max_w); if (ret) { @@ -1152,7 +1159,8 @@ static void goodix_ts_report_pen(struct input_dev *dev, } static void goodix_ts_report_finger(struct input_dev *dev, - struct goodix_touch_data *touch_data) + struct goodix_touch_data *touch_data, + bool invert_xy) { unsigned int touch_num = touch_data->touch_num; int i; @@ -1165,6 +1173,11 @@ static void goodix_ts_report_finger(struct input_dev *dev, touch_data->coords[i].x, touch_data->coords[i].y, touch_data->coords[i].w); + + if (invert_xy) + swap(touch_data->coords[i].x, + touch_data->coords[i].y); + input_mt_slot(dev, i); input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); input_report_abs(dev, ABS_MT_POSITION_X, @@ -1246,7 +1259,8 @@ static irqreturn_t goodix_ts_threadirq_func(int irq, void *data) if (ts_event->event_type == EVENT_TOUCH) { /* report touch */ goodix_ts_report_finger(core_data->input_dev, - &ts_event->touch_data); + &ts_event->touch_data, + core_data->board_data.invert_xy); } if (core_data->board_data.pen_enable && ts_event->event_type == EVENT_PEN) { diff --git a/goodix_berlin_driver/goodix_ts_core.h b/goodix_berlin_driver/goodix_ts_core.h index 0c3b3bb145..0f0fce5e64 100644 --- a/goodix_berlin_driver/goodix_ts_core.h +++ b/goodix_berlin_driver/goodix_ts_core.h @@ -270,6 +270,7 @@ struct goodix_module { * @irq_flag: irq trigger type * @swap_axis: whether swaw x y axis * @panel_max_x/y/w/p: resolution and size + * @invert_xy: invert x and y for inversely mounted IC * @pannel_key_map: key map * @fw_name: name of the firmware image */ @@ -287,6 +288,7 @@ struct goodix_ts_board_data { unsigned int panel_max_y; unsigned int panel_max_w; /*major and minor*/ unsigned int panel_max_p; /*pressure*/ + bool invert_xy; bool pen_enable; char fw_name[GOODIX_MAX_STR_LABLE_LEN]; From 912fce5a745fce3854f618faecf925444941707c Mon Sep 17 00:00:00 2001 From: Shashank Babu Chinta Venkata Date: Fri, 25 Feb 2022 15:22:17 -0800 Subject: [PATCH 012/170] touch: goodix: force update firmware on each boot Force update firmware on each boot to appropriate firmware. Signed-off-by: Shashank Babu Chinta Venkata --- goodix_berlin_driver/goodix_brl_fwupdate.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/goodix_berlin_driver/goodix_brl_fwupdate.c b/goodix_berlin_driver/goodix_brl_fwupdate.c index dbd9c63912..fc2db13e6f 100644 --- a/goodix_berlin_driver/goodix_brl_fwupdate.c +++ b/goodix_berlin_driver/goodix_brl_fwupdate.c @@ -949,10 +949,6 @@ int goodix_fw_update_proc(struct fw_update_ctrl *fwu_ctrl) if (!(fwu_ctrl->mode & UPDATE_MODE_FORCE)) { ret = goodix_fw_version_compare(fwu_ctrl); - if (!ret) { - ts_info("no need to upgrade"); - return 0; - } ts_info("need to upgrade"); } From 5fda2cdf25e7db28c92d5203bc6bc76982ffb1ac Mon Sep 17 00:00:00 2001 From: Jessica Zhang Date: Tue, 15 Feb 2022 13:40:18 -0800 Subject: [PATCH 013/170] touch: add atmel touch driver Change-Id: I50b30549602e669a102523fc73ccb1e40c689ff0 Signed-off-by: Jessica Zhang --- Android.mk | 11 + Kbuild | 8 + NOTICE | 14 +- atmel_mxt/atmel_mxt_ts.c | 3996 ++++++++++++++++++++++++++++++++++ config/gki_kalamatouch.conf | 1 + config/gki_kalamatouchconf.h | 1 + touch_driver_board.mk | 3 +- touch_driver_product.mk | 3 +- 8 files changed, 4034 insertions(+), 3 deletions(-) create mode 100644 atmel_mxt/atmel_mxt_ts.c diff --git a/Android.mk b/Android.mk index 3654990a2b..7c830a327f 100644 --- a/Android.mk +++ b/Android.mk @@ -46,4 +46,15 @@ LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### +########################################################### +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) +LOCAL_MODULE := atmel_mxt_ts.ko +LOCAL_MODULE_KBUILD_NAME := atmel_mxt_ts.ko +LOCAL_MODULE_TAGS := optional +#LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/Build_external_kernelmodule.mk +########################################################### + endif # DLKM check diff --git a/Kbuild b/Kbuild index b55957af94..7e1d2ca54c 100644 --- a/Kbuild +++ b/Kbuild @@ -104,4 +104,12 @@ ifeq ($(CONFIG_TOUCHSCREEN_GOODIX_BRL), y) obj-$(CONFIG_MSM_TOUCH) += goodix_ts.o endif +ifeq ($(CONFIG_TOUCHSCREEN_ATMEL_MXT), y) + + atmel_mxt_ts-y := \ + ./atmel_mxt/atmel_mxt_ts.o + + obj-$(CONFIG_MSM_TOUCH) += atmel_mxt_ts.o +endif + CDEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/NOTICE b/NOTICE index e4c914eac5..0b3cbd5aa5 100644 --- a/NOTICE +++ b/NOTICE @@ -109,4 +109,16 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - */ \ No newline at end of file + */ + +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Atmel maXTouch Touchscreen driver + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Copyright (C) 2011-2014 Atmel Corporation + * Copyright (C) 2012 Google, Inc. + * Copyright (C) 2016 Zodiac Inflight Innovations + * + * Author: Joonyoung Shim + */ diff --git a/atmel_mxt/atmel_mxt_ts.c b/atmel_mxt/atmel_mxt_ts.c new file mode 100644 index 0000000000..d2f9162a4b --- /dev/null +++ b/atmel_mxt/atmel_mxt_ts.c @@ -0,0 +1,3996 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Atmel maXTouch Touchscreen driver + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Copyright (C) 2011-2014 Atmel Corporation + * Copyright (C) 2012 Google, Inc. + * Copyright (C) 2016 Zodiac Inflight Innovations + * + * Author: Joonyoung Shim + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_DRM +#include +#endif + +/* Firmware files */ +#define MXT_FW_NAME "maxtouch.fw" +#define MXT_CFG_NAME "maxtouch.cfg" +#define MXT_CFG_MAGIC "OBP_RAW V1" + +/* Registers */ +#define MXT_OBJECT_START 0x07 +#define MXT_OBJECT_SIZE 6 +#define MXT_INFO_CHECKSUM_SIZE 3 +#define MXT_MAX_BLOCK_WRITE 256 + +/* Object types */ +#define MXT_DEBUG_DIAGNOSTIC_T37 37 +#define MXT_GEN_MESSAGE_T5 5 +#define MXT_GEN_COMMAND_T6 6 +#define MXT_GEN_POWER_T7 7 +#define MXT_GEN_ACQUIRE_T8 8 +#define MXT_GEN_DATASOURCE_T53 53 +#define MXT_TOUCH_MULTI_T9 9 +#define MXT_TOUCH_KEYARRAY_T15 15 +#define MXT_TOUCH_PROXIMITY_T23 23 +#define MXT_TOUCH_PROXKEY_T52 52 +#define MXT_PROCI_GRIPFACE_T20 20 +#define MXT_PROCG_NOISE_T22 22 +#define MXT_PROCI_ONETOUCH_T24 24 +#define MXT_PROCI_TWOTOUCH_T27 27 +#define MXT_PROCI_GRIP_T40 40 +#define MXT_PROCI_PALM_T41 41 +#define MXT_PROCI_TOUCHSUPPRESSION_T42 42 +#define MXT_PROCI_STYLUS_T47 47 +#define MXT_PROCG_NOISESUPPRESSION_T48 48 +#define MXT_SPT_COMMSCONFIG_T18 18 +#define MXT_SPT_GPIOPWM_T19 19 +#define MXT_SPT_SELFTEST_T25 25 +#define MXT_SPT_CTECONFIG_T28 28 +#define MXT_SPT_USERDATA_T38 38 +#define MXT_SPT_DIGITIZER_T43 43 +#define MXT_SPT_MESSAGECOUNT_T44 44 +#define MXT_SPT_CTECONFIG_T46 46 +#define MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71 71 +#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100 + +/* Delay times */ +#define MXT_BACKUP_TIME 50 /* msec */ +#define MXT_RESET_TIME 200 /* msec */ +#define MXT_RESET_TIMEOUT 3000 /* msec */ +#define MXT_CRC_TIMEOUT 1000 /* msec */ +#define MXT_FW_RESET_TIME 3000 /* msec */ +#define MXT_FW_CHG_TIMEOUT 300 /* msec */ +#define MXT_WAKEUP_TIME 25 /* msec */ +#define MXT_REGULATOR_DELAY 150 /* msec */ +#define MXT_POWERON_DELAY 150 /* msec */ + +/* recommended voltage specifications */ +#define MXT_VDD_VTG_MIN_UV 1800000 +#define MXT_VDD_VTG_MAX_UV 1800000 +#define MXT_AVDD_VTG_MIN_UV 3000000 +#define MXT_AVDD_VTG_MAX_UV 3300000 +#define MXT_XVDD_VTG_MIN_UV 2700000 +#define MXT_XVDD_VTG_MAX_UV 10000000 + +/* recommended load specifications */ +#define MXT_ENABLE_LOAD 10000 +#define MXT_DISABLE_LOAD 0 + +/* MXT_GEN_MESSAGE_T5 object */ +#define MXT_RPTID_NOMSG 0xff + +/* MXT_GEN_COMMAND_T6 field */ +#define MXT_COMMAND_RESET 0 +#define MXT_COMMAND_BACKUPNV 1 +#define MXT_COMMAND_CALIBRATE 2 +#define MXT_COMMAND_REPORTALL 3 +#define MXT_COMMAND_DIAGNOSTIC 5 + +/* Define for T6 status byte */ +#define MXT_T6_STATUS_RESET BIT(7) +#define MXT_T6_STATUS_OFL BIT(6) +#define MXT_T6_STATUS_SIGERR BIT(5) +#define MXT_T6_STATUS_CAL BIT(4) +#define MXT_T6_STATUS_CFGERR BIT(3) +#define MXT_T6_STATUS_COMSERR BIT(2) + +/* MXT_GEN_POWER_T7 field */ +struct t7_config { + u8 idle; + u8 active; +} __packed; + +#define MXT_POWER_CFG_RUN 0 +#define MXT_POWER_CFG_DEEPSLEEP 1 + +/* MXT_TOUCH_MULTI_T9 field */ +#define MXT_T9_CTRL 0 +#define MXT_T9_XSIZE 3 +#define MXT_T9_YSIZE 4 +#define MXT_T9_ORIENT 9 +#define MXT_T9_RANGE 18 + +/* MXT_TOUCH_MULTI_T9 status */ +#define MXT_T9_UNGRIP BIT(0) +#define MXT_T9_SUPPRESS BIT(1) +#define MXT_T9_AMP BIT(2) +#define MXT_T9_VECTOR BIT(3) +#define MXT_T9_MOVE BIT(4) +#define MXT_T9_RELEASE BIT(5) +#define MXT_T9_PRESS BIT(6) +#define MXT_T9_DETECT BIT(7) + +struct t9_range { + __le16 x; + __le16 y; +} __packed; + +/* MXT_TOUCH_MULTI_T9 orient */ +#define MXT_T9_ORIENT_SWITCH BIT(0) +#define MXT_T9_ORIENT_INVERTX BIT(1) +#define MXT_T9_ORIENT_INVERTY BIT(2) + +/* MXT_SPT_COMMSCONFIG_T18 */ +#define MXT_COMMS_CTRL 0 +#define MXT_COMMS_CMD 1 +#define MXT_COMMS_RETRIGEN BIT(6) + +/* MXT_DEBUG_DIAGNOSTIC_T37 */ +#define MXT_DIAGNOSTIC_PAGEUP 0x01 +#define MXT_DIAGNOSTIC_DELTAS 0x10 +#define MXT_DIAGNOSTIC_REFS 0x11 +#define MXT_DIAGNOSTIC_SIZE 128 + +#define MXT_FAMILY_1386 160 +#define MXT1386_COLUMNS 3 +#define MXT1386_PAGES_PER_COLUMN 8 + +struct t37_debug { +#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 + u8 mode; + u8 page; + u8 data[MXT_DIAGNOSTIC_SIZE]; +#endif +}; + +/* Define for MXT_GEN_COMMAND_T6 */ +#define MXT_BOOT_VALUE 0xa5 +#define MXT_RESET_VALUE 0x01 +#define MXT_BACKUP_VALUE 0x55 + +/* T100 Multiple Touch Touchscreen */ +#define MXT_T100_CTRL 0 +#define MXT_T100_CFG1 1 +#define MXT_T100_TCHAUX 3 +#define MXT_T100_XSIZE 9 +#define MXT_T100_XRANGE 13 +#define MXT_T100_YSIZE 20 +#define MXT_T100_YRANGE 24 + +#define MXT_T100_CFG_SWITCHXY BIT(5) +#define MXT_T100_CFG_INVERTY BIT(6) +#define MXT_T100_CFG_INVERTX BIT(7) + +#define MXT_T100_TCHAUX_VECT BIT(0) +#define MXT_T100_TCHAUX_AMPL BIT(1) +#define MXT_T100_TCHAUX_AREA BIT(2) + +#define MXT_T100_DETECT BIT(7) +#define MXT_T100_TYPE_MASK 0x70 + +enum t100_type { + MXT_T100_TYPE_FINGER = 1, + MXT_T100_TYPE_PASSIVE_STYLUS = 2, + MXT_T100_TYPE_HOVERING_FINGER = 4, + MXT_T100_TYPE_GLOVE = 5, + MXT_T100_TYPE_LARGE_TOUCH = 6, +}; + +#define MXT_DISTANCE_ACTIVE_TOUCH 0 +#define MXT_DISTANCE_HOVERING 1 + +#define MXT_TOUCH_MAJOR_DEFAULT 1 +#define MXT_PRESSURE_DEFAULT 1 + +/* Delay times */ +#define MXT_BACKUP_TIME 50 /* msec */ +#define MXT_RESET_GPIO_TIME 20 /* msec */ +#define MXT_RESET_INVALID_CHG 100 /* msec */ +#define MXT_RESET_TIME 200 /* msec */ +#define MXT_RESET_TIMEOUT 3000 /* msec */ +#define MXT_CRC_TIMEOUT 1000 /* msec */ +#define MXT_FW_RESET_TIME 3000 /* msec */ +#define MXT_FW_CHG_TIMEOUT 300 /* msec */ + +/* Command to unlock bootloader */ +#define MXT_UNLOCK_CMD_MSB 0xaa +#define MXT_UNLOCK_CMD_LSB 0xdc + +/* Bootloader mode status */ +#define MXT_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */ +#define MXT_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */ +#define MXT_FRAME_CRC_CHECK 0x02 +#define MXT_FRAME_CRC_FAIL 0x03 +#define MXT_FRAME_CRC_PASS 0x04 +#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ +#define MXT_BOOT_STATUS_MASK 0x3f +#define MXT_BOOT_EXTENDED_ID BIT(5) +#define MXT_BOOT_ID_MASK 0x1f + +/* Touchscreen absolute values */ +#define MXT_MAX_AREA 0xff + +#define MXT_PIXELS_PER_MM 20 + +#define MXT_COORDS_ARR_SIZE 4 + +struct mxt_info { + u8 family_id; + u8 variant_id; + u8 version; + u8 build; + u8 matrix_xsize; + u8 matrix_ysize; + u8 object_num; +}; + +struct mxt_object { + u8 type; + u16 start_address; + u8 size_minus_one; + u8 instances_minus_one; + u8 num_report_ids; +} __packed; + +struct mxt_dbg { + u16 t37_address; + u16 diag_cmd_address; + struct t37_debug *t37_buf; + unsigned int t37_pages; + unsigned int t37_nodes; + + struct v4l2_device v4l2; + struct v4l2_pix_format format; + struct video_device vdev; + struct vb2_queue queue; + struct mutex lock; + int input; +}; + +enum v4l_dbg_inputs { + MXT_V4L_INPUT_DELTAS, + MXT_V4L_INPUT_REFS, + MXT_V4L_INPUT_MAX, +}; + +enum mxt_suspend_mode { + MXT_SUSPEND_DEEP_SLEEP = 0, + MXT_SUSPEND_T9_CTRL = 1, +}; + +/* Config update context */ +struct mxt_cfg { + u8 *raw; + size_t raw_size; + off_t raw_pos; + + u8 *mem; + size_t mem_size; + int start_ofs; + + struct mxt_info info; +}; + +/* Each client has this additional data */ +struct mxt_data { + struct i2c_client *client; + struct input_dev *input_dev; + char phys[64]; /* device physical location */ + struct mxt_object *object_table; + struct mxt_info *info; + void *raw_info_block; + unsigned int irq; + unsigned int max_x; + unsigned int max_y; + bool invertx; + bool inverty; + bool xy_switch; + u8 xsize; + u8 ysize; + bool in_bootloader; + u16 mem_size; + u8 t100_aux_ampl; + u8 t100_aux_area; + u8 t100_aux_vect; + u8 max_reportid; + u32 config_crc; + u32 info_crc; + u8 bootloader_addr; + u8 *msg_buf; + u8 *cmd_buf; + u8 *read_buf; + u8 t6_status; + bool update_input; + u8 last_message_count; + u8 num_touchids; + u8 multitouch; + struct t7_config t7_cfg; + struct mxt_dbg dbg; + struct gpio_desc *reset_gpio; + struct gpio_desc *irq_gpio; + bool use_retrigen_workaround; + + /* Cached parameters from object table */ + u16 T5_address; + u8 T5_msg_size; + u8 T6_reportid; + u16 T6_address; + u16 T7_address; + u16 T71_address; + u8 T9_reportid_min; + u8 T9_reportid_max; + u16 T18_address; + u8 T19_reportid; + u16 T44_address; + u8 T100_reportid_min; + u8 T100_reportid_max; + + /* for fw update in bootloader */ + struct completion bl_completion; + + /* for reset handling */ + struct completion reset_completion; + + /* for config update handling */ + struct completion crc_completion; + + /* Enable reporting of input events */ + bool enable_reporting; + + /* Indicates whether device is in suspend */ + bool suspended; + + u32 *t19_keymap; + unsigned int t19_num_keys; + + enum mxt_suspend_mode suspend_mode; + + bool use_regulator; + struct regulator *reg_vdd; + struct regulator *reg_avdd; + struct regulator *reg_xvdd; + struct pinctrl *ts_pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; + + u32 panel_minx, panel_miny, panel_maxx, panel_maxy; + u32 disp_minx, disp_miny, disp_maxx, disp_maxy; + void *notifier_cookie; +}; + +struct mxt_vb2_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + +static size_t mxt_obj_size(const struct mxt_object *obj) +{ + return obj->size_minus_one + 1; +} + +static size_t mxt_obj_instances(const struct mxt_object *obj) +{ + return obj->instances_minus_one + 1; +} + +static int mxt_regulator_configure(struct mxt_data *data, bool enable); + +static bool mxt_object_readable(unsigned int type) +{ + switch (type) { + case MXT_GEN_COMMAND_T6: + case MXT_GEN_POWER_T7: + case MXT_GEN_ACQUIRE_T8: + case MXT_GEN_DATASOURCE_T53: + case MXT_TOUCH_MULTI_T9: + case MXT_TOUCH_KEYARRAY_T15: + case MXT_TOUCH_PROXIMITY_T23: + case MXT_TOUCH_PROXKEY_T52: + case MXT_TOUCH_MULTITOUCHSCREEN_T100: + case MXT_PROCI_GRIPFACE_T20: + case MXT_PROCG_NOISE_T22: + case MXT_PROCI_ONETOUCH_T24: + case MXT_PROCI_TWOTOUCH_T27: + case MXT_PROCI_GRIP_T40: + case MXT_PROCI_PALM_T41: + case MXT_PROCI_TOUCHSUPPRESSION_T42: + case MXT_PROCI_STYLUS_T47: + case MXT_PROCG_NOISESUPPRESSION_T48: + case MXT_SPT_COMMSCONFIG_T18: + case MXT_SPT_GPIOPWM_T19: + case MXT_SPT_SELFTEST_T25: + case MXT_SPT_CTECONFIG_T28: + case MXT_SPT_USERDATA_T38: + case MXT_SPT_DIGITIZER_T43: + case MXT_SPT_CTECONFIG_T46: + case MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71: + return true; + default: + return false; + } +} + +static void mxt_dump_message(struct mxt_data *data, u8 *message) +{ + dev_dbg(&data->client->dev, "message: %*ph\n", + data->T5_msg_size, message); +} + +static int mxt_wait_for_completion(struct mxt_data *data, + struct completion *comp, + unsigned int timeout_ms) +{ + struct device *dev = &data->client->dev; + unsigned long timeout = msecs_to_jiffies(timeout_ms); + long ret; + + ret = wait_for_completion_interruptible_timeout(comp, timeout); + if (ret < 0) { + return ret; + } else if (ret == 0) { + dev_err(dev, "Wait for completion timed out.\n"); + return -ETIMEDOUT; + } + return 0; +} + +static int mxt_bootloader_read(struct mxt_data *data, + u8 *val, unsigned int count) +{ + int ret; + struct i2c_msg msg; + + msg.addr = data->bootloader_addr; + msg.flags = data->client->flags & I2C_M_TEN; + msg.flags |= I2C_M_RD; + msg.len = count; + msg.buf = data->read_buf; + + ret = i2c_transfer(data->client->adapter, &msg, 1); + if (ret == 1) { + memcpy(val, msg.buf, count); + ret = 0; + } else { + ret = ret < 0 ? ret : -EIO; + dev_err(&data->client->dev, "%s: i2c recv failed (%d)\n", + __func__, ret); + } + + return ret; +} + +static int mxt_bootloader_write(struct mxt_data *data, + const u8 * const val, unsigned int count) +{ + int ret; + struct i2c_msg msg; + + msg.addr = data->bootloader_addr; + msg.flags = data->client->flags & I2C_M_TEN; + msg.len = count; + msg.buf = (u8 *)val; + + ret = i2c_transfer(data->client->adapter, &msg, 1); + if (ret == 1) { + ret = 0; + } else { + ret = ret < 0 ? ret : -EIO; + dev_err(&data->client->dev, "%s: i2c send failed (%d)\n", + __func__, ret); + } + + return ret; +} + +static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry) +{ + u8 appmode = data->client->addr; + u8 bootloader; + u8 family_id = data->info ? data->info->family_id : 0; + + switch (appmode) { + case 0x4a: + case 0x4b: + /* Chips after 1664S use different scheme */ + if (retry || family_id >= 0xa2) { + bootloader = appmode - 0x24; + break; + } + fallthrough; /* for normal case */ + case 0x4c: + case 0x4d: + case 0x5a: + case 0x5b: + bootloader = appmode - 0x26; + break; + + default: + dev_err(&data->client->dev, + "Appmode i2c address 0x%02x not found\n", + appmode); + return -EINVAL; + } + + data->bootloader_addr = bootloader; + return 0; +} + +static int mxt_probe_bootloader(struct mxt_data *data, bool alt_address) +{ + struct device *dev = &data->client->dev; + int error; + u8 val; + bool crc_failure; + + error = mxt_lookup_bootloader_address(data, alt_address); + if (error) + return error; + + error = mxt_bootloader_read(data, &val, 1); + if (error) + return error; + + /* Check app crc fail mode */ + crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL; + + dev_err(dev, "Detected bootloader, status:%02X%s\n", + val, crc_failure ? ", APP_CRC_FAIL" : ""); + + return 0; +} + +static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) +{ + struct device *dev = &data->client->dev; + u8 ver = 0; + u8 *buf = kzalloc(4, GFP_KERNEL); + + if ((val & MXT_BOOT_EXTENDED_ID) && buf) { + if (mxt_bootloader_read(data, buf, 3) != 0) { + dev_err(dev, "%s: i2c failure\n", __func__); + return val; + } + + dev_dbg(dev, "Bootloader ID:%d Version:%d\n", + *(buf + 1), *(buf + 2)); + ver = *(buf + 0); + kfree(buf); + return ver; + } else { + dev_dbg(dev, "Bootloader ID:%d\n", val & MXT_BOOT_ID_MASK); + + return val; + } +} + +static int mxt_check_bootloader(struct mxt_data *data, unsigned int state, + bool wait) +{ + struct device *dev = &data->client->dev; + u8 val; + int ret; + +recheck: + if (wait) { + /* + * In application update mode, the interrupt + * line signals state transitions. We must wait for the + * CHG assertion before reading the status byte. + * Once the status byte has been read, the line is deasserted. + */ + ret = mxt_wait_for_completion(data, &data->bl_completion, + MXT_FW_CHG_TIMEOUT); + if (ret) { + /* + * TODO: handle -ERESTARTSYS better by terminating + * fw update process before returning to userspace + * by writing length 0x000 to device (iff we are in + * WAITING_FRAME_DATA state). + */ + dev_err(dev, "Update wait error %d\n", ret); + return ret; + } + } + + ret = mxt_bootloader_read(data, &val, 1); + if (ret) + return ret; + + if (state == MXT_WAITING_BOOTLOAD_CMD) + val = mxt_get_bootloader_version(data, val); + + switch (state) { + case MXT_WAITING_BOOTLOAD_CMD: + case MXT_WAITING_FRAME_DATA: + case MXT_APP_CRC_FAIL: + val &= ~MXT_BOOT_STATUS_MASK; + break; + case MXT_FRAME_CRC_PASS: + if (val == MXT_FRAME_CRC_CHECK) { + goto recheck; + } else if (val == MXT_FRAME_CRC_FAIL) { + dev_err(dev, "Bootloader CRC fail\n"); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + if (val != state) { + dev_err(dev, "Invalid bootloader state %02X != %02X\n", + val, state); + return -EINVAL; + } + + return 0; +} + +static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock) +{ + int ret; + u8 *buf = data->cmd_buf; + + if (unlock) { + *buf = MXT_UNLOCK_CMD_LSB; + *(buf + 1) = MXT_UNLOCK_CMD_MSB; + } else { + *buf = 0x01; + *(buf + 1) = 0x01; + } + + ret = mxt_bootloader_write(data, buf, 2); + if (ret) + return ret; + + return 0; +} + +static int __mxt_read_reg(struct mxt_data *data, u16 reg, u16 len, void *val) +{ + struct i2c_msg xfer[2]; + int ret; + u8 *buf = data->cmd_buf; + struct i2c_client *client = data->client; + + *buf = reg & 0xff; + *(buf + 1) = (reg >> 8) & 0xff; + + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 2; + xfer[0].buf = buf; + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = len; + xfer[1].buf = val; + + ret = i2c_transfer(client->adapter, xfer, 2); + if (ret == 2) { + ret = 0; + } else { + if (ret >= 0) + ret = -EIO; + dev_err(&client->dev, "%s: i2c transfer failed (%d)\n", + __func__, ret); + } + + return ret; +} + +static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len, + const void *val) +{ + u8 *buf; + size_t count; + int ret; + + count = len + 2; + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = reg & 0xff; + buf[1] = (reg >> 8) & 0xff; + memcpy(&buf[2], val, len); + + ret = i2c_master_send(client, buf, count); + if (ret == count) { + ret = 0; + } else { + if (ret >= 0) + ret = -EIO; + dev_dbg(&client->dev, "%s: i2c send failed (%d)\n", + __func__, ret); + } + + kfree(buf); + return ret; +} + +static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val) +{ + return __mxt_write_reg(client, reg, 1, &val); +} + +static struct mxt_object * +mxt_get_object(struct mxt_data *data, u8 type) +{ + struct mxt_object *object; + int i; + + for (i = 0; i < data->info->object_num; i++) { + object = data->object_table + i; + if (object->type == type) + return object; + } + + dev_warn(&data->client->dev, "Invalid object type T%u\n", type); + return NULL; +} + +static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg) +{ + struct device *dev = &data->client->dev; + u8 status = msg[1]; + u32 crc = msg[2] | (msg[3] << 8) | (msg[4] << 16); + + if (crc != data->config_crc) { + data->config_crc = crc; + dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc); + } + + complete(&data->crc_completion); + + /* Detect reset */ + if (status & MXT_T6_STATUS_RESET) + complete(&data->reset_completion); + + /* Output debug if status has changed */ + if (status != data->t6_status) + dev_dbg(dev, "T6 Status 0x%02X%s%s%s%s%s%s%s\n", + status, + status == 0 ? " OK" : "", + status & MXT_T6_STATUS_RESET ? " RESET" : "", + status & MXT_T6_STATUS_OFL ? " OFL" : "", + status & MXT_T6_STATUS_SIGERR ? " SIGERR" : "", + status & MXT_T6_STATUS_CAL ? " CAL" : "", + status & MXT_T6_STATUS_CFGERR ? " CFGERR" : "", + status & MXT_T6_STATUS_COMSERR ? " COMSERR" : ""); + + /* Save current status */ + data->t6_status = status; +} + +static int mxt_write_object(struct mxt_data *data, + u8 type, u8 offset, u8 val) +{ + struct mxt_object *object; + u16 reg; + + object = mxt_get_object(data, type); + if (!object || offset >= mxt_obj_size(object)) + return -EINVAL; + + reg = object->start_address; + return mxt_write_reg(data->client, reg + offset, val); +} + +static void mxt_input_button(struct mxt_data *data, u8 *message) +{ + struct input_dev *input = data->input_dev; + int i; + + for (i = 0; i < data->t19_num_keys; i++) { + if (data->t19_keymap[i] == KEY_RESERVED) + continue; + + /* Active-low switch */ + input_report_key(input, data->t19_keymap[i], + !(message[1] & BIT(i))); + } +} + +static void mxt_input_sync(struct mxt_data *data) +{ + input_mt_report_pointer_emulation(data->input_dev, + data->t19_num_keys); + input_sync(data->input_dev); +} + +static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev = data->input_dev; + int id; + u8 status; + int x; + int y; + int area; + int amplitude; + + id = message[0] - data->T9_reportid_min; + status = message[1]; + x = (message[2] << 4) | ((message[4] >> 4) & 0xf); + y = (message[3] << 4) | ((message[4] & 0xf)); + + /* Handle 10/12 bit switching */ + if (data->max_x < 1024) + x >>= 2; + if (data->max_y < 1024) + y >>= 2; + + area = message[5]; + amplitude = message[6]; + + dev_dbg(dev, + "[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n", + id, + (status & MXT_T9_DETECT) ? 'D' : '.', + (status & MXT_T9_PRESS) ? 'P' : '.', + (status & MXT_T9_RELEASE) ? 'R' : '.', + (status & MXT_T9_MOVE) ? 'M' : '.', + (status & MXT_T9_VECTOR) ? 'V' : '.', + (status & MXT_T9_AMP) ? 'A' : '.', + (status & MXT_T9_SUPPRESS) ? 'S' : '.', + (status & MXT_T9_UNGRIP) ? 'U' : '.', + x, y, area, amplitude); + + input_mt_slot(input_dev, id); + + if (status & MXT_T9_DETECT) { + /* + * Multiple bits may be set if the host is slow to read + * the status messages, indicating all the events that + * have happened. + */ + if (status & MXT_T9_RELEASE) { + input_mt_report_slot_inactive(input_dev); + mxt_input_sync(data); + } + + /* if active, pressure must be non-zero */ + if (!amplitude) + amplitude = MXT_PRESSURE_DEFAULT; + + /* Handle orientation */ + if (data->xy_switch) + swap(x, y); + if (data->invertx) + x = data->max_x - x; + if (data->inverty) + y = data->max_y - y; + + /* Touch active */ + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, + x - data->disp_minx); + input_report_abs(input_dev, ABS_MT_POSITION_Y, + y - data->disp_miny); + input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area); + } else { + /* Touch no longer active, close out slot */ + input_mt_report_slot_inactive(input_dev); + } + + data->update_input = true; +} + +static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev = data->input_dev; + int id; + u8 status; + u8 type = 0; + u16 x; + u16 y; + int distance = 0; + int tool = 0; + u8 major = 0; + u8 pressure = 0; + u8 orientation = 0; + + id = message[0] - data->T100_reportid_min - 2; + + /* ignore SCRSTATUS events */ + if (id < 0) + return; + + status = message[1]; + x = get_unaligned_le16(&message[2]); + y = get_unaligned_le16(&message[4]); + + if (status & MXT_T100_DETECT) { + type = (status & MXT_T100_TYPE_MASK) >> 4; + + switch (type) { + case MXT_T100_TYPE_HOVERING_FINGER: + tool = MT_TOOL_FINGER; + distance = MXT_DISTANCE_HOVERING; + + if (data->t100_aux_vect) + orientation = message[data->t100_aux_vect]; + + break; + + case MXT_T100_TYPE_FINGER: + case MXT_T100_TYPE_GLOVE: + tool = MT_TOOL_FINGER; + distance = MXT_DISTANCE_ACTIVE_TOUCH; + + if (data->t100_aux_area) + major = message[data->t100_aux_area]; + + if (data->t100_aux_ampl) + pressure = message[data->t100_aux_ampl]; + + if (data->t100_aux_vect) + orientation = message[data->t100_aux_vect]; + + break; + + case MXT_T100_TYPE_PASSIVE_STYLUS: + tool = MT_TOOL_PEN; + + /* + * Passive stylus is reported with size zero so + * hardcode. + */ + major = MXT_TOUCH_MAJOR_DEFAULT; + + if (data->t100_aux_ampl) + pressure = message[data->t100_aux_ampl]; + + break; + + case MXT_T100_TYPE_LARGE_TOUCH: + /* Ignore suppressed touch */ + break; + + default: + dev_dbg(dev, "Unexpected T100 type\n"); + return; + } + } + + /* + * Values reported should be non-zero if tool is touching the + * device + */ + if (!pressure && type != MXT_T100_TYPE_HOVERING_FINGER) + pressure = MXT_PRESSURE_DEFAULT; + + input_mt_slot(input_dev, id); + + if (status & MXT_T100_DETECT) { + dev_dbg(dev, "[%u] type:%u x:%u y:%u a:%02X p:%02X v:%02X\n", + id, type, x, y, major, pressure, orientation); + + /* Handle orientation */ + if (data->xy_switch) + swap(x, y); + if (data->invertx) + x = data->max_x - x; + if (data->inverty) + y = data->max_y - y; + + input_mt_report_slot_state(input_dev, tool, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, + x - data->disp_minx); + input_report_abs(input_dev, ABS_MT_POSITION_Y, + y - data->disp_miny); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, major); + input_report_abs(input_dev, ABS_MT_PRESSURE, pressure); + input_report_abs(input_dev, ABS_MT_DISTANCE, distance); + input_report_abs(input_dev, ABS_MT_ORIENTATION, orientation); + } else { + dev_dbg(dev, "[%u] release\n", id); + + /* close out slot */ + input_mt_report_slot_inactive(input_dev); + } + + data->update_input = true; +} + +static int mxt_proc_message(struct mxt_data *data, u8 *message) +{ + u8 report_id = message[0]; + + if (report_id == MXT_RPTID_NOMSG) + return 0; + + if (report_id == data->T6_reportid) { + mxt_proc_t6_messages(data, message); + } else if (!data->input_dev) { + /* + * Do not report events if input device + * is not yet registered. + */ + mxt_dump_message(data, message); + } else if (report_id >= data->T9_reportid_min && + report_id <= data->T9_reportid_max) { + mxt_proc_t9_message(data, message); + } else if (report_id >= data->T100_reportid_min && + report_id <= data->T100_reportid_max) { + mxt_proc_t100_message(data, message); + } else if (report_id == data->T19_reportid) { + mxt_input_button(data, message); + data->update_input = true; + } else { + mxt_dump_message(data, message); + } + + return 1; +} + +static int mxt_read_and_process_messages(struct mxt_data *data, u8 count) +{ + struct device *dev = &data->client->dev; + int ret; + int i; + u8 num_valid = 0; + + /* Safety check for msg_buf */ + if (count > data->max_reportid) + return -EINVAL; + + /* Process remaining messages if necessary */ + ret = __mxt_read_reg(data, data->T5_address, + data->T5_msg_size * count, data->msg_buf); + if (ret) { + dev_err(dev, "Failed to read %u messages (%d)\n", count, ret); + return ret; + } + + for (i = 0; i < count; i++) { + ret = mxt_proc_message(data, + data->msg_buf + data->T5_msg_size * i); + + if (ret == 1) + num_valid++; + } + + /* return number of messages read */ + return num_valid; +} + +static irqreturn_t mxt_process_messages_t44(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + u8 count, num_left; + + /* Read T44 and T5 together */ + ret = __mxt_read_reg(data, data->T44_address, + data->T5_msg_size + 1, data->msg_buf); + if (ret) { + dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret); + return IRQ_NONE; + } + + count = data->msg_buf[0]; + + /* + * This condition may be caused by the CHG line being configured in + * Mode 0. It results in unnecessary I2C operations but it is benign. + */ + if (count == 0) + return IRQ_NONE; + + if (count > data->max_reportid) { + dev_warn(dev, "T44 count %d exceeded max report id\n", count); + count = data->max_reportid; + } + + /* Process first message */ + ret = mxt_proc_message(data, data->msg_buf + 1); + if (ret < 0) { + dev_warn(dev, "Unexpected invalid message\n"); + return IRQ_NONE; + } + + num_left = count - 1; + + /* Process remaining messages if necessary */ + if (num_left) { + ret = mxt_read_and_process_messages(data, num_left); + if (ret < 0) + goto end; + else if (ret != num_left) + dev_warn(dev, "Unexpected invalid message\n"); + } + +end: + if (data->update_input) { + mxt_input_sync(data); + data->update_input = false; + } + + return IRQ_HANDLED; +} + +static int mxt_process_messages_until_invalid(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int count, read; + u8 tries = 2; + + count = data->max_reportid; + + /* Read messages until we force an invalid */ + do { + read = mxt_read_and_process_messages(data, count); + if (read < count) + return 0; + } while (--tries); + + if (data->update_input) { + mxt_input_sync(data); + data->update_input = false; + } + + dev_err(dev, "CHG pin isn't cleared\n"); + return -EBUSY; +} + +static irqreturn_t mxt_process_messages(struct mxt_data *data) +{ + int total_handled, num_handled; + u8 count = data->last_message_count; + + if (count < 1 || count > data->max_reportid) + count = 1; + + /* include final invalid message */ + total_handled = mxt_read_and_process_messages(data, count + 1); + if (total_handled < 0) + return IRQ_NONE; + /* if there were invalid messages, then we are done */ + else if (total_handled <= count) + goto update_count; + + /* keep reading two msgs until one is invalid or reportid limit */ + do { + num_handled = mxt_read_and_process_messages(data, 2); + if (num_handled < 0) + return IRQ_NONE; + + total_handled += num_handled; + + if (num_handled < 2) + break; + } while (total_handled < data->num_touchids); + +update_count: + data->last_message_count = total_handled; + + if (data->update_input) { + mxt_input_sync(data); + data->update_input = false; + } + + return IRQ_HANDLED; +} + +static irqreturn_t mxt_interrupt(int irq, void *dev_id) +{ + struct mxt_data *data = dev_id; + + if (data->in_bootloader) { + /* bootloader state transition completion */ + complete(&data->bl_completion); + return IRQ_HANDLED; + } + + if (!data->object_table) + return IRQ_HANDLED; + + if (data->T44_address) { + return mxt_process_messages_t44(data); + } else { + return mxt_process_messages(data); + } +} + +static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset, + u8 value, bool wait) +{ + u16 reg; + u8 command_register; + int timeout_counter = 0; + int ret; + + reg = data->T6_address + cmd_offset; + + ret = mxt_write_reg(data->client, reg, value); + if (ret) + return ret; + + if (!wait) + return 0; + + do { + msleep(20); + ret = __mxt_read_reg(data, reg, 1, data->read_buf); + if (ret) + return ret; + command_register = *(data->read_buf); + } while (command_register != 0 && timeout_counter++ <= 100); + + if (timeout_counter > 100) { + dev_err(&data->client->dev, "Command failed!\n"); + return -EIO; + } + + return 0; +} + +static int mxt_acquire_irq(struct mxt_data *data) +{ + int error; + + enable_irq(data->irq); + + if (data->use_retrigen_workaround) { + error = mxt_process_messages_until_invalid(data); + if (error) + return error; + } + + return 0; +} + +static int mxt_soft_reset(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int ret = 0; + + dev_dbg(dev, "Resetting device\n"); + + disable_irq(data->irq); + + reinit_completion(&data->reset_completion); + + ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_RESET_VALUE, false); + if (ret) + return ret; + + /* Ignore CHG line for 100ms after reset */ + msleep(MXT_RESET_INVALID_CHG); + + mxt_acquire_irq(data); + + ret = mxt_wait_for_completion(data, &data->reset_completion, + MXT_RESET_TIMEOUT); + if (ret) + return ret; + + return 0; +} + +static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value) +{ + /* + * On failure, CRC is set to 0 and config will always be + * downloaded. + */ + data->config_crc = 0; + reinit_completion(&data->crc_completion); + + mxt_t6_command(data, cmd, value, true); + + /* + * Wait for crc message. On failure, CRC is set to 0 and config will + * always be downloaded. + */ + mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT); +} + +static void mxt_calc_crc24(u32 *crc, u8 firstbyte, u8 secondbyte) +{ + static const unsigned int crcpoly = 0x80001B; + u32 result; + u32 data_word; + + data_word = (secondbyte << 8) | firstbyte; + result = ((*crc << 1) ^ data_word); + + if (result & 0x1000000) + result ^= crcpoly; + + *crc = result; +} + +static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off) +{ + u32 crc = 0; + u8 *ptr = base + start_off; + u8 *last_val = base + end_off - 1; + + if (end_off < start_off) + return -EINVAL; + + while (ptr < last_val) { + mxt_calc_crc24(&crc, *ptr, *(ptr + 1)); + ptr += 2; + } + + /* if len is odd, fill the last byte with 0 */ + if (ptr == last_val) + mxt_calc_crc24(&crc, *ptr, 0); + + /* Mask to 24-bit */ + crc &= 0x00FFFFFF; + + return crc; +} + +static int mxt_check_retrigen(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + int val; + struct irq_data *irqd; + + data->use_retrigen_workaround = false; + + irqd = irq_get_irq_data(data->irq); + if (!irqd) + return -EINVAL; + + if (irqd_is_level_type(irqd)) + return 0; + + if (data->T18_address) { + error = __mxt_read_reg(data, + data->T18_address + MXT_COMMS_CTRL, + 1, &val); + if (error) + return error; + + if (val & MXT_COMMS_RETRIGEN) + return 0; + } + + dev_warn(&client->dev, "Enabling RETRIGEN workaround\n"); + data->use_retrigen_workaround = true; + return 0; +} + +static int mxt_prepare_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg) +{ + struct device *dev = &data->client->dev; + struct mxt_object *object; + unsigned int type, instance, size, byte_offset; + int offset; + int ret; + int i; + u16 reg; + u8 val; + + while (cfg->raw_pos < cfg->raw_size) { + /* Read type, instance, length */ + ret = sscanf(cfg->raw + cfg->raw_pos, "%x %x %x%n", + &type, &instance, &size, &offset); + if (ret == 0) { + /* EOF */ + break; + } else if (ret != 3) { + dev_err(dev, "Bad format: failed to parse object\n"); + return -EINVAL; + } + + if (cfg->raw_pos + offset >= cfg->raw_size) + return -EINVAL; + + cfg->raw_pos += offset; + + object = mxt_get_object(data, type); + if (!object) { + /* Skip object */ + for (i = 0; i < size; i++) { + ret = sscanf(cfg->raw + cfg->raw_pos, "%hhx%n", + &val, &offset); + if (ret != 1 || (cfg->raw_pos + offset >= + cfg->raw_size)) { + dev_err(dev, "Bad format in T%d at %d\n", + type, i); + return -EINVAL; + } + cfg->raw_pos += offset; + + } + continue; + } + + if (size > mxt_obj_size(object)) { + /* + * Either we are in fallback mode due to wrong + * config or config from a later fw version, + * or the file is corrupt or hand-edited. + */ + dev_warn(dev, "Discarding %zu byte(s) in T%u\n", + size - mxt_obj_size(object), type); + } else if (mxt_obj_size(object) > size) { + /* + * If firmware is upgraded, new bytes may be added to + * end of objects. It is generally forward compatible + * to zero these bytes - previous behaviour will be + * retained. However this does invalidate the CRC and + * will force fallback mode until the configuration is + * updated. We warn here but do nothing else - the + * malloc has zeroed the entire configuration. + */ + dev_warn(dev, "Zeroing %zu byte(s) in T%d\n", + mxt_obj_size(object) - size, type); + } + + if (instance >= mxt_obj_instances(object)) { + dev_err(dev, "Object instances exceeded!\n"); + return -EINVAL; + } + + reg = object->start_address + mxt_obj_size(object) * instance; + + for (i = 0; i < size; i++) { + ret = sscanf(cfg->raw + cfg->raw_pos, "%hhx%n", + &val, + &offset); + if (ret != 1 || + (cfg->raw_pos + offset >= cfg->raw_size)) { + dev_err(dev, "Bad format in T%d at %d\n", + type, i); + return -EINVAL; + } + cfg->raw_pos += offset; + + if (i > mxt_obj_size(object)) + continue; + + byte_offset = reg + i - cfg->start_ofs; + + if (byte_offset >= 0 && byte_offset < cfg->mem_size) { + *(cfg->mem + byte_offset) = val; + } else { + dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n", + reg, object->type, byte_offset); + return -EINVAL; + } + } + } + + return 0; +} + +static int mxt_upload_cfg_mem(struct mxt_data *data, struct mxt_cfg *cfg) +{ + unsigned int byte_offset = 0; + int error; + + /* Write configuration as blocks */ + while (byte_offset < cfg->mem_size) { + unsigned int size = cfg->mem_size - byte_offset; + + if (size > MXT_MAX_BLOCK_WRITE) + size = MXT_MAX_BLOCK_WRITE; + + error = __mxt_write_reg(data->client, + cfg->start_ofs + byte_offset, + size, cfg->mem + byte_offset); + if (error) { + dev_err(&data->client->dev, + "Config write error, ret=%d\n", error); + return error; + } + + byte_offset += size; + } + + return 0; +} + +static int mxt_init_t7_power_cfg(struct mxt_data *data); + +/* + * mxt_update_cfg - download configuration to chip + * + * Atmel Raw Config File Format + * + * The first four lines of the raw config file contain: + * 1) Version + * 2) Chip ID Information (first 7 bytes of device memory) + * 3) Chip Information Block 24-bit CRC Checksum + * 4) Chip Configuration 24-bit CRC Checksum + * + * The rest of the file consists of one line per object instance: + * + * + * - 2-byte object type as hex + * - 2-byte object instance number as hex + * - 2-byte object size as hex + * - array of 1-byte hex values + */ +static int mxt_update_cfg(struct mxt_data *data, const struct firmware *fw) +{ + struct device *dev = &data->client->dev; + struct mxt_cfg cfg; + int ret; + int offset; + int i; + u32 info_crc, config_crc, calculated_crc; + u16 crc_start = 0; + + /* Make zero terminated copy of the OBP_RAW file */ + cfg.raw = kmemdup_nul(fw->data, fw->size, GFP_KERNEL); + if (!cfg.raw) + return -ENOMEM; + + cfg.raw_size = fw->size; + + mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); + + if (memcmp(cfg.raw, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { + dev_err(dev, "Unrecognised config file\n"); + ret = -EINVAL; + goto release_raw; + } + + cfg.raw_pos = strlen(MXT_CFG_MAGIC); + + /* Load information block and check */ + for (i = 0; i < sizeof(struct mxt_info); i++) { + ret = sscanf(cfg.raw + cfg.raw_pos, "%hhx%n", + (unsigned char *)&cfg.info + i, + &offset); + if (ret != 1 || (cfg.raw_pos + offset >= cfg.raw_size)) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + goto release_raw; + } + + cfg.raw_pos += offset; + } + + if (cfg.info.family_id != data->info->family_id) { + dev_err(dev, "Family ID mismatch!\n"); + ret = -EINVAL; + goto release_raw; + } + + if (cfg.info.variant_id != data->info->variant_id) { + dev_err(dev, "Variant ID mismatch!\n"); + ret = -EINVAL; + goto release_raw; + } + + /* Read CRCs */ + ret = sscanf(cfg.raw + cfg.raw_pos, "%x%n", &info_crc, &offset); + if (ret != 1 || (cfg.raw_pos + offset >= cfg.raw_size)) { + dev_err(dev, "Bad format: failed to parse Info CRC\n"); + ret = -EINVAL; + goto release_raw; + } + cfg.raw_pos += offset; + + ret = sscanf(cfg.raw + cfg.raw_pos, "%x%n", &config_crc, &offset); + if (ret != 1 || (cfg.raw_pos + offset >= cfg.raw_size)) { + dev_err(dev, "Bad format: failed to parse Config CRC\n"); + ret = -EINVAL; + goto release_raw; + } + cfg.raw_pos += offset; + + /* + * The Info Block CRC is calculated over mxt_info and the object + * table. If it does not match then we are trying to load the + * configuration from a different chip or firmware version, so + * the configuration CRC is invalid anyway. + */ + if (info_crc == data->info_crc) { + if (config_crc == 0 || data->config_crc == 0) { + dev_info(dev, "CRC zero, attempting to apply config\n"); + } else if (config_crc == data->config_crc) { + dev_dbg(dev, "Config CRC 0x%06X: OK\n", + data->config_crc); + ret = 0; + goto release_raw; + } else { + dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", + data->config_crc, config_crc); + } + } else { + dev_warn(dev, + "Warning: Info CRC error - device=0x%06X file=0x%06X\n", + data->info_crc, info_crc); + } + + /* Malloc memory to store configuration */ + cfg.start_ofs = MXT_OBJECT_START + + data->info->object_num * sizeof(struct mxt_object) + + MXT_INFO_CHECKSUM_SIZE; + cfg.mem_size = data->mem_size - cfg.start_ofs; + cfg.mem = kzalloc(cfg.mem_size, GFP_KERNEL); + if (!cfg.mem) { + ret = -ENOMEM; + goto release_raw; + } + + ret = mxt_prepare_cfg_mem(data, &cfg); + if (ret) + goto release_mem; + + /* Calculate crc of the received configs (not the raw config file) */ + if (data->T71_address) + crc_start = data->T71_address; + else if (data->T7_address) + crc_start = data->T7_address; + else + dev_warn(dev, "Could not find CRC start\n"); + + if (crc_start > cfg.start_ofs) { + calculated_crc = mxt_calculate_crc(cfg.mem, + crc_start - cfg.start_ofs, + cfg.mem_size); + + if (config_crc > 0 && config_crc != calculated_crc) + dev_warn(dev, "Config CRC in file inconsistent, calculated=%06X, file=%06X\n", + calculated_crc, config_crc); + } + + ret = mxt_upload_cfg_mem(data, &cfg); + if (ret) + goto release_mem; + + mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); + + ret = mxt_check_retrigen(data); + if (ret) + goto release_mem; + + ret = mxt_soft_reset(data); + if (ret) + goto release_mem; + + dev_info(dev, "Config successfully updated\n"); + + /* T7 config may have changed */ + mxt_init_t7_power_cfg(data); + +release_mem: + kfree(cfg.mem); +release_raw: + kfree(cfg.raw); + return ret; +} + +static void mxt_free_input_device(struct mxt_data *data) +{ + if (data->input_dev) { + input_unregister_device(data->input_dev); + data->input_dev = NULL; + } +} + +static void mxt_free_object_table(struct mxt_data *data) +{ +#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 + video_unregister_device(&data->dbg.vdev); + v4l2_device_unregister(&data->dbg.v4l2); +#endif + data->object_table = NULL; + data->info = NULL; + kfree(data->raw_info_block); + data->raw_info_block = NULL; + kfree(data->msg_buf); + data->msg_buf = NULL; + data->T5_address = 0; + data->T5_msg_size = 0; + data->T6_reportid = 0; + data->T7_address = 0; + data->T71_address = 0; + data->T9_reportid_min = 0; + data->T9_reportid_max = 0; + data->T18_address = 0; + data->T19_reportid = 0; + data->T44_address = 0; + data->T100_reportid_min = 0; + data->T100_reportid_max = 0; + data->max_reportid = 0; +} + +static int mxt_parse_object_table(struct mxt_data *data, + struct mxt_object *object_table) +{ + struct i2c_client *client = data->client; + int i; + u8 reportid; + u16 end_address; + + /* Valid Report IDs start counting from 1 */ + reportid = 1; + data->mem_size = 0; + for (i = 0; i < data->info->object_num; i++) { + struct mxt_object *object = object_table + i; + u8 min_id, max_id; + + le16_to_cpus(&object->start_address); + + if (object->num_report_ids) { + min_id = reportid; + reportid += object->num_report_ids * + mxt_obj_instances(object); + max_id = reportid - 1; + } else { + min_id = 0; + max_id = 0; + } + + dev_dbg(&data->client->dev, + "T%u Start:%u Size:%zu Instances:%zu Report IDs:%u-%u\n", + object->type, object->start_address, + mxt_obj_size(object), mxt_obj_instances(object), + min_id, max_id); + + switch (object->type) { + case MXT_GEN_MESSAGE_T5: + if (data->info->family_id == 0x80 && + data->info->version < 0x20) { + /* + * On mXT224 firmware versions prior to V2.0 + * read and discard unused CRC byte otherwise + * DMA reads are misaligned. + */ + data->T5_msg_size = mxt_obj_size(object); + } else { + /* CRC not enabled, so skip last byte */ + data->T5_msg_size = mxt_obj_size(object) - 1; + } + data->T5_address = object->start_address; + break; + case MXT_GEN_COMMAND_T6: + data->T6_reportid = min_id; + data->T6_address = object->start_address; + break; + case MXT_GEN_POWER_T7: + data->T7_address = object->start_address; + break; + case MXT_SPT_DYNAMICCONFIGURATIONCONTAINER_T71: + data->T71_address = object->start_address; + break; + case MXT_TOUCH_MULTI_T9: + data->multitouch = MXT_TOUCH_MULTI_T9; + /* Only handle messages from first T9 instance */ + data->T9_reportid_min = min_id; + data->T9_reportid_max = min_id + + object->num_report_ids - 1; + data->num_touchids = object->num_report_ids; + break; + case MXT_SPT_COMMSCONFIG_T18: + data->T18_address = object->start_address; + break; + case MXT_SPT_MESSAGECOUNT_T44: + data->T44_address = object->start_address; + break; + case MXT_SPT_GPIOPWM_T19: + data->T19_reportid = min_id; + break; + case MXT_TOUCH_MULTITOUCHSCREEN_T100: + data->multitouch = MXT_TOUCH_MULTITOUCHSCREEN_T100; + data->T100_reportid_min = min_id; + data->T100_reportid_max = max_id; + /* first two report IDs reserved */ + data->num_touchids = object->num_report_ids - 2; + break; + } + + end_address = object->start_address + + mxt_obj_size(object) * mxt_obj_instances(object) - 1; + + if (end_address >= data->mem_size) + data->mem_size = end_address + 1; + } + + /* Store maximum reportid */ + data->max_reportid = reportid; + + /* If T44 exists, T5 position has to be directly after */ + if (data->T44_address && (data->T5_address != data->T44_address + 1)) { + dev_err(&client->dev, "Invalid T44 position\n"); + return -EINVAL; + } + + data->msg_buf = kcalloc(data->max_reportid, + data->T5_msg_size, GFP_KERNEL); + if (!data->msg_buf) + return -ENOMEM; + + return 0; +} + +static int mxt_read_info_block(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int error; + size_t size; + void *id_buf, *buf; + uint8_t num_objects; + u32 calculated_crc; + u8 *crc_ptr; + + /* If info block already allocated, free it */ + if (data->raw_info_block) + mxt_free_object_table(data); + + /* Read 7-byte ID information block starting at address 0 */ + size = sizeof(struct mxt_info); + id_buf = kzalloc(size, GFP_KERNEL); + if (!id_buf) + return -ENOMEM; + + error = __mxt_read_reg(data, 0, size, id_buf); + if (error) + goto err_free_mem; + + /* Resize buffer to give space for rest of info block */ + num_objects = ((struct mxt_info *)id_buf)->object_num; + size += (num_objects * sizeof(struct mxt_object)) + + MXT_INFO_CHECKSUM_SIZE; + + buf = krealloc(id_buf, size, GFP_KERNEL); + if (!buf) { + error = -ENOMEM; + goto err_free_mem; + } + id_buf = buf; + + /* Read rest of info block */ + error = __mxt_read_reg(data, MXT_OBJECT_START, + size - MXT_OBJECT_START, + id_buf + MXT_OBJECT_START); + if (error) + goto err_free_mem; + + /* Extract & calculate checksum */ + crc_ptr = id_buf + size - MXT_INFO_CHECKSUM_SIZE; + data->info_crc = crc_ptr[0] | (crc_ptr[1] << 8) | (crc_ptr[2] << 16); + + calculated_crc = mxt_calculate_crc(id_buf, 0, + size - MXT_INFO_CHECKSUM_SIZE); + + /* + * CRC mismatch can be caused by data corruption due to I2C comms + * issue or else device is not using Object Based Protocol (eg i2c-hid) + */ + if ((data->info_crc == 0) || (data->info_crc != calculated_crc)) { + dev_err(&client->dev, + "Info Block CRC error calculated=0x%06X read=0x%06X\n", + calculated_crc, data->info_crc); + error = -EIO; + goto err_free_mem; + } + + data->raw_info_block = id_buf; + data->info = (struct mxt_info *)id_buf; + + dev_info(&client->dev, + "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n", + data->info->family_id, data->info->variant_id, + data->info->version >> 4, data->info->version & 0xf, + data->info->build, data->info->object_num); + + /* Parse object table information */ + error = mxt_parse_object_table(data, id_buf + MXT_OBJECT_START); + if (error) { + dev_err(&client->dev, "Error %d parsing object table\n", error); + mxt_free_object_table(data); + goto err_free_mem; + } + + data->object_table = (struct mxt_object *)(id_buf + MXT_OBJECT_START); + + return 0; + +err_free_mem: + kfree(id_buf); + return error; +} + +static int mxt_pinctrl_init(struct mxt_data *data) +{ + int error; + + /* Get pinctrl if target uses pinctrl */ + data->ts_pinctrl = devm_pinctrl_get((&data->client->dev)); + if (IS_ERR_OR_NULL(data->ts_pinctrl)) { + dev_dbg(&data->client->dev, + "Device does not use pinctrl\n"); + error = PTR_ERR(data->ts_pinctrl); + data->ts_pinctrl = NULL; + return error; + } + + data->gpio_state_active + = pinctrl_lookup_state(data->ts_pinctrl, "pmx_ts_active"); + if (IS_ERR_OR_NULL(data->gpio_state_active)) { + dev_dbg(&data->client->dev, + "Can not get ts default pinstate\n"); + error = PTR_ERR(data->gpio_state_active); + data->ts_pinctrl = NULL; + return error; + } + + data->gpio_state_suspend + = pinctrl_lookup_state(data->ts_pinctrl, "pmx_ts_suspend"); + if (IS_ERR_OR_NULL(data->gpio_state_suspend)) { + dev_dbg(&data->client->dev, + "Can not get ts sleep pinstate\n"); + error = PTR_ERR(data->gpio_state_suspend); + data->ts_pinctrl = NULL; + return error; + } + + return 0; +} + +static int mxt_pinctrl_select(struct mxt_data *data, bool on) +{ + struct pinctrl_state *pins_state; + int error; + + pins_state = on ? data->gpio_state_active + : data->gpio_state_suspend; + if (!IS_ERR_OR_NULL(pins_state)) { + error = pinctrl_select_state(data->ts_pinctrl, pins_state); + if (error) { + dev_err(&data->client->dev, + "can not set %s pins\n", + on ? "pmx_ts_active" : "pmx_ts_suspend"); + return error; + } + } else { + dev_err(&data->client->dev, + "not a valid '%s' pinstate\n", + on ? "pmx_ts_active" : "pmx_ts_suspend"); + } + + return 0; +} + +static int mxt_gpio_enable(struct mxt_data *data, bool enable) +{ + int error; + + if (data->ts_pinctrl) { + error = mxt_pinctrl_select(data, enable); + if (error < 0) + return error; + } + + if (enable) { + error = gpiod_direction_input(data->irq_gpio); + if (error) + dev_err(&data->client->dev, + "unable to set dir for %d gpio(%d)\n", + desc_to_gpio(data->irq_gpio), error); + if (data->reset_gpio) + gpiod_set_value(data->reset_gpio, 1); + } else { + if (data->reset_gpio) + gpiod_set_value(data->reset_gpio, 0); + } + + return 0; +} + +static int mxt_regulator_enable(struct mxt_data *data) +{ + int error; + + if (!data->use_regulator) + return 0; + + gpiod_set_value(data->reset_gpio, 0); + + error = mxt_regulator_configure(data, true); + if (error) { + dev_err(&data->client->dev, "Failed to configure regulators on\n"); + return error; + } + + error = regulator_enable(data->reg_avdd); + if (error) { + dev_err(&data->client->dev, + "avdd enable failed, error=%d\n", error); + goto err_dis_configure; + } + usleep_range(10000, 15000); + + error = regulator_enable(data->reg_vdd); + if (error) { + dev_err(&data->client->dev, + "vdd enable failed, error=%d\n", error); + goto err_dis_avdd; + } + + if (!IS_ERR(data->reg_xvdd)) { + usleep_range(10000, 15000); + error = regulator_enable(data->reg_xvdd); + if (error) { + dev_err(&data->client->dev, + "xvdd enable failed, error=%d\n", error); + goto err_dis_vdd; + } + } + msleep(MXT_REGULATOR_DELAY); + + reinit_completion(&data->bl_completion); + gpiod_set_value(data->reset_gpio, 1); + mxt_wait_for_completion(data, &data->bl_completion, MXT_POWERON_DELAY); + + return 0; + +err_dis_vdd: + regulator_disable(data->reg_vdd); +err_dis_avdd: + regulator_disable(data->reg_avdd); +err_dis_configure: + mxt_regulator_configure(data, false); + return error; +} + +static void mxt_regulator_disable(struct mxt_data *data) +{ + int error; + + regulator_disable(data->reg_vdd); + regulator_disable(data->reg_avdd); + if (!IS_ERR(data->reg_xvdd)) + regulator_disable(data->reg_xvdd); + + error = mxt_regulator_configure(data, false); + if (error) + dev_err(&data->client->dev, "Failed to configure regulators off\n"); + +} + +static int mxt_regulator_parse(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + struct device_node *np = dev->of_node; + struct property *prop; + int error = 0; + + if (!data->reset_gpio) { + dev_warn(dev, "Must have reset GPIO to use regulator support\n"); + return 0; + } + + data->reg_vdd = regulator_get(dev, "vdd"); + if (IS_ERR(data->reg_vdd)) { + error = PTR_ERR(data->reg_vdd); + dev_err(dev, "Error %d getting vdd regulator\n", error); + return error; + } + + data->reg_avdd = regulator_get(dev, "avdd"); + if (IS_ERR(data->reg_avdd)) { + error = PTR_ERR(data->reg_avdd); + dev_err(dev, "Error %d getting avdd regulator\n", error); + goto fail_put_vdd; + } + + data->reg_xvdd = regulator_get(dev, "xvdd"); + if (IS_ERR(data->reg_xvdd)) { + error = PTR_ERR(data->reg_xvdd); + prop = of_find_property(np, "xvdd-supply", NULL); + if (prop && (error == -EPROBE_DEFER)) + return -EPROBE_DEFER; + dev_dbg(dev, "xvdd regulator is not used\n"); + } + + data->use_regulator = true; + + dev_err(dev, "Initialised regulators\n"); + return 0; + +fail_put_vdd: + regulator_put(data->reg_vdd); + return error; +} + +static int mxt_regulator_configure(struct mxt_data *data, bool enable) +{ + struct device *dev = &data->client->dev; + int error = 0; + + /* According to maXTouch power sequencing specification, RESET line + * must be kept low until some time after regulators come up to + * voltage + */ + if (!data->reset_gpio) { + dev_warn(dev, "Must have reset GPIO to use regulator support\n"); + return 0; + } + + if (!enable) { + if (regulator_count_voltages(data->reg_avdd) > 0) { + error = regulator_set_load(data->reg_avdd, MXT_DISABLE_LOAD); + if (error) { + dev_err(&data->client->dev, + "avdd set_load failed err=%d\n", error); + return error; + } + } + + if (regulator_count_voltages(data->reg_vdd) > 0) { + error = regulator_set_load(data->reg_vdd, MXT_DISABLE_LOAD); + if (error) { + dev_err(&data->client->dev, + "vdd set_load failed err=%d\n", error); + return error; + } + } + + if (!IS_ERR(data->reg_xvdd)) { + if (regulator_count_voltages(data->reg_xvdd) > 0) { + error = regulator_set_load(data->reg_xvdd, MXT_DISABLE_LOAD); + if (error) + dev_err(&data->client->dev, + "xvdd set_load failed err=%d\n", error); + } + } + } else { + if (regulator_count_voltages(data->reg_vdd) > 0) { + error = regulator_set_load(data->reg_vdd, MXT_ENABLE_LOAD); + if (error) { + dev_err(&data->client->dev, + "vdd set_load failed err=%d\n", error); + return error; + } + error = regulator_set_voltage(data->reg_vdd, + MXT_VDD_VTG_MIN_UV, MXT_VDD_VTG_MAX_UV); + if (error) { + dev_err(&data->client->dev, + "vdd set_vtg failed err=%d\n", error); + goto err_vdd_load; + } + } + + if (regulator_count_voltages(data->reg_avdd) > 0) { + error = regulator_set_load(data->reg_avdd, MXT_ENABLE_LOAD); + if (error) { + dev_err(&data->client->dev, + "avdd set_load failed err=%d\n", error); + goto err_vdd_load; + } + error = regulator_set_voltage(data->reg_avdd, + MXT_AVDD_VTG_MIN_UV, MXT_AVDD_VTG_MAX_UV); + if (error) { + dev_err(&data->client->dev, + "avdd set_vtg failed err=%d\n", error); + goto err_avdd_load; + } + } + + if (!IS_ERR(data->reg_xvdd)) { + if (regulator_count_voltages(data->reg_xvdd) > 0) { + error = regulator_set_load(data->reg_xvdd, MXT_ENABLE_LOAD); + if (error) { + dev_err(&data->client->dev, + "xvdd set_load failed err=%d\n", error); + goto err_avdd_load; + } + error = regulator_set_voltage(data->reg_xvdd, + MXT_XVDD_VTG_MIN_UV, MXT_XVDD_VTG_MAX_UV); + if (error) { + dev_err(&data->client->dev, + "xvdd set_vtg failed err=%d\n", error); + goto err_xvdd_load; + } + } + } + } + return error; + +err_xvdd_load: + regulator_set_load(data->reg_xvdd, MXT_DISABLE_LOAD); +err_avdd_load: + regulator_set_load(data->reg_avdd, MXT_DISABLE_LOAD); +err_vdd_load: + regulator_set_load(data->reg_vdd, MXT_DISABLE_LOAD); + return error; +} + +static int mxt_read_t9_resolution(struct mxt_data *data) +{ + int error; + unsigned char orient; + struct mxt_object *object; + + object = mxt_get_object(data, MXT_TOUCH_MULTI_T9); + if (!object) + return -EINVAL; + + error = __mxt_read_reg(data, + object->start_address + MXT_T9_XSIZE, + sizeof(data->xsize), &data->xsize); + if (error) + return error; + + error = __mxt_read_reg(data, + object->start_address + MXT_T9_YSIZE, + sizeof(data->ysize), &data->ysize); + if (error) + return error; + + error = __mxt_read_reg(data, + object->start_address + MXT_T9_RANGE, + sizeof(struct t9_range), data->read_buf); + if (error) + return error; + + data->max_x = get_unaligned_le16(data->read_buf); + data->max_y = get_unaligned_le16(data->read_buf + sizeof(__le16)); + + error = __mxt_read_reg(data, + object->start_address + MXT_T9_ORIENT, + 1, data->read_buf); + if (error) + return error; + orient = *(data->read_buf); + + if (data->xy_switch != (orient & MXT_T9_ORIENT_SWITCH)) + dev_warn(&data->client->dev, + "T9 data->xy_switch:%d\n", + data->xy_switch); + + if (data->invertx != (orient & MXT_T9_ORIENT_INVERTX)) + dev_warn(&data->client->dev, + "T9 data->data->invertx:%d\n", + data->invertx); + + if (data->inverty != (orient & MXT_T9_ORIENT_INVERTY)) + dev_warn(&data->client->dev, + "T9 data->data->inverty:%d\n", + data->inverty); + + return 0; +} + +static int mxt_read_t100_config(struct mxt_data *data) +{ + int error; + struct mxt_object *object; + u8 cfg, tchaux; + u8 aux; + + object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100); + if (!object) + return -EINVAL; + + /* read touchscreen dimensions */ + error = __mxt_read_reg(data, + object->start_address + MXT_T100_XRANGE, + sizeof(__le16), data->read_buf); + if (error) + return error; + + data->max_x = get_unaligned_le16(data->read_buf); + + error = __mxt_read_reg(data, + object->start_address + MXT_T100_YRANGE, + sizeof(__le16), data->read_buf); + + if (error) + return error; + + data->max_y = get_unaligned_le16(data->read_buf); + + error = __mxt_read_reg(data, + object->start_address + MXT_T100_XSIZE, + sizeof(data->xsize), &data->xsize); + if (error) + return error; + + error = __mxt_read_reg(data, + object->start_address + MXT_T100_YSIZE, + sizeof(data->ysize), &data->ysize); + if (error) + return error; + + /* read orientation config */ + error = __mxt_read_reg(data, + object->start_address + MXT_T100_CFG1, + 1, data->read_buf); + if (error) + return error; + cfg = *(data->read_buf); + + if (data->xy_switch != (cfg & MXT_T100_CFG_SWITCHXY)) + dev_warn(&data->client->dev, + "T100 data->xy_switch:%d\n", + data->xy_switch); + + if (data->invertx != (cfg & MXT_T100_CFG_INVERTX)) + dev_warn(&data->client->dev, + "T100 data->data->invertx:%d\n", + data->invertx); + + if (data->inverty != (cfg & MXT_T100_CFG_INVERTY)) + dev_warn(&data->client->dev, + "T100 data->data->inverty:%d\n", + data->inverty); + + /* allocate aux bytes */ + error = __mxt_read_reg(data, + object->start_address + MXT_T100_TCHAUX, + 1, data->read_buf); + if (error) + return error; + tchaux = *(data->read_buf); + + aux = 6; + + if (tchaux & MXT_T100_TCHAUX_VECT) + data->t100_aux_vect = aux++; + + if (tchaux & MXT_T100_TCHAUX_AMPL) + data->t100_aux_ampl = aux++; + + if (tchaux & MXT_T100_TCHAUX_AREA) + data->t100_aux_area = aux++; + + dev_dbg(&data->client->dev, + "T100 aux mappings vect:%u ampl:%u area:%u\n", + data->t100_aux_vect, data->t100_aux_ampl, data->t100_aux_area); + + return 0; +} + +static int mxt_input_open(struct input_dev *dev); +static void mxt_input_close(struct input_dev *dev); + +static void mxt_set_up_as_touchpad(struct input_dev *input_dev, + struct mxt_data *data) +{ + int i; + + input_dev->name = "Atmel maXTouch Touchpad"; + + __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + + input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_X, + MXT_PIXELS_PER_MM); + input_abs_set_res(input_dev, ABS_MT_POSITION_Y, + MXT_PIXELS_PER_MM); + + for (i = 0; i < data->t19_num_keys; i++) + if (data->t19_keymap[i] != KEY_RESERVED) + input_set_capability(input_dev, EV_KEY, + data->t19_keymap[i]); +} + +static int mxt_initialize_input_device(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + struct input_dev *input_dev; + int error; + unsigned int num_mt_slots; + unsigned int mt_flags = 0; + + switch (data->multitouch) { + case MXT_TOUCH_MULTI_T9: + num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; + error = mxt_read_t9_resolution(data); + if (error) + dev_warn(dev, "Failed to initialize T9 resolution\n"); + break; + + case MXT_TOUCH_MULTITOUCHSCREEN_T100: + num_mt_slots = data->num_touchids; + error = mxt_read_t100_config(data); + if (error) + dev_warn(dev, "Failed to read T100 config\n"); + break; + + default: + dev_err(dev, "Invalid multitouch object\n"); + return -EINVAL; + } + + /* Handle default values and orientation switch */ + if (data->max_x == 0) + data->max_x = 1023; + + if (data->max_y == 0) + data->max_y = 1023; + + if (data->xy_switch) + swap(data->max_x, data->max_y); + + dev_info(dev, "Touchscreen size X%uY%u\n", data->max_x, data->max_y); + + /* Register input device */ + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + input_dev->name = "Atmel maXTouch Touchscreen"; + input_dev->phys = data->phys; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = dev; + input_dev->open = mxt_input_open; + input_dev->close = mxt_input_close; + + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + + if (data->disp_maxx == 0 || data->disp_maxx > data->max_x) + data->disp_maxx = data->max_x; + if (data->disp_maxy == 0 || data->disp_maxy > data->max_y) + data->disp_maxy = data->max_y; + + if (data->multitouch == MXT_TOUCH_MULTI_T9 || + (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_ampl)) { + input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0); + } + + /* If device has buttons we assume it is a touchpad */ + if (data->t19_num_keys) { + mxt_set_up_as_touchpad(input_dev, data); + mt_flags |= INPUT_MT_POINTER; + } else { + mt_flags |= INPUT_MT_DIRECT; + } + + /* For multi touch */ + error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); + if (error) { + dev_err(dev, "Error %d initialising slots\n", error); + goto err_free_mem; + } + + if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100) { + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, + 0, MT_TOOL_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_MT_DISTANCE, + MXT_DISTANCE_ACTIVE_TOUCH, + MXT_DISTANCE_HOVERING, + 0, 0); + } + + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, data->disp_maxx - data->disp_minx, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, data->disp_maxy - data->disp_miny, 0, 0); + + if (data->multitouch == MXT_TOUCH_MULTI_T9 || + (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_area)) { + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, MXT_MAX_AREA, 0, 0); + } + + if (data->multitouch == MXT_TOUCH_MULTI_T9 || + (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_ampl)) { + input_set_abs_params(input_dev, ABS_MT_PRESSURE, + 0, 255, 0, 0); + } + + if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_vect) { + input_set_abs_params(input_dev, ABS_MT_ORIENTATION, + 0, 255, 0, 0); + } + + if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 && + data->t100_aux_vect) { + input_set_abs_params(input_dev, ABS_MT_ORIENTATION, + 0, 255, 0, 0); + } + + input_set_drvdata(input_dev, data); + + error = input_register_device(input_dev); + if (error) { + dev_err(dev, "Error %d registering input device\n", error); + goto err_free_mem; + } + + data->input_dev = input_dev; + + return 0; + +err_free_mem: + input_free_device(input_dev); + return error; +} + +static int mxt_configure_objects(struct mxt_data *data, + const struct firmware *cfg); + +static void mxt_config_cb(const struct firmware *cfg, void *ctx) +{ + mxt_configure_objects(ctx, cfg); + release_firmware(cfg); +} + +static int mxt_initialize(struct mxt_data *data) +{ + struct i2c_client *client = data->client; + int recovery_attempts = 0; + int error; + + while (1) { + error = mxt_read_info_block(data); + if (!error) + break; + + /* Check bootloader state */ + error = mxt_probe_bootloader(data, false); + if (error) { + dev_info(&client->dev, "Trying alternate bootloader address\n"); + mxt_soft_reset(data); + error = mxt_probe_bootloader(data, true); + if (error) { + /* Chip is not in appmode or bootloader mode */ + dev_err(&client->dev, "Fail probe boot, continue\n"); + } + } + + /* OK, we are in bootloader, see if we can recover */ + if (++recovery_attempts > 10) { + dev_err(&client->dev, "Could not recover from bootloader mode\n"); + /* + * We can reflash from this state, so do not + * abort initialization. + */ + data->in_bootloader = true; + return 0; + } + + /* Attempt to exit bootloader into app mode */ + mxt_send_bootloader_cmd(data, false); + msleep(MXT_FW_RESET_TIME); + } + + error = mxt_check_retrigen(data); + if (error) + return error; + + error = mxt_acquire_irq(data); + if (error) + return error; + + error = request_firmware_nowait(THIS_MODULE, true, MXT_CFG_NAME, + &client->dev, GFP_KERNEL, data, + mxt_config_cb); + if (error) { + dev_err(&client->dev, "Failed to invoke firmware loader: %d\n", + error); + return error; + } + + return 0; +} + +static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep) +{ + struct device *dev = &data->client->dev; + int error; + struct t7_config *new_config; + struct t7_config deepsleep = { .active = 0, .idle = 0 }; + + if (sleep == MXT_POWER_CFG_DEEPSLEEP) + new_config = &deepsleep; + else + new_config = &data->t7_cfg; + + error = __mxt_write_reg(data->client, data->T7_address, + sizeof(data->t7_cfg), new_config); + if (error) + return error; + + dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n", + new_config->active, new_config->idle); + + return 0; +} + +static int mxt_init_t7_power_cfg(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int error; + bool retry = false; + +recheck: + error = __mxt_read_reg(data, data->T7_address, + sizeof(data->t7_cfg), &data->t7_cfg); + if (error) + return error; + + if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) { + if (!retry) { + dev_dbg(dev, "T7 cfg zero, resetting\n"); + mxt_soft_reset(data); + retry = true; + goto recheck; + } else { + dev_dbg(dev, "T7 cfg zero after reset, overriding\n"); + data->t7_cfg.active = 20; + data->t7_cfg.idle = 100; + return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + } + } + + dev_dbg(dev, "Initialized power cfg: ACTV %d, IDLE %d\n", + data->t7_cfg.active, data->t7_cfg.idle); + return 0; +} + +#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37 +static const struct v4l2_file_operations mxt_video_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .unlocked_ioctl = video_ioctl2, + .read = vb2_fop_read, + .mmap = vb2_fop_mmap, + .poll = vb2_fop_poll, +}; + +static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x, + unsigned int y) +{ + struct mxt_info *info = data->info; + struct mxt_dbg *dbg = &data->dbg; + unsigned int ofs, page; + unsigned int col = 0; + unsigned int col_width; + + if (info->family_id == MXT_FAMILY_1386) { + col_width = info->matrix_ysize / MXT1386_COLUMNS; + col = y / col_width; + y = y % col_width; + } else { + col_width = info->matrix_ysize; + } + + ofs = (y + (x * col_width)) * sizeof(u16); + page = ofs / MXT_DIAGNOSTIC_SIZE; + ofs %= MXT_DIAGNOSTIC_SIZE; + + if (info->family_id == MXT_FAMILY_1386) + page += col * MXT1386_PAGES_PER_COLUMN; + + return get_unaligned_le16(&dbg->t37_buf[page].data[ofs]); +} + +static int mxt_convert_debug_pages(struct mxt_data *data, u16 *outbuf) +{ + struct mxt_dbg *dbg = &data->dbg; + unsigned int x = 0; + unsigned int y = 0; + unsigned int i, rx, ry; + + for (i = 0; i < dbg->t37_nodes; i++) { + /* Handle orientation */ + rx = data->xy_switch ? y : x; + ry = data->xy_switch ? x : y; + rx = data->invertx ? (data->xsize - 1 - rx) : rx; + ry = data->inverty ? (data->ysize - 1 - ry) : ry; + + outbuf[i] = mxt_get_debug_value(data, rx, ry); + + /* Next value */ + if (++x >= (data->xy_switch ? data->ysize : data->xsize)) { + x = 0; + y++; + } + } + + return 0; +} + +static int mxt_read_diagnostic_debug(struct mxt_data *data, u8 mode, + u16 *outbuf) +{ + struct mxt_dbg *dbg = &data->dbg; + int retries = 0; + int page; + int ret; + u8 cmd = mode; + struct t37_debug *p; + u8 cmd_poll; + + for (page = 0; page < dbg->t37_pages; page++) { + p = dbg->t37_buf + page; + + ret = mxt_write_reg(data->client, dbg->diag_cmd_address, + cmd); + if (ret) + return ret; + + retries = 0; + msleep(20); +wait_cmd: + /* Read back command byte */ + ret = __mxt_read_reg(data, dbg->diag_cmd_address, + sizeof(cmd_poll), data->read_buf); + if (ret) + return ret; + + cmd_poll = *(data->read_buf); + /* Field is cleared once the command has been processed */ + if (cmd_poll) { + if (retries++ > 100) + return -EINVAL; + + msleep(20); + goto wait_cmd; + } + + /* Read T37 page */ + ret = __mxt_read_reg(data, dbg->t37_address, + sizeof(struct t37_debug), p); + if (ret) + return ret; + + if (p->mode != mode || p->page != page) { + dev_err(&data->client->dev, "T37 page mismatch\n"); + return -EINVAL; + } + + dev_dbg(&data->client->dev, "%s page:%d retries:%d\n", + __func__, page, retries); + + /* For remaining pages, write PAGEUP rather than mode */ + cmd = MXT_DIAGNOSTIC_PAGEUP; + } + + return mxt_convert_debug_pages(data, outbuf); +} + +static int mxt_queue_setup(struct vb2_queue *q, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct mxt_data *data = q->drv_priv; + size_t size = data->dbg.t37_nodes * sizeof(u16); + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static void mxt_buffer_queue(struct vb2_buffer *vb) +{ + struct mxt_data *data = vb2_get_drv_priv(vb->vb2_queue); + u16 *ptr; + int ret; + u8 mode; + + ptr = vb2_plane_vaddr(vb, 0); + if (!ptr) { + dev_err(&data->client->dev, "Error acquiring frame ptr\n"); + goto fault; + } + + switch (data->dbg.input) { + case MXT_V4L_INPUT_DELTAS: + default: + mode = MXT_DIAGNOSTIC_DELTAS; + break; + + case MXT_V4L_INPUT_REFS: + mode = MXT_DIAGNOSTIC_REFS; + break; + } + + ret = mxt_read_diagnostic_debug(data, mode, ptr); + if (ret) + goto fault; + + vb2_set_plane_payload(vb, 0, data->dbg.t37_nodes * sizeof(u16)); + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + return; + +fault: + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); +} + +/* V4L2 structures */ +static const struct vb2_ops mxt_queue_ops = { + .queue_setup = mxt_queue_setup, + .buf_queue = mxt_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static const struct vb2_queue mxt_queue = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ, + .buf_struct_size = sizeof(struct mxt_vb2_buffer), + .ops = &mxt_queue_ops, + .mem_ops = &vb2_vmalloc_memops, + .timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC, + .min_buffers_needed = 1, +}; + +static int mxt_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct mxt_data *data = video_drvdata(file); + + strlcpy(cap->driver, "atmel_mxt_ts", sizeof(cap->driver)); + strlcpy(cap->card, "atmel_mxt_ts touch", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "I2C:%s", dev_name(&data->client->dev)); + return 0; +} + +static int mxt_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index >= MXT_V4L_INPUT_MAX) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_TOUCH; + + switch (i->index) { + case MXT_V4L_INPUT_REFS: + strlcpy(i->name, "Mutual Capacitance References", + sizeof(i->name)); + break; + case MXT_V4L_INPUT_DELTAS: + strlcpy(i->name, "Mutual Capacitance Deltas", sizeof(i->name)); + break; + } + + return 0; +} + +static int mxt_set_input(struct mxt_data *data, unsigned int i) +{ + struct v4l2_pix_format *f = &data->dbg.format; + + if (i >= MXT_V4L_INPUT_MAX) + return -EINVAL; + + if (i == MXT_V4L_INPUT_DELTAS) + f->pixelformat = V4L2_TCH_FMT_DELTA_TD16; + else + f->pixelformat = V4L2_TCH_FMT_TU16; + + f->width = data->xy_switch ? data->ysize : data->xsize; + f->height = data->xy_switch ? data->xsize : data->ysize; + f->field = V4L2_FIELD_NONE; + f->colorspace = V4L2_COLORSPACE_RAW; + f->bytesperline = f->width * sizeof(u16); + f->sizeimage = f->width * f->height * sizeof(u16); + + data->dbg.input = i; + + return 0; +} + +static int mxt_vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + return mxt_set_input(video_drvdata(file), i); +} + +static int mxt_vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct mxt_data *data = video_drvdata(file); + + *i = data->dbg.input; + + return 0; +} + +static int mxt_vidioc_fmt(struct file *file, void *priv, struct v4l2_format *f) +{ + struct mxt_data *data = video_drvdata(file); + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix = data->dbg.format; + + return 0; +} + +static int mxt_vidioc_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (fmt->index) { + case 0: + fmt->pixelformat = V4L2_TCH_FMT_TU16; + break; + + case 1: + fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int mxt_vidioc_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + a->parm.capture.readbuffers = 1; + a->parm.capture.timeperframe.numerator = 1; + a->parm.capture.timeperframe.denominator = 10; + return 0; +} + +static const struct v4l2_ioctl_ops mxt_video_ioctl_ops = { + .vidioc_querycap = mxt_vidioc_querycap, + + .vidioc_enum_fmt_vid_cap = mxt_vidioc_enum_fmt, + .vidioc_s_fmt_vid_cap = mxt_vidioc_fmt, + .vidioc_g_fmt_vid_cap = mxt_vidioc_fmt, + .vidioc_try_fmt_vid_cap = mxt_vidioc_fmt, + .vidioc_g_parm = mxt_vidioc_g_parm, + + .vidioc_enum_input = mxt_vidioc_enum_input, + .vidioc_g_input = mxt_vidioc_g_input, + .vidioc_s_input = mxt_vidioc_s_input, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, +}; + +static const struct video_device mxt_video_device = { + .name = "Atmel maxTouch", + .fops = &mxt_video_fops, + .ioctl_ops = &mxt_video_ioctl_ops, + .release = video_device_release_empty, + .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH | + V4L2_CAP_READWRITE | V4L2_CAP_STREAMING, +}; + +static void mxt_debug_init(struct mxt_data *data) +{ + struct mxt_info *info = data->info; + struct mxt_dbg *dbg = &data->dbg; + struct mxt_object *object; + int error; + + object = mxt_get_object(data, MXT_GEN_COMMAND_T6); + if (!object) + goto error; + + dbg->diag_cmd_address = object->start_address + MXT_COMMAND_DIAGNOSTIC; + + object = mxt_get_object(data, MXT_DEBUG_DIAGNOSTIC_T37); + if (!object) + goto error; + + if (mxt_obj_size(object) != sizeof(struct t37_debug)) { + dev_warn(&data->client->dev, "Bad T37 size"); + goto error; + } + + dbg->t37_address = object->start_address; + + /* Calculate size of data and allocate buffer */ + dbg->t37_nodes = data->xsize * data->ysize; + + if (info->family_id == MXT_FAMILY_1386) + dbg->t37_pages = MXT1386_COLUMNS * MXT1386_PAGES_PER_COLUMN; + else + dbg->t37_pages = DIV_ROUND_UP(data->xsize * + info->matrix_ysize * + sizeof(u16), + sizeof(dbg->t37_buf->data)); + + dbg->t37_buf = devm_kmalloc_array(&data->client->dev, dbg->t37_pages, + sizeof(struct t37_debug), GFP_KERNEL); + if (!dbg->t37_buf) + goto error; + + /* init channel to zero */ + mxt_set_input(data, 0); + + /* register video device */ + snprintf(dbg->v4l2.name, sizeof(dbg->v4l2.name), "%s", "atmel_mxt_ts"); + error = v4l2_device_register(&data->client->dev, &dbg->v4l2); + if (error) + goto error; + + /* initialize the queue */ + mutex_init(&dbg->lock); + dbg->queue = mxt_queue; + dbg->queue.drv_priv = data; + dbg->queue.lock = &dbg->lock; + dbg->queue.dev = &data->client->dev; + + error = vb2_queue_init(&dbg->queue); + if (error) + goto error_unreg_v4l2; + + dbg->vdev = mxt_video_device; + dbg->vdev.v4l2_dev = &dbg->v4l2; + dbg->vdev.lock = &dbg->lock; + dbg->vdev.vfl_dir = VFL_DIR_RX; + dbg->vdev.queue = &dbg->queue; + video_set_drvdata(&dbg->vdev, data); + + error = video_register_device(&dbg->vdev, VFL_TYPE_TOUCH, -1); + if (error) + goto error_unreg_v4l2; + + return; + +error_unreg_v4l2: + v4l2_device_unregister(&dbg->v4l2); +error: + dev_warn(&data->client->dev, "Error initializing T37\n"); +} +#else +static void mxt_debug_init(struct mxt_data *data) +{ +} +#endif + +static int mxt_configure_objects(struct mxt_data *data, + const struct firmware *cfg) +{ + struct device *dev = &data->client->dev; + int error; + + error = mxt_init_t7_power_cfg(data); + if (error) { + dev_err(dev, "Failed to initialize power cfg\n"); + return error; + } + + if (cfg) { + error = mxt_update_cfg(data, cfg); + if (error) + dev_warn(dev, "Error %d updating config\n", error); + } + + if (data->multitouch) { + error = mxt_initialize_input_device(data); + if (error) + return error; + } else { + dev_warn(dev, "No touch object detected\n"); + } + + mxt_debug_init(data); + + enable_irq(data->irq); + return 0; +} + +/* Firmware Version is returned as Major.Minor.Build */ +static ssize_t mxt_fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + struct mxt_info *info = data->info; + return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n", + info->version >> 4, info->version & 0xf, info->build); +} + +/* Hardware Version is returned as FamilyID.VariantID */ +static ssize_t mxt_hw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + struct mxt_info *info = data->info; + return scnprintf(buf, PAGE_SIZE, "%u.%u\n", + info->family_id, info->variant_id); +} + +static ssize_t mxt_show_instance(char *buf, int count, + struct mxt_object *object, int instance, + const u8 *val) +{ + int i; + + if (mxt_obj_instances(object) > 1) + count += scnprintf(buf + count, PAGE_SIZE - count, + "Instance %u\n", instance); + + for (i = 0; i < mxt_obj_size(object); i++) + count += scnprintf(buf + count, PAGE_SIZE - count, + "\t[%2u]: %02x (%d)\n", i, val[i], val[i]); + count += scnprintf(buf + count, PAGE_SIZE - count, "\n"); + + return count; +} + +static ssize_t mxt_object_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mxt_data *data = dev_get_drvdata(dev); + struct mxt_object *object; + int count = 0; + int i, j; + int error; + u8 *obuf; + + /* Pre-allocate buffer large enough to hold max sized object. */ + obuf = kmalloc(256, GFP_KERNEL); + if (!obuf) + return -ENOMEM; + + error = 0; + for (i = 0; i < data->info->object_num; i++) { + object = data->object_table + i; + + if (!mxt_object_readable(object->type)) + continue; + + count += scnprintf(buf + count, PAGE_SIZE - count, + "T%u:\n", object->type); + + for (j = 0; j < mxt_obj_instances(object); j++) { + u16 size = mxt_obj_size(object); + u16 addr = object->start_address + j * size; + + error = __mxt_read_reg(data, addr, size, obuf); + if (error) + goto done; + + count = mxt_show_instance(buf, count, object, j, obuf); + } + } + +done: + kfree(obuf); + return error ?: count; +} + +static int mxt_check_firmware_format(struct device *dev, + const struct firmware *fw) +{ + unsigned int pos = 0; + char c; + + while (pos < fw->size) { + c = *(fw->data + pos); + + if (c < '0' || (c > '9' && c < 'A') || c > 'F') + return 0; + + pos++; + } + + /* + * To convert file try: + * xxd -r -p mXTXXX__APP_VX-X-XX.enc > maxtouch.fw + */ + dev_err(dev, "Aborting: firmware file must be in binary format\n"); + + return -EINVAL; +} + +static int mxt_load_fw(struct device *dev, const char *fn) +{ + struct mxt_data *data = dev_get_drvdata(dev); + const struct firmware *fw = NULL; + unsigned int frame_size; + unsigned int pos = 0; + unsigned int retry = 0; + unsigned int frame = 0; + int ret; + + ret = request_firmware(&fw, fn, dev); + if (ret) { + dev_err(dev, "Unable to open firmware %s\n", fn); + return ret; + } + + /* Check for incorrect enc file */ + ret = mxt_check_firmware_format(dev, fw); + if (ret) + goto release_firmware; + + if (!data->in_bootloader) { + /* Change to the bootloader mode */ + data->in_bootloader = true; + + ret = mxt_t6_command(data, MXT_COMMAND_RESET, + MXT_BOOT_VALUE, false); + if (ret) + goto release_firmware; + + msleep(MXT_RESET_TIME); + + /* Do not need to scan since we know family ID */ + ret = mxt_lookup_bootloader_address(data, 0); + if (ret) + goto release_firmware; + + mxt_free_input_device(data); + mxt_free_object_table(data); + } else { + enable_irq(data->irq); + } + + reinit_completion(&data->bl_completion); + + ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); + if (ret) { + /* Bootloader may still be unlocked from previous attempt */ + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false); + if (ret) + goto disable_irq; + } else { + dev_info(dev, "Unlocking bootloader\n"); + + /* Unlock bootloader */ + ret = mxt_send_bootloader_cmd(data, true); + if (ret) + goto disable_irq; + } + + while (pos < fw->size) { + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true); + if (ret) + goto disable_irq; + + frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); + + /* Take account of CRC bytes */ + frame_size += 2; + + /* Write one frame to device */ + ret = mxt_bootloader_write(data, fw->data + pos, frame_size); + if (ret) + goto disable_irq; + + ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true); + if (ret) { + retry++; + + /* Back off by 20ms per retry */ + msleep(retry * 20); + + if (retry > 20) { + dev_err(dev, "Retry count exceeded\n"); + goto disable_irq; + } + } else { + retry = 0; + pos += frame_size; + frame++; + } + + if (frame % 50 == 0) + dev_dbg(dev, "Sent %d frames, %d/%zd bytes\n", + frame, pos, fw->size); + } + + /* Wait for flash. */ + ret = mxt_wait_for_completion(data, &data->bl_completion, + MXT_FW_RESET_TIME); + if (ret) + goto disable_irq; + + dev_dbg(dev, "Sent %d frames, %d bytes\n", frame, pos); + + /* + * Wait for device to reset. Some bootloader versions do not assert + * the CHG line after bootloading has finished, so ignore potential + * errors. + */ + mxt_wait_for_completion(data, &data->bl_completion, MXT_FW_RESET_TIME); + + data->in_bootloader = false; + +disable_irq: + disable_irq(data->irq); +release_firmware: + release_firmware(fw); + return ret; +} + +static ssize_t mxt_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mxt_data *data = dev_get_drvdata(dev); + int error; + + error = mxt_load_fw(dev, MXT_FW_NAME); + if (error) { + dev_err(dev, "The firmware update failed(%d)\n", error); + count = error; + } else { + dev_info(dev, "The firmware update succeeded\n"); + + error = mxt_initialize(data); + if (error) + return error; + } + + return count; +} + +static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL); +static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL); +static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL); +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store); + +static struct attribute *mxt_attrs[] = { + &dev_attr_fw_version.attr, + &dev_attr_hw_version.attr, + &dev_attr_object.attr, + &dev_attr_update_fw.attr, + NULL +}; + +static const struct attribute_group mxt_attr_group = { + .attrs = mxt_attrs, +}; + +static void mxt_start(struct mxt_data *data) +{ + if (!data->suspended || data->in_bootloader) + return; + + /* enable gpios */ + mxt_gpio_enable(data, true); + + /* enable regulators */ + mxt_regulator_enable(data); + + switch (data->suspend_mode) { + case MXT_SUSPEND_T9_CTRL: + mxt_soft_reset(data); + + /* Touch enable */ + /* 0x83 = SCANEN | RPTEN | ENABLE */ + mxt_write_object(data, + MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0x83); + break; + + case MXT_SUSPEND_DEEP_SLEEP: + default: + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + + /* Recalibrate since chip has been in deep sleep */ + mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false); + break; + } + + mxt_acquire_irq(data); + data->suspended = false; +} + +static void mxt_stop(struct mxt_data *data) +{ + if (data->suspended || data->in_bootloader) + return; + + disable_irq(data->irq); + + switch (data->suspend_mode) { + case MXT_SUSPEND_T9_CTRL: + /* Touch disable */ + mxt_write_object(data, + MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0); + break; + + case MXT_SUSPEND_DEEP_SLEEP: + default: + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); + break; + } + + /* disable regulators */ + mxt_regulator_disable(data); + /* disable gpios */ + mxt_gpio_enable(data, false); + + data->suspended = true; +} + +static int mxt_input_open(struct input_dev *dev) +{ +#if !defined(CONFIG_DRM) + struct mxt_data *data = input_get_drvdata(dev); + + mxt_start(data); +#endif + return 0; +} + +static void mxt_input_close(struct input_dev *dev) +{ +#if !defined(CONFIG_DRM) + struct mxt_data *data = input_get_drvdata(dev); + + mxt_stop(data); +#endif +} + +static int mxt_parse_device_properties(struct mxt_data *data) +{ + static const char keymap_property[] = "linux,gpio-keymap"; + struct device *dev = &data->client->dev; + u32 *keymap; + int n_keys; + int error; + + if (device_property_present(dev, keymap_property)) { + n_keys = device_property_read_u32_array(dev, keymap_property, + NULL, 0); + if (n_keys <= 0) { + error = n_keys < 0 ? n_keys : -EINVAL; + dev_err(dev, "invalid/malformed '%s' property: %d\n", + keymap_property, error); + return error; + } + + keymap = devm_kmalloc_array(dev, n_keys, sizeof(*keymap), + GFP_KERNEL); + if (!keymap) + return -ENOMEM; + + error = device_property_read_u32_array(dev, keymap_property, + keymap, n_keys); + if (error) { + dev_err(dev, "failed to parse '%s' property: %d\n", + keymap_property, error); + return error; + } + + data->t19_keymap = keymap; + data->t19_num_keys = n_keys; + } + + return 0; +} + +#ifdef CONFIG_OF + +static int mxt_check_dedicated_touch(struct device_node *dt, const char *prop, + const char *active_prop) +{ + const char *active_touch; + const char *compatible; + char *temp; + int ret = 0; + + ret = of_property_read_string(dt->parent, active_prop, &active_touch); + if (ret < 0) { + pr_info(" %s:not dedicated active touch\n", __func__); + return -ENODEV; + } + + ret = of_property_read_string(dt, prop, &compatible); + if (ret < 0) { + pr_err(" %s:fail to read %s %d\n", __func__, "compatible", ret); + return -ENODEV; + } + + temp = strnstr(active_touch, compatible, strlen(active_touch)); + if (!temp) { + pr_info(" %s:no match compatible, %s, %s\n", + __func__, compatible, active_touch); + return -ENODEV; + + } + + return ret; +} + +#ifdef CONFIG_DRM +static struct drm_panel *active_panel; +static int mxt_check_dt(struct device_node *np) +{ + int i; + int count; + struct device_node *node; + struct drm_panel *panel; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) + return 0; + + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + panel = of_drm_find_panel(node); + of_node_put(node); + if (!IS_ERR(panel)) { + active_panel = panel; + return 0; + } + } + + return PTR_ERR(panel); +} +#endif + +static int mxt_get_dt_coords(struct device *dev, char *name, + struct mxt_data *data) +{ + u32 coords[MXT_COORDS_ARR_SIZE]; + struct property *prop; + struct device_node *np = dev->of_node; + size_t coords_size; + int error; + + prop = of_find_property(np, name, NULL); + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + + coords_size = prop->length / sizeof(u32); + if (coords_size != MXT_COORDS_ARR_SIZE) { + dev_err(dev, "invalid %s\n", name); + return -EINVAL; + } + + error = of_property_read_u32_array(np, name, coords, coords_size); + if (error && (error != -EINVAL)) { + dev_err(dev, "Unable to read %s\n", name); + return error; + } + + if (strcmp(name, "atmel,panel-coords") == 0) { + data->panel_minx = coords[0]; + data->panel_miny = coords[1]; + data->panel_maxx = coords[2]; + data->panel_maxy = coords[3]; + } else if (strcmp(name, "atmel,display-coords") == 0) { + data->disp_minx = coords[0]; + data->disp_miny = coords[1]; + data->disp_maxx = coords[2]; + data->disp_maxy = coords[3]; + } else { + dev_err(dev, "unsupported property %s\n", name); + return -EINVAL; + } + + return 0; +} + +#endif + +static const struct dmi_system_id chromebook_T9_suspend_dmi[] = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), + DMI_MATCH(DMI_PRODUCT_NAME, "Link"), + }, + }, + { + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"), + }, + }, + { } +}; + +#ifdef CONFIG_DRM + +static void mxt_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, + void *client_data) +{ + struct mxt_data *mxt = client_data; + struct input_dev *input_dev; + + if (!notification) { + pr_err("Invalid notification\n"); + return; + } + + if (!mxt) { + pr_err("Invalid mxt data\n"); + return; + } + + pr_debug("Notification type:%d, early_trigger:%d\n", + notification->notif_type, + notification->notif_data.early_trigger); + + input_dev = mxt->input_dev; + + switch (notification->notif_type) { + case DRM_PANEL_EVENT_UNBLANK: + if (!notification->notif_data.early_trigger) { + if (input_dev) + mutex_lock(&input_dev->mutex); + + mxt_start(mxt); + + if (input_dev) + mutex_unlock(&input_dev->mutex); + } + break; + case DRM_PANEL_EVENT_BLANK: + if (input_dev) + mutex_lock(&input_dev->mutex); + + mxt_stop(mxt); + + if (input_dev) + mutex_unlock(&input_dev->mutex); + break; + default: + break; + } +} + +static void mxt_register_for_panel_events(struct device_node *dt, + struct mxt_data *data) +{ + void *cookie = NULL; + + cookie = panel_event_notifier_register( + PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, + active_panel, &mxt_panel_notifier_callback, data); + if (!cookie) { + pr_err("Failed to register for panel events\n"); + return; + } + pr_debug("registered for panel notifications on panel: 0x%x\n", + active_panel); + + data->notifier_cookie = cookie; +} +#endif + +static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct mxt_data *data; + int error = 0; +#ifdef CONFIG_OF + struct device_node *dt = client->dev.of_node; +#endif + /* + * Ignore devices that do not have device properties attached to + * them, as we need help determining whether we are dealing with + * touch screen or touchpad. + * + * So far on x86 the only users of Atmel touch controllers are + * Chromebooks, and chromeos_laptop driver will ensure that + * necessary properties are provided (if firmware does not do that). + */ + if (!device_property_present(&client->dev, "compatible")) + return -ENXIO; + + /* + * Ignore ACPI devices representing bootloader mode. + * + * This is a bit of a hack: Google Chromebook BIOS creates ACPI + * devices for both application and bootloader modes, but we are + * interested in application mode only (if device is in bootloader + * mode we'll end up switching into application anyway). So far + * application mode addresses were all above 0x40, so we'll use it + * as a threshold. + */ + if (ACPI_COMPANION(&client->dev) && client->addr < 0x40) + return -ENXIO; + +#ifdef CONFIG_OF +#ifdef CONFIG_DRM + error = mxt_check_dt(dt); + if (error == -EPROBE_DEFER) + return error; + + if (error) { + if (!(mxt_check_dedicated_touch(dt, "compatible", + "qcom,i2c-touch-active"))) + return -EPROBE_DEFER; + else + return -ENODEV; + } +#else + if (mxt_check_dedicated_touch(dt, "compatible", + "qcom,i2c-touch-active") < 0) + return -ENODEV; +#endif +#endif + data = devm_kzalloc(&client->dev, sizeof(struct mxt_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", + client->adapter->nr, client->addr); + + data->client = client; + data->irq = client->irq; + i2c_set_clientdata(client, data); + +#ifdef CONFIG_OF + error = mxt_get_dt_coords(&client->dev, "atmel,display-coords", data); + if (error) + return error; + + data->xy_switch = of_property_read_bool(dt, "atmel,xy_switch"); + data->invertx = of_property_read_bool(dt, "atmel,invertx"); + data->inverty = of_property_read_bool(dt, "atmel,inverty"); +#endif + + data->cmd_buf = devm_kzalloc(&client->dev, 64, GFP_KERNEL); + if (!data->cmd_buf) + return -ENOMEM; + data->read_buf = data->cmd_buf + 32; + + init_completion(&data->bl_completion); + init_completion(&data->reset_completion); + init_completion(&data->crc_completion); + +#if defined(CONFIG_OF) && defined(CONFIG_DRM) + data->suspend_mode = MXT_SUSPEND_T9_CTRL; +#else + data->suspend_mode = dmi_check_system(chromebook_T9_suspend_dmi) ? + MXT_SUSPEND_T9_CTRL : MXT_SUSPEND_DEEP_SLEEP; +#endif + + error = mxt_parse_device_properties(data); + if (error) + return error; + + error = mxt_pinctrl_init(data); + if (error) + dev_info(&client->dev, "No pinctrl support\n"); + + data->reset_gpio = devm_gpiod_get_optional(&client->dev, + "reset", GPIOD_OUT_LOW); + data->irq_gpio = devm_gpiod_get_optional(&client->dev, + "irq", GPIOD_IN); + if (IS_ERR(data->reset_gpio)) { + error = PTR_ERR(data->reset_gpio); + dev_err(&client->dev, "Failed to get reset gpio: %d\n", error); + return error; + } + + gpiod_direction_input(data->irq_gpio); + gpiod_direction_output(data->reset_gpio, 0); + + error = mxt_regulator_parse(data); + if (error) { + dev_err(&client->dev, "Failed to parse regulators\n"); + return error; + } + + error = mxt_regulator_enable(data); + if (error) { + dev_err(&client->dev, "Error %d enabling regulators\n", error); + return error; + } + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, mxt_interrupt, IRQF_ONESHOT, + client->name, data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_disable_regulator; + } + + disable_irq(client->irq); + + if (data->reset_gpio) { + gpiod_set_value(data->reset_gpio, 0); + msleep(MXT_RESET_GPIO_TIME); + gpiod_set_value(data->reset_gpio, 1); + msleep(MXT_RESET_INVALID_CHG); + } + + error = mxt_gpio_enable(data, true); + if (error) + dev_err(&client->dev, "Failed to configure gpios\n"); + + data->irq = data->client->irq = + gpiod_to_irq(data->irq_gpio); + + error = mxt_initialize(data); + if (error) + goto err_disable_regulator; + + error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); + if (error) { + dev_err(&client->dev, "Failure %d creating sysfs group\n", + error); + goto err_free_object; + } +#ifdef CONFIG_DRM + if (active_panel) + mxt_register_for_panel_events(dt, data); +#endif + return 0; + +err_free_object: + mxt_free_input_device(data); + mxt_free_object_table(data); + +err_disable_regulator: + mxt_regulator_disable(data); + return error; +} + +static int mxt_remove(struct i2c_client *client) +{ + struct mxt_data *data = i2c_get_clientdata(client); + +#ifdef CONFIG_DRM + if (active_panel && data->notifier_cookie) + panel_event_notifier_unregister(data->notifier_cookie); +#endif + disable_irq(data->irq); + mxt_regulator_disable(data); + sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); + mxt_free_input_device(data); + mxt_free_object_table(data); + + return 0; +} + +static int __maybe_unused mxt_suspend(struct device *dev) +{ +#if !defined(CONFIG_DRM) + struct i2c_client *client = to_i2c_client(dev); + struct mxt_data *data = i2c_get_clientdata(client); + struct input_dev *input_dev = data->input_dev; + + if (!input_dev) + return 0; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + mxt_stop(data); + + mutex_unlock(&input_dev->mutex); + + disable_irq(data->irq); +#endif + + return 0; +} + +static int __maybe_unused mxt_resume(struct device *dev) +{ +#if !defined(CONFIG_DRM) + struct i2c_client *client = to_i2c_client(dev); + struct mxt_data *data = i2c_get_clientdata(client); + struct input_dev *input_dev = data->input_dev; + + if (!input_dev) + return 0; + + enable_irq(data->irq); + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + mxt_start(data); + + mutex_unlock(&input_dev->mutex); +#endif + + return 0; +} + +static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume); + +static const struct of_device_id mxt_of_match[] = { + { .compatible = "atmel,maxtouch", }, + /* Compatibles listed below are deprecated */ + { .compatible = "atmel,qt602240_ts", }, + { .compatible = "atmel,atmel_mxt_ts", }, + { .compatible = "atmel,atmel_mxt_tp", }, + { .compatible = "atmel,mXT224", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mxt_of_match); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id mxt_acpi_id[] = { + { "ATML0000", 0 }, /* Touchpad */ + { "ATML0001", 0 }, /* Touchscreen */ + { } +}; +MODULE_DEVICE_TABLE(acpi, mxt_acpi_id); +#endif + +static const struct i2c_device_id mxt_id[] = { + { "qt602240_ts", 0 }, + { "atmel_mxt_ts", 0 }, + { "atmel_mxt_tp", 0 }, + { "maxtouch", 0 }, + { "mXT224", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mxt_id); + +static struct i2c_driver mxt_driver = { + .driver = { + .name = "atmel_mxt_ts", + .of_match_table = mxt_of_match, + .acpi_match_table = ACPI_PTR(mxt_acpi_id), + .pm = &mxt_pm_ops, + }, + .probe = mxt_probe, + .remove = mxt_remove, + .id_table = mxt_id, +}; + +module_i2c_driver(mxt_driver); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver"); +MODULE_LICENSE("GPL"); diff --git a/config/gki_kalamatouch.conf b/config/gki_kalamatouch.conf index 4445b828df..de356a66c9 100644 --- a/config/gki_kalamatouch.conf +++ b/config/gki_kalamatouch.conf @@ -1,3 +1,4 @@ export CONFIG_TOUCHSCREEN_NT36XXX_I2C=y export CONFIG_TOUCHSCREEN_GOODIX_BRL=y +export CONFIG_TOUCHSCREEN_ATMEL_MXT=y export CONFIG_MSM_TOUCH=m diff --git a/config/gki_kalamatouchconf.h b/config/gki_kalamatouchconf.h index 4747411498..7774e7359f 100644 --- a/config/gki_kalamatouchconf.h +++ b/config/gki_kalamatouchconf.h @@ -5,4 +5,5 @@ #define CONFIG_TOUCHSCREEN_NT36XXX_I2C 1 #define CONFIG_TOUCHSCREEN_GOODIX_BRL 1 +#define CONFIG_TOUCHSCREEN_ATMEL_MXT 1 diff --git a/touch_driver_board.mk b/touch_driver_board.mk index 3f26729e74..643742d8e0 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -2,6 +2,7 @@ ifneq ($(TARGET_BOARD_AUTO),true) ifeq ($(call is-board-platform-in-list,$(TARGET_BOARD_PLATFORM)),true) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ - $(KERNEL_MODULES_OUT)/goodix_ts.ko + $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko endif endif diff --git a/touch_driver_product.mk b/touch_driver_product.mk index 28ee488175..08c37015ff 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -1,3 +1,4 @@ PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ - $(KERNEL_MODULES_OUT)/goodix_ts.ko + $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko From 20f5c43e92e04ca9793872ae3af3eb2f7cc0a99c Mon Sep 17 00:00:00 2001 From: Yu Wu Date: Wed, 23 Feb 2022 16:21:41 +0800 Subject: [PATCH 014/170] touch: goodix: property "qcom,touch-environment" Parse the property "qcom,touch-environment". Change-Id: I0cc00e10b409a31c2d240078d7a603baaaf3b30e --- goodix_berlin_driver/goodix_ts_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/goodix_berlin_driver/goodix_ts_core.c b/goodix_berlin_driver/goodix_ts_core.c index e67b253df0..e984422ba0 100644 --- a/goodix_berlin_driver/goodix_ts_core.c +++ b/goodix_berlin_driver/goodix_ts_core.c @@ -2300,6 +2300,10 @@ static int goodix_ts_probe(struct platform_device *pdev) ts_err("failed parse device info form dts, %d", ret); return -EINVAL; } +#if defined(CONFIG_DRM) + of_property_read_string(node, "qcom,touch-environment", + &core_data->touch_environment); +#endif } else { ts_err("no valid device tree node found"); return -ENODEV; From 7e0bf2f396ba0a1cc576ee2d41a0cf5ef62d2023 Mon Sep 17 00:00:00 2001 From: Ritesh Kumar Date: Fri, 18 Mar 2022 17:47:51 -0700 Subject: [PATCH 015/170] touch: qts: add QTS framework changes QTS framework defines a unified set of APIs that any vendor touch driver module can implement in order to seamlessly integrate with value added functionalities supported by msm display drivers. Define and implement this framework with an initial support for TUI touch functionality. Change-Id: I1785713263af07d58baf613a49f99345d7dddaea Signed-off-by: Raviteja Tamatam Signed-off-by: Ritesh Kumar --- qts/qts_core.c | 1694 +++++++++++++++++++++++++++++++++++++++++ qts/qts_core.h | 168 ++++ qts/qts_core_common.h | 46 ++ 3 files changed, 1908 insertions(+) create mode 100644 qts/qts_core.c create mode 100644 qts/qts_core.h create mode 100644 qts/qts_core_common.h diff --git a/qts/qts_core.c b/qts/qts_core.c new file mode 100644 index 0000000000..227d711845 --- /dev/null +++ b/qts/qts_core.c @@ -0,0 +1,1694 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qts_core.h" + +static struct qts_data_entries *qts_data_entries; + +struct drm_panel *active_panel; + +static void qts_trusted_touch_abort_handler(struct qts_data *qts_data, int error); + +static struct gh_acl_desc *qts_vm_get_acl(enum gh_vm_names vm_name) +{ + struct gh_acl_desc *acl_desc; + gh_vmid_t vmid; + + gh_rm_get_vmid(vm_name, &vmid); + + acl_desc = kzalloc(offsetof(struct gh_acl_desc, acl_entries[1]), + GFP_KERNEL); + if (!acl_desc) + return ERR_PTR(ENOMEM); + + acl_desc->n_acl_entries = 1; + acl_desc->acl_entries[0].vmid = vmid; + acl_desc->acl_entries[0].perms = GH_RM_ACL_R | GH_RM_ACL_W; + + return acl_desc; +} + +static struct gh_sgl_desc *qts_vm_get_sgl(struct trusted_touch_vm_info *vm_info) +{ + struct gh_sgl_desc *sgl_desc; + int i; + + sgl_desc = kzalloc(offsetof(struct gh_sgl_desc, + sgl_entries[vm_info->iomem_list_size]), GFP_KERNEL); + if (!sgl_desc) + return ERR_PTR(ENOMEM); + + sgl_desc->n_sgl_entries = vm_info->iomem_list_size; + + for (i = 0; i < vm_info->iomem_list_size; i++) { + sgl_desc->sgl_entries[i].ipa_base = vm_info->iomem_bases[i]; + sgl_desc->sgl_entries[i].size = vm_info->iomem_sizes[i]; + } + + return sgl_desc; +} + +static int qts_populate_vm_info_iomem(struct qts_data *qts_data) +{ + int i, gpio, rc = 0; + int num_regs, num_sizes, num_gpios, list_size; + struct resource res; + struct device_node *np = qts_data->dev->of_node; + struct trusted_touch_vm_info *vm_info = qts_data->vm_info; + + num_regs = of_property_count_u32_elems(np, "qts,trusted-touch-io-bases"); + if (num_regs < 0) { + pr_err("Invalid number of IO regions specified\n"); + return -EINVAL; + } + + num_sizes = of_property_count_u32_elems(np, "qts,trusted-touch-io-sizes"); + if (num_sizes < 0) { + pr_err("Invalid number of IO regions specified\n"); + return -EINVAL; + } + + if (num_regs != num_sizes) { + pr_err("IO bases and sizes array lengths mismatch\n"); + return -EINVAL; + } + + num_gpios = of_gpio_named_count(np, "qts,trusted-touch-vm-gpio-list"); + if (num_gpios < 0) { + pr_warn("Ignoring invalid trusted gpio list: %d\n", num_gpios); + num_gpios = 0; + } + + list_size = num_regs + num_gpios; + vm_info->iomem_list_size = list_size; + vm_info->iomem_bases = devm_kcalloc(qts_data->dev, list_size, sizeof(*vm_info->iomem_bases), + GFP_KERNEL); + if (!vm_info->iomem_bases) + return -ENOMEM; + + vm_info->iomem_sizes = devm_kcalloc(qts_data->dev, list_size, sizeof(*vm_info->iomem_sizes), + GFP_KERNEL); + if (!vm_info->iomem_sizes) + return -ENOMEM; + + for (i = 0; i < num_gpios; ++i) { + gpio = of_get_named_gpio(np, "qts,trusted-touch-vm-gpio-list", i); + if (gpio < 0 || !gpio_is_valid(gpio)) { + pr_err("Invalid gpio %d at position %d\n", gpio, i); + return gpio; + } + + if (!msm_gpio_get_pin_address(gpio, &res)) { + pr_err("Failed to retrieve gpio-%d resource\n", gpio); + return -ENODATA; + } + + vm_info->iomem_bases[i] = res.start; + vm_info->iomem_sizes[i] = resource_size(&res); + } + + rc = of_property_read_u32_array(np, "qts,trusted-touch-io-bases", + &vm_info->iomem_bases[i], list_size - i); + if (rc) { + pr_err("Failed to read trusted touch io bases:%d\n", rc); + return rc; + } + + rc = of_property_read_u32_array(np, "qts,trusted-touch-io-sizes", + &vm_info->iomem_sizes[i], list_size - i); + if (rc) { + pr_err("Failed to read trusted touch io sizes:%d\n", rc); + return rc; + } + + return 0; +} + +static int qts_populate_vm_info(struct qts_data *qts_data) +{ + int rc; + struct trusted_touch_vm_info *vm_info; + struct device_node *np = qts_data->dev->of_node; + + vm_info = devm_kzalloc(qts_data->dev, sizeof(struct trusted_touch_vm_info), GFP_KERNEL); + if (!vm_info) + return -ENOMEM; + + qts_data->vm_info = vm_info; + vm_info->vm_name = GH_TRUSTED_VM; + rc = of_property_read_u32(np, "qts,trusted-touch-spi-irq", &vm_info->hw_irq); + if (rc) { + pr_err("Failed to read trusted touch SPI irq:%d\n", rc); + return rc; + } + + rc = qts_populate_vm_info_iomem(qts_data); + if (rc) { + pr_err("Failed to read trusted touch mmio ranges:%d\n", rc); + return rc; + } + + rc = of_property_read_string(np, "qts,trusted-touch-type", + &vm_info->trusted_touch_type); + if (rc) { + pr_warn("%s: No trusted touch type selection made\n"); + vm_info->mem_tag = GH_MEM_NOTIFIER_TAG_TOUCH_PRIMARY; + vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH_PRIMARY; + rc = 0; + } else if (!strcmp(vm_info->trusted_touch_type, "primary")) { + vm_info->mem_tag = GH_MEM_NOTIFIER_TAG_TOUCH_PRIMARY; + vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH_PRIMARY; + } else if (!strcmp(vm_info->trusted_touch_type, "secondary")) { + vm_info->mem_tag = GH_MEM_NOTIFIER_TAG_TOUCH_SECONDARY; + vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH_SECONDARY; + } + + return 0; +} + +static void qts_destroy_vm_info(struct qts_data *qts_data) +{ + kfree(qts_data->vm_info->iomem_sizes); + kfree(qts_data->vm_info->iomem_bases); + kfree(qts_data->vm_info); +} + +static void qts_vm_deinit(struct qts_data *qts_data) +{ + if (qts_data->vm_info->mem_cookie) + gh_mem_notifier_unregister(qts_data->vm_info->mem_cookie); + qts_destroy_vm_info(qts_data); +} + +static int qts_trusted_touch_get_vm_state(struct qts_data *qts_data) +{ + return atomic_read(&qts_data->vm_info->vm_state); +} + +static void qts_trusted_touch_set_vm_state(struct qts_data *qts_data, + int state) +{ + pr_debug("state %d\n", state); + atomic_set(&qts_data->vm_info->vm_state, state); +} + +#ifdef CONFIG_ARCH_QTI_VM +static int qts_vm_mem_release(struct qts_data *qts_data); +static void qts_trusted_touch_tvm_vm_mode_disable(struct qts_data *qts_data); +static void qts_trusted_touch_abort_tvm(struct qts_data *qts_data); +static void qts_trusted_touch_event_notify(struct qts_data *qts_data, int event); + +static void qts_irq_enable(struct qts_data *qts_data, bool en) +{ + if (en) { + if (qts_data->irq_disabled) { + pr_debug("qts irq enable\n"); + enable_irq(qts_data->irq); + qts_data->irq_disabled = false; + } + } else { + if (!qts_data->irq_disabled) { + pr_debug("qts irq disable\n"); + disable_irq_nosync(qts_data->irq); + qts_data->irq_disabled = true; + } + } +} + +static int qts_irq_registration(struct qts_data *qts_data) +{ + int ret = 0; + + qts_data->irq_gpio_flags = IRQF_TRIGGER_RISING; + pr_debug("irq:%d, flag:%x\n", qts_data->irq, qts_data->irq_gpio_flags); + ret = request_threaded_irq(qts_data->irq, NULL, qts_data->vendor_ops.irq_handler, + qts_data->irq_gpio_flags | IRQF_ONESHOT, + QTS_NAME, qts_data->vendor_data); + if (ret != 0) + pr_err("request_threaded_irq failed\n"); + if (ret == 0) + qts_irq_enable(qts_data, false); + + return ret; +} + +void qts_trusted_touch_tvm_i2c_failure_report(struct qts_data *qts_data) +{ + pr_warn("initiating trusted touch abort due to i2c failure\n"); + qts_trusted_touch_abort_handler(qts_data, TRUSTED_TOUCH_EVENT_I2C_FAILURE); +} + +static void qts_trusted_touch_reset_gpio_toggle(struct qts_data *qts_data) +{ + void __iomem *base; + + if (qts_data->bus_type != QTS_BUS_TYPE_I2C) + return; + + base = ioremap(TOUCH_RESET_GPIO_BASE, TOUCH_RESET_GPIO_SIZE); + writel_relaxed(0x1, base + TOUCH_RESET_GPIO_OFFSET); + /* wait until toggle to finish*/ + wmb(); + writel_relaxed(0x0, base + TOUCH_RESET_GPIO_OFFSET); + /* wait until toggle to finish*/ + wmb(); + iounmap(base); +} + +static void qts_trusted_touch_intr_gpio_toggle(struct qts_data *qts_data, + bool enable) +{ + void __iomem *base; + u32 val; + + if (qts_data->bus_type != QTS_BUS_TYPE_I2C) + return; + + base = ioremap(TOUCH_INTR_GPIO_BASE, TOUCH_INTR_GPIO_SIZE); + val = readl_relaxed(base + TOUCH_RESET_GPIO_OFFSET); + if (enable) { + val |= BIT(0); + writel_relaxed(val, base + TOUCH_INTR_GPIO_OFFSET); + /* wait until toggle to finish*/ + wmb(); + } else { + val &= ~BIT(0); + writel_relaxed(val, base + TOUCH_INTR_GPIO_OFFSET); + /* wait until toggle to finish*/ + wmb(); + } + iounmap(base); +} + +static int qts_sgl_cmp(const void *a, const void *b) +{ + struct gh_sgl_entry *left = (struct gh_sgl_entry *)a; + struct gh_sgl_entry *right = (struct gh_sgl_entry *)b; + + return (left->ipa_base - right->ipa_base); +} + +static int qts_vm_compare_sgl_desc(struct gh_sgl_desc *expected, + struct gh_sgl_desc *received) +{ + int idx; + + if (expected->n_sgl_entries != received->n_sgl_entries) + return -E2BIG; + sort(received->sgl_entries, received->n_sgl_entries, + sizeof(received->sgl_entries[0]), qts_sgl_cmp, NULL); + sort(expected->sgl_entries, expected->n_sgl_entries, + sizeof(expected->sgl_entries[0]), qts_sgl_cmp, NULL); + + for (idx = 0; idx < expected->n_sgl_entries; idx++) { + struct gh_sgl_entry *left = &expected->sgl_entries[idx]; + struct gh_sgl_entry *right = &received->sgl_entries[idx]; + + if ((left->ipa_base != right->ipa_base) || + (left->size != right->size)) { + pr_err("sgl mismatch: left_base:%d right base:%d left size:%d right size:%d\n", + left->ipa_base, right->ipa_base, left->size, right->size); + + return -EINVAL; + } + } + return 0; +} + +static int qts_vm_handle_vm_hardware(struct qts_data *qts_data) +{ + int rc = 0; + + if (atomic_read(&qts_data->delayed_tvm_probe_pending)) { + rc = qts_irq_registration(qts_data); + if (rc) { + pr_err("irq registration failure on TVM!\n"); + return rc; + } + atomic_set(&qts_data->delayed_tvm_probe_pending, 0); + } + + qts_irq_enable(qts_data, true); + qts_trusted_touch_set_vm_state(qts_data, TVM_INTERRUPT_ENABLED); + return rc; +} + +static void qts_trusted_touch_tvm_vm_mode_enable(struct qts_data *qts_data) +{ + + struct gh_sgl_desc *sgl_desc, *expected_sgl_desc; + struct gh_acl_desc *acl_desc; + struct irq_data *irq_data; + int rc = 0; + int irq = 0; + + if (qts_trusted_touch_get_vm_state(qts_data) != TVM_ALL_RESOURCES_LENT_NOTIFIED) { + pr_info("All lend notifications not received\n"); + qts_trusted_touch_event_notify(qts_data, + TRUSTED_TOUCH_EVENT_NOTIFICATIONS_PENDING); + return; + } + + if (qts_data->vendor_ops.pre_le_tui_enable) + qts_data->vendor_ops.pre_le_tui_enable(qts_data->vendor_data); + + acl_desc = qts_vm_get_acl(GH_TRUSTED_VM); + if (IS_ERR(acl_desc)) { + pr_err("failed to populated acl data:rc=%d\n", PTR_ERR(acl_desc)); + goto accept_fail; + } + + sgl_desc = gh_rm_mem_accept(qts_data->vm_info->vm_mem_handle, + GH_RM_MEM_TYPE_IO, + GH_RM_TRANS_TYPE_LEND, + GH_RM_MEM_ACCEPT_VALIDATE_ACL_ATTRS | + GH_RM_MEM_ACCEPT_VALIDATE_LABEL | + GH_RM_MEM_ACCEPT_DONE, TRUSTED_TOUCH_MEM_LABEL, + acl_desc, NULL, NULL, 0); + if (IS_ERR_OR_NULL(sgl_desc)) { + pr_err("failed to do mem accept :rc=%d\n", PTR_ERR(sgl_desc)); + goto acl_fail; + } + qts_trusted_touch_set_vm_state(qts_data, TVM_IOMEM_ACCEPTED); + + /* Initiate session on tvm */ + if (qts_data->bus_type == QTS_BUS_TYPE_I2C) + rc = pm_runtime_get_sync(qts_data->client->adapter->dev.parent); + else + rc = pm_runtime_get_sync(qts_data->spi->master->dev.parent); + + if (rc < 0) { + pr_err("failed to get sync rc:%d\n", rc); + goto acl_fail; + } + qts_trusted_touch_set_vm_state(qts_data, TVM_I2C_SESSION_ACQUIRED); + + expected_sgl_desc = qts_vm_get_sgl(qts_data->vm_info); + if (qts_vm_compare_sgl_desc(expected_sgl_desc, sgl_desc)) { + pr_err("IO sg list does not match\n"); + goto sgl_cmp_fail; + } + + kfree(expected_sgl_desc); + kfree(acl_desc); + + irq = gh_irq_accept(qts_data->vm_info->irq_label, -1, IRQ_TYPE_EDGE_RISING); + qts_trusted_touch_intr_gpio_toggle(qts_data, false); + if (irq < 0) { + pr_err("failed to accept irq\n"); + goto accept_fail; + } + qts_trusted_touch_set_vm_state(qts_data, TVM_IRQ_ACCEPTED); + + + irq_data = irq_get_irq_data(irq); + if (!irq_data) { + pr_err("Invalid irq data for trusted touch\n"); + goto accept_fail; + } + if (!irq_data->hwirq) { + pr_err("Invalid irq in irq data\n"); + goto accept_fail; + } + if (irq_data->hwirq != qts_data->vm_info->hw_irq) { + pr_err("Invalid irq lent\n"); + goto accept_fail; + } + + pr_debug("irq:returned from accept:%d\n", irq); + qts_data->irq = irq; + + rc = qts_vm_handle_vm_hardware(qts_data); + if (rc) { + pr_err("Delayed probe failure on TVM!\n"); + goto accept_fail; + } + atomic_set(&qts_data->trusted_touch_enabled, 1); + + if (qts_data->vendor_ops.post_le_tui_enable) + qts_data->vendor_ops.post_le_tui_enable(qts_data->vendor_data); + + pr_debug("trusted touch enabled\n"); + + return; +sgl_cmp_fail: + kfree(expected_sgl_desc); +acl_fail: + kfree(acl_desc); +accept_fail: + qts_trusted_touch_abort_handler(qts_data, + TRUSTED_TOUCH_EVENT_ACCEPT_FAILURE); +} + +static void qts_vm_irq_on_lend_callback(void *data, + unsigned long notif_type, + enum gh_irq_label label) +{ + struct qts_data *qts_data = data; + + pr_debug("received irq lend request for label:%d\n", label); + if (qts_trusted_touch_get_vm_state(qts_data) == TVM_IOMEM_LENT_NOTIFIED) + qts_trusted_touch_set_vm_state(qts_data, TVM_ALL_RESOURCES_LENT_NOTIFIED); + else + qts_trusted_touch_set_vm_state(qts_data, TVM_IRQ_LENT_NOTIFIED); +} + +static void qts_vm_mem_on_lend_handler(enum gh_mem_notifier_tag tag, + unsigned long notif_type, void *entry_data, void *notif_msg) +{ + struct gh_rm_notif_mem_shared_payload *payload; + struct trusted_touch_vm_info *vm_info; + struct qts_data *qts_data; + + qts_data = (struct qts_data *)entry_data; + vm_info = qts_data->vm_info; + if (!vm_info) { + pr_err("Invalid vm_info\n"); + return; + } + + if (notif_type != GH_RM_NOTIF_MEM_SHARED || + tag != vm_info->mem_tag) { + pr_err("Invalid command passed from rm\n"); + return; + } + + if (!entry_data || !notif_msg) { + pr_err("Invalid entry data passed from rm\n"); + return; + } + + payload = (struct gh_rm_notif_mem_shared_payload *)notif_msg; + if (payload->trans_type != GH_RM_TRANS_TYPE_LEND || + payload->label != TRUSTED_TOUCH_MEM_LABEL) { + pr_err("Invalid label or transaction type\n"); + return; + } + + vm_info->vm_mem_handle = payload->mem_handle; + pr_debug("received mem lend request with handle:%d\n", vm_info->vm_mem_handle); + + if (qts_trusted_touch_get_vm_state(qts_data) == TVM_IRQ_LENT_NOTIFIED) + qts_trusted_touch_set_vm_state(qts_data, TVM_ALL_RESOURCES_LENT_NOTIFIED); + else + qts_trusted_touch_set_vm_state(qts_data, TVM_IOMEM_LENT_NOTIFIED); +} + +static int qts_vm_mem_release(struct qts_data *qts_data) +{ + int rc = 0; + + if (!qts_data->vm_info->vm_mem_handle) { + pr_err("Invalid memory handle\n"); + return -EINVAL; + } + + rc = gh_rm_mem_release(qts_data->vm_info->vm_mem_handle, 0); + if (rc) + pr_err("VM mem release failed: rc=%d\n", rc); + + rc = gh_rm_mem_notify(qts_data->vm_info->vm_mem_handle, + GH_RM_MEM_NOTIFY_OWNER_RELEASED, + qts_data->vm_info->mem_tag, 0); + if (rc) + pr_err("Failed to notify mem release to PVM: rc=%d\n", rc); + + pr_debug("vm mem release success\n"); + + qts_data->vm_info->vm_mem_handle = 0; + return rc; +} + +static void qts_trusted_touch_tvm_vm_mode_disable(struct qts_data *qts_data) +{ + int rc = 0; + + if (atomic_read(&qts_data->trusted_touch_abort_status)) { + qts_trusted_touch_abort_tvm(qts_data); + return; + } + + if (qts_data->vendor_ops.pre_le_tui_disable) + qts_data->vendor_ops.pre_le_tui_disable(qts_data->vendor_data); + + qts_irq_enable(qts_data, false); + qts_trusted_touch_set_vm_state(qts_data, TVM_INTERRUPT_DISABLED); + + rc = gh_irq_release(qts_data->vm_info->irq_label); + if (rc) { + pr_err("Failed to release irq rc:%d\n", rc); + goto error; + } else { + qts_trusted_touch_set_vm_state(qts_data, TVM_IRQ_RELEASED); + } + rc = gh_irq_release_notify(qts_data->vm_info->irq_label); + if (rc) + pr_err("Failed to notify release irq rc:%d\n", rc); + + pr_debug("vm irq release success\n"); + + if (qts_data->bus_type == QTS_BUS_TYPE_I2C) + pm_runtime_put_sync(qts_data->client->adapter->dev.parent); + else + pm_runtime_put_sync(qts_data->spi->master->dev.parent); + + qts_trusted_touch_set_vm_state(qts_data, TVM_I2C_SESSION_RELEASED); + rc = qts_vm_mem_release(qts_data); + if (rc) { + pr_err("Failed to release mem rc:%d\n", rc); + goto error; + } else { + qts_trusted_touch_set_vm_state(qts_data, TVM_IOMEM_RELEASED); + } + qts_trusted_touch_set_vm_state(qts_data, TRUSTED_TOUCH_TVM_INIT); + atomic_set(&qts_data->trusted_touch_enabled, 0); + + if (qts_data->vendor_ops.post_le_tui_disable) + qts_data->vendor_ops.post_le_tui_disable(qts_data->vendor_data); + + pr_debug("trusted touch disabled\n"); + return; +error: + qts_trusted_touch_abort_handler(qts_data, + TRUSTED_TOUCH_EVENT_RELEASE_FAILURE); +} + +static int qts_handle_trusted_touch_tvm(struct qts_data *qts_data, int value) +{ + int err = 0; + + switch (value) { + case 0: + if ((atomic_read(&qts_data->trusted_touch_enabled) == 0) && + (atomic_read(&qts_data->trusted_touch_abort_status) == 0)) { + pr_err("Trusted touch is already disabled\n"); + break; + } + if (atomic_read(&qts_data->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + qts_trusted_touch_tvm_vm_mode_disable(qts_data); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + case 1: + if (atomic_read(&qts_data->trusted_touch_enabled)) { + pr_err("Trusted touch usecase underway\n"); + err = -EBUSY; + break; + } + if (atomic_read(&qts_data->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + qts_trusted_touch_tvm_vm_mode_enable(qts_data); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + default: + pr_err("unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + + return err; +} + +static void qts_trusted_touch_abort_tvm(struct qts_data *qts_data) +{ + int rc = 0; + int vm_state = qts_trusted_touch_get_vm_state(qts_data); + + if (vm_state >= TRUSTED_TOUCH_TVM_STATE_MAX) { + pr_err("invalid tvm driver state: %d\n", vm_state); + return; + } + + switch (vm_state) { + case TVM_INTERRUPT_ENABLED: + qts_irq_enable(qts_data, false); + case TVM_IRQ_ACCEPTED: + case TVM_INTERRUPT_DISABLED: + rc = gh_irq_release(qts_data->vm_info->irq_label); + if (rc) + pr_err("Failed to release irq rc:%d\n", rc); + rc = gh_irq_release_notify(qts_data->vm_info->irq_label); + if (rc) + pr_err("Failed to notify irq release rc:%d\n", rc); + case TVM_I2C_SESSION_ACQUIRED: + case TVM_IOMEM_ACCEPTED: + case TVM_IRQ_RELEASED: + if (qts_data->bus_type == QTS_BUS_TYPE_I2C) + pm_runtime_put_sync(qts_data->client->adapter->dev.parent); + else + pm_runtime_put_sync(qts_data->spi->master->dev.parent); + case TVM_I2C_SESSION_RELEASED: + rc = qts_vm_mem_release(qts_data); + if (rc) + pr_err("Failed to release mem rc:%d\n", rc); + case TVM_IOMEM_RELEASED: + case TVM_ALL_RESOURCES_LENT_NOTIFIED: + case TRUSTED_TOUCH_TVM_INIT: + case TVM_IRQ_LENT_NOTIFIED: + case TVM_IOMEM_LENT_NOTIFIED: + atomic_set(&qts_data->trusted_touch_enabled, 0); + } + + atomic_set(&qts_data->trusted_touch_abort_status, 0); + qts_trusted_touch_set_vm_state(qts_data, TRUSTED_TOUCH_TVM_INIT); +} + +#else + +static void qts_bus_put(struct qts_data *qts_data); +static int qts_enable_reg(struct qts_data *qts_data, bool enable); + +static void qts_trusted_touch_abort_pvm(struct qts_data *qts_data) +{ + int rc = 0; + int vm_state = qts_trusted_touch_get_vm_state(qts_data); + + if (vm_state >= TRUSTED_TOUCH_PVM_STATE_MAX) { + pr_err("Invalid driver state: %d\n", vm_state); + return; + } + + switch (vm_state) { + case PVM_IRQ_RELEASE_NOTIFIED: + case PVM_ALL_RESOURCES_RELEASE_NOTIFIED: + case PVM_IRQ_LENT: + case PVM_IRQ_LENT_NOTIFIED: + rc = gh_irq_reclaim(qts_data->vm_info->irq_label); + if (rc) + pr_err("failed to reclaim irq on pvm rc:%d\n", rc); + case PVM_IRQ_RECLAIMED: + case PVM_IOMEM_LENT: + case PVM_IOMEM_LENT_NOTIFIED: + case PVM_IOMEM_RELEASE_NOTIFIED: + rc = gh_rm_mem_reclaim(qts_data->vm_info->vm_mem_handle, 0); + if (rc) + pr_err("failed to reclaim iomem on pvm rc:%d\n", rc); + qts_data->vm_info->vm_mem_handle = 0; + case PVM_IOMEM_RECLAIMED: + case PVM_INTERRUPT_DISABLED: + if (qts_data->vendor_ops.enable_touch_irq) + qts_data->vendor_ops.enable_touch_irq(qts_data->vendor_data, true); + case PVM_I2C_RESOURCE_ACQUIRED: + case PVM_INTERRUPT_ENABLED: + qts_bus_put(qts_data); + case TRUSTED_TOUCH_PVM_INIT: + case PVM_I2C_RESOURCE_RELEASED: + atomic_set(&qts_data->trusted_touch_enabled, 0); + atomic_set(&qts_data->trusted_touch_transition, 0); + } + + atomic_set(&qts_data->trusted_touch_abort_status, 0); + + qts_trusted_touch_set_vm_state(qts_data, TRUSTED_TOUCH_PVM_INIT); +} + +static int qts_clk_prepare_enable(struct qts_data *qts_data) +{ + int ret; + + ret = clk_prepare_enable(qts_data->iface_clk); + if (ret) { + pr_err("error on clk_prepare_enable(iface_clk):%d\n", ret); + return ret; + } + + ret = clk_prepare_enable(qts_data->core_clk); + if (ret) { + clk_disable_unprepare(qts_data->iface_clk); + pr_err("error clk_prepare_enable(core_clk):%d\n", ret); + } + return ret; +} + +static void qts_clk_disable_unprepare(struct qts_data *qts_data) +{ + clk_disable_unprepare(qts_data->core_clk); + clk_disable_unprepare(qts_data->iface_clk); +} + +static int qts_bus_get(struct qts_data *qts_data) +{ + int rc = 0; + struct device *dev = NULL; + + if (qts_data->schedule_suspend) + cancel_work_sync(&qts_data->suspend_work); + if (qts_data->schedule_resume) + cancel_work_sync(&qts_data->resume_work); + + reinit_completion(&qts_data->trusted_touch_powerdown); + + qts_enable_reg(qts_data, true); + + if (qts_data->bus_type == QTS_BUS_TYPE_I2C) + dev = qts_data->client->adapter->dev.parent; + else + dev = qts_data->spi->master->dev.parent; + + mutex_lock(&qts_data->qts_clk_io_ctrl_mutex); + rc = pm_runtime_get_sync(dev); + if (rc >= 0 && qts_data->core_clk != NULL && + qts_data->iface_clk != NULL) { + rc = qts_clk_prepare_enable(qts_data); + if (rc) + pm_runtime_put_sync(dev); + } + + mutex_unlock(&qts_data->qts_clk_io_ctrl_mutex); + return rc; +} + +static void qts_bus_put(struct qts_data *qts_data) +{ + struct device *dev = NULL; + + if (qts_data->bus_type == QTS_BUS_TYPE_I2C) + dev = qts_data->client->adapter->dev.parent; + else + dev = qts_data->spi->master->dev.parent; + + mutex_lock(&qts_data->qts_clk_io_ctrl_mutex); + if (qts_data->core_clk != NULL && qts_data->iface_clk != NULL) + qts_clk_disable_unprepare(qts_data); + pm_runtime_put_sync(dev); + mutex_unlock(&qts_data->qts_clk_io_ctrl_mutex); + complete(&qts_data->trusted_touch_powerdown); + qts_enable_reg(qts_data, false); +} + +static struct gh_notify_vmid_desc *qts_vm_get_vmid(gh_vmid_t vmid) +{ + struct gh_notify_vmid_desc *vmid_desc; + + vmid_desc = kzalloc(offsetof(struct gh_notify_vmid_desc, + vmid_entries[1]), GFP_KERNEL); + if (!vmid_desc) + return ERR_PTR(ENOMEM); + + vmid_desc->n_vmid_entries = 1; + vmid_desc->vmid_entries[0].vmid = vmid; + return vmid_desc; +} + +static void qts_trusted_touch_pvm_vm_mode_disable(struct qts_data *qts_data) +{ + int rc = 0; + + atomic_set(&qts_data->trusted_touch_transition, 1); + + if (atomic_read(&qts_data->trusted_touch_abort_status)) { + qts_trusted_touch_abort_pvm(qts_data); + return; + } + + if (qts_trusted_touch_get_vm_state(qts_data) != PVM_ALL_RESOURCES_RELEASE_NOTIFIED) + pr_debug("all release notifications are not received yet\n"); + + if (qts_data->vendor_ops.pre_la_tui_disable) + qts_data->vendor_ops.pre_la_tui_disable(qts_data->vendor_data); + + rc = gh_rm_mem_reclaim(qts_data->vm_info->vm_mem_handle, 0); + if (rc) { + pr_err("Trusted touch VM mem reclaim failed rc:%d\n", rc); + goto error; + } + qts_trusted_touch_set_vm_state(qts_data, PVM_IOMEM_RECLAIMED); + qts_data->vm_info->vm_mem_handle = 0; + pr_debug("vm mem reclaim success!\n"); + + rc = gh_irq_reclaim(qts_data->vm_info->irq_label); + if (rc) { + pr_err("failed to reclaim irq on pvm rc:%d\n", rc); + goto error; + } + qts_trusted_touch_set_vm_state(qts_data, PVM_IRQ_RECLAIMED); + pr_debug("vm irq reclaim success!\n"); + + if (qts_data->vendor_ops.enable_touch_irq) + qts_data->vendor_ops.enable_touch_irq(qts_data->vendor_data, true); + + qts_trusted_touch_set_vm_state(qts_data, PVM_INTERRUPT_ENABLED); + qts_bus_put(qts_data); + atomic_set(&qts_data->trusted_touch_transition, 0); + qts_trusted_touch_set_vm_state(qts_data, PVM_I2C_RESOURCE_RELEASED); + qts_trusted_touch_set_vm_state(qts_data, TRUSTED_TOUCH_PVM_INIT); + atomic_set(&qts_data->trusted_touch_enabled, 0); + + if (qts_data->vendor_ops.post_la_tui_disable) + qts_data->vendor_ops.post_la_tui_disable(qts_data->vendor_data); + + pr_debug("trusted touch disabled\n"); + return; +error: + qts_trusted_touch_abort_handler(qts_data, + TRUSTED_TOUCH_EVENT_RECLAIM_FAILURE); +} + +static void qts_vm_irq_on_release_callback(void *data, + unsigned long notif_type, + enum gh_irq_label label) +{ + struct qts_data *qts_data = data; + + if (notif_type != GH_RM_NOTIF_VM_IRQ_RELEASED) { + pr_err("invalid notification type\n"); + return; + } + + if (qts_trusted_touch_get_vm_state(qts_data) == PVM_IOMEM_RELEASE_NOTIFIED) + qts_trusted_touch_set_vm_state(qts_data, PVM_ALL_RESOURCES_RELEASE_NOTIFIED); + else + qts_trusted_touch_set_vm_state(qts_data, PVM_IRQ_RELEASE_NOTIFIED); +} + +static void qts_vm_mem_on_release_handler(enum gh_mem_notifier_tag tag, + unsigned long notif_type, void *entry_data, void *notif_msg) +{ + struct gh_rm_notif_mem_released_payload *release_payload; + struct trusted_touch_vm_info *vm_info; + struct qts_data *qts_data; + + qts_data = (struct qts_data *)entry_data; + vm_info = qts_data->vm_info; + if (!vm_info) { + pr_err("Invalid vm_info\n"); + return; + } + + if (notif_type != GH_RM_NOTIF_MEM_RELEASED) { + pr_err("Invalid notification type\n"); + return; + } + + if (tag != vm_info->mem_tag) { + pr_err("Invalid tag\n"); + return; + } + + if (!entry_data || !notif_msg) { + pr_err("Invalid data or notification message\n"); + return; + } + + release_payload = (struct gh_rm_notif_mem_released_payload *)notif_msg; + if (release_payload->mem_handle != vm_info->vm_mem_handle) { + pr_err("Invalid mem handle detected\n"); + return; + } + + if (qts_trusted_touch_get_vm_state(qts_data) == PVM_IRQ_RELEASE_NOTIFIED) + qts_trusted_touch_set_vm_state(qts_data, PVM_ALL_RESOURCES_RELEASE_NOTIFIED); + else + qts_trusted_touch_set_vm_state(qts_data, PVM_IOMEM_RELEASE_NOTIFIED); +} + +static int qts_vm_mem_lend(struct qts_data *qts_data) +{ + struct gh_acl_desc *acl_desc; + struct gh_sgl_desc *sgl_desc; + struct gh_notify_vmid_desc *vmid_desc; + gh_memparcel_handle_t mem_handle; + gh_vmid_t trusted_vmid; + int rc = 0; + + acl_desc = qts_vm_get_acl(GH_TRUSTED_VM); + if (IS_ERR(acl_desc)) { + pr_err("Failed to get acl of IO memories for Trusted touch\n"); + rc = PTR_ERR(acl_desc); + return rc; + } + + sgl_desc = qts_vm_get_sgl(qts_data->vm_info); + if (IS_ERR(sgl_desc)) { + pr_err("Failed to get sgl of IO memories for Trusted touch\n"); + rc = PTR_ERR(sgl_desc); + goto sgl_error; + } + + rc = gh_rm_mem_lend(GH_RM_MEM_TYPE_IO, 0, TRUSTED_TOUCH_MEM_LABEL, + acl_desc, sgl_desc, NULL, &mem_handle); + if (rc) { + pr_err("Failed to lend IO memories for Trusted touch rc:%d\n", rc); + goto error; + } + + pr_debug("vm mem lend success\n"); + + qts_trusted_touch_set_vm_state(qts_data, PVM_IOMEM_LENT); + + gh_rm_get_vmid(GH_TRUSTED_VM, &trusted_vmid); + + vmid_desc = qts_vm_get_vmid(trusted_vmid); + + rc = gh_rm_mem_notify(mem_handle, GH_RM_MEM_NOTIFY_RECIPIENT_SHARED, + qts_data->vm_info->mem_tag, vmid_desc); + if (rc) { + pr_err("Failed to notify mem lend to hypervisor rc:%d\n", rc); + goto vmid_error; + } + + qts_trusted_touch_set_vm_state(qts_data, PVM_IOMEM_LENT_NOTIFIED); + + qts_data->vm_info->vm_mem_handle = mem_handle; +vmid_error: + kfree(vmid_desc); +error: + kfree(sgl_desc); +sgl_error: + kfree(acl_desc); + + return rc; +} + +static int qts_trusted_touch_pvm_vm_mode_enable(struct qts_data *qts_data) +{ + int rc = 0; + struct trusted_touch_vm_info *vm_info = qts_data->vm_info; + + atomic_set(&qts_data->trusted_touch_transition, 1); + mutex_lock(&qts_data->transition_lock); + + if (qts_data->suspended) { + pr_err("Invalid power state for operation\n"); + atomic_set(&qts_data->trusted_touch_transition, 0); + rc = -EPERM; + goto error; + } + + if (qts_data->vendor_ops.pre_la_tui_enable) + qts_data->vendor_ops.pre_la_tui_enable(qts_data->vendor_data); + + /* i2c session start and resource acquire */ + if (qts_bus_get(qts_data) < 0) { + pr_err("qts_bus_get failed\n"); + rc = -EIO; + goto error; + } + + qts_trusted_touch_set_vm_state(qts_data, PVM_I2C_RESOURCE_ACQUIRED); + if (qts_data->vendor_ops.enable_touch_irq) + qts_data->vendor_ops.enable_touch_irq(qts_data->vendor_data, false); + qts_trusted_touch_set_vm_state(qts_data, PVM_INTERRUPT_DISABLED); + + rc = qts_vm_mem_lend(qts_data); + if (rc) { + pr_err("Failed to lend memory\n"); + goto abort_handler; + } + pr_debug("vm mem lend success\n"); + + if (atomic_read(&qts_data->delayed_pvm_probe_pending)) { + if (qts_data->vendor_ops.get_irq_num) + qts_data->irq = qts_data->vendor_ops.get_irq_num(qts_data->vendor_data); + + atomic_set(&qts_data->delayed_tvm_probe_pending, 0); + } + + rc = gh_irq_lend_v2(vm_info->irq_label, vm_info->vm_name, + qts_data->irq, &qts_vm_irq_on_release_callback, qts_data); + if (rc) { + pr_err("Failed to lend irq\n"); + goto abort_handler; + } + + pr_debug("vm irq lend success for irq:%d\n", qts_data->irq); + qts_trusted_touch_set_vm_state(qts_data, PVM_IRQ_LENT); + + rc = gh_irq_lend_notify(vm_info->irq_label); + if (rc) { + pr_err("Failed to notify irq\n"); + goto abort_handler; + } + qts_trusted_touch_set_vm_state(qts_data, PVM_IRQ_LENT_NOTIFIED); + + if (qts_data->vendor_ops.post_la_tui_enable) + qts_data->vendor_ops.post_la_tui_enable(qts_data->vendor_data); + + mutex_unlock(&qts_data->transition_lock); + atomic_set(&qts_data->trusted_touch_transition, 0); + atomic_set(&qts_data->trusted_touch_enabled, 1); + pr_debug("trusted touch enabled\n"); + return rc; + +abort_handler: + qts_trusted_touch_abort_handler(qts_data, TRUSTED_TOUCH_EVENT_LEND_FAILURE); + +error: + mutex_unlock(&qts_data->transition_lock); + return rc; +} + +static int qts_handle_trusted_touch_pvm(struct qts_data *qts_data, int value) +{ + int err = 0; + + switch (value) { + case 0: + if (atomic_read(&qts_data->trusted_touch_enabled) == 0 && + (atomic_read(&qts_data->trusted_touch_abort_status) == 0)) { + pr_err("Trusted touch is already disabled\n"); + break; + } + if (atomic_read(&qts_data->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + qts_trusted_touch_pvm_vm_mode_disable(qts_data); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + case 1: + if (atomic_read(&qts_data->trusted_touch_enabled)) { + pr_err("Trusted touch usecase underway\n"); + err = -EBUSY; + break; + } + if (atomic_read(&qts_data->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + err = qts_trusted_touch_pvm_vm_mode_enable(qts_data); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + default: + pr_err("unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + return err; +} + +#endif + +static void qts_trusted_touch_event_notify(struct qts_data *qts_data, int event) +{ + atomic_set(&qts_data->trusted_touch_event, event); + sysfs_notify(&qts_data->dev->kobj, NULL, "trusted_touch_event"); +} + +static void qts_trusted_touch_abort_handler(struct qts_data *qts_data, int error) +{ + atomic_set(&qts_data->trusted_touch_abort_status, error); + pr_info("TUI session aborted with failure:%d\n", error); + qts_trusted_touch_event_notify(qts_data, error); +#ifdef CONFIG_ARCH_QTI_VM + pr_info("Resetting touch controller\n"); + if (qts_trusted_touch_get_vm_state(qts_data) >= TVM_IOMEM_ACCEPTED && + error == TRUSTED_TOUCH_EVENT_I2C_FAILURE) { + pr_info("Resetting touch controller\n"); + qts_trusted_touch_reset_gpio_toggle(qts_data); + } +#endif +} + +static int qts_vm_init(struct qts_data *qts_data) +{ + int rc = 0; + struct trusted_touch_vm_info *vm_info; + void *mem_cookie; + + rc = qts_populate_vm_info(qts_data); + if (rc) { + pr_err("Cannot setup vm pipeline\n"); + rc = -EINVAL; + goto fail; + } + + vm_info = qts_data->vm_info; +#ifdef CONFIG_ARCH_QTI_VM + mem_cookie = gh_mem_notifier_register(vm_info->mem_tag, + qts_vm_mem_on_lend_handler, qts_data); + if (!mem_cookie) { + pr_err("Failed to register on lend mem notifier\n"); + rc = -EINVAL; + goto init_fail; + } + vm_info->mem_cookie = mem_cookie; + rc = gh_irq_wait_for_lend_v2(vm_info->irq_label, GH_PRIMARY_VM, + &qts_vm_irq_on_lend_callback, qts_data); + qts_trusted_touch_set_vm_state(qts_data, TRUSTED_TOUCH_TVM_INIT); +#else + mem_cookie = gh_mem_notifier_register(vm_info->mem_tag, + qts_vm_mem_on_release_handler, qts_data); + if (!mem_cookie) { + pr_err("Failed to register on release mem notifier\n"); + rc = -EINVAL; + goto init_fail; + } + vm_info->mem_cookie = mem_cookie; + qts_trusted_touch_set_vm_state(qts_data, TRUSTED_TOUCH_PVM_INIT); +#endif + return rc; +init_fail: + qts_vm_deinit(qts_data); +fail: + return rc; +} + +static void qts_dt_parse_trusted_touch_info(struct qts_data *qts_data) +{ + struct device_node *np = qts_data->dev->of_node; + int rc = 0; + const char *selection; + const char *environment; + + rc = of_property_read_string(np, "qts,trusted-touch-mode", &selection); + if (rc) { + pr_err("No trusted touch mode selection made\n"); + atomic_set(&qts_data->trusted_touch_mode, + TRUSTED_TOUCH_MODE_NONE); + return; + } + + if (!strcmp(selection, "vm_mode")) { + atomic_set(&qts_data->trusted_touch_mode, + TRUSTED_TOUCH_VM_MODE); + pr_debug("Selected trusted touch mode to VM mode\n"); + } else { + atomic_set(&qts_data->trusted_touch_mode, + TRUSTED_TOUCH_MODE_NONE); + pr_err("Invalid trusted_touch mode\n"); + } + + rc = of_property_read_string(np, "qts,touch-environment", + &environment); + if (rc) + pr_err("No trusted touch mode environment\n"); + + qts_data->touch_environment = environment; + qts_data->tui_supported = true; + pr_debug("Trusted touch environment:%s\n", qts_data->touch_environment); +} + +static void qts_trusted_touch_init(struct qts_data *qts_data) +{ + int rc = 0; + + atomic_set(&qts_data->trusted_touch_initialized, 0); + qts_dt_parse_trusted_touch_info(qts_data); + + if (atomic_read(&qts_data->trusted_touch_mode) == TRUSTED_TOUCH_MODE_NONE) + return; + + init_completion(&qts_data->trusted_touch_powerdown); + + /* Get clocks */ + qts_data->core_clk = devm_clk_get(qts_data->dev->parent, "m-ahb"); + + if (IS_ERR(qts_data->core_clk)) { + qts_data->core_clk = NULL; + pr_err("core_clk is not defined\n"); + } + + qts_data->iface_clk = devm_clk_get(qts_data->dev->parent, "se-clk"); + + if (IS_ERR(qts_data->iface_clk)) { + qts_data->iface_clk = NULL; + pr_err("iface_clk is not defined\n"); + } + + if (atomic_read(&qts_data->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + rc = qts_vm_init(qts_data); + if (rc) + pr_err("Failed to init VM\n"); + } + atomic_set(&qts_data->trusted_touch_initialized, 1); +} + +static ssize_t trusted_touch_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct qts_data *qts_data; + + if (!client) + return scnprintf(buf, PAGE_SIZE, "client is null\n"); + + qts_data = &qts_data_entries->info[QTS_CLIENT_PRIMARY_TOUCH]; + + return scnprintf(buf, PAGE_SIZE, "%d", + atomic_read(&qts_data->trusted_touch_enabled)); +} + +static ssize_t trusted_touch_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct qts_data *qts_data; + unsigned long value; + int err = 0; + + if (!client) + return -EIO; + + if (count > 2) + return -EINVAL; + + err = kstrtoul(buf, 10, &value); + if (err != 0) + return err; + + qts_data = &qts_data_entries->info[QTS_CLIENT_PRIMARY_TOUCH]; + + if (!atomic_read(&qts_data->trusted_touch_initialized)) + return -EIO; + +#ifdef CONFIG_ARCH_QTI_VM + err = qts_handle_trusted_touch_tvm(qts_data, value); + if (err) { + pr_err("Failed to handle trusted touch in tvm\n"); + return -EINVAL; + } +#else + err = qts_handle_trusted_touch_pvm(qts_data, value); + if (err) { + pr_err("Failed to handle trusted touch in pvm\n"); + return -EINVAL; + } +#endif + err = count; + return err; +} + +static ssize_t trusted_touch_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct qts_data *qts_data; + + if (!client) + return scnprintf(buf, PAGE_SIZE, "client is null\n"); + + qts_data = &qts_data_entries->info[QTS_CLIENT_PRIMARY_TOUCH]; + + return scnprintf(buf, PAGE_SIZE, "%d", + atomic_read(&qts_data->trusted_touch_event)); +} + +static ssize_t trusted_touch_event_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct qts_data *qts_data; + unsigned long value; + int err = 0; + + if (!client) + return -EIO; + + if (count > 2) + return -EINVAL; + + err = kstrtoul(buf, 10, &value); + if (err != 0) + return err; + + qts_data = &qts_data_entries->info[QTS_CLIENT_PRIMARY_TOUCH]; + + if (!atomic_read(&qts_data->trusted_touch_initialized)) + return -EIO; + + if (value) + return -EIO; + + atomic_set(&qts_data->trusted_touch_event, value); + + return count; +} + +static ssize_t trusted_touch_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qts_data *qts_data = &qts_data_entries->info[QTS_CLIENT_PRIMARY_TOUCH]; + + return scnprintf(buf, PAGE_SIZE, "%s", qts_data->vm_info->trusted_touch_type); +} + +static DEVICE_ATTR_RW(trusted_touch_enable); +static DEVICE_ATTR_RW(trusted_touch_event); +static DEVICE_ATTR_RO(trusted_touch_type); + +static struct attribute *qts_attributes[] = { + &dev_attr_trusted_touch_enable.attr, + &dev_attr_trusted_touch_event.attr, + &dev_attr_trusted_touch_type.attr, + NULL, +}; + +static struct attribute_group qts_attribute_group = { + .attrs = qts_attributes, +}; + +static int qts_create_sysfs(struct qts_data *qts_data) +{ + int ret = 0; + + if (qts_data->client_type == QTS_CLIENT_PRIMARY_TOUCH) { + ret = sysfs_create_group(&qts_data->client->dev.kobj, &qts_attribute_group); + if (ret) { + pr_err("%s sysfs_create_group() failed\n", __func__); + sysfs_remove_group(&qts_data->client->dev.kobj, &qts_attribute_group); + return -ENOMEM; + } + } + + pr_debug("sysfs_create_group() succeeded\n"); + return ret; +} + +static int qts_ts_check_dt(struct device_node *np) +{ + int i; + int count; + struct device_node *node; + struct drm_panel *panel; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) + return 0; + + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + panel = of_drm_find_panel(node); + of_node_put(node); + if (!IS_ERR(panel)) { + active_panel = panel; + return 0; + } + } + return PTR_ERR(panel); +} + +static void qts_power_source_init(struct qts_data *qts_data) +{ + qts_data->vdd = regulator_get(qts_data->dev, "vdd"); + if (IS_ERR_OR_NULL(qts_data->vdd)) + pr_debug("get vdd regulator failed\n"); + + qts_data->avdd = regulator_get(qts_data->dev, "avdd"); + if (IS_ERR_OR_NULL(qts_data->avdd)) + pr_debug("get avdd regulator failed\n"); +} + +#ifndef CONFIG_ARCH_QTI_VM +static int qts_enable_reg(struct qts_data *qts_data, bool enable) +{ + int ret = 0; + + if (IS_ERR_OR_NULL(qts_data->vdd)) { + pr_err("vdd is invalid\n"); + return ret; + } + + if (enable) { + ret = regulator_enable(qts_data->vdd); + if (ret) { + pr_err("enable vdd regulator failed,ret=%d\n", ret); + goto error; + } + + if (!IS_ERR_OR_NULL(qts_data->avdd)) { + ret = regulator_enable(qts_data->avdd); + if (ret) { + pr_err("enable avdd regulator failed,ret=%d\n", ret); + goto error_avdd_en; + } + } + } else { + ret = regulator_disable(qts_data->vdd); + if (ret) + pr_err("disable vdd regulator failed,ret=%d\n", ret); + + if (!IS_ERR_OR_NULL(qts_data->avdd)) { + ret = regulator_disable(qts_data->avdd); + if (ret) + pr_err("disable avdd regulator failed,ret=%d\n", ret); + } + } + + pr_debug("enable %d completed\n", enable); + return ret; + +error_avdd_en: + (void)regulator_disable(qts_data->vdd); +error: + return ret; +} + +#endif + +static void qts_ts_suspend(struct qts_data *qts_data) +{ + int rc = 0; + + if (qts_data->suspended) { + pr_warn("already in suspend state\n"); + return; + } + + if (qts_data->tui_supported) { + if (atomic_read(&qts_data->trusted_touch_transition) + || atomic_read(&qts_data->trusted_touch_enabled)) + wait_for_completion_interruptible(&qts_data->trusted_touch_powerdown); + } + mutex_lock(&qts_data->transition_lock); + + rc = qts_data->vendor_ops.suspend(qts_data->vendor_data); + if (rc) + pr_err("suspend failed, rc = %d\n", rc); + + qts_data->suspended = true; + mutex_unlock(&qts_data->transition_lock); +} + +static void qts_ts_resume(struct qts_data *qts_data) +{ + int rc = 0; + + if (!qts_data->suspended) { + pr_warn("Already in awake state\n"); + return; + } + + if (qts_data->tui_supported) + if (atomic_read(&qts_data->trusted_touch_transition)) + wait_for_completion_interruptible(&qts_data->trusted_touch_powerdown); + + mutex_lock(&qts_data->transition_lock); + + rc = qts_data->vendor_ops.resume(qts_data->vendor_data); + if (rc) + pr_err("resume failed, rc = %d\n", rc); + + qts_data->suspended = false; + mutex_unlock(&qts_data->transition_lock); +} + +static void qts_resume_work(struct work_struct *work) +{ + struct qts_data *qts_data = container_of(work, struct qts_data, + resume_work); + + qts_ts_resume(qts_data); +} + +static void qts_suspend_work(struct work_struct *work) +{ + struct qts_data *qts_data = container_of(work, struct qts_data, + suspend_work); + + qts_ts_suspend(qts_data); +} + +static void qts_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data) +{ + struct qts_data *qts_data = client_data; + + if (!notification) { + pr_err("Invalid notification\n"); + return; + } + + pr_debug("Notification type:%d, early_trigger:%d\n", + notification->notif_type, notification->notif_data.early_trigger); + + switch (notification->notif_type) { + case DRM_PANEL_EVENT_UNBLANK: + if (notification->notif_data.early_trigger) { + pr_debug("resume notification pre commit\n"); + } else { + if (qts_data->schedule_resume) + queue_work(qts_data->ts_workqueue, &qts_data->resume_work); + else + qts_ts_resume(qts_data); + } + break; + case DRM_PANEL_EVENT_BLANK: + if (notification->notif_data.early_trigger) { + if (qts_data->schedule_resume) + cancel_work_sync(&qts_data->resume_work); + if (qts_data->schedule_suspend) + queue_work(qts_data->ts_workqueue, &qts_data->suspend_work); + else + qts_ts_suspend(qts_data); + } else { + pr_debug("suspend notification post commit\n"); + } + break; + case DRM_PANEL_EVENT_BLANK_LP: + pr_debug("received lp event\n"); + break; + case DRM_PANEL_EVENT_FPS_CHANGE: + pr_debug("Received fps change old fps:%d new fps:%d\n", + notification->notif_data.old_fps, + notification->notif_data.new_fps); + break; + default: + pr_debug("notification serviced :%d\n", + notification->notif_type); + break; + } +} + +static void qts_ts_register_for_panel_events(struct qts_data *qts_data) +{ + void *cookie = NULL; + + if (qts_data->client_type != QTS_CLIENT_PRIMARY_TOUCH) { + pr_err("Invalid touch type\n"); + return; + } + + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, qts_data->panel, + &qts_panel_notifier_callback, qts_data); + if (!cookie) { + pr_err("Failed to register for panel events\n"); + return; + } + + pr_debug("registered for panel notifications panel: 0x%x\n", qts_data->panel); + + qts_data->notifier_cookie = cookie; +} + +int qts_client_register(struct qts_vendor_data qts_vendor_data) +{ + struct qts_data *qts_data; + struct device_node *dp; + int rc = 0; + + if (!qts_data_entries) { + pr_debug("QTS client register\n"); + qts_data_entries = kzalloc(sizeof(*qts_data_entries), GFP_KERNEL); + if (!qts_data_entries) { + pr_err("mem allocation failed\n"); + return -EPROBE_DEFER; + } + } + + mutex_init(&qts_data_entries->qts_data_entries_lock); + + mutex_lock(&qts_data_entries->qts_data_entries_lock); + + if (qts_vendor_data.bus_type == QTS_BUS_TYPE_I2C) + dp = qts_vendor_data.client->dev.of_node; + else + dp = qts_vendor_data.spi->dev.of_node; + + rc = qts_ts_check_dt(dp); + if (rc) { + pr_debug("qts_ts_check_dt failed, rc = %d\n", rc); + goto qts_register_end; + } + + pr_debug("QTS client register starts\n"); + qts_data = &qts_data_entries->info[qts_vendor_data.client_type]; + + qts_data->client = qts_vendor_data.client; + qts_data->spi = qts_vendor_data.spi; + + if (qts_vendor_data.bus_type == QTS_BUS_TYPE_I2C) + qts_data->dev = &qts_data->client->dev; + else + qts_data->dev = &qts_data->spi->dev; + + qts_data->bus_type = qts_vendor_data.bus_type; + qts_data->client_type = qts_vendor_data.client_type; + qts_data->dp = dp; + qts_data->vendor_data = qts_vendor_data.vendor_data; + qts_data->panel = active_panel; + qts_data->vendor_ops = qts_vendor_data.qts_vendor_ops; + qts_data->schedule_suspend = qts_vendor_data.schedule_suspend; + qts_data->schedule_resume = qts_vendor_data.schedule_resume; + + qts_trusted_touch_init(qts_data); + mutex_init(&(qts_data->qts_clk_io_ctrl_mutex)); + if (qts_data->tui_supported) + qts_create_sysfs(qts_data); + + mutex_init(&qts_data->transition_lock); + +#ifdef CONFIG_ARCH_QTI_VM + atomic_set(&qts_data->delayed_tvm_probe_pending, 1); + goto qts_register_end; +#else + atomic_set(&qts_data->delayed_pvm_probe_pending, 1); +#endif + + qts_power_source_init(qts_data); + + qts_data->ts_workqueue = create_singlethread_workqueue("qts_wq"); + if (!qts_data->ts_workqueue) + pr_err("create qts workqueue fail\n"); + + if (qts_data->ts_workqueue && qts_data->schedule_resume) + INIT_WORK(&qts_data->resume_work, qts_resume_work); + + if (qts_data->ts_workqueue && qts_data->schedule_suspend) + INIT_WORK(&qts_data->suspend_work, qts_suspend_work); + + qts_ts_register_for_panel_events(qts_data); + +qts_register_end: + pr_debug("client register end\n"); + mutex_unlock(&qts_data_entries->qts_data_entries_lock); + return rc; +} +EXPORT_SYMBOL(qts_client_register); + diff --git a/qts/qts_core.h b/qts/qts_core.h new file mode 100644 index 0000000000..217bcc27b5 --- /dev/null +++ b/qts/qts_core.h @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "linux/gunyah/gh_msgq.h" +#include "linux/gunyah/gh_rm_drv.h" +#include +#include +#include "qts_core_common.h" +#include +#include + +#define QTS_NAME "qti-ts" + +enum trusted_touch_mode_config { + TRUSTED_TOUCH_VM_MODE, + TRUSTED_TOUCH_MODE_NONE +}; + +enum trusted_touch_pvm_states { + TRUSTED_TOUCH_PVM_INIT, + PVM_I2C_RESOURCE_ACQUIRED, + PVM_INTERRUPT_DISABLED, + PVM_IOMEM_LENT, + PVM_IOMEM_LENT_NOTIFIED, + PVM_IRQ_LENT, + PVM_IRQ_LENT_NOTIFIED, + PVM_IOMEM_RELEASE_NOTIFIED, + PVM_IRQ_RELEASE_NOTIFIED, + PVM_ALL_RESOURCES_RELEASE_NOTIFIED, + PVM_IRQ_RECLAIMED, + PVM_IOMEM_RECLAIMED, + PVM_INTERRUPT_ENABLED, + PVM_I2C_RESOURCE_RELEASED, + TRUSTED_TOUCH_PVM_STATE_MAX +}; + +enum trusted_touch_tvm_states { + TRUSTED_TOUCH_TVM_INIT, + TVM_IOMEM_LENT_NOTIFIED, + TVM_IRQ_LENT_NOTIFIED, + TVM_ALL_RESOURCES_LENT_NOTIFIED, + TVM_IOMEM_ACCEPTED, + TVM_I2C_SESSION_ACQUIRED, + TVM_IRQ_ACCEPTED, + TVM_INTERRUPT_ENABLED, + TVM_INTERRUPT_DISABLED, + TVM_IRQ_RELEASED, + TVM_I2C_SESSION_RELEASED, + TVM_IOMEM_RELEASED, + TRUSTED_TOUCH_TVM_STATE_MAX +}; + +#define TRUSTED_TOUCH_MEM_LABEL 0x7 + +#define TOUCH_RESET_GPIO_BASE 0xf118000 +#define TOUCH_RESET_GPIO_SIZE 0x1000 +#define TOUCH_RESET_GPIO_OFFSET 0x4 +#define TOUCH_INTR_GPIO_BASE 0xf119000 +#define TOUCH_INTR_GPIO_SIZE 0x1000 +#define TOUCH_INTR_GPIO_OFFSET 0x8 + +#define TRUSTED_TOUCH_EVENT_LEND_FAILURE -1 +#define TRUSTED_TOUCH_EVENT_LEND_NOTIFICATION_FAILURE -2 +#define TRUSTED_TOUCH_EVENT_ACCEPT_FAILURE -3 +#define TRUSTED_TOUCH_EVENT_FUNCTIONAL_FAILURE -4 +#define TRUSTED_TOUCH_EVENT_RELEASE_FAILURE -5 +#define TRUSTED_TOUCH_EVENT_RECLAIM_FAILURE -6 +#define TRUSTED_TOUCH_EVENT_I2C_FAILURE -7 +#define TRUSTED_TOUCH_EVENT_NOTIFICATIONS_PENDING 5 + +struct trusted_touch_vm_info { + enum gh_irq_label irq_label; + enum gh_mem_notifier_tag mem_tag; + enum gh_vm_names vm_name; + const char *trusted_touch_type; + u32 hw_irq; + gh_memparcel_handle_t vm_mem_handle; + u32 *iomem_bases; + u32 *iomem_sizes; + u32 iomem_list_size; + void *mem_cookie; + atomic_t vm_state; +}; + +struct qts_data; + +struct qts_data { + struct i2c_client *client; + struct spi_device *spi; + struct device *dev; + struct device_node *dp; + void *vendor_data; /* vendor touch driver data */ + u32 bus_type; /*i2c or spi*/ + u32 client_type; /* primary or secondary */ + struct drm_panel *panel; + struct workqueue_struct *ts_workqueue; + struct work_struct resume_work; + struct work_struct suspend_work; + bool schedule_suspend; + bool schedule_resume; + void *notifier_cookie; + + /* Resources */ + struct regulator *vdd; + struct regulator *avdd; + u32 irq_gpio; + u32 irq_gpio_flags; + u32 reset_gpio; + u32 reset_gpio_flags; + int irq; + bool irq_disabled; + bool power_disabled; + + struct mutex transition_lock; + bool suspended; + + /* vendor callback ops */ + struct qts_vendor_callback_ops vendor_ops; + + /* TUI */ + bool tui_supported; + struct trusted_touch_vm_info *vm_info; + struct mutex qts_clk_io_ctrl_mutex; + const char *touch_environment; + struct completion trusted_touch_powerdown; + struct clk *core_clk; + struct clk *iface_clk; + atomic_t trusted_touch_initialized; + atomic_t trusted_touch_enabled; + atomic_t trusted_touch_transition; + atomic_t trusted_touch_event; + atomic_t trusted_touch_abort_status; + atomic_t delayed_tvm_probe_pending; + atomic_t delayed_pvm_probe_pending; + atomic_t trusted_touch_mode; +}; + +struct qts_data_entries; + +struct qts_data_entries { + struct qts_data info[QTS_CLIENT_MAX]; + struct mutex qts_data_entries_lock; +}; + +#ifdef CONFIG_ARCH_QTI_VM +void qts_trusted_touch_tvm_i2c_failure_report(struct qts_data *qts_data); +#endif diff --git a/qts/qts_core_common.h b/qts/qts_core_common.h new file mode 100644 index 0000000000..08de7c1c75 --- /dev/null +++ b/qts/qts_core_common.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ +enum qts_client { + QTS_CLIENT_PRIMARY_TOUCH, + QTS_CLIENT_SECONDARY_TOUCH, + QTS_CLIENT_MAX +}; + +enum qts_bus_type { + QTS_BUS_TYPE_NONE, + QTS_BUS_TYPE_I2C, + QTS_BUS_TYPE_SPI, + QTS_BUS_TYPE_SPI_V2, + QTS_BUS_TYPE_MAX +}; + +struct qts_vendor_callback_ops { + int (*suspend)(void *data); + int (*resume)(void *data); + int (*enable_touch_irq)(void *data, bool en); + irqreturn_t (*irq_handler)(int irq, void *data); + int (*get_irq_num)(void *data); + int (*pre_la_tui_enable)(void *data); + int (*post_la_tui_enable)(void *data); + int (*pre_la_tui_disable)(void *data); + int (*post_la_tui_disable)(void *data); + int (*pre_le_tui_enable)(void *data); + int (*post_le_tui_enable)(void *data); + int (*pre_le_tui_disable)(void *data); + int (*post_le_tui_disable)(void *data); +}; + +struct qts_vendor_data { + struct i2c_client *client; + struct spi_device *spi; + void *vendor_data; + struct qts_vendor_callback_ops qts_vendor_ops; + u32 client_type; + u32 bus_type; + bool schedule_suspend; + bool schedule_resume; +}; + +int qts_client_register(struct qts_vendor_data qts_vendor_data); From 0e0d02e94cd77fce1e31d22e162a3f53b9ebb880 Mon Sep 17 00:00:00 2001 From: Raviteja Tamatam Date: Wed, 6 Apr 2022 18:35:12 -0700 Subject: [PATCH 016/170] touch: goodix: enable TUI using QTS on kalama Add changes to enable secure touch on goodix through QTS framework. Change-Id: I403bd9bc296b40ad97f6a0d066958d48b53d701e Signed-off-by: Raviteja Tamatam --- Kbuild | 5 +- goodix_berlin_driver/goodix_brl_i2c.c | 3 + goodix_berlin_driver/goodix_ts_core.c | 218 +++++++++++++++++++++++++- goodix_berlin_driver/goodix_ts_core.h | 5 + nt36xxx/nt36xxx_mp_ctrlram.c | 3 +- 5 files changed, 225 insertions(+), 9 deletions(-) diff --git a/Kbuild b/Kbuild index b55957af94..a2fd6a628d 100644 --- a/Kbuild +++ b/Kbuild @@ -88,6 +88,8 @@ endif ifeq ($(CONFIG_TOUCHSCREEN_GOODIX_BRL), y) LINUX_INC += -include $(TOUCH_ROOT)/goodix_berlin_driver/goodix_ts_core.h + LINUX_INC += -include $(TOUCH_ROOT)/qts/qts_core.h + LINUX_INC += -include $(TOUCH_ROOT)/qts/qts_core_common.h goodix_ts-y := \ ./goodix_berlin_driver/goodix_ts_core.o \ @@ -99,7 +101,8 @@ ifeq ($(CONFIG_TOUCHSCREEN_GOODIX_BRL), y) ./goodix_berlin_driver/goodix_ts_gesture.o \ ./goodix_berlin_driver/goodix_ts_inspect.o \ ./goodix_berlin_driver/goodix_brl_spi.o \ - ./goodix_berlin_driver/goodix_brl_i2c.o + ./goodix_berlin_driver/goodix_brl_i2c.o \ + ./qts/qts_core.o obj-$(CONFIG_MSM_TOUCH) += goodix_ts.o endif diff --git a/goodix_berlin_driver/goodix_brl_i2c.c b/goodix_berlin_driver/goodix_brl_i2c.c index 3767c95e99..b9824551a1 100644 --- a/goodix_berlin_driver/goodix_brl_i2c.c +++ b/goodix_berlin_driver/goodix_brl_i2c.c @@ -167,9 +167,12 @@ static int goodix_i2c_probe(struct i2c_client *client, int ret = 0; ts_info("goodix i2c probe in"); + +#ifndef CONFIG_ARCH_QTI_VM ret = i2c_check_functionality(client->adapter, I2C_FUNC_I2C); if (!ret) return -EIO; +#endif /* get ic type */ ret = goodix_get_ic_type(client->dev.of_node); diff --git a/goodix_berlin_driver/goodix_ts_core.c b/goodix_berlin_driver/goodix_ts_core.c index e984422ba0..17f083930f 100644 --- a/goodix_berlin_driver/goodix_ts_core.c +++ b/goodix_berlin_driver/goodix_ts_core.c @@ -35,6 +35,7 @@ static struct drm_panel *active_panel; static void goodix_panel_notifier_callback(enum panel_event_notifier_tag tag, struct panel_event_notification *event, void *client_data); +static irqreturn_t goodix_irq_handler(int irq, void *data); static void goodix_register_for_panel_events(struct device_node *dp, struct goodix_ts_core *cd) @@ -1023,6 +1024,7 @@ static int goodix_parse_dt(struct device_node *node, board_data->iovdd_gpio = r; } +#ifndef CONFIG_ARCH_QTI_VM r = of_get_named_gpio(node, "goodix,reset-gpio", 0); if (r < 0) { ts_err("invalid reset-gpio in dt: %d", r); @@ -1045,6 +1047,7 @@ static int goodix_parse_dt(struct device_node *node, ts_err("invalid irq-flags"); return -EINVAL; } +#endif memset(board_data->avdd_name, 0, sizeof(board_data->avdd_name)); r = of_property_read_string(node, "goodix,avdd-name", &name_tmp); @@ -1294,7 +1297,7 @@ static int goodix_ts_irq_setup(struct goodix_ts_core *core_data) ts_info("IRQ:%u,flags:%d", core_data->irq, (int)ts_bdata->irq_flags); ret = devm_request_threaded_irq(&core_data->pdev->dev, core_data->irq, NULL, - goodix_ts_threadirq_func, + goodix_irq_handler, ts_bdata->irq_flags | IRQF_ONESHOT, GOODIX_CORE_DRIVER_NAME, core_data); @@ -2012,6 +2015,9 @@ int goodix_ts_stage2_init(struct goodix_ts_core *cd) goto err_finger; } } +#ifdef CONFIG_ARCH_QTI_VM + goto skip_goodix_ts_irq_setup; +#endif /* request irq line */ ret = goodix_ts_irq_setup(cd); if (ret < 0) { @@ -2028,6 +2034,10 @@ int goodix_ts_stage2_init(struct goodix_ts_core *cd) cd->fb_notifier.notifier_call = goodix_ts_fb_notifier_callback; if (fb_register_client(&cd->fb_notifier)) ts_err("Failed to register fb notifier client:%d", ret); +#endif + +#ifdef CONFIG_ARCH_QTI_VM +skip_goodix_ts_irq_setup: #endif /* create sysfs files */ goodix_ts_sysfs_init(cd); @@ -2093,6 +2103,10 @@ static int goodix_later_init_thread(void *data) struct goodix_ts_core *cd = data; struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; +#ifdef CONFIG_ARCH_QTI_VM + goto skip_to_stage2_init; +#endif + /* step 1: read version */ ret = cd->hw_ops->read_version(cd, &cd->fw_version); if (ret < 0) { @@ -2142,7 +2156,9 @@ upgrade: * if not we will send config with interactive mode */ goodix_send_ic_config(cd, CONFIG_TYPE_NORMAL); - +#ifdef CONFIG_ARCH_QTI_VM +skip_to_stage2_init: +#endif /* init other resources */ ret = goodix_ts_stage2_init(cd); if (ret) { @@ -2250,6 +2266,165 @@ out: } #endif + +static int goodix_ts_suspend_helper(void *data) +{ + struct goodix_ts_core *core_data = data; + + return goodix_ts_suspend(core_data); +} + +static int goodix_ts_resume_helper(void *data) +{ + struct goodix_ts_core *core_data = data; + + return goodix_ts_resume(core_data); +} + +static int goodix_ts_enable_touch_irq(void *data, bool enable) +{ + struct goodix_ts_core *core_data = data; + struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + + hw_ops->irq_enable(core_data, enable); + return 0; +} + +static int goodix_ts_pre_la_tui_enable(void *data) +{ + struct goodix_ts_core *core_data = data; + struct goodix_ts_esd *ts_esd = &core_data->ts_esd; + + mutex_lock(&core_data->tui_transition_lock); + atomic_set(&ts_esd->esd_on, 0); + + return 0; +} + +static int goodix_ts_post_la_tui_enable(void *data) +{ + struct goodix_ts_core *core_data = data; + + goodix_ts_release_connects(core_data); + mutex_unlock(&core_data->tui_transition_lock); + return 0; +} + +static int goodix_ts_post_la_tui_disable(void *data) +{ + struct goodix_ts_core *core_data = data; + struct goodix_ts_esd *ts_esd = &core_data->ts_esd; + + atomic_set(&ts_esd->esd_on, 1); + return 0; +} + +static int goodix_ts_post_le_tui_enable(void *data) +{ + int ret; + struct goodix_ts_core *cd = data; + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + + ret = hw_ops->read_version(cd, &cd->fw_version); + if (ret) { + ts_err("invalid fw version, abort"); + return -EINVAL; + } + ret = hw_ops->get_ic_info(cd, &cd->ic_info); + if (ret) { + ts_err("invalid ic info, abort"); + return -EINVAL; + } + return 0; +} + +static int goodix_ts_post_le_tui_disable(void *data) +{ + struct goodix_ts_core *core_data = data; + + goodix_ts_release_connects(core_data); + return 0; +} + +static int goodix_ts_get_irq_num(void *data) +{ + struct goodix_ts_core *core_data = data; + + return core_data->irq; +} + +static irqreturn_t goodix_irq_handler(int irq, void *data) +{ + struct goodix_ts_core *core_data = data; + + if (!mutex_trylock(&core_data->tui_transition_lock)) + return IRQ_HANDLED; + + goodix_ts_threadirq_func(irq, data); + mutex_unlock(&core_data->tui_transition_lock); + + return IRQ_HANDLED; +} + +static void goodix_ts_fill_qts_vendor_data(struct qts_vendor_data *qts_vendor_data, + struct goodix_ts_core *core_data) +{ + struct goodix_bus_interface *bus_interface; + struct device_node *node; + const char *touch_type; + int rc = 0; + + bus_interface = core_data->bus; + node = bus_interface->dev->of_node; + + rc = of_property_read_string(node, "goodix,touch-type", &touch_type); + if (rc) { + ts_err("No touch type\n"); + return; + } + + if (!strcmp(touch_type, "primary")) + qts_vendor_data->client_type = QTS_CLIENT_PRIMARY_TOUCH; + else + qts_vendor_data->client_type = QTS_CLIENT_SECONDARY_TOUCH; + + switch (bus_interface->bus_type) { + case GOODIX_BUS_TYPE_I2C: + qts_vendor_data->client = to_i2c_client(bus_interface->dev); + qts_vendor_data->spi = NULL; + qts_vendor_data->bus_type = QTS_BUS_TYPE_I2C; + break; + + case GOODIX_BUS_TYPE_SPI: + qts_vendor_data->client = NULL; + qts_vendor_data->spi = to_spi_device(bus_interface->dev); + qts_vendor_data->bus_type = QTS_BUS_TYPE_SPI; + break; + + default: + ts_err("Invalid bus type :%d\n", + bus_interface->bus_type); + break; + } + + qts_vendor_data->vendor_data = core_data; + qts_vendor_data->schedule_suspend = false; + qts_vendor_data->schedule_resume = false; + qts_vendor_data->qts_vendor_ops.suspend = goodix_ts_suspend_helper; + qts_vendor_data->qts_vendor_ops.resume = goodix_ts_resume_helper; + qts_vendor_data->qts_vendor_ops.enable_touch_irq = goodix_ts_enable_touch_irq; + qts_vendor_data->qts_vendor_ops.get_irq_num = goodix_ts_get_irq_num; + qts_vendor_data->qts_vendor_ops.pre_la_tui_enable = goodix_ts_pre_la_tui_enable; + qts_vendor_data->qts_vendor_ops.post_la_tui_enable = goodix_ts_post_la_tui_enable; + qts_vendor_data->qts_vendor_ops.pre_la_tui_disable = NULL; + qts_vendor_data->qts_vendor_ops.post_la_tui_disable = goodix_ts_post_la_tui_disable; + qts_vendor_data->qts_vendor_ops.pre_le_tui_enable = NULL; + qts_vendor_data->qts_vendor_ops.post_le_tui_enable = goodix_ts_post_le_tui_enable; + qts_vendor_data->qts_vendor_ops.pre_le_tui_disable = NULL; + qts_vendor_data->qts_vendor_ops.post_le_tui_disable = goodix_ts_post_le_tui_disable; + qts_vendor_data->qts_vendor_ops.irq_handler = goodix_irq_handler; +} + /** * goodix_ts_probe - called by kernel when Goodix touch * platform driver is added. @@ -2260,6 +2435,8 @@ static int goodix_ts_probe(struct platform_device *pdev) struct goodix_bus_interface *bus_interface; int ret; struct device_node *node; + bool qts_en = false; + struct qts_vendor_data qts_vendor_data; ts_info("goodix_ts_probe IN"); @@ -2293,12 +2470,27 @@ static int goodix_ts_probe(struct platform_device *pdev) return -ENOMEM; } + core_data->bus = bus_interface; + qts_en = of_property_read_bool(node, "goodix,qts_en"); + if (qts_en) { + mutex_init(&core_data->tui_transition_lock); + goodix_ts_fill_qts_vendor_data(&qts_vendor_data, core_data); + + ret = qts_client_register(qts_vendor_data); + if (ret) { + pr_err("qts client register failed, rc %d\n", ret); + goto err_out; + } + core_data->qts_en = qts_en; + } + if (IS_ENABLED(CONFIG_OF) && bus_interface->dev->of_node) { /* parse devicetree property */ ret = goodix_parse_dt(node, &core_data->board_data); if (ret) { ts_err("failed parse device info form dts, %d", ret); - return -EINVAL; + ret = -EINVAL; + goto err_out; } #if defined(CONFIG_DRM) of_property_read_string(node, "qcom,touch-environment", @@ -2306,22 +2498,25 @@ static int goodix_ts_probe(struct platform_device *pdev) #endif } else { ts_err("no valid device tree node found"); - return -ENODEV; + ret = -ENODEV; + goto err_out; } core_data->hw_ops = goodix_get_hw_ops(); if (!core_data->hw_ops) { ts_err("hw ops is NULL"); - core_module_prob_sate = CORE_MODULE_PROB_FAILED; - return -EINVAL; + ret = -EINVAL; + goto err_out; } goodix_core_module_init(); /* touch core layer is a platform driver */ core_data->pdev = pdev; - core_data->bus = bus_interface; platform_set_drvdata(pdev, core_data); /* get GPIO resource */ +#ifdef CONFIG_ARCH_QTI_VM + goto skip_to_power_gpio_setup; +#endif ret = goodix_ts_gpio_setup(core_data); if (ret) { ts_err("failed init gpio"); @@ -2340,6 +2535,10 @@ static int goodix_ts_probe(struct platform_device *pdev) goto err_out; } +#ifdef CONFIG_ARCH_QTI_VM +skip_to_power_gpio_setup: +#endif + /* generic notifier callback */ core_data->ts_notifier.notifier_call = goodix_generic_noti_callback; goodix_ts_register_notifier(&core_data->ts_notifier); @@ -2358,6 +2557,7 @@ static int goodix_ts_probe(struct platform_device *pdev) return 0; err_out: + devm_kfree(&pdev->dev, core_data); core_data->init_stage = CORE_INIT_FAIL; core_module_prob_sate = CORE_MODULE_PROB_FAILED; ts_err("goodix_ts_core failed, ret:%d", ret); @@ -2453,7 +2653,11 @@ static void __exit goodix_ts_core_exit(void) goodix_i2c_bus_exit(); } +#ifdef CONFIG_ARCH_QTI_VM +module_init(goodix_ts_core_init); +#else late_initcall(goodix_ts_core_init); +#endif module_exit(goodix_ts_core_exit); MODULE_DESCRIPTION("Goodix Touchscreen Core Module"); diff --git a/goodix_berlin_driver/goodix_ts_core.h b/goodix_berlin_driver/goodix_ts_core.h index 0f0fce5e64..1a7502defd 100644 --- a/goodix_berlin_driver/goodix_ts_core.h +++ b/goodix_berlin_driver/goodix_ts_core.h @@ -39,6 +39,9 @@ #include #include #endif +#include +#include +#include "../qts/qts_core_common.h" #define GOODIX_CORE_DRIVER_NAME "goodix_ts" #define GOODIX_PEN_DRIVER_NAME "goodix_ts,pen" @@ -524,6 +527,8 @@ struct goodix_ts_core { atomic_t delayed_vm_probe_pending; atomic_t trusted_touch_mode; #endif + bool qts_en; + struct mutex tui_transition_lock; }; /* external module structures */ diff --git a/nt36xxx/nt36xxx_mp_ctrlram.c b/nt36xxx/nt36xxx_mp_ctrlram.c index 5c07488521..5957f2c761 100644 --- a/nt36xxx/nt36xxx_mp_ctrlram.c +++ b/nt36xxx/nt36xxx_mp_ctrlram.c @@ -72,6 +72,7 @@ static int32_t *RawData_FW_CC = NULL; static struct proc_dir_entry *NVT_proc_selftest_entry = NULL; static int8_t nvt_mp_test_result_printed = 0; static uint8_t fw_ver = 0; +unsigned char mpcriteria[PAGE_SIZE]; //novatek-mp-criteria-default extern void nvt_change_mode(uint8_t mode); extern uint8_t nvt_get_fw_pipe(void); @@ -1054,8 +1055,8 @@ return: static int32_t nvt_selftest_open(struct inode *inode, struct file *file) { struct device_node *np = ts->client->dev.of_node; - unsigned char mpcriteria[PAGE_SIZE] = {0}; //novatek-mp-criteria-default + memset(mpcriteria, 0, PAGE_SIZE * sizeof(mpcriteria[0])); TestResult_Short = 0; TestResult_Open = 0; TestResult_FW_Rawdata = 0; From 9569e9059c7654c7834a80b72d9a58db4c8792d1 Mon Sep 17 00:00:00 2001 From: Raviteja Tamatam Date: Tue, 5 Apr 2022 18:32:35 -0700 Subject: [PATCH 017/170] touch: QTS: create common sysfs node for secure touch create sysfs node for secure touch enable and events. It is independent of bus protocol or device specific path. Change-Id: I84f3f015fdc81f4c90dd71983baab9616b5eccd0 Signed-off-by: Raviteja Tamatam --- qts/qts_core.c | 91 +++++++++++++++++++++++++++++++------------------- qts/qts_core.h | 1 + 2 files changed, 57 insertions(+), 35 deletions(-) diff --git a/qts/qts_core.c b/qts/qts_core.c index 227d711845..392e0656a9 100644 --- a/qts/qts_core.c +++ b/qts/qts_core.c @@ -1249,32 +1249,24 @@ static void qts_trusted_touch_init(struct qts_data *qts_data) atomic_set(&qts_data->trusted_touch_initialized, 1); } -static ssize_t trusted_touch_enable_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t trusted_touch_enable_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) { - struct i2c_client *client = to_i2c_client(dev); struct qts_data *qts_data; - if (!client) - return scnprintf(buf, PAGE_SIZE, "client is null\n"); - qts_data = &qts_data_entries->info[QTS_CLIENT_PRIMARY_TOUCH]; return scnprintf(buf, PAGE_SIZE, "%d", atomic_read(&qts_data->trusted_touch_enabled)); } -static ssize_t trusted_touch_enable_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) +static ssize_t trusted_touch_enable_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); struct qts_data *qts_data; unsigned long value; int err = 0; - if (!client) - return -EIO; - if (count > 2) return -EINVAL; @@ -1304,32 +1296,24 @@ static ssize_t trusted_touch_enable_store(struct device *dev, return err; } -static ssize_t trusted_touch_event_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t trusted_touch_event_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) { - struct i2c_client *client = to_i2c_client(dev); struct qts_data *qts_data; - if (!client) - return scnprintf(buf, PAGE_SIZE, "client is null\n"); - qts_data = &qts_data_entries->info[QTS_CLIENT_PRIMARY_TOUCH]; return scnprintf(buf, PAGE_SIZE, "%d", atomic_read(&qts_data->trusted_touch_event)); } -static ssize_t trusted_touch_event_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) +static ssize_t trusted_touch_event_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); struct qts_data *qts_data; unsigned long value; int err = 0; - if (!client) - return -EIO; - if (count > 2) return -EINVAL; @@ -1350,22 +1334,27 @@ static ssize_t trusted_touch_event_store(struct device *dev, return count; } -static ssize_t trusted_touch_type_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t trusted_touch_type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct qts_data *qts_data = &qts_data_entries->info[QTS_CLIENT_PRIMARY_TOUCH]; return scnprintf(buf, PAGE_SIZE, "%s", qts_data->vm_info->trusted_touch_type); } -static DEVICE_ATTR_RW(trusted_touch_enable); -static DEVICE_ATTR_RW(trusted_touch_event); -static DEVICE_ATTR_RO(trusted_touch_type); + +static struct kobj_attribute trusted_touch_enable_attr = + __ATTR(trusted_touch_enable, 0664, trusted_touch_enable_show, trusted_touch_enable_store); + +static struct kobj_attribute trusted_touch_event_attr = + __ATTR(trusted_touch_event, 0664, trusted_touch_event_show, trusted_touch_event_store); + +static struct kobj_attribute trusted_touch_type_attr = + __ATTR(trusted_touch_type, 0664, trusted_touch_type_show, NULL); static struct attribute *qts_attributes[] = { - &dev_attr_trusted_touch_enable.attr, - &dev_attr_trusted_touch_event.attr, - &dev_attr_trusted_touch_type.attr, + &trusted_touch_enable_attr.attr, + &trusted_touch_event_attr.attr, + &trusted_touch_type_attr.attr, NULL, }; @@ -1376,12 +1365,37 @@ static struct attribute_group qts_attribute_group = { static int qts_create_sysfs(struct qts_data *qts_data) { int ret = 0; + struct kobject *qts_kobj; + struct kobject *client_kobj; + + qts_kobj = &qts_data_entries->qts_kset->kobj; if (qts_data->client_type == QTS_CLIENT_PRIMARY_TOUCH) { - ret = sysfs_create_group(&qts_data->client->dev.kobj, &qts_attribute_group); + + client_kobj = kobject_create_and_add("primary", qts_kobj); + if (!client_kobj) { + pr_err("primary kobject_create_and_add failed\n"); + return -ENOMEM; + } + + ret = sysfs_create_group(client_kobj, &qts_attribute_group); if (ret) { - pr_err("%s sysfs_create_group() failed\n", __func__); - sysfs_remove_group(&qts_data->client->dev.kobj, &qts_attribute_group); + pr_err("[EX]: sysfs_create_group() failed!!\n"); + sysfs_remove_group(client_kobj, &qts_attribute_group); + return -ENOMEM; + } + } else if (qts_data->client_type == QTS_CLIENT_SECONDARY_TOUCH) { + + client_kobj = kobject_create_and_add("secondary", qts_kobj); + if (!client_kobj) { + pr_err("secondary kobject_create_and_add failed\n"); + return -ENOMEM; + } + + ret = sysfs_create_group(client_kobj, &qts_attribute_group); + if (ret) { + pr_err("[EX]: sysfs_create_group() failed!!\n"); + sysfs_remove_group(client_kobj, &qts_attribute_group); return -ENOMEM; } } @@ -1658,6 +1672,13 @@ int qts_client_register(struct qts_vendor_data qts_vendor_data) qts_data->schedule_resume = qts_vendor_data.schedule_resume; qts_trusted_touch_init(qts_data); + + qts_data_entries->qts_kset = kset_create_and_add("qts", NULL, kernel_kobj); + if (!qts_data_entries->qts_kset) { + pr_err("qts kset create failed\n"); + return -ENOMEM; + } + mutex_init(&(qts_data->qts_clk_io_ctrl_mutex)); if (qts_data->tui_supported) qts_create_sysfs(qts_data); diff --git a/qts/qts_core.h b/qts/qts_core.h index 217bcc27b5..6ed9bd0d98 100644 --- a/qts/qts_core.h +++ b/qts/qts_core.h @@ -159,6 +159,7 @@ struct qts_data { struct qts_data_entries; struct qts_data_entries { + struct kset *qts_kset; struct qts_data info[QTS_CLIENT_MAX]; struct mutex qts_data_entries_lock; }; From 536392bcad69d87c507104c77478d37732798106 Mon Sep 17 00:00:00 2001 From: Raviteja Tamatam Date: Mon, 11 Apr 2022 14:24:46 -0700 Subject: [PATCH 018/170] touch: goodix: set parent of touch device Set parent of touch device so that input device node path can be obtained by TUI App from /proc/bus/input. Change-Id: If3cc14e8d2ae64ff0d8f62ab523c089b9b0dd74a Signed-off-by: Raviteja Tamatam --- goodix_berlin_driver/goodix_ts_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/goodix_berlin_driver/goodix_ts_core.c b/goodix_berlin_driver/goodix_ts_core.c index 17f083930f..134e9ef309 100644 --- a/goodix_berlin_driver/goodix_ts_core.c +++ b/goodix_berlin_driver/goodix_ts_core.c @@ -1470,6 +1470,7 @@ static int goodix_ts_input_dev_config(struct goodix_ts_core *core_data) input_dev->id.product = 0xDEAD; input_dev->id.vendor = 0xBEEF; input_dev->id.version = 10427; + input_dev->dev.parent = core_data->bus->dev; set_bit(EV_SYN, input_dev->evbit); set_bit(EV_KEY, input_dev->evbit); From d1ca4dc958dc4c86f39e99fac7152de8aaa7ae7a Mon Sep 17 00:00:00 2001 From: Raviteja Tamatam Date: Tue, 19 Apr 2022 10:49:25 -0700 Subject: [PATCH 019/170] touch: QTS: create trusted_touch_device_path entry Create trusted_touch_device_path entry that provides full sys path of the bus device node. It is used by TUI app to get corresponding touch event in /proc/bus/input. Change-Id: I872c1404f194db2b636fac5ccd25094f03e58032 Signed-off-by: Raviteja Tamatam --- qts/qts_core.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/qts/qts_core.c b/qts/qts_core.c index 392e0656a9..4934d197fe 100644 --- a/qts/qts_core.c +++ b/qts/qts_core.c @@ -1341,6 +1341,17 @@ static ssize_t trusted_touch_type_show(struct kobject *kobj, struct kobj_attribu return scnprintf(buf, PAGE_SIZE, "%s", qts_data->vm_info->trusted_touch_type); } +static ssize_t trusted_touch_device_path_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct qts_data *qts_data = &qts_data_entries->info[QTS_CLIENT_PRIMARY_TOUCH]; + char *path = NULL; + + if (qts_data && qts_data->dev) + path = kobject_get_path(&qts_data->dev->kobj, GFP_KERNEL); + + return scnprintf(buf, PAGE_SIZE, "%s", path ? path : ""); +} static struct kobj_attribute trusted_touch_enable_attr = __ATTR(trusted_touch_enable, 0664, trusted_touch_enable_show, trusted_touch_enable_store); @@ -1351,10 +1362,14 @@ static struct kobj_attribute trusted_touch_event_attr = static struct kobj_attribute trusted_touch_type_attr = __ATTR(trusted_touch_type, 0664, trusted_touch_type_show, NULL); +static struct kobj_attribute trusted_touch_device_path_attr = + __ATTR(trusted_touch_device_path, 0444, trusted_touch_device_path_show, NULL); + static struct attribute *qts_attributes[] = { &trusted_touch_enable_attr.attr, &trusted_touch_event_attr.attr, &trusted_touch_type_attr.attr, + &trusted_touch_device_path_attr.attr, NULL, }; From a4a18f9bf8a7e49dba0e230813547c8a47bd3ade Mon Sep 17 00:00:00 2001 From: Yu Wu Date: Fri, 25 Mar 2022 20:45:13 +0800 Subject: [PATCH 020/170] touch: goodix: fix static code analyze warnings Fix static code analyze warnings. Change-Id: Iabfd2a8fc1fee52c463fc16e0db6b9287c3f50bf Signed-off-by: Yu Wu --- goodix_berlin_driver/goodix_ts_inspect.c | 15 ++++++++----- goodix_berlin_driver/goodix_ts_utils.c | 28 ++++++++++++++---------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/goodix_berlin_driver/goodix_ts_inspect.c b/goodix_berlin_driver/goodix_ts_inspect.c index 33d0a9b59c..3dd2f4bcd4 100644 --- a/goodix_berlin_driver/goodix_ts_inspect.c +++ b/goodix_berlin_driver/goodix_ts_inspect.c @@ -584,6 +584,10 @@ static int goodix_init_testlimits(struct goodix_ts_test *ts_test) ts_err("limits file [%s] not available", limit_file); return -EINVAL; } + if (!firmware) { + ts_err("request_firmware failed"); + return -EINVAL; + } if (firmware->size <= 0) { ts_err("request_firmware, limits param length error,len:%zu", firmware->size); @@ -699,8 +703,7 @@ static int goodix_init_testlimits(struct goodix_ts_test *ts_test) exit_free: kfree(temp_buf); - if (firmware) - release_firmware(firmware); + release_firmware(firmware); return ret; } @@ -1542,7 +1545,7 @@ static void goodix_cache_deltadata(struct goodix_ts_test *ts_test) max_val = MAX(max_val, temp); } /* calcu delta with left node */ - if (j % tx) { + if (j > 0 && j % tx) { temp = ts_test->rawdata[i].data[j - 1]; temp = ABS(temp - raw); max_val = MAX(max_val, temp); @@ -2839,7 +2842,8 @@ static void goodix_put_test_result(struct goodix_ts_test *ts_test, /* calculate self_rawdata min avg max value*/ if (ts_test->test_params.test_items[GTP_SELFCAP_TEST]) { - if (ts_test->self_rawdata.size) { + if (ts_test->self_rawdata.size <= + sizeof(ts_test->self_rawdata.data) / sizeof(ts_test->self_rawdata.data[0])) { goodix_data_statistics( ts_test->self_rawdata.data, ts_test->self_rawdata.size, @@ -2856,7 +2860,8 @@ static void goodix_put_test_result(struct goodix_ts_test *ts_test, /* calculate self_noisedata min avg max value*/ if (ts_test->test_params.test_items[GTP_SELFNOISE_TEST]) { - if (ts_test->self_noisedata.size) { + if (ts_test->self_noisedata.size <= + sizeof(ts_test->self_noisedata.data) / sizeof(ts_test->self_noisedata.data[0])) { goodix_data_statistics( ts_test->self_noisedata.data, ts_test->self_noisedata.size, diff --git a/goodix_berlin_driver/goodix_ts_utils.c b/goodix_berlin_driver/goodix_ts_utils.c index 6d55eb3d9e..2415b5b82d 100644 --- a/goodix_berlin_driver/goodix_ts_utils.c +++ b/goodix_berlin_driver/goodix_ts_utils.c @@ -70,23 +70,29 @@ int checksum_cmp(const u8 *data, int size, int mode) u32 r_checksum = 0; u32 i; + if (((mode == CHECKSUM_MODE_U8_LE) && (size < 2)) || + ((mode == CHECKSUM_MODE_U16_LE) && (size < 4))) + return 1; + if (mode == CHECKSUM_MODE_U8_LE) { - if (size < 2) - return 1; for (i = 0; i < size - 2; i++) cal_checksum += data[i]; - - r_checksum = data[size - 2] + (data[size - 1] << 8); + r_checksum += data[i++]; + r_checksum += (data[i] << 8); return (cal_checksum & 0xFFFF) == r_checksum ? 0 : 1; } - if (size < 4) - return 1; - for (i = 0; i < size - 4; i += 2) - cal_checksum += data[i] + (data[i + 1] << 8); - r_checksum = data[size - 4] + (data[size - 3] << 8) + - (data[size - 2] << 16) + (data[size - 1] << 24); - return cal_checksum == r_checksum ? 0 : 1; + if (mode == CHECKSUM_MODE_U16_LE) { + for (i = 0; i < size - 4; i += 2) + cal_checksum += data[i] + (data[i + 1] << 8); + r_checksum += data[i++]; + r_checksum += (data[i++] << 8); + r_checksum += (data[i++] << 16); + r_checksum += (data[i] << 24); + return cal_checksum == r_checksum ? 0 : 1; + } + + return 1; } /* return 1 if all data is zero or ff From fb8b1097a2ff09ab2bca1399f181306b7ceedabb Mon Sep 17 00:00:00 2001 From: Raviteja Tamatam Date: Mon, 2 May 2022 11:11:16 -0700 Subject: [PATCH 021/170] touch: goodix: handle suspend/resume in probe fail cases In case of goodix probe failure, avoid touch suspend/ resume callbacks from dpms panel event notifier. Change-Id: Ib3be59e02296a3edd2bb8ec39c5fdbd1cc25ae61 Signed-off-by: Raviteja Tamatam --- goodix_berlin_driver/goodix_ts_core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/goodix_berlin_driver/goodix_ts_core.c b/goodix_berlin_driver/goodix_ts_core.c index 134e9ef309..10579f650a 100644 --- a/goodix_berlin_driver/goodix_ts_core.c +++ b/goodix_berlin_driver/goodix_ts_core.c @@ -2272,6 +2272,9 @@ static int goodix_ts_suspend_helper(void *data) { struct goodix_ts_core *core_data = data; + if (!core_data || core_module_prob_sate != CORE_MODULE_PROB_SUCCESS) + return 0; + return goodix_ts_suspend(core_data); } @@ -2279,6 +2282,9 @@ static int goodix_ts_resume_helper(void *data) { struct goodix_ts_core *core_data = data; + if (!core_data || core_module_prob_sate != CORE_MODULE_PROB_SUCCESS) + return 0; + return goodix_ts_resume(core_data); } From cf9cbcfc0508dc1e56a38c3c8d8739f35484eeb4 Mon Sep 17 00:00:00 2001 From: Raviteja Tamatam Date: Wed, 11 May 2022 10:46:29 -0700 Subject: [PATCH 022/170] touch: qts: add mutex protection during TUI transition During TUI touch transition there is race between irq handler and tvm_vm_mode_enable/disable. While the irq handler was running in one thread,tvm_vm_mode_disable was called removing i2c power resources causing timeout. Created qts_irq_handler and added transition_lock to avoid such cases. Change-Id: I669e24572adb020aca1883353a661c5d0daa546c Signed-off-by: Raviteja Tamatam --- qts/qts_core.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/qts/qts_core.c b/qts/qts_core.c index 4934d197fe..3e2920a272 100644 --- a/qts/qts_core.c +++ b/qts/qts_core.c @@ -241,15 +241,27 @@ static void qts_irq_enable(struct qts_data *qts_data, bool en) } } +static irqreturn_t qts_irq_handler(int irq, void *data) +{ + struct qts_data *qts_data = data; + + if (!mutex_trylock(&qts_data->transition_lock)) + return IRQ_HANDLED; + + qts_data->vendor_ops.irq_handler(irq, qts_data->vendor_data); + mutex_unlock(&qts_data->transition_lock); + return IRQ_HANDLED; +} + static int qts_irq_registration(struct qts_data *qts_data) { int ret = 0; qts_data->irq_gpio_flags = IRQF_TRIGGER_RISING; pr_debug("irq:%d, flag:%x\n", qts_data->irq, qts_data->irq_gpio_flags); - ret = request_threaded_irq(qts_data->irq, NULL, qts_data->vendor_ops.irq_handler, + ret = request_threaded_irq(qts_data->irq, NULL, qts_irq_handler, qts_data->irq_gpio_flags | IRQF_ONESHOT, - QTS_NAME, qts_data->vendor_data); + QTS_NAME, qts_data); if (ret != 0) pr_err("request_threaded_irq failed\n"); if (ret == 0) @@ -368,10 +380,12 @@ static void qts_trusted_touch_tvm_vm_mode_enable(struct qts_data *qts_data) int rc = 0; int irq = 0; + mutex_lock(&qts_data->transition_lock); if (qts_trusted_touch_get_vm_state(qts_data) != TVM_ALL_RESOURCES_LENT_NOTIFIED) { pr_info("All lend notifications not received\n"); qts_trusted_touch_event_notify(qts_data, TRUSTED_TOUCH_EVENT_NOTIFICATIONS_PENDING); + mutex_unlock(&qts_data->transition_lock); return; } @@ -456,6 +470,7 @@ static void qts_trusted_touch_tvm_vm_mode_enable(struct qts_data *qts_data) pr_debug("trusted touch enabled\n"); + mutex_unlock(&qts_data->transition_lock); return; sgl_cmp_fail: kfree(expected_sgl_desc); @@ -464,6 +479,7 @@ acl_fail: accept_fail: qts_trusted_touch_abort_handler(qts_data, TRUSTED_TOUCH_EVENT_ACCEPT_FAILURE); + mutex_unlock(&qts_data->transition_lock); } static void qts_vm_irq_on_lend_callback(void *data, @@ -549,8 +565,10 @@ static void qts_trusted_touch_tvm_vm_mode_disable(struct qts_data *qts_data) { int rc = 0; + mutex_lock(&qts_data->transition_lock); if (atomic_read(&qts_data->trusted_touch_abort_status)) { qts_trusted_touch_abort_tvm(qts_data); + mutex_unlock(&qts_data->transition_lock); return; } @@ -593,10 +611,12 @@ static void qts_trusted_touch_tvm_vm_mode_disable(struct qts_data *qts_data) qts_data->vendor_ops.post_le_tui_disable(qts_data->vendor_data); pr_debug("trusted touch disabled\n"); + mutex_unlock(&qts_data->transition_lock); return; error: qts_trusted_touch_abort_handler(qts_data, TRUSTED_TOUCH_EVENT_RELEASE_FAILURE); + mutex_unlock(&qts_data->transition_lock); } static int qts_handle_trusted_touch_tvm(struct qts_data *qts_data, int value) From cd21f85b95abd73714896fe9da8473e49ec7ccc9 Mon Sep 17 00:00:00 2001 From: Yu Wu Date: Thu, 19 May 2022 00:54:05 +0800 Subject: [PATCH 023/170] touch: goodix: set ESD check disabled as default ESD check causes power consumption issue, so disable ESD check by default. User can still enable ESD check by echo 1 to esd_info sysfs node. Change-Id: I10f339b76bf9cddcdd165c3dd5e6574d85640dbf Signed-off-by: Yu Wu --- goodix_berlin_driver/goodix_ts_core.c | 16 +++++++++++----- goodix_berlin_driver/goodix_ts_core.h | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/goodix_berlin_driver/goodix_ts_core.c b/goodix_berlin_driver/goodix_ts_core.c index 10579f650a..95851fc459 100644 --- a/goodix_berlin_driver/goodix_ts_core.c +++ b/goodix_berlin_driver/goodix_ts_core.c @@ -61,6 +61,8 @@ static void goodix_register_for_panel_events(struct device_node *dp, struct goodix_module goodix_modules; int core_module_prob_sate = CORE_MODULE_UNPROBED; +int goodix_ts_esd_init(struct goodix_ts_core *cd); + static int goodix_send_ic_config(struct goodix_ts_core *cd, int type); /** * __do_register_ext_module - register external module @@ -725,13 +727,19 @@ static ssize_t goodix_ts_esd_info_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + if (!buf || count <= 0) return -EINVAL; - if (buf[0] != '0') + if (buf[0] != '0') { + if (!core_data->esd_initialized) + goodix_ts_esd_init(core_data); goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); - else + } else if (core_data->esd_initialized) { goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); + } + return count; } @@ -1703,6 +1711,7 @@ int goodix_ts_esd_init(struct goodix_ts_core *cd) ts_esd->esd_notifier.notifier_call = goodix_esd_notifier_callback; goodix_ts_register_notifier(&ts_esd->esd_notifier); goodix_ts_esd_on(cd); + cd->esd_initialized = true; return 0; } @@ -2046,9 +2055,6 @@ skip_goodix_ts_irq_setup: /* create procfs files */ goodix_ts_procfs_init(cd); - /* esd protector */ - goodix_ts_esd_init(cd); - /* gesture init */ gesture_module_init(); diff --git a/goodix_berlin_driver/goodix_ts_core.h b/goodix_berlin_driver/goodix_ts_core.h index 1a7502defd..6999923ef9 100644 --- a/goodix_berlin_driver/goodix_ts_core.h +++ b/goodix_berlin_driver/goodix_ts_core.h @@ -504,6 +504,7 @@ struct goodix_ts_core { struct notifier_block ts_notifier; struct goodix_ts_esd ts_esd; + bool esd_initialized; #if defined(CONFIG_DRM) struct notifier_block fb_notifier; From 9d186e44c9220a10d12d74291f84658b4ca69a46 Mon Sep 17 00:00:00 2001 From: Jeykumar Sankaran Date: Mon, 16 May 2022 18:38:21 -0700 Subject: [PATCH 024/170] touch: add qti dummy touch driver Add support for dummy touchscreen driver to help boot up the HW with operating systems requiring at least one input devices in pre-silicon platforms. Change-Id: I0bb5c5a3dd2a8d945c3b0ecee738652ec9d2b561 Signed-off-by: Jeykumar Sankaran --- Android.mk | 11 +++++ Kbuild | 6 +++ config/gki_kalamatouch.conf | 1 + dummy_touch/dummy_touch.c | 87 +++++++++++++++++++++++++++++++++++++ touch_driver_board.mk | 3 +- touch_driver_product.mk | 3 +- 6 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 dummy_touch/dummy_touch.c diff --git a/Android.mk b/Android.mk index 7c830a327f..0c4a569d97 100644 --- a/Android.mk +++ b/Android.mk @@ -57,4 +57,15 @@ LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### +########################################################### +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) +LOCAL_MODULE := dummy_ts.ko +LOCAL_MODULE_KBUILD_NAME := dummy_ts.ko +LOCAL_MODULE_TAGS := optional +#LOCAL_MODULE_DEBUG_ENABLE := true +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/Build_external_kernelmodule.mk +########################################################### + endif # DLKM check diff --git a/Kbuild b/Kbuild index 7f83e09f96..fabdb54b34 100644 --- a/Kbuild +++ b/Kbuild @@ -115,4 +115,10 @@ ifeq ($(CONFIG_TOUCHSCREEN_ATMEL_MXT), y) obj-$(CONFIG_MSM_TOUCH) += atmel_mxt_ts.o endif +ifeq ($(CONFIG_TOUCHSCREEN_DUMMY), y) + dummy_ts-y := ./dummy_touch/dummy_touch.o + + obj-$(CONFIG_MSM_TOUCH) += dummy_ts.o +endif + CDEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/config/gki_kalamatouch.conf b/config/gki_kalamatouch.conf index de356a66c9..0849bec783 100644 --- a/config/gki_kalamatouch.conf +++ b/config/gki_kalamatouch.conf @@ -1,4 +1,5 @@ export CONFIG_TOUCHSCREEN_NT36XXX_I2C=y export CONFIG_TOUCHSCREEN_GOODIX_BRL=y export CONFIG_TOUCHSCREEN_ATMEL_MXT=y +export CONFIG_TOUCHSCREEN_DUMMY=y export CONFIG_MSM_TOUCH=m diff --git a/dummy_touch/dummy_touch.c b/dummy_touch/dummy_touch.c new file mode 100644 index 0000000000..59558bbb6b --- /dev/null +++ b/dummy_touch/dummy_touch.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include + +MODULE_DESCRIPTION("QTI dummy touchscreen driver"); +MODULE_LICENSE("GPL v2"); + +#define DEVICE_COMPATIBLE "qti,dummy_ts" +#define DRIVER_NAME "qti_dummy_ts" + +static int dummy_touch_probe(struct platform_device *device) +{ + struct input_dev *touch_dev = NULL; + int rc; + + touch_dev = input_allocate_device(); + if (!touch_dev) { + pr_err("%s: input device allocate failed\n", __func__); + return -ENOMEM; + } + + touch_dev->name = "qti_dummy_ts"; + touch_dev->id.bustype = BUS_VIRTUAL; + touch_dev->evbit[0] = BIT_MASK(EV_KEY); + touch_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); + touch_dev->dev.parent = NULL; + + rc = input_register_device(touch_dev); + if (rc) { + pr_err("%s: touch device register failed, rc = %d\n", __func__, rc); + input_free_device(touch_dev); + return rc; + } + + pr_info("qti_dummy_ts device registered\n"); + + platform_set_drvdata(device, (void *)touch_dev); + + return 0; +} + +static int dummy_touch_remove(struct platform_device *device) +{ + struct input_dev *touch_dev = NULL; + + touch_dev = (struct input_dev *)platform_get_drvdata(device); + if (touch_dev) + input_free_device(touch_dev); + + return 0; +} + +static const struct of_device_id dummy_touch_id[] = { + {.compatible = DEVICE_COMPATIBLE}, + {} +}; + +static struct platform_driver dummy_touch_driver = { + + .driver = { + .name = DRIVER_NAME, + .of_match_table = dummy_touch_id, + }, + .probe = dummy_touch_probe, + .remove = dummy_touch_remove, +}; + +static int __init dummy_touch_init(void) +{ + platform_driver_register(&dummy_touch_driver); + + return 0; +} + +static void __exit dummy_touch_exit(void) +{ + platform_driver_unregister(&dummy_touch_driver); +} + +late_initcall(dummy_touch_init); +module_exit(dummy_touch_exit); diff --git a/touch_driver_board.mk b/touch_driver_board.mk index 643742d8e0..40cc44cb5d 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -3,6 +3,7 @@ ifneq ($(TARGET_BOARD_AUTO),true) ifeq ($(call is-board-platform-in-list,$(TARGET_BOARD_PLATFORM)),true) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ - $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ + $(KERNEL_MODULES_OUT)/dummy_ts.ko endif endif diff --git a/touch_driver_product.mk b/touch_driver_product.mk index 08c37015ff..36f7dc1763 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -1,4 +1,5 @@ PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ - $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ + $(KERNEL_MODULES_OUT)/dummy_ts.ko From 5567d48bacf8931096f6656d836042418514113f Mon Sep 17 00:00:00 2001 From: Shashank Babu Chinta Venkata Date: Fri, 20 May 2022 15:37:55 -0700 Subject: [PATCH 025/170] touch: add support to compile out touch modules Add support to selective compile out touch module based on compile flag. Change-Id: I0525b7664576740acfeb7febce12e666a9a6c7ba Signed-off-by: Shashank Babu Chinta Venkata --- Android.mk | 115 ++++++++++++++++++++-------------------- touch_driver_board.mk | 21 +++++--- touch_driver_product.mk | 15 ++++-- 3 files changed, 82 insertions(+), 69 deletions(-) diff --git a/Android.mk b/Android.mk index 0c4a569d97..8f87eb8d51 100644 --- a/Android.mk +++ b/Android.mk @@ -1,71 +1,70 @@ # Android makefile for display kernel modules -TOUCH_SELECT := CONFIG_MSM_TOUCH=m -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) +TOUCH_DLKM_ENABLE := true +ifeq ($(TARGET_KERNEL_DLKM_DISABLE), true) + ifeq ($(TARGET_KERNEL_DLKM_TOUCH_OVERRIDE), false) + TOUCH_DLKM_ENABLE := false + endif +endif -# This makefile is only for DLKM -ifneq ($(findstring vendor,$(LOCAL_PATH)),) +ifeq ($(TOUCH_DLKM_ENABLE), true) + TOUCH_SELECT := CONFIG_MSM_TOUCH=m -ifneq ($(findstring opensource,$(LOCAL_PATH)),) - TOUCH_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/touch-drivers -endif # opensource + LOCAL_PATH := $(call my-dir) + include $(CLEAR_VARS) -DLKM_DIR := $(TOP)/device/qcom/common/dlkm + # This makefile is only for DLKM + ifneq ($(findstring vendor,$(LOCAL_PATH)),) -LOCAL_ADDITIONAL_DEPENDENCIES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + ifneq ($(findstring opensource,$(LOCAL_PATH)),) + TOUCH_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/touch-drivers + endif # opensource -# Build -########################################################### -# This is set once per LOCAL_PATH, not per (kernel) module -KBUILD_OPTIONS := TOUCH_ROOT=$(TOUCH_BLD_DIR) + DLKM_DIR := $(TOP)/device/qcom/common/dlkm -KBUILD_OPTIONS += MODNAME=touch_dlkm -KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) -KBUILD_OPTIONS += $(TOUCH_SELECT) + LOCAL_ADDITIONAL_DEPENDENCIES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) -########################################################### -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) -LOCAL_MODULE := nt36xxx-i2c.ko -LOCAL_MODULE_KBUILD_NAME := nt36xxx-i2c.ko -LOCAL_MODULE_TAGS := optional -#LOCAL_MODULE_DEBUG_ENABLE := true -LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) -include $(DLKM_DIR)/Build_external_kernelmodule.mk -########################################################### + # Build + ########################################################### + # This is set once per LOCAL_PATH, not per (kernel) module + KBUILD_OPTIONS := TOUCH_ROOT=$(TOUCH_BLD_DIR) -########################################################### -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) -LOCAL_MODULE := goodix_ts.ko -LOCAL_MODULE_KBUILD_NAME := goodix_ts.ko -LOCAL_MODULE_TAGS := optional -#LOCAL_MODULE_DEBUG_ENABLE := true -LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) -include $(DLKM_DIR)/Build_external_kernelmodule.mk -########################################################### + KBUILD_OPTIONS += MODNAME=touch_dlkm + KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) + KBUILD_OPTIONS += $(TOUCH_SELECT) -########################################################### -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) -LOCAL_MODULE := atmel_mxt_ts.ko -LOCAL_MODULE_KBUILD_NAME := atmel_mxt_ts.ko -LOCAL_MODULE_TAGS := optional -#LOCAL_MODULE_DEBUG_ENABLE := true -LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) -include $(DLKM_DIR)/Build_external_kernelmodule.mk -########################################################### + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := nt36xxx-i2c.ko + LOCAL_MODULE_KBUILD_NAME := nt36xxx-i2c.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### -########################################################### -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) -LOCAL_MODULE := dummy_ts.ko -LOCAL_MODULE_KBUILD_NAME := dummy_ts.ko -LOCAL_MODULE_TAGS := optional -#LOCAL_MODULE_DEBUG_ENABLE := true -LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) -include $(DLKM_DIR)/Build_external_kernelmodule.mk -########################################################### + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := goodix_ts.ko + LOCAL_MODULE_KBUILD_NAME := goodix_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### -endif # DLKM check + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := atmel_mxt_ts.ko + LOCAL_MODULE_KBUILD_NAME := atmel_mxt_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + + endif # DLKM check +endif diff --git a/touch_driver_board.mk b/touch_driver_board.mk index 40cc44cb5d..4b7ee0fcd3 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -1,9 +1,16 @@ - -ifneq ($(TARGET_BOARD_AUTO),true) - ifeq ($(call is-board-platform-in-list,$(TARGET_BOARD_PLATFORM)),true) - BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ - $(KERNEL_MODULES_OUT)/goodix_ts.ko \ - $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ - $(KERNEL_MODULES_OUT)/dummy_ts.ko +TOUCH_DLKM_ENABLE := true +ifeq ($(TARGET_KERNEL_DLKM_DISABLE), true) + ifeq ($(TARGET_KERNEL_DLKM_TOUCH_OVERRIDE), false) + TOUCH_DLKM_ENABLE := false + endif +endif + +ifeq ($(TOUCH_DLKM_ENABLE), true) + ifneq ($(TARGET_BOARD_AUTO),true) + ifeq ($(call is-board-platform-in-list,$(TARGET_BOARD_PLATFORM)),true) + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko + endif endif endif diff --git a/touch_driver_product.mk b/touch_driver_product.mk index 36f7dc1763..6af71d5338 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -1,5 +1,12 @@ +TOUCH_DLKM_ENABLE := true +ifeq ($(TARGET_KERNEL_DLKM_DISABLE), true) + ifeq ($(TARGET_KERNEL_DLKM_TOUCH_OVERRIDE), false) + TOUCH_DLKM_ENABLE := false + endif +endif -PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ - $(KERNEL_MODULES_OUT)/goodix_ts.ko \ - $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ - $(KERNEL_MODULES_OUT)/dummy_ts.ko +ifeq ($(TOUCH_DLKM_ENABLE), true) + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko +endif From 9b89984f3a6a7441e095b18db9e6c856ff480c58 Mon Sep 17 00:00:00 2001 From: Raviteja Tamatam Date: Thu, 30 Jun 2022 12:43:37 -0700 Subject: [PATCH 026/170] touch: goodix: handle use after free in probe fail case In case of goodix probe failure, avoid core_data being modified after free. Change-Id: If22ecdd38b18df7a26a97f80d8f724324c03e7f0 Signed-off-by: Raviteja Tamatam --- goodix_berlin_driver/goodix_ts_core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/goodix_berlin_driver/goodix_ts_core.c b/goodix_berlin_driver/goodix_ts_core.c index 95851fc459..7e91c945e2 100644 --- a/goodix_berlin_driver/goodix_ts_core.c +++ b/goodix_berlin_driver/goodix_ts_core.c @@ -2571,7 +2571,6 @@ skip_to_power_gpio_setup: err_out: devm_kfree(&pdev->dev, core_data); - core_data->init_stage = CORE_INIT_FAIL; core_module_prob_sate = CORE_MODULE_PROB_FAILED; ts_err("goodix_ts_core failed, ret:%d", ret); return ret; From 173c930db6e17311fc77b5d35202df99850d2680 Mon Sep 17 00:00:00 2001 From: Raviteja Tamatam Date: Wed, 20 Jul 2022 23:09:12 -0700 Subject: [PATCH 027/170] touch: goodix: add support for dual touch with SPI Add changes to support secondary touch with SPI on goodix driver. Change-Id: I7bf5ac146b2e2f6d616041fa6aedb3a27be24b8f Signed-off-by: Raviteja Tamatam --- goodix_berlin_driver/goodix_brl_spi.c | 32 ++++++++++++++++--------- goodix_berlin_driver/goodix_ts_core.c | 33 ++++++++++++++++++++++---- goodix_berlin_driver/goodix_ts_core.h | 9 +++++++ goodix_berlin_driver/goodix_ts_utils.c | 22 +++++++++++++++++ qts/qts_core.c | 14 +++++------ 5 files changed, 86 insertions(+), 24 deletions(-) diff --git a/goodix_berlin_driver/goodix_brl_spi.c b/goodix_berlin_driver/goodix_brl_spi.c index 7dbe405ad1..7198c42409 100644 --- a/goodix_berlin_driver/goodix_brl_spi.c +++ b/goodix_berlin_driver/goodix_brl_spi.c @@ -32,7 +32,7 @@ #define SPI_READ_FLAG 0xF1 static struct platform_device *goodix_pdev; -struct goodix_bus_interface goodix_spi_bus; +struct goodix_bus_interface goodix_spi_bus[MAX_SUPPORTED_TOUCH_PANELS]; /** * goodix_spi_read_bra- read device register through spi bus @@ -191,7 +191,7 @@ static void goodix_pdev_release(struct device *dev) static int goodix_spi_probe(struct spi_device *spi) { - int ret = 0; + int ret = 0, idx; ts_info("goodix spi probe in"); @@ -210,20 +210,30 @@ static int goodix_spi_probe(struct spi_device *spi) if (ret < 0) return ret; - goodix_spi_bus.ic_type = ret; - goodix_spi_bus.bus_type = GOODIX_BUS_TYPE_SPI; - goodix_spi_bus.dev = &spi->dev; - if (goodix_spi_bus.ic_type == IC_TYPE_BERLIN_A) - goodix_spi_bus.read = goodix_spi_read_bra; + idx = goodix_get_touch_type(spi->dev.of_node); + if (idx < 0 || idx >= MAX_SUPPORTED_TOUCH_PANELS) { + ts_err("unsupported touch type idx:%d", idx); + return -ENODEV; + } + + goodix_spi_bus[idx].ic_type = ret; + goodix_spi_bus[idx].bus_type = GOODIX_BUS_TYPE_SPI; + goodix_spi_bus[idx].dev = &spi->dev; + if (goodix_spi_bus[idx].ic_type == IC_TYPE_BERLIN_A) + goodix_spi_bus[idx].read = goodix_spi_read_bra; else - goodix_spi_bus.read = goodix_spi_read; - goodix_spi_bus.write = goodix_spi_write; + goodix_spi_bus[idx].read = goodix_spi_read; + goodix_spi_bus[idx].write = goodix_spi_write; /* ts core device */ goodix_pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); if (!goodix_pdev) return -ENOMEM; - goodix_pdev->name = GOODIX_CORE_DRIVER_NAME; + if (idx) + goodix_pdev->name = GOODIX_CORE_DEVICE_2_NAME; + else + goodix_pdev->name = GOODIX_CORE_DEVICE_NAME; + goodix_pdev->id = 0; goodix_pdev->num_resources = 0; /* @@ -231,7 +241,7 @@ static int goodix_spi_probe(struct spi_device *spi) * /sys/devices/platfrom/goodix_ts.0 * goodix_pdev->dev.parent = &client->dev; */ - goodix_pdev->dev.platform_data = &goodix_spi_bus; + goodix_pdev->dev.platform_data = &goodix_spi_bus[idx]; goodix_pdev->dev.release = goodix_pdev_release; /* diff --git a/goodix_berlin_driver/goodix_ts_core.c b/goodix_berlin_driver/goodix_ts_core.c index 7e91c945e2..30f069a045 100644 --- a/goodix_berlin_driver/goodix_ts_core.c +++ b/goodix_berlin_driver/goodix_ts_core.c @@ -60,6 +60,7 @@ static void goodix_register_for_panel_events(struct device_node *dp, struct goodix_module goodix_modules; int core_module_prob_sate = CORE_MODULE_UNPROBED; +struct mutex goodix_later_init_tmutex; int goodix_ts_esd_init(struct goodix_ts_core *cd); @@ -2010,6 +2011,14 @@ static int goodix_generic_noti_callback(struct notifier_block *self, int goodix_ts_stage2_init(struct goodix_ts_core *cd) { int ret; + struct goodix_bus_interface *bus_interface; + struct device_node *node; + bool is_primary; + + bus_interface = cd->bus; + node = bus_interface->dev->of_node; + is_primary = (goodix_get_touch_type(node) == PRIMARY_TOUCH_IDX) ? 1 : 0; + /* alloc/config/register input device */ ret = goodix_ts_input_dev_config(cd); @@ -2037,7 +2046,7 @@ int goodix_ts_stage2_init(struct goodix_ts_core *cd) ts_info("success register irq"); #if defined(CONFIG_DRM) - if (cd->touch_environment && !strcmp(cd->touch_environment, "pvm")) + if (is_primary && cd->touch_environment && !strcmp(cd->touch_environment, "pvm")) goodix_register_for_panel_events(cd->bus->dev->of_node, cd); #elif defined(CONFIG_FB) @@ -2052,6 +2061,9 @@ skip_goodix_ts_irq_setup: /* create sysfs files */ goodix_ts_sysfs_init(cd); + if (!is_primary) + return 0; + /* create procfs files */ goodix_ts_procfs_init(cd); @@ -2110,6 +2122,7 @@ static int goodix_later_init_thread(void *data) struct goodix_ts_core *cd = data; struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + mutex_lock(&goodix_later_init_tmutex); #ifdef CONFIG_ARCH_QTI_VM goto skip_to_stage2_init; #endif @@ -2173,7 +2186,7 @@ skip_to_stage2_init: goto uninit_fw; } cd->init_stage = CORE_INIT_STAGE2; - + mutex_unlock(&goodix_later_init_tmutex); return 0; uninit_fw: @@ -2185,6 +2198,7 @@ err_out: kfree(cd->ic_configs[i]); cd->ic_configs[i] = NULL; } + mutex_unlock(&goodix_later_init_tmutex); return ret; } @@ -2450,9 +2464,13 @@ static int goodix_ts_probe(struct platform_device *pdev) struct device_node *node; bool qts_en = false; struct qts_vendor_data qts_vendor_data; + bool is_primary; ts_info("goodix_ts_probe IN"); + if (!core_module_prob_sate) + mutex_init(&goodix_later_init_tmutex); + bus_interface = pdev->dev.platform_data; if (!bus_interface) { ts_err("Invalid touch device"); @@ -2460,6 +2478,7 @@ static int goodix_ts_probe(struct platform_device *pdev) return -ENODEV; } node = bus_interface->dev->of_node; + is_primary = (goodix_get_touch_type(node) == PRIMARY_TOUCH_IDX) ? 1 : 0; #if defined(CONFIG_DRM) ret = goodix_check_dt(node); @@ -2521,7 +2540,9 @@ static int goodix_ts_probe(struct platform_device *pdev) ret = -EINVAL; goto err_out; } - goodix_core_module_init(); + + if (is_primary) + goodix_core_module_init(); /* touch core layer is a platform driver */ core_data->pdev = pdev; platform_set_drvdata(pdev, core_data); @@ -2557,7 +2578,8 @@ skip_to_power_gpio_setup: goodix_ts_register_notifier(&core_data->ts_notifier); /* debug node init */ - goodix_tools_init(); + if (is_primary) + goodix_tools_init(); core_data->init_stage = CORE_INIT_STAGE1; goodix_modules.core_data = core_data; @@ -2622,7 +2644,8 @@ static const struct dev_pm_ops dev_pm_ops = { #endif static const struct platform_device_id ts_core_ids[] = { - {.name = GOODIX_CORE_DRIVER_NAME}, + {.name = GOODIX_CORE_DEVICE_NAME}, + {.name = GOODIX_CORE_DEVICE_2_NAME}, {} }; MODULE_DEVICE_TABLE(platform, ts_core_ids); diff --git a/goodix_berlin_driver/goodix_ts_core.h b/goodix_berlin_driver/goodix_ts_core.h index 6999923ef9..929f96511b 100644 --- a/goodix_berlin_driver/goodix_ts_core.h +++ b/goodix_berlin_driver/goodix_ts_core.h @@ -45,6 +45,8 @@ #define GOODIX_CORE_DRIVER_NAME "goodix_ts" #define GOODIX_PEN_DRIVER_NAME "goodix_ts,pen" +#define GOODIX_CORE_DEVICE_NAME "goodix_ts" +#define GOODIX_CORE_DEVICE_2_NAME "goodix_ts2" #define GOODIX_DRIVER_VERSION "v1.2.4" #define GOODIX_MAX_TOUCH 10 #define GOODIX_PEN_MAX_PRESSURE 4096 @@ -117,6 +119,12 @@ enum CHECKSUM_MODE { CHECKSUM_MODE_U16_LE, }; +enum DUAL_TOUCH_TYPE { + PRIMARY_TOUCH_IDX, + SECONDARY_TOUCH_IDX, + MAX_SUPPORTED_TOUCH_PANELS, +}; + #define MAX_SCAN_FREQ_NUM 8 #define MAX_SCAN_RATE_NUM 8 #define MAX_FREQ_NUM_STYLUS 8 @@ -689,6 +697,7 @@ int inspect_module_init(void); void inspect_module_exit(void); int goodix_tools_init(void); void goodix_tools_exit(void); +int goodix_get_touch_type(struct device_node *np); /* goodix FB test */ /* diff --git a/goodix_berlin_driver/goodix_ts_utils.c b/goodix_berlin_driver/goodix_ts_utils.c index 2415b5b82d..e1388acaa9 100644 --- a/goodix_berlin_driver/goodix_ts_utils.c +++ b/goodix_berlin_driver/goodix_ts_utils.c @@ -183,3 +183,25 @@ int goodix_get_ic_type(struct device_node *node) return ret; } + +/* get touch type */ +int goodix_get_touch_type(struct device_node *node) +{ + const char *touch_type; + int ret; + + ret = of_property_read_string(node, "goodix,touch-type", &touch_type); + if (ret) { + ts_err("No touch type found\n"); + return -EINVAL; + } + + if (!strcmp(touch_type, "primary")) + ret = PRIMARY_TOUCH_IDX; + else if (!strcmp(touch_type, "secondary")) + ret = SECONDARY_TOUCH_IDX; + else + ret = -EINVAL; + + return ret; +} \ No newline at end of file diff --git a/qts/qts_core.c b/qts/qts_core.c index 3e2920a272..36bf059f5c 100644 --- a/qts/qts_core.c +++ b/qts/qts_core.c @@ -1669,10 +1669,14 @@ int qts_client_register(struct qts_vendor_data qts_vendor_data) pr_err("mem allocation failed\n"); return -EPROBE_DEFER; } + mutex_init(&qts_data_entries->qts_data_entries_lock); + qts_data_entries->qts_kset = kset_create_and_add("qts", NULL, kernel_kobj); + if (!qts_data_entries->qts_kset) { + pr_err("qts kset create failed\n"); + return -ENOMEM; + } } - mutex_init(&qts_data_entries->qts_data_entries_lock); - mutex_lock(&qts_data_entries->qts_data_entries_lock); if (qts_vendor_data.bus_type == QTS_BUS_TYPE_I2C) @@ -1708,12 +1712,6 @@ int qts_client_register(struct qts_vendor_data qts_vendor_data) qts_trusted_touch_init(qts_data); - qts_data_entries->qts_kset = kset_create_and_add("qts", NULL, kernel_kobj); - if (!qts_data_entries->qts_kset) { - pr_err("qts kset create failed\n"); - return -ENOMEM; - } - mutex_init(&(qts_data->qts_clk_io_ctrl_mutex)); if (qts_data->tui_supported) qts_create_sysfs(qts_data); From 0efdbe4ab978151faced3e654e359dad40bba671 Mon Sep 17 00:00:00 2001 From: Raviteja Tamatam Date: Tue, 26 Jul 2022 17:03:49 -0700 Subject: [PATCH 028/170] touch: goodix: add compatible string for dual touch Add gt9916S2 compatible string for dual touch handle dependency. Change-Id: I9efb9af1a56bc218a9b2b7d21a92e337d093c630 Signed-off-by: Raviteja Tamatam --- goodix_berlin_driver/goodix_brl_spi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/goodix_berlin_driver/goodix_brl_spi.c b/goodix_berlin_driver/goodix_brl_spi.c index 7198c42409..7155a1aaab 100644 --- a/goodix_berlin_driver/goodix_brl_spi.c +++ b/goodix_berlin_driver/goodix_brl_spi.c @@ -275,6 +275,7 @@ static const struct of_device_id spi_matchs[] = { {.compatible = "goodix,gt9897T",}, {.compatible = "goodix,gt9966S",}, {.compatible = "goodix,gt9916S",}, + {.compatible = "goodix,gt9916S2",}, {}, }; #endif From f6dea32cd46cf174e06d309273b07a2ae59ac136 Mon Sep 17 00:00:00 2001 From: Raviteja Tamatam Date: Thu, 28 Jul 2022 16:14:43 -0700 Subject: [PATCH 029/170] touch: qts: add support for secondary touch in TUI Add changes to support secondary secure qts touch nodes and select corresponding TUI transtions. Change-Id: I72cfe24ea3579111fb2142d2933dfa95c27c8ea2 Signed-off-by: Raviteja Tamatam --- qts/qts_core.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/qts/qts_core.c b/qts/qts_core.c index 36bf059f5c..6272cce3e8 100644 --- a/qts/qts_core.c +++ b/qts/qts_core.c @@ -1269,12 +1269,28 @@ static void qts_trusted_touch_init(struct qts_data *qts_data) atomic_set(&qts_data->trusted_touch_initialized, 1); } +static bool qts_ts_is_primary(struct kobject *kobj) +{ + char *path = NULL; + + if (!kobj) + return true; + + path = kobject_get_path(kobj, GFP_KERNEL); + + if (strstr(path, "primary")) + return true; + else + return false; +} + static ssize_t trusted_touch_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct qts_data *qts_data; + u32 idx = qts_ts_is_primary(kobj) ? 0 : 1; - qts_data = &qts_data_entries->info[QTS_CLIENT_PRIMARY_TOUCH]; + qts_data = &qts_data_entries->info[idx]; return scnprintf(buf, PAGE_SIZE, "%d", atomic_read(&qts_data->trusted_touch_enabled)); @@ -1286,6 +1302,7 @@ static ssize_t trusted_touch_enable_store(struct kobject *kobj, struct kobj_attr struct qts_data *qts_data; unsigned long value; int err = 0; + u32 idx = qts_ts_is_primary(kobj) ? 0 : 1; if (count > 2) return -EINVAL; @@ -1294,7 +1311,7 @@ static ssize_t trusted_touch_enable_store(struct kobject *kobj, struct kobj_attr if (err != 0) return err; - qts_data = &qts_data_entries->info[QTS_CLIENT_PRIMARY_TOUCH]; + qts_data = &qts_data_entries->info[idx]; if (!atomic_read(&qts_data->trusted_touch_initialized)) return -EIO; @@ -1320,8 +1337,9 @@ static ssize_t trusted_touch_event_show(struct kobject *kobj, struct kobj_attrib char *buf) { struct qts_data *qts_data; + u32 idx = qts_ts_is_primary(kobj) ? 0 : 1; - qts_data = &qts_data_entries->info[QTS_CLIENT_PRIMARY_TOUCH]; + qts_data = &qts_data_entries->info[idx]; return scnprintf(buf, PAGE_SIZE, "%d", atomic_read(&qts_data->trusted_touch_event)); @@ -1333,6 +1351,7 @@ static ssize_t trusted_touch_event_store(struct kobject *kobj, struct kobj_attri struct qts_data *qts_data; unsigned long value; int err = 0; + u32 idx = qts_ts_is_primary(kobj) ? 0 : 1; if (count > 2) return -EINVAL; @@ -1341,7 +1360,7 @@ static ssize_t trusted_touch_event_store(struct kobject *kobj, struct kobj_attri if (err != 0) return err; - qts_data = &qts_data_entries->info[QTS_CLIENT_PRIMARY_TOUCH]; + qts_data = &qts_data_entries->info[idx]; if (!atomic_read(&qts_data->trusted_touch_initialized)) return -EIO; @@ -1356,7 +1375,10 @@ static ssize_t trusted_touch_event_store(struct kobject *kobj, struct kobj_attri static ssize_t trusted_touch_type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - struct qts_data *qts_data = &qts_data_entries->info[QTS_CLIENT_PRIMARY_TOUCH]; + struct qts_data *qts_data; + u32 idx = qts_ts_is_primary(kobj) ? 0 : 1; + + qts_data = &qts_data_entries->info[idx]; return scnprintf(buf, PAGE_SIZE, "%s", qts_data->vm_info->trusted_touch_type); } @@ -1364,8 +1386,11 @@ static ssize_t trusted_touch_type_show(struct kobject *kobj, struct kobj_attribu static ssize_t trusted_touch_device_path_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - struct qts_data *qts_data = &qts_data_entries->info[QTS_CLIENT_PRIMARY_TOUCH]; + struct qts_data *qts_data; char *path = NULL; + u32 idx = qts_ts_is_primary(kobj) ? 0 : 1; + + qts_data = &qts_data_entries->info[idx]; if (qts_data && qts_data->dev) path = kobject_get_path(&qts_data->dev->kobj, GFP_KERNEL); From 0991d38ec2dc0cf289a1a16da1a3bdf553f889c2 Mon Sep 17 00:00:00 2001 From: Yu Wu Date: Tue, 9 Aug 2022 15:23:08 +0800 Subject: [PATCH 030/170] touch: goodix: Turn off touch power when display off Currently when display off, host will send command to make touch IC enter sleep mode, but from power team's requirement, touch IC should be totally power off when display off. Change-Id: Ie97d6f80cd9547b2e40ee3f5674bc0aa7034d2ed Signed-off-by: Yu Wu --- goodix_berlin_driver/goodix_brl_hw.c | 27 +++++++++++++++++++++++++-- goodix_berlin_driver/goodix_ts_core.c | 7 +++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/goodix_berlin_driver/goodix_brl_hw.c b/goodix_berlin_driver/goodix_brl_hw.c index 3cb79b7ba8..d4646dad3d 100644 --- a/goodix_berlin_driver/goodix_brl_hw.c +++ b/goodix_berlin_driver/goodix_brl_hw.c @@ -1,6 +1,7 @@ /* * Goodix Touchscreen Driver * Copyright (C) 2020 - 2021 Goodix, Inc. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. 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 @@ -260,19 +261,41 @@ power_off: #define GOODIX_SLEEP_CMD 0x84 int brl_suspend(struct goodix_ts_core *cd) { +#ifdef GOODIX_SUSPEND_GESTURE_ENABLE struct goodix_ts_cmd sleep_cmd; sleep_cmd.cmd = GOODIX_SLEEP_CMD; sleep_cmd.len = 4; if (cd->hw_ops->send_cmd(cd, &sleep_cmd)) ts_err("failed send sleep cmd"); - +#else + if (cd->hw_ops->power_on(cd, 0)) + ts_err("failed power off"); +#endif return 0; } int brl_resume(struct goodix_ts_core *cd) { - return cd->hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); + int ret = 0; + +#ifdef GOODIX_SUSPEND_GESTURE_ENABLE + ret = cd->hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); +#else + ret = cd->hw_ops->power_on(cd, 1); + if (ret) { + ts_err("failed power on"); + return ret; + } + + ret = cd->hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); + if (ret) { + ts_err("failed reset tp"); + return ret; + } +#endif + + return ret; } #define GOODIX_GESTURE_CMD_BA 0x12 diff --git a/goodix_berlin_driver/goodix_ts_core.c b/goodix_berlin_driver/goodix_ts_core.c index 30f069a045..aadf88cffd 100644 --- a/goodix_berlin_driver/goodix_ts_core.c +++ b/goodix_berlin_driver/goodix_ts_core.c @@ -1,6 +1,7 @@ /* * Goodix Touchscreen Driver * Copyright (C) 2020 - 2021 Goodix, Inc. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. 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 @@ -2066,10 +2067,10 @@ skip_goodix_ts_irq_setup: /* create procfs files */ goodix_ts_procfs_init(cd); - +#ifdef GOODIX_SUSPEND_GESTURE_ENABLE /* gesture init */ gesture_module_init(); - +#endif /* inspect init */ inspect_module_init(); @@ -2608,7 +2609,9 @@ static int goodix_ts_remove(struct platform_device *pdev) goodix_tools_exit(); if (core_data->init_stage >= CORE_INIT_STAGE2) { + #ifdef GOODIX_SUSPEND_GESTURE_ENABLE gesture_module_exit(); + #endif inspect_module_exit(); hw_ops->irq_enable(core_data, false); From dba1aa7d95b95c9c2630de914bc552e032b770ef Mon Sep 17 00:00:00 2001 From: Raghu Dudda Papanna Date: Thu, 4 Aug 2022 14:36:31 +0530 Subject: [PATCH 031/170] touch: enable synaptics_tcm touch driver This is a snapshot based on msm-5.4 mainline commit cd6c99dc8879 ("input: touchscreen: Remove linking across different modules"). Change-Id: I19cfacad730ba917d54d037958e040bbadca9388 Signed-off-by: Raghu Dudda Papanna Signed-off-by: Nirmal Abraham --- synaptics_tcm/synaptics_tcm.h | 65 + synaptics_tcm/synaptics_tcm_core.c | 3779 +++++++++++++++++++++ synaptics_tcm/synaptics_tcm_core.h | 684 ++++ synaptics_tcm/synaptics_tcm_device.c | 707 ++++ synaptics_tcm/synaptics_tcm_diagnostics.c | 561 +++ synaptics_tcm/synaptics_tcm_i2c.c | 523 +++ synaptics_tcm/synaptics_tcm_recovery.c | 898 +++++ synaptics_tcm/synaptics_tcm_reflash.c | 2193 ++++++++++++ synaptics_tcm/synaptics_tcm_spi.c | 670 ++++ synaptics_tcm/synaptics_tcm_testing.c | 1938 +++++++++++ synaptics_tcm/synaptics_tcm_testing.h | 85 + synaptics_tcm/synaptics_tcm_touch.c | 1272 +++++++ synaptics_tcm/synaptics_tcm_zeroflash.c | 1012 ++++++ 13 files changed, 14387 insertions(+) create mode 100644 synaptics_tcm/synaptics_tcm.h create mode 100644 synaptics_tcm/synaptics_tcm_core.c create mode 100644 synaptics_tcm/synaptics_tcm_core.h create mode 100644 synaptics_tcm/synaptics_tcm_device.c create mode 100644 synaptics_tcm/synaptics_tcm_diagnostics.c create mode 100644 synaptics_tcm/synaptics_tcm_i2c.c create mode 100644 synaptics_tcm/synaptics_tcm_recovery.c create mode 100644 synaptics_tcm/synaptics_tcm_reflash.c create mode 100644 synaptics_tcm/synaptics_tcm_spi.c create mode 100644 synaptics_tcm/synaptics_tcm_testing.c create mode 100644 synaptics_tcm/synaptics_tcm_testing.h create mode 100644 synaptics_tcm/synaptics_tcm_touch.c create mode 100644 synaptics_tcm/synaptics_tcm_zeroflash.c diff --git a/synaptics_tcm/synaptics_tcm.h b/synaptics_tcm/synaptics_tcm.h new file mode 100644 index 0000000000..1d650b451f --- /dev/null +++ b/synaptics_tcm/synaptics_tcm.h @@ -0,0 +1,65 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2019 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2019 Scott Lin + * + * 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. + */ + +#ifndef _SYNAPTICS_TCM_H_ +#define _SYNAPTICS_TCM_H_ + +#define I2C_MODULE_NAME "synaptics_tcm_i2c" +#define SPI_MODULE_NAME "synaptics_tcm_spi" + +struct syna_tcm_board_data { + bool x_flip; + bool y_flip; + bool swap_axes; + int irq_gpio; + int irq_on_state; + int power_gpio; + int power_on_state; + int reset_gpio; + int reset_on_state; + unsigned int spi_mode; + unsigned int power_delay_ms; + unsigned int reset_delay_ms; + unsigned int reset_active_ms; + unsigned int byte_delay_us; + unsigned int block_delay_us; + unsigned int ubl_i2c_addr; + unsigned int ubl_max_freq; + unsigned int ubl_byte_delay_us; + unsigned long irq_flags; + const char *pwr_reg_name; + const char *bus_reg_name; + const char *fw_name; + bool extend_report; +}; + +#endif diff --git a/synaptics_tcm/synaptics_tcm_core.c b/synaptics_tcm/synaptics_tcm_core.c new file mode 100644 index 0000000000..f095624ddf --- /dev/null +++ b/synaptics_tcm/synaptics_tcm_core.c @@ -0,0 +1,3779 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2019 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2019 Scott Lin + * + * 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. + */ + +#include +#include +#include +#include +#include "synaptics_tcm_core.h" + +/* #define RESET_ON_RESUME */ + +/* #define RESUME_EARLY_UNBLANK */ + +#define RESET_ON_RESUME_DELAY_MS 50 + +#define PREDICTIVE_READING + +#define MIN_READ_LENGTH 9 + +#define KEEP_DRIVER_ON_ERROR + +/* #define FORCE_RUN_APPLICATION_FIRMWARE */ + +#define SYNA_VTG_MIN_UV 2800000 + +#define SYNA_VTG_MAX_UV 3300000 + +#define SYNA_LOAD_MAX_UA 30000 + +#define SYNA_VDD_VTG_MIN_UV 1800000 + +#define SYNA_VDD_VTG_MAX_UV 2000000 + +#define NOTIFIER_PRIORITY 2 + +#define RESPONSE_TIMEOUT_MS 3000 + +#define APP_STATUS_POLL_TIMEOUT_MS 1000 + +#define APP_STATUS_POLL_MS 100 + +#define ENABLE_IRQ_DELAY_MS 20 + +#define FALL_BACK_ON_POLLING + +#define POLLING_DELAY_MS 5 + +#define RUN_WATCHDOG true + +#define WATCHDOG_TRIGGER_COUNT 2 + +#define WATCHDOG_DELAY_MS 50000 + +#define MODE_SWITCH_DELAY_MS 100 + +#define READ_RETRY_US_MIN 5000 + +#define READ_RETRY_US_MAX 10000 + +#define WRITE_DELAY_US_MIN 500 + +#define WRITE_DELAY_US_MAX 1000 + +#define HOST_DOWNLOAD_WAIT_MS 100 + +#define HOST_DOWNLOAD_TIMEOUT_MS 1000 + +#define DYNAMIC_CONFIG_SYSFS_DIR_NAME "dynamic_config" + +#define dynamic_config_sysfs(c_name, id) \ +static ssize_t syna_tcm_sysfs_##c_name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + int retval; \ + unsigned short value; \ + struct device *p_dev; \ + struct kobject *p_kobj; \ + struct syna_tcm_hcd *tcm_hcd; \ +\ + p_kobj = sysfs_dir->parent; \ + p_dev = container_of(p_kobj, struct device, kobj); \ + tcm_hcd = dev_get_drvdata(p_dev); \ +\ + mutex_lock(&tcm_hcd->extif_mutex); \ +\ + retval = tcm_hcd->get_dynamic_config(tcm_hcd, id, &value); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to get dynamic config\n"); \ + goto exit; \ + } \ +\ + retval = snprintf(buf, PAGE_SIZE, "%u\n", value); \ +\ +exit: \ + mutex_unlock(&tcm_hcd->extif_mutex); \ +\ + return retval; \ +} \ +\ +static ssize_t syna_tcm_sysfs_##c_name##_store(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + int retval; \ + unsigned int input; \ + struct device *p_dev; \ + struct kobject *p_kobj; \ + struct syna_tcm_hcd *tcm_hcd; \ +\ + p_kobj = sysfs_dir->parent; \ + p_dev = container_of(p_kobj, struct device, kobj); \ + tcm_hcd = dev_get_drvdata(p_dev); \ +\ + if (kstrtouint(buf, 10, &input)) \ + return -EINVAL; \ +\ + mutex_lock(&tcm_hcd->extif_mutex); \ +\ + retval = tcm_hcd->set_dynamic_config(tcm_hcd, id, input); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to set dynamic config\n"); \ + goto exit; \ + } \ +\ + retval = count; \ +\ +exit: \ + mutex_unlock(&tcm_hcd->extif_mutex); \ +\ + return retval; \ +} + +DECLARE_COMPLETION(response_complete); + +static struct kobject *sysfs_dir; + +static struct syna_tcm_module_pool mod_pool; + +SHOW_PROTOTYPE(syna_tcm, info); +STORE_PROTOTYPE(syna_tcm, irq_en); +STORE_PROTOTYPE(syna_tcm, reset); +STORE_PROTOTYPE(syna_tcm, watchdog); +SHOW_STORE_PROTOTYPE(syna_tcm, no_doze); +SHOW_STORE_PROTOTYPE(syna_tcm, disable_noise_mitigation); +SHOW_STORE_PROTOTYPE(syna_tcm, inhibit_frequency_shift); +SHOW_STORE_PROTOTYPE(syna_tcm, requested_frequency); +SHOW_STORE_PROTOTYPE(syna_tcm, disable_hsync); +SHOW_STORE_PROTOTYPE(syna_tcm, rezero_on_exit_deep_sleep); +SHOW_STORE_PROTOTYPE(syna_tcm, charger_connected); +SHOW_STORE_PROTOTYPE(syna_tcm, no_baseline_relaxation); +SHOW_STORE_PROTOTYPE(syna_tcm, in_wakeup_gesture_mode); +SHOW_STORE_PROTOTYPE(syna_tcm, stimulus_fingers); +SHOW_STORE_PROTOTYPE(syna_tcm, grip_suppression_enabled); +SHOW_STORE_PROTOTYPE(syna_tcm, enable_thick_glove); +SHOW_STORE_PROTOTYPE(syna_tcm, enable_glove); + +static struct device_attribute *attrs[] = { + ATTRIFY(info), + ATTRIFY(irq_en), + ATTRIFY(reset), + ATTRIFY(watchdog), +}; + +static struct device_attribute *dynamic_config_attrs[] = { + ATTRIFY(no_doze), + ATTRIFY(disable_noise_mitigation), + ATTRIFY(inhibit_frequency_shift), + ATTRIFY(requested_frequency), + ATTRIFY(disable_hsync), + ATTRIFY(rezero_on_exit_deep_sleep), + ATTRIFY(charger_connected), + ATTRIFY(no_baseline_relaxation), + ATTRIFY(in_wakeup_gesture_mode), + ATTRIFY(stimulus_fingers), + ATTRIFY(grip_suppression_enabled), + ATTRIFY(enable_thick_glove), + ATTRIFY(enable_glove), +}; + +static ssize_t syna_tcm_sysfs_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int count; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm_hcd *tcm_hcd; + + p_kobj = sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm_hcd = dev_get_drvdata(p_dev); + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = tcm_hcd->identify(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto exit; + } + + count = 0; + + retval = snprintf(buf, PAGE_SIZE - count, + "TouchComm version: %d\n", + tcm_hcd->id_info.version); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + if (SYNAPTICS_TCM_ID_SUBVERSION == 0) { + retval = snprintf(buf, PAGE_SIZE - count, + "Driver version: %d.%d\n", + (unsigned char)(SYNAPTICS_TCM_ID_VERSION >> 8), + (unsigned char)SYNAPTICS_TCM_ID_VERSION); + } else { + retval = snprintf(buf, PAGE_SIZE - count, + "Driver version: %d.%d.%d\n", + (unsigned char)(SYNAPTICS_TCM_ID_VERSION >> 8), + (unsigned char)SYNAPTICS_TCM_ID_VERSION, + SYNAPTICS_TCM_ID_SUBVERSION); + } + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + switch (tcm_hcd->id_info.mode) { + case MODE_APPLICATION: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Application\n"); + if (retval < 0) + goto exit; + break; + case MODE_HOST_DOWNLOAD: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Host Download\n"); + if (retval < 0) + goto exit; + break; + case MODE_BOOTLOADER: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Bootloader\n"); + if (retval < 0) + goto exit; + break; + case MODE_TDDI_BOOTLOADER: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: TDDI Bootloader\n"); + if (retval < 0) + goto exit; + break; + default: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Unknown (%d)\n", + tcm_hcd->id_info.mode); + if (retval < 0) + goto exit; + break; + } + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "Part number: "); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = secure_memcpy(buf, + PAGE_SIZE - count, + tcm_hcd->id_info.part_number, + sizeof(tcm_hcd->id_info.part_number), + sizeof(tcm_hcd->id_info.part_number)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy part number string\n"); + goto exit; + } + buf += sizeof(tcm_hcd->id_info.part_number); + count += sizeof(tcm_hcd->id_info.part_number); + + retval = snprintf(buf, PAGE_SIZE - count, + "\n"); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "Packrat number: %d\n", + tcm_hcd->packrat_number); + if (retval < 0) + goto exit; + + count += retval; + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t syna_tcm_sysfs_irq_en_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm_hcd *tcm_hcd; + + p_kobj = sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm_hcd = dev_get_drvdata(p_dev); + + if (kstrtouint(buf, 10, &input)) + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (input == 0) { + retval = tcm_hcd->enable_irq(tcm_hcd, false, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable interrupt\n"); + goto exit; + } + } else if (input == 1) { + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable interrupt\n"); + goto exit; + } + } else { + retval = -EINVAL; + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t syna_tcm_sysfs_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + bool hw_reset; + unsigned int input; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm_hcd *tcm_hcd; + + p_kobj = sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm_hcd = dev_get_drvdata(p_dev); + + if (kstrtouint(buf, 10, &input)) + return -EINVAL; + + if (input == 1) + hw_reset = false; + else if (input == 2) + hw_reset = true; + else + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = tcm_hcd->reset(tcm_hcd, hw_reset, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t syna_tcm_sysfs_watchdog_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm_hcd *tcm_hcd; + + p_kobj = sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm_hcd = dev_get_drvdata(p_dev); + + if (kstrtouint(buf, 10, &input)) + return -EINVAL; + + if (input != 0 && input != 1) + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + tcm_hcd->watchdog.run = input; + tcm_hcd->update_watchdog(tcm_hcd, input); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return count; +} + +dynamic_config_sysfs(no_doze, DC_NO_DOZE) + +dynamic_config_sysfs(disable_noise_mitigation, DC_DISABLE_NOISE_MITIGATION) + +dynamic_config_sysfs(inhibit_frequency_shift, DC_INHIBIT_FREQUENCY_SHIFT) + +dynamic_config_sysfs(requested_frequency, DC_REQUESTED_FREQUENCY) + +dynamic_config_sysfs(disable_hsync, DC_DISABLE_HSYNC) + +dynamic_config_sysfs(rezero_on_exit_deep_sleep, DC_REZERO_ON_EXIT_DEEP_SLEEP) + +dynamic_config_sysfs(charger_connected, DC_CHARGER_CONNECTED) + +dynamic_config_sysfs(no_baseline_relaxation, DC_NO_BASELINE_RELAXATION) + +dynamic_config_sysfs(in_wakeup_gesture_mode, DC_IN_WAKEUP_GESTURE_MODE) + +dynamic_config_sysfs(stimulus_fingers, DC_STIMULUS_FINGERS) + +dynamic_config_sysfs(grip_suppression_enabled, DC_GRIP_SUPPRESSION_ENABLED) + +dynamic_config_sysfs(enable_thick_glove, DC_ENABLE_THICK_GLOVE) + +dynamic_config_sysfs(enable_glove, DC_ENABLE_GLOVE) + +int syna_tcm_add_module(struct syna_tcm_module_cb *mod_cb, bool insert) +{ + struct syna_tcm_module_handler *mod_handler; + + if (!mod_pool.initialized) { + mutex_init(&mod_pool.mutex); + INIT_LIST_HEAD(&mod_pool.list); + mod_pool.initialized = true; + } + + mutex_lock(&mod_pool.mutex); + + if (insert) { + mod_handler = kzalloc(sizeof(*mod_handler), GFP_KERNEL); + if (!mod_handler) { + mutex_unlock(&mod_pool.mutex); + return -ENOMEM; + } + mod_handler->mod_cb = mod_cb; + mod_handler->insert = true; + mod_handler->detach = false; + list_add_tail(&mod_handler->link, &mod_pool.list); + } else if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (mod_handler->mod_cb->type == mod_cb->type) { + mod_handler->insert = false; + mod_handler->detach = true; + goto exit; + } + } + } + +exit: + mutex_unlock(&mod_pool.mutex); + + if (mod_pool.queue_work) + queue_work(mod_pool.workqueue, &mod_pool.work); + + return 0; +} +EXPORT_SYMBOL(syna_tcm_add_module); + +static void syna_tcm_module_work(struct work_struct *work) +{ + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_module_handler *tmp_handler; + struct syna_tcm_hcd *tcm_hcd = mod_pool.tcm_hcd; + + mutex_lock(&mod_pool.mutex); + mod_pool.reconstructing = true; + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry_safe(mod_handler, + tmp_handler, + &mod_pool.list, + link) { + if (mod_handler->insert) { + if (mod_handler->mod_cb->init) + mod_handler->mod_cb->init(tcm_hcd); + mod_handler->insert = false; + } + if (mod_handler->detach) { + if (mod_handler->mod_cb->remove) + mod_handler->mod_cb->remove(tcm_hcd); + list_del(&mod_handler->link); + kfree(mod_handler); + } + } + } + + mod_pool.reconstructing = false; + mutex_unlock(&mod_pool.mutex); +} + +/** + * syna_tcm_report_notifier() - notify occurrence of report received from device + * + * @data: handle of core module + * + * The occurrence of the report generated by the device is forwarded to the + * asynchronous inbox of each registered application module. + */ +static int syna_tcm_report_notifier(void *data) +{ + struct sched_param param = { .sched_priority = NOTIFIER_PRIORITY }; + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_hcd *tcm_hcd = data; + + sched_setscheduler_nocheck(current, SCHED_RR, ¶m); + + set_current_state(TASK_INTERRUPTIBLE); + + while (!kthread_should_stop()) { + schedule(); + + if (kthread_should_stop()) + break; + + set_current_state(TASK_RUNNING); + + mutex_lock(&mod_pool.mutex); + mod_pool.reconstructing = true; + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->asyncbox)) + mod_handler->mod_cb->asyncbox(tcm_hcd); + } + } + + mod_pool.reconstructing = false; + mutex_unlock(&mod_pool.mutex); + + set_current_state(TASK_INTERRUPTIBLE); + }; + + return 0; +} + +/** + * syna_tcm_dispatch_report() - dispatch report received from device + * + * @tcm_hcd: handle of core module + * + * The report generated by the device is forwarded to the synchronous inbox of + * each registered application module for further processing. In addition, the + * report notifier thread is woken up for asynchronous notification of the + * report occurrence. + */ +static void syna_tcm_dispatch_report(struct syna_tcm_hcd *tcm_hcd) +{ + struct syna_tcm_module_handler *mod_handler; + + LOCK_BUFFER(tcm_hcd->in); + LOCK_BUFFER(tcm_hcd->report.buffer); + + tcm_hcd->report.buffer.buf = &tcm_hcd->in.buf[MESSAGE_HEADER_SIZE]; + + tcm_hcd->report.buffer.buf_size = tcm_hcd->in.buf_size; + tcm_hcd->report.buffer.buf_size -= MESSAGE_HEADER_SIZE; + + tcm_hcd->report.buffer.data_length = tcm_hcd->payload_length; + + tcm_hcd->report.id = tcm_hcd->status_report_code; + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->syncbox)) + mod_handler->mod_cb->syncbox(tcm_hcd); + } + } + + tcm_hcd->async_report_id = tcm_hcd->status_report_code; + + mutex_unlock(&mod_pool.mutex); + + UNLOCK_BUFFER(tcm_hcd->report.buffer); + UNLOCK_BUFFER(tcm_hcd->in); + + wake_up_process(tcm_hcd->notifier_thread); +} + +/** + * syna_tcm_dispatch_response() - dispatch response received from device + * + * @tcm_hcd: handle of core module + * + * The response to a command is forwarded to the sender of the command. + */ +static void syna_tcm_dispatch_response(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (atomic_read(&tcm_hcd->command_status) != CMD_BUSY) + return; + + tcm_hcd->response_code = tcm_hcd->status_report_code; + + if (tcm_hcd->payload_length == 0) { + atomic_set(&tcm_hcd->command_status, CMD_IDLE); + goto exit; + } + + LOCK_BUFFER(tcm_hcd->resp); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->resp, + tcm_hcd->payload_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for tcm_hcd->resp.buf\n"); + UNLOCK_BUFFER(tcm_hcd->resp); + atomic_set(&tcm_hcd->command_status, CMD_ERROR); + goto exit; + } + + LOCK_BUFFER(tcm_hcd->in); + + retval = secure_memcpy(tcm_hcd->resp.buf, + tcm_hcd->resp.buf_size, + &tcm_hcd->in.buf[MESSAGE_HEADER_SIZE], + tcm_hcd->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_hcd->payload_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy payload\n"); + UNLOCK_BUFFER(tcm_hcd->in); + UNLOCK_BUFFER(tcm_hcd->resp); + atomic_set(&tcm_hcd->command_status, CMD_ERROR); + goto exit; + } + + tcm_hcd->resp.data_length = tcm_hcd->payload_length; + + UNLOCK_BUFFER(tcm_hcd->in); + UNLOCK_BUFFER(tcm_hcd->resp); + + atomic_set(&tcm_hcd->command_status, CMD_IDLE); + +exit: + complete(&response_complete); +} + +/** + * syna_tcm_dispatch_message() - dispatch message received from device + * + * @tcm_hcd: handle of core module + * + * The information received in the message read in from the device is dispatched + * to the appropriate destination based on whether the information represents a + * report or a response to a command. + */ +static void syna_tcm_dispatch_message(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *build_id; + unsigned int payload_length; + unsigned int max_write_size; + + if (tcm_hcd->status_report_code == REPORT_IDENTIFY) { + payload_length = tcm_hcd->payload_length; + + LOCK_BUFFER(tcm_hcd->in); + + retval = secure_memcpy((unsigned char *)&tcm_hcd->id_info, + sizeof(tcm_hcd->id_info), + &tcm_hcd->in.buf[MESSAGE_HEADER_SIZE], + tcm_hcd->in.buf_size - MESSAGE_HEADER_SIZE, + MIN(sizeof(tcm_hcd->id_info), payload_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy identification info\n"); + UNLOCK_BUFFER(tcm_hcd->in); + return; + } + + UNLOCK_BUFFER(tcm_hcd->in); + + build_id = tcm_hcd->id_info.build_id; + tcm_hcd->packrat_number = le4_to_uint(build_id); + + max_write_size = le2_to_uint(tcm_hcd->id_info.max_write_size); + tcm_hcd->wr_chunk_size = MIN(max_write_size, WR_CHUNK_SIZE); + if (tcm_hcd->wr_chunk_size == 0) + tcm_hcd->wr_chunk_size = max_write_size; + + LOGD(tcm_hcd->pdev->dev.parent, + "Received identify report (firmware mode = 0x%02x)\n", + tcm_hcd->id_info.mode); + + if (atomic_read(&tcm_hcd->command_status) == CMD_BUSY) { + switch (tcm_hcd->command) { + case CMD_RESET: + case CMD_RUN_BOOTLOADER_FIRMWARE: + case CMD_RUN_APPLICATION_FIRMWARE: + case CMD_ENTER_PRODUCTION_TEST_MODE: + tcm_hcd->response_code = STATUS_OK; + atomic_set(&tcm_hcd->command_status, CMD_IDLE); + complete(&response_complete); + break; + default: + LOGN(tcm_hcd->pdev->dev.parent, + "Device has been reset\n"); + atomic_set(&tcm_hcd->command_status, CMD_ERROR); + complete(&response_complete); + break; + } + } + + if (tcm_hcd->id_info.mode == MODE_HOST_DOWNLOAD) { + tcm_hcd->host_download_mode = true; + return; + } + +#ifdef FORCE_RUN_APPLICATION_FIRMWARE + if (tcm_hcd->id_info.mode != MODE_APPLICATION && + !mutex_is_locked(&tcm_hcd->reset_mutex)) { + if (atomic_read(&tcm_hcd->helper.task) == HELP_NONE) { + atomic_set(&tcm_hcd->helper.task, + HELP_RUN_APPLICATION_FIRMWARE); + queue_work(tcm_hcd->helper.workqueue, + &tcm_hcd->helper.work); + return; + } + } +#endif + } + + if (tcm_hcd->status_report_code >= REPORT_IDENTIFY) { + if ((mod_pool.reconstructing) + && (tcm_hcd->status_report_code == REPORT_TOUCH)) + return; + syna_tcm_dispatch_report(tcm_hcd); + + } else + syna_tcm_dispatch_response(tcm_hcd); + +} + +/** + * syna_tcm_continued_read() - retrieve entire payload from device + * + * @tcm_hcd: handle of core module + * + * Read transactions are carried out until the entire payload is retrieved from + * the device and stored in the handle of the core module. + */ +static int syna_tcm_continued_read(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char marker; + 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; + + total_length = MESSAGE_HEADER_SIZE + tcm_hcd->payload_length + 1; + + remaining_length = total_length - tcm_hcd->read_length; + + LOCK_BUFFER(tcm_hcd->in); + + retval = syna_tcm_realloc_mem(tcm_hcd, + &tcm_hcd->in, + total_length + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reallocate memory for tcm_hcd->in.buf\n"); + UNLOCK_BUFFER(tcm_hcd->in); + return retval; + } + + /** + * available chunk space for payload = total chunk size minus header + * marker byte and header code byte + */ + if (tcm_hcd->rd_chunk_size == 0) + chunk_space = remaining_length; + else + chunk_space = tcm_hcd->rd_chunk_size - 2; + + chunks = ceil_div(remaining_length, chunk_space); + + chunks = chunks == 0 ? 1 : chunks; + + offset = tcm_hcd->read_length; + + LOCK_BUFFER(tcm_hcd->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_hcd->in.buf[offset] = MESSAGE_PADDING; + offset += xfer_length; + remaining_length -= xfer_length; + continue; + } + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->temp, + xfer_length + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for temp.buf\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + return retval; + } + + retval = syna_tcm_read(tcm_hcd, + tcm_hcd->temp.buf, + xfer_length + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read from device\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + return retval; + } + + marker = tcm_hcd->temp.buf[0]; + code = tcm_hcd->temp.buf[1]; + + if (marker != MESSAGE_MARKER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect header marker (0x%02x)\n", + marker); + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + return -EIO; + } + + if (code != STATUS_CONTINUED_READ) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect header code (0x%02x)\n", + code); + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + return -EIO; + } + + retval = secure_memcpy(&tcm_hcd->in.buf[offset], + tcm_hcd->in.buf_size - offset, + &tcm_hcd->temp.buf[2], + tcm_hcd->temp.buf_size - 2, + xfer_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy payload\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + return retval; + } + + offset += xfer_length; + + remaining_length -= xfer_length; + } + + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + + return 0; +} + +/** + * syna_tcm_raw_read() - retrieve specific number of data bytes from device + * + * @tcm_hcd: handle of core module + * @in_buf: buffer for storing data retrieved from device + * @length: number of bytes to retrieve from device + * + * Read transactions are carried out until the specific number of data bytes + * are retrieved from the device and stored in in_buf. + */ +static int syna_tcm_raw_read(struct syna_tcm_hcd *tcm_hcd, + unsigned char *in_buf, unsigned int length) +{ + int retval; + unsigned char code; + unsigned int idx; + unsigned int offset; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int remaining_length; + + if (length < 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid length information\n"); + return -EINVAL; + } + + /* minus header marker byte and header code byte */ + remaining_length = length - 2; + + /** + * available chunk space for data = total chunk size minus header + * marker byte and header code byte + */ + if (tcm_hcd->rd_chunk_size == 0) + chunk_space = remaining_length; + else + chunk_space = tcm_hcd->rd_chunk_size - 2; + + chunks = ceil_div(remaining_length, chunk_space); + + chunks = chunks == 0 ? 1 : chunks; + + offset = 0; + + LOCK_BUFFER(tcm_hcd->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) { + in_buf[offset] = MESSAGE_PADDING; + offset += xfer_length; + remaining_length -= xfer_length; + continue; + } + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->temp, + xfer_length + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for temp.buf\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + return retval; + } + + retval = syna_tcm_read(tcm_hcd, + tcm_hcd->temp.buf, + xfer_length + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read from device\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + return retval; + } + + code = tcm_hcd->temp.buf[1]; + + if (idx == 0) { + retval = secure_memcpy(&in_buf[0], + length, + &tcm_hcd->temp.buf[0], + tcm_hcd->temp.buf_size, + xfer_length + 2); + } else { + if (code != STATUS_CONTINUED_READ) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect header code (0x%02x)\n", + code); + UNLOCK_BUFFER(tcm_hcd->temp); + return -EIO; + } + + retval = secure_memcpy(&in_buf[offset], + length - offset, + &tcm_hcd->temp.buf[2], + tcm_hcd->temp.buf_size - 2, + xfer_length); + } + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy data\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + return retval; + } + + if (idx == 0) + offset += (xfer_length + 2); + else + offset += xfer_length; + + remaining_length -= xfer_length; + } + + UNLOCK_BUFFER(tcm_hcd->temp); + + return 0; +} + +/** + * syna_tcm_raw_write() - write command/data to device without receiving + * response + * + * @tcm_hcd: handle of core module + * @command: command to send to device + * @data: data to send to device + * @length: length of data in bytes + * + * A command and its data, if any, are sent to the device. + */ +static int syna_tcm_raw_write(struct syna_tcm_hcd *tcm_hcd, + unsigned char command, unsigned char *data, unsigned int length) +{ + int retval; + unsigned int idx; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int remaining_length; + + remaining_length = length; + + /** + * available chunk space for data = total chunk size minus command + * byte + */ + if (tcm_hcd->wr_chunk_size == 0) + chunk_space = remaining_length; + else + chunk_space = tcm_hcd->wr_chunk_size - 1; + + chunks = ceil_div(remaining_length, chunk_space); + + chunks = chunks == 0 ? 1 : chunks; + + LOCK_BUFFER(tcm_hcd->out); + + for (idx = 0; idx < chunks; idx++) { + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->out, + xfer_length + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for out.buf\n"); + UNLOCK_BUFFER(tcm_hcd->out); + return retval; + } + + if (idx == 0) + tcm_hcd->out.buf[0] = command; + else + tcm_hcd->out.buf[0] = CMD_CONTINUE_WRITE; + + if (xfer_length) { + retval = secure_memcpy(&tcm_hcd->out.buf[1], + tcm_hcd->out.buf_size - 1, + &data[idx * chunk_space], + remaining_length, + xfer_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy data\n"); + UNLOCK_BUFFER(tcm_hcd->out); + return retval; + } + } + + retval = syna_tcm_write(tcm_hcd, + tcm_hcd->out.buf, + xfer_length + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to device\n"); + UNLOCK_BUFFER(tcm_hcd->out); + return retval; + } + + remaining_length -= xfer_length; + } + + UNLOCK_BUFFER(tcm_hcd->out); + + return 0; +} + +/** + * syna_tcm_read_message() - read message from device + * + * @tcm_hcd: handle of core module + * @in_buf: buffer for storing data in raw read mode + * @length: length of data in bytes in raw read mode + * + * If in_buf is not NULL, raw read mode is used and syna_tcm_raw_read() is + * called. Otherwise, a message including its entire payload is retrieved from + * the device and dispatched to the appropriate destination. + */ +static int syna_tcm_read_message(struct syna_tcm_hcd *tcm_hcd, + unsigned char *in_buf, unsigned int length) +{ + int retval; + bool retry; + unsigned int total_length; + struct syna_tcm_message_header *header; + + mutex_lock(&tcm_hcd->rw_ctrl_mutex); + + if (in_buf != NULL) { + retval = syna_tcm_raw_read(tcm_hcd, in_buf, length); + goto exit; + } + + retry = true; + +retry: + LOCK_BUFFER(tcm_hcd->in); + + retval = syna_tcm_read(tcm_hcd, + tcm_hcd->in.buf, + tcm_hcd->read_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read from device\n"); + UNLOCK_BUFFER(tcm_hcd->in); + if (retry) { + usleep_range(READ_RETRY_US_MIN, READ_RETRY_US_MAX); + retry = false; + goto retry; + } + goto exit; + } + + header = (struct syna_tcm_message_header *)tcm_hcd->in.buf; + + if (header->marker != MESSAGE_MARKER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect header marker (0x%02x)\n", + header->marker); + UNLOCK_BUFFER(tcm_hcd->in); + retval = -ENXIO; + if (retry) { + usleep_range(READ_RETRY_US_MIN, READ_RETRY_US_MAX); + retry = false; + goto retry; + } + goto exit; + } + + tcm_hcd->status_report_code = header->code; + + tcm_hcd->payload_length = le2_to_uint(header->length); + + LOGD(tcm_hcd->pdev->dev.parent, + "Header code = 0x%02x\n", + tcm_hcd->status_report_code); + + LOGD(tcm_hcd->pdev->dev.parent, + "Payload length = %d\n", + tcm_hcd->payload_length); + + if (tcm_hcd->status_report_code <= STATUS_ERROR || + tcm_hcd->status_report_code == STATUS_INVALID) { + switch (tcm_hcd->status_report_code) { + case STATUS_OK: + break; + case STATUS_CONTINUED_READ: + LOGD(tcm_hcd->pdev->dev.parent, + "Out-of-sync continued read\n"); + case STATUS_IDLE: + case STATUS_BUSY: + tcm_hcd->payload_length = 0; + UNLOCK_BUFFER(tcm_hcd->in); + retval = 0; + goto exit; + default: + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect header code (0x%02x)\n", + tcm_hcd->status_report_code); + if (tcm_hcd->status_report_code == STATUS_INVALID) { + if (retry) { + usleep_range(READ_RETRY_US_MIN, + READ_RETRY_US_MAX); + retry = false; + goto retry; + } else { + tcm_hcd->payload_length = 0; + } + } + } + } + + total_length = MESSAGE_HEADER_SIZE + tcm_hcd->payload_length + 1; + +#ifdef PREDICTIVE_READING + if (total_length <= tcm_hcd->read_length) { + goto check_padding; + } else if (total_length - 1 == tcm_hcd->read_length) { + tcm_hcd->in.buf[total_length - 1] = MESSAGE_PADDING; + goto check_padding; + } +#else + if (tcm_hcd->payload_length == 0) { + tcm_hcd->in.buf[total_length - 1] = MESSAGE_PADDING; + goto check_padding; + } +#endif + + UNLOCK_BUFFER(tcm_hcd->in); + + retval = syna_tcm_continued_read(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do continued read\n"); + goto exit; + }; + + LOCK_BUFFER(tcm_hcd->in); + + tcm_hcd->in.buf[0] = MESSAGE_MARKER; + tcm_hcd->in.buf[1] = tcm_hcd->status_report_code; + tcm_hcd->in.buf[2] = (unsigned char)tcm_hcd->payload_length; + tcm_hcd->in.buf[3] = (unsigned char)(tcm_hcd->payload_length >> 8); + +check_padding: + if (tcm_hcd->in.buf[total_length - 1] != MESSAGE_PADDING) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect message padding byte (0x%02x)\n", + tcm_hcd->in.buf[total_length - 1]); + UNLOCK_BUFFER(tcm_hcd->in); + retval = -EIO; + goto exit; + } + + UNLOCK_BUFFER(tcm_hcd->in); + +#ifdef PREDICTIVE_READING + total_length = MAX(total_length, MIN_READ_LENGTH); + tcm_hcd->read_length = MIN(total_length, tcm_hcd->rd_chunk_size); + if (tcm_hcd->rd_chunk_size == 0) + tcm_hcd->read_length = total_length; +#endif + + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + + syna_tcm_dispatch_message(tcm_hcd); + + retval = 0; + + return retval; + +exit: + if (retval < 0) { + if (atomic_read(&tcm_hcd->command_status) == CMD_BUSY) { + atomic_set(&tcm_hcd->command_status, CMD_ERROR); + complete(&response_complete); + } + } + + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + + return retval; +} + +/** + * syna_tcm_write_message() - write message to device and receive response + * + * @tcm_hcd: handle of core module + * @command: command to send to device + * @payload: payload of command + * @length: length of payload in bytes + * @resp_buf: buffer for storing command response + * @resp_buf_size: size of response buffer in bytes + * @resp_length: length of command response in bytes + * @response_code: status code returned in command response + * @polling_delay_ms: delay time after sending command before resuming polling + * + * If resp_buf is NULL, raw write mode is used and syna_tcm_raw_write() is + * called. Otherwise, a command and its payload, if any, are sent to the device + * and the response to the command generated by the device is read in. + */ +static int syna_tcm_write_message(struct syna_tcm_hcd *tcm_hcd, + unsigned char command, unsigned char *payload, + unsigned int length, unsigned char **resp_buf, + unsigned int *resp_buf_size, unsigned int *resp_length, + unsigned char *response_code, unsigned int polling_delay_ms) +{ + int retval; + unsigned int idx; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int remaining_length; + unsigned int command_status; + + if (response_code != NULL) + *response_code = STATUS_INVALID; + + if (!tcm_hcd->do_polling && current->pid == tcm_hcd->isr_pid) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid execution context\n"); + return -EINVAL; + } + + mutex_lock(&tcm_hcd->command_mutex); + + mutex_lock(&tcm_hcd->rw_ctrl_mutex); + + if (resp_buf == NULL) { + retval = syna_tcm_raw_write(tcm_hcd, command, payload, length); + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + goto exit; + } + + if (tcm_hcd->do_polling && polling_delay_ms) { + cancel_delayed_work_sync(&tcm_hcd->polling_work); + flush_workqueue(tcm_hcd->polling_workqueue); + } + + atomic_set(&tcm_hcd->command_status, CMD_BUSY); + + reinit_completion(&response_complete); + + tcm_hcd->command = command; + + LOCK_BUFFER(tcm_hcd->resp); + + tcm_hcd->resp.buf = *resp_buf; + tcm_hcd->resp.buf_size = *resp_buf_size; + tcm_hcd->resp.data_length = 0; + + UNLOCK_BUFFER(tcm_hcd->resp); + + /* adding two length bytes as part of payload */ + remaining_length = length + 2; + + /** + * available chunk space for payload = total chunk size minus command + * byte + */ + if (tcm_hcd->wr_chunk_size == 0) + chunk_space = remaining_length; + else + chunk_space = tcm_hcd->wr_chunk_size - 1; + + chunks = ceil_div(remaining_length, chunk_space); + + chunks = chunks == 0 ? 1 : chunks; + + LOGD(tcm_hcd->pdev->dev.parent, + "Command = 0x%02x\n", + command); + + LOCK_BUFFER(tcm_hcd->out); + + for (idx = 0; idx < chunks; idx++) { + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->out, + xfer_length + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for out.buf\n"); + UNLOCK_BUFFER(tcm_hcd->out); + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + goto exit; + } + + if (idx == 0) { + tcm_hcd->out.buf[0] = command; + tcm_hcd->out.buf[1] = (unsigned char)length; + tcm_hcd->out.buf[2] = (unsigned char)(length >> 8); + + if (xfer_length > 2) { + retval = secure_memcpy(&tcm_hcd->out.buf[3], + tcm_hcd->out.buf_size - 3, + payload, + remaining_length - 2, + xfer_length - 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy payload\n"); + UNLOCK_BUFFER(tcm_hcd->out); + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + goto exit; + } + } + } else { + tcm_hcd->out.buf[0] = CMD_CONTINUE_WRITE; + + retval = secure_memcpy(&tcm_hcd->out.buf[1], + tcm_hcd->out.buf_size - 1, + &payload[idx * chunk_space - 2], + remaining_length, + xfer_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy payload\n"); + UNLOCK_BUFFER(tcm_hcd->out); + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + goto exit; + } + } + + retval = syna_tcm_write(tcm_hcd, + tcm_hcd->out.buf, + xfer_length + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to device\n"); + UNLOCK_BUFFER(tcm_hcd->out); + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + goto exit; + } + + remaining_length -= xfer_length; + + if (chunks > 1) + usleep_range(WRITE_DELAY_US_MIN, WRITE_DELAY_US_MAX); + } + + UNLOCK_BUFFER(tcm_hcd->out); + + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + + if (tcm_hcd->do_polling && polling_delay_ms) { + queue_delayed_work(tcm_hcd->polling_workqueue, + &tcm_hcd->polling_work, + msecs_to_jiffies(polling_delay_ms)); + } + + retval = wait_for_completion_timeout(&response_complete, + msecs_to_jiffies(RESPONSE_TIMEOUT_MS)); + if (retval == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Timed out waiting for response (command 0x%02x)\n", + tcm_hcd->command); + retval = -EIO; + goto exit; + } + + command_status = atomic_read(&tcm_hcd->command_status); + if (command_status != CMD_IDLE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get valid response (command 0x%02x)\n", + tcm_hcd->command); + retval = -EIO; + goto exit; + } + + LOCK_BUFFER(tcm_hcd->resp); + + if (tcm_hcd->response_code != STATUS_OK) { + if (tcm_hcd->resp.data_length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Error code = 0x%02x (command 0x%02x)\n", + tcm_hcd->resp.buf[0], tcm_hcd->command); + } + retval = -EIO; + } else { + retval = 0; + } + + *resp_buf = tcm_hcd->resp.buf; + *resp_buf_size = tcm_hcd->resp.buf_size; + *resp_length = tcm_hcd->resp.data_length; + + if (response_code != NULL) + *response_code = tcm_hcd->response_code; + + UNLOCK_BUFFER(tcm_hcd->resp); + +exit: + tcm_hcd->command = CMD_NONE; + + atomic_set(&tcm_hcd->command_status, CMD_IDLE); + + mutex_unlock(&tcm_hcd->command_mutex); + + return retval; +} + +static int syna_tcm_wait_hdl(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + msleep(HOST_DOWNLOAD_WAIT_MS); + + if (!atomic_read(&tcm_hcd->host_downloading)) + return 0; + + retval = wait_event_interruptible_timeout(tcm_hcd->hdl_wq, + !atomic_read(&tcm_hcd->host_downloading), + msecs_to_jiffies(HOST_DOWNLOAD_TIMEOUT_MS)); + if (retval == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Timed out waiting for completion of host download\n"); + atomic_set(&tcm_hcd->host_downloading, 0); + retval = -EIO; + } else { + retval = 0; + } + + return retval; +} + +static void syna_tcm_check_hdl(struct syna_tcm_hcd *tcm_hcd) +{ + struct syna_tcm_module_handler *mod_handler; + + LOCK_BUFFER(tcm_hcd->report.buffer); + + tcm_hcd->report.buffer.buf = NULL; + tcm_hcd->report.buffer.buf_size = 0; + tcm_hcd->report.buffer.data_length = 0; + tcm_hcd->report.id = REPORT_HDL; + + UNLOCK_BUFFER(tcm_hcd->report.buffer); + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->syncbox)) + mod_handler->mod_cb->syncbox(tcm_hcd); + } + } + + mutex_unlock(&mod_pool.mutex); +} + +static void syna_tcm_update_watchdog(struct syna_tcm_hcd *tcm_hcd, bool en) +{ + cancel_delayed_work_sync(&tcm_hcd->watchdog.work); + flush_workqueue(tcm_hcd->watchdog.workqueue); + + if (!tcm_hcd->watchdog.run) { + tcm_hcd->watchdog.count = 0; + return; + } + + if (en) { + queue_delayed_work(tcm_hcd->watchdog.workqueue, + &tcm_hcd->watchdog.work, + msecs_to_jiffies(WATCHDOG_DELAY_MS)); + } else { + tcm_hcd->watchdog.count = 0; + } +} + +static void syna_tcm_watchdog_work(struct work_struct *work) +{ + int retval; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct syna_tcm_watchdog *watchdog = + container_of(delayed_work, struct syna_tcm_watchdog, + work); + struct syna_tcm_hcd *tcm_hcd = + container_of(watchdog, struct syna_tcm_hcd, watchdog); + + if (mutex_is_locked(&tcm_hcd->rw_ctrl_mutex)) + goto exit; + + mutex_lock(&tcm_hcd->rw_ctrl_mutex); + + retval = syna_tcm_read(tcm_hcd, + &tcm_hcd->marker, + 1); + + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + + if (retval < 0 || tcm_hcd->marker != MESSAGE_MARKER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read from device\n"); + + tcm_hcd->watchdog.count++; + + if (tcm_hcd->watchdog.count >= WATCHDOG_TRIGGER_COUNT) { + retval = tcm_hcd->reset(tcm_hcd, true, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + tcm_hcd->watchdog.count = 0; + } + } + +exit: + queue_delayed_work(tcm_hcd->watchdog.workqueue, + &tcm_hcd->watchdog.work, + msecs_to_jiffies(WATCHDOG_DELAY_MS)); +} + +static void syna_tcm_polling_work(struct work_struct *work) +{ + int retval; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct syna_tcm_hcd *tcm_hcd = + container_of(delayed_work, struct syna_tcm_hcd, + polling_work); + + if (!tcm_hcd->do_polling) + return; + + retval = tcm_hcd->read_message(tcm_hcd, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read message\n"); + if (retval == -ENXIO && tcm_hcd->hw_if->bus_io->type == BUS_SPI) + syna_tcm_check_hdl(tcm_hcd); + } + + if (!(tcm_hcd->in_suspend && retval < 0)) { + queue_delayed_work(tcm_hcd->polling_workqueue, + &tcm_hcd->polling_work, + msecs_to_jiffies(POLLING_DELAY_MS)); + } +} + +static irqreturn_t syna_tcm_isr(int irq, void *data) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = data; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (unlikely(gpio_get_value(bdata->irq_gpio) != bdata->irq_on_state)) + goto exit; + + tcm_hcd->isr_pid = current->pid; + + retval = tcm_hcd->read_message(tcm_hcd, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, "Failed to read message\n"); + if (retval == -ENXIO && + tcm_hcd->hw_if->bus_io->type == BUS_SPI) + syna_tcm_check_hdl(tcm_hcd); + } + +exit: + return IRQ_HANDLED; +} + +static int syna_tcm_enable_irq(struct syna_tcm_hcd *tcm_hcd, bool en, bool ns) +{ + int retval; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + static bool irq_freed = true; + + mutex_lock(&tcm_hcd->irq_en_mutex); + + if (en) { + if (tcm_hcd->irq_enabled) { + LOGD(tcm_hcd->pdev->dev.parent, + "Interrupt already enabled\n"); + retval = 0; + goto exit; + } + + if (bdata->irq_gpio < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid IRQ GPIO\n"); + retval = -EINVAL; + goto queue_polling_work; + } + + if (irq_freed) { + retval = request_threaded_irq(tcm_hcd->irq, NULL, + syna_tcm_isr, bdata->irq_flags, + PLATFORM_DRIVER_NAME, tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create interrupt thread\n"); + } + } else { + enable_irq(tcm_hcd->irq); + retval = 0; + } + +queue_polling_work: + if (retval < 0) { +#ifdef FALL_BACK_ON_POLLING + queue_delayed_work(tcm_hcd->polling_workqueue, + &tcm_hcd->polling_work, + msecs_to_jiffies(POLLING_DELAY_MS)); + tcm_hcd->do_polling = true; + retval = 0; +#endif + } + + if (retval < 0) + goto exit; + else + msleep(ENABLE_IRQ_DELAY_MS); + } else { + if (!tcm_hcd->irq_enabled) { + LOGD(tcm_hcd->pdev->dev.parent, + "Interrupt already disabled\n"); + retval = 0; + goto exit; + } + + if (bdata->irq_gpio >= 0) { + if (ns) { + disable_irq_nosync(tcm_hcd->irq); + } else { + disable_irq(tcm_hcd->irq); + free_irq(tcm_hcd->irq, tcm_hcd); + } + irq_freed = !ns; + } + + if (ns) { + cancel_delayed_work(&tcm_hcd->polling_work); + } else { + cancel_delayed_work_sync(&tcm_hcd->polling_work); + flush_workqueue(tcm_hcd->polling_workqueue); + } + + tcm_hcd->do_polling = false; + } + + retval = 0; + +exit: + if (retval == 0) + tcm_hcd->irq_enabled = en; + + mutex_unlock(&tcm_hcd->irq_en_mutex); + + return retval; +} + +static int syna_tcm_set_gpio(struct syna_tcm_hcd *tcm_hcd, int gpio, + bool config, int dir, int state) +{ + int retval; + char label[16]; + + if (config) { + retval = snprintf(label, 16, "tcm_gpio_%d\n", gpio); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set GPIO label\n"); + return retval; + } + + retval = gpio_request(gpio, label); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to request GPIO %d\n", + gpio); + return retval; + } + + if (dir == 0) + retval = gpio_direction_input(gpio); + else + retval = gpio_direction_output(gpio, state); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set GPIO %d direction\n", + gpio); + return retval; + } + } else { + gpio_free(gpio); + } + + return 0; +} + +static int syna_tcm_config_gpio(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (bdata->irq_gpio >= 0) { + retval = syna_tcm_set_gpio(tcm_hcd, bdata->irq_gpio, + true, 0, 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to configure interrupt GPIO\n"); + goto err_set_gpio_irq; + } + } + + if (bdata->power_gpio >= 0) { + retval = syna_tcm_set_gpio(tcm_hcd, bdata->power_gpio, + true, 1, !bdata->power_on_state); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to configure power GPIO\n"); + goto err_set_gpio_power; + } + } + + if (bdata->reset_gpio >= 0) { + retval = syna_tcm_set_gpio(tcm_hcd, bdata->reset_gpio, + true, 1, !bdata->reset_on_state); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to configure reset GPIO\n"); + goto err_set_gpio_reset; + } + } + + if (bdata->power_gpio >= 0) { + gpio_set_value(bdata->power_gpio, bdata->power_on_state); + msleep(bdata->power_delay_ms); + } + + if (bdata->reset_gpio >= 0) { + gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + gpio_set_value(bdata->reset_gpio, !bdata->reset_on_state); + msleep(bdata->reset_delay_ms); + } + + return 0; + +err_set_gpio_reset: + if (bdata->power_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->power_gpio, false, 0, 0); + +err_set_gpio_power: + if (bdata->irq_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->irq_gpio, false, 0, 0); + +err_set_gpio_irq: + return retval; +} + +static int syna_tcm_enable_regulator(struct syna_tcm_hcd *tcm_hcd, bool en) +{ + int retval; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (!en) { + retval = 0; + goto disable_pwr_reg; + } + + if (tcm_hcd->bus_reg) { + retval = regulator_set_voltage(tcm_hcd->bus_reg, + SYNA_VDD_VTG_MIN_UV, SYNA_VDD_VTG_MAX_UV); + if (retval) { + LOGE(tcm_hcd->pdev->dev.parent, + "set bus regulator voltage failed\n"); + goto exit; + } + + retval = regulator_set_load(tcm_hcd->bus_reg, + SYNA_LOAD_MAX_UA); + if (retval) { + LOGE(tcm_hcd->pdev->dev.parent, + "set bus regulator load failed\n"); + goto exit; + } + + retval = regulator_enable(tcm_hcd->bus_reg); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable bus regulator\n"); + goto exit; + } + } + + if (tcm_hcd->pwr_reg) { + if (regulator_count_voltages(tcm_hcd->pwr_reg) > 0) { + retval = regulator_set_voltage(tcm_hcd->pwr_reg, + SYNA_VTG_MIN_UV, SYNA_VTG_MAX_UV); + if (retval) { + LOGE(tcm_hcd->pdev->dev.parent, + "set power regulator voltage failed\n"); + goto disable_bus_reg; + } + retval = regulator_set_load(tcm_hcd->pwr_reg, + SYNA_LOAD_MAX_UA); + if (retval) { + LOGE(tcm_hcd->pdev->dev.parent, + "set power regulator load failed\n"); + goto disable_bus_reg; + } + } + + retval = regulator_enable(tcm_hcd->pwr_reg); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable power regulator\n"); + goto disable_bus_reg; + } + msleep(bdata->power_delay_ms); + } + + return 0; + +disable_pwr_reg: + if (tcm_hcd->pwr_reg) { + if (regulator_count_voltages(tcm_hcd->pwr_reg) > 0) { + regulator_set_load(tcm_hcd->pwr_reg, 0); + regulator_set_voltage(tcm_hcd->pwr_reg, 0, + SYNA_VTG_MAX_UV); + } + regulator_disable(tcm_hcd->pwr_reg); + } + +disable_bus_reg: + if (tcm_hcd->bus_reg) { + regulator_set_load(tcm_hcd->bus_reg, 0); + regulator_set_voltage(tcm_hcd->bus_reg, 0, + SYNA_VDD_VTG_MAX_UV); + regulator_disable(tcm_hcd->bus_reg); + } + +exit: + return retval; +} + +static int syna_tcm_get_regulator(struct syna_tcm_hcd *tcm_hcd, bool get) +{ + int retval; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (!get) { + retval = 0; + goto regulator_put; + } + + if (bdata->bus_reg_name != NULL && *bdata->bus_reg_name != 0) { + tcm_hcd->bus_reg = regulator_get(tcm_hcd->pdev->dev.parent, + bdata->bus_reg_name); + if (IS_ERR(tcm_hcd->bus_reg)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get bus regulator\n"); + retval = PTR_ERR(tcm_hcd->bus_reg); + goto regulator_put; + } + } + + if (bdata->pwr_reg_name != NULL && *bdata->pwr_reg_name != 0) { + tcm_hcd->pwr_reg = regulator_get(tcm_hcd->pdev->dev.parent, + bdata->pwr_reg_name); + if (IS_ERR(tcm_hcd->pwr_reg)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get power regulator\n"); + retval = PTR_ERR(tcm_hcd->pwr_reg); + goto regulator_put; + } + } + + return 0; + +regulator_put: + if (tcm_hcd->bus_reg) { + regulator_put(tcm_hcd->bus_reg); + tcm_hcd->bus_reg = NULL; + } + + if (tcm_hcd->pwr_reg) { + regulator_put(tcm_hcd->pwr_reg); + tcm_hcd->pwr_reg = NULL; + } + + return retval; +} + +static int syna_tcm_get_app_info(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + unsigned int timeout; + + timeout = APP_STATUS_POLL_TIMEOUT_MS; + + resp_buf = NULL; + resp_buf_size = 0; + +get_app_info: + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_APPLICATION_INFO, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_APPLICATION_INFO)); + goto exit; + } + + retval = secure_memcpy((unsigned char *)&tcm_hcd->app_info, + sizeof(tcm_hcd->app_info), + resp_buf, + resp_buf_size, + MIN(sizeof(tcm_hcd->app_info), resp_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy application info\n"); + goto exit; + } + + tcm_hcd->app_status = le2_to_uint(tcm_hcd->app_info.status); + + if (tcm_hcd->app_status == APP_STATUS_BOOTING || + tcm_hcd->app_status == APP_STATUS_UPDATING) { + if (timeout > 0) { + msleep(APP_STATUS_POLL_MS); + timeout -= APP_STATUS_POLL_MS; + goto get_app_info; + } + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_get_boot_info(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + resp_buf = NULL; + resp_buf_size = 0; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_BOOT_INFO, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_BOOT_INFO)); + goto exit; + } + + retval = secure_memcpy((unsigned char *)&tcm_hcd->boot_info, + sizeof(tcm_hcd->boot_info), + resp_buf, + resp_buf_size, + MIN(sizeof(tcm_hcd->boot_info), resp_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy boot info\n"); + goto exit; + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_identify(struct syna_tcm_hcd *tcm_hcd, bool id) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + unsigned int max_write_size; + + resp_buf = NULL; + resp_buf_size = 0; + + mutex_lock(&tcm_hcd->identify_mutex); + + if (!id) + goto get_info; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_IDENTIFY, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_IDENTIFY)); + goto exit; + } + + retval = secure_memcpy((unsigned char *)&tcm_hcd->id_info, + sizeof(tcm_hcd->id_info), + resp_buf, + resp_buf_size, + MIN(sizeof(tcm_hcd->id_info), resp_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy identification info\n"); + goto exit; + } + + tcm_hcd->packrat_number = le4_to_uint(tcm_hcd->id_info.build_id); + + max_write_size = le2_to_uint(tcm_hcd->id_info.max_write_size); + tcm_hcd->wr_chunk_size = MIN(max_write_size, WR_CHUNK_SIZE); + if (tcm_hcd->wr_chunk_size == 0) + tcm_hcd->wr_chunk_size = max_write_size; + +get_info: + switch (tcm_hcd->id_info.mode) { + case MODE_APPLICATION: + retval = syna_tcm_get_app_info(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get application info\n"); + goto exit; + } + break; + case MODE_BOOTLOADER: + case MODE_TDDI_BOOTLOADER: + retval = syna_tcm_get_boot_info(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get boot info\n"); + goto exit; + } + break; + default: + break; + } + + retval = 0; + +exit: + mutex_unlock(&tcm_hcd->identify_mutex); + + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_run_production_test_firmware(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + bool retry; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + retry = true; + + resp_buf = NULL; + resp_buf_size = 0; + +retry: + retval = tcm_hcd->write_message(tcm_hcd, + CMD_ENTER_PRODUCTION_TEST_MODE, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + MODE_SWITCH_DELAY_MS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_ENTER_PRODUCTION_TEST_MODE)); + goto exit; + } + + if (tcm_hcd->id_info.mode != MODE_PRODUCTION_TEST) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run production test firmware\n"); + if (retry) { + retry = false; + goto retry; + } + retval = -EINVAL; + goto exit; + } else if (tcm_hcd->app_status != APP_STATUS_OK) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application status = 0x%02x\n", + tcm_hcd->app_status); + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_run_application_firmware(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + bool retry; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + retry = true; + + resp_buf = NULL; + resp_buf_size = 0; + +retry: + retval = tcm_hcd->write_message(tcm_hcd, + CMD_RUN_APPLICATION_FIRMWARE, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + MODE_SWITCH_DELAY_MS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_RUN_APPLICATION_FIRMWARE)); + goto exit; + } + + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto exit; + } + + if (tcm_hcd->id_info.mode != MODE_APPLICATION) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application (status = 0x%02x)\n", + tcm_hcd->boot_info.status); + if (retry) { + retry = false; + goto retry; + } + retval = -EINVAL; + goto exit; + } else if (tcm_hcd->app_status != APP_STATUS_OK) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application status = 0x%02x\n", + tcm_hcd->app_status); + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_run_bootloader_firmware(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + resp_buf = NULL; + resp_buf_size = 0; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_RUN_BOOTLOADER_FIRMWARE, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + MODE_SWITCH_DELAY_MS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_RUN_BOOTLOADER_FIRMWARE)); + goto exit; + } + + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto exit; + } + + if (tcm_hcd->id_info.mode == MODE_APPLICATION) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enter bootloader mode\n"); + retval = -EINVAL; + goto exit; + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_switch_mode(struct syna_tcm_hcd *tcm_hcd, + enum firmware_mode mode) +{ + int retval; + + mutex_lock(&tcm_hcd->reset_mutex); + + tcm_hcd->update_watchdog(tcm_hcd, false); + + switch (mode) { + case FW_MODE_BOOTLOADER: + retval = syna_tcm_run_bootloader_firmware(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to switch to bootloader mode\n"); + goto exit; + } + break; + case FW_MODE_APPLICATION: + retval = syna_tcm_run_application_firmware(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to switch to application mode\n"); + goto exit; + } + break; + case FW_MODE_PRODUCTION_TEST: + retval = syna_tcm_run_production_test_firmware(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to switch to production test mode\n"); + goto exit; + } + break; + default: + LOGE(tcm_hcd->pdev->dev.parent, "Invalid firmware mode\n"); + retval = -EINVAL; + goto exit; + } + + retval = 0; + +exit: + tcm_hcd->update_watchdog(tcm_hcd, true); + + mutex_unlock(&tcm_hcd->reset_mutex); + + return retval; +} + +static int syna_tcm_get_dynamic_config(struct syna_tcm_hcd *tcm_hcd, + enum dynamic_config_id id, unsigned short *value) +{ + int retval; + unsigned char out_buf; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + resp_buf = NULL; + resp_buf_size = 0; + + out_buf = (unsigned char)id; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_DYNAMIC_CONFIG, + &out_buf, + sizeof(out_buf), + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_DYNAMIC_CONFIG)); + goto exit; + } + + if (resp_length < 2) { + LOGE(tcm_hcd->pdev->dev.parent, "Invalid data length\n"); + retval = -EINVAL; + goto exit; + } + + *value = (unsigned short)le2_to_uint(resp_buf); + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_set_dynamic_config(struct syna_tcm_hcd *tcm_hcd, + enum dynamic_config_id id, unsigned short value) +{ + int retval; + unsigned char out_buf[3]; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + resp_buf = NULL; + resp_buf_size = 0; + + out_buf[0] = (unsigned char)id; + out_buf[1] = (unsigned char)value; + out_buf[2] = (unsigned char)(value >> 8); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_SET_DYNAMIC_CONFIG, + out_buf, + sizeof(out_buf), + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_SET_DYNAMIC_CONFIG)); + goto exit; + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_get_data_location(struct syna_tcm_hcd *tcm_hcd, + enum flash_area area, unsigned int *addr, unsigned int *length) +{ + int retval; + unsigned char out_buf; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + switch (area) { + case CUSTOM_LCM: + out_buf = LCM_DATA; + break; + case CUSTOM_OEM: + out_buf = OEM_DATA; + break; + case PPDT: + out_buf = PPDT_DATA; + break; + default: + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid flash area\n"); + return -EINVAL; + } + + resp_buf = NULL; + resp_buf_size = 0; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_DATA_LOCATION, + &out_buf, + sizeof(out_buf), + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_DATA_LOCATION)); + goto exit; + } + + if (resp_length != 4) { + LOGE(tcm_hcd->pdev->dev.parent, "Invalid data length\n"); + retval = -EINVAL; + goto exit; + } + + *addr = le2_to_uint(&resp_buf[0]); + *length = le2_to_uint(&resp_buf[2]); + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_sleep(struct syna_tcm_hcd *tcm_hcd, bool en) +{ + int retval; + unsigned char command; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + command = en ? CMD_ENTER_DEEP_SLEEP : CMD_EXIT_DEEP_SLEEP; + + resp_buf = NULL; + resp_buf_size = 0; + + retval = tcm_hcd->write_message(tcm_hcd, + command, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + en ? + STR(CMD_ENTER_DEEP_SLEEP) : + STR(CMD_EXIT_DEEP_SLEEP)); + goto exit; + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_reset(struct syna_tcm_hcd *tcm_hcd, bool hw, bool update_wd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + struct syna_tcm_module_handler *mod_handler; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + resp_buf = NULL; + resp_buf_size = 0; + + mutex_lock(&tcm_hcd->reset_mutex); + + if (update_wd) + tcm_hcd->update_watchdog(tcm_hcd, false); + + if (hw) { + if (bdata->reset_gpio < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Hardware reset unavailable\n"); + retval = -EINVAL; + goto exit; + } + gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + gpio_set_value(bdata->reset_gpio, !bdata->reset_on_state); + } else { + retval = tcm_hcd->write_message(tcm_hcd, + CMD_RESET, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + bdata->reset_delay_ms); + if (retval < 0 && !tcm_hcd->host_download_mode) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_RESET)); + goto exit; + } + } + + if (tcm_hcd->host_download_mode) { + mutex_unlock(&tcm_hcd->reset_mutex); + kfree(resp_buf); + retval = syna_tcm_wait_hdl(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to wait for completion of download\n"); + return retval; + } + if (update_wd) + tcm_hcd->update_watchdog(tcm_hcd, true); + return 0; + } + + msleep(bdata->reset_delay_ms); + + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto exit; + } + + if (tcm_hcd->id_info.mode == MODE_APPLICATION) + goto get_features; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_RUN_APPLICATION_FIRMWARE, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + MODE_SWITCH_DELAY_MS); + if (retval < 0) { + LOGN(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_RUN_APPLICATION_FIRMWARE)); + } + + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto exit; + } + +get_features: + LOGD(tcm_hcd->pdev->dev.parent, + "Firmware mode = 0x%02x\n", + tcm_hcd->id_info.mode); + + if (tcm_hcd->id_info.mode != MODE_APPLICATION) { + LOGN(tcm_hcd->pdev->dev.parent, + "Boot status = 0x%02x\n", + tcm_hcd->boot_info.status); + } else if (tcm_hcd->app_status != APP_STATUS_OK) { + LOGN(tcm_hcd->pdev->dev.parent, + "Application status = 0x%02x\n", + tcm_hcd->app_status); + } + + if (tcm_hcd->id_info.mode != MODE_APPLICATION) + goto dispatch_reset; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_FEATURES, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) + LOGN(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_FEATURES)); + else { + retval = secure_memcpy((unsigned char *)&tcm_hcd->features, + sizeof(tcm_hcd->features), + resp_buf, + resp_buf_size, + MIN(sizeof(tcm_hcd->features), resp_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy feature description\n"); + } + } + +dispatch_reset: + mutex_lock(&mod_pool.mutex); + mod_pool.reconstructing = true; + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->reset)) + mod_handler->mod_cb->reset(tcm_hcd); + } + } + + mod_pool.reconstructing = false; + mutex_unlock(&mod_pool.mutex); + + retval = 0; + +exit: + if (update_wd) + tcm_hcd->update_watchdog(tcm_hcd, true); + + mutex_unlock(&tcm_hcd->reset_mutex); + + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_rezero(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + resp_buf = NULL; + resp_buf_size = 0; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_REZERO, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_REZERO)); + goto exit; + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static void syna_tcm_helper_work(struct work_struct *work) +{ + int retval; + unsigned char task; + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_helper *helper = + container_of(work, struct syna_tcm_helper, work); + struct syna_tcm_hcd *tcm_hcd = + container_of(helper, struct syna_tcm_hcd, helper); + + task = atomic_read(&helper->task); + + switch (task) { + case HELP_RUN_APPLICATION_FIRMWARE: + mutex_lock(&tcm_hcd->reset_mutex); + tcm_hcd->update_watchdog(tcm_hcd, false); + retval = syna_tcm_run_application_firmware(tcm_hcd); + if (retval < 0) + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to switch to application mode\n"); + tcm_hcd->update_watchdog(tcm_hcd, true); + mutex_unlock(&tcm_hcd->reset_mutex); + break; + case HELP_SEND_RESET_NOTIFICATION: + mutex_lock(&tcm_hcd->reset_mutex); + retval = tcm_hcd->identify(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + mutex_unlock(&tcm_hcd->reset_mutex); + break; + } + mutex_lock(&mod_pool.mutex); + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->reset)) + mod_handler->mod_cb->reset(tcm_hcd); + } + } + mutex_unlock(&mod_pool.mutex); + mutex_unlock(&tcm_hcd->reset_mutex); + wake_up_interruptible(&tcm_hcd->hdl_wq); + break; + default: + break; + } + + atomic_set(&helper->task, HELP_NONE); +} + +#if defined(CONFIG_PM) || defined(CONFIG_DRM) || defined(CONFIG_FB) + +static int syna_tcm_deferred_probe(struct device *dev); + +static int syna_tcm_resume(struct device *dev) +{ + int retval; + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_hcd *tcm_hcd = dev_get_drvdata(dev); + + if (!tcm_hcd->init_okay) + syna_tcm_deferred_probe(dev); + + if (!tcm_hcd->in_suspend) + return 0; + else { + if (tcm_hcd->irq_enabled) { + tcm_hcd->watchdog.run = false; + tcm_hcd->update_watchdog(tcm_hcd, false); + tcm_hcd->enable_irq(tcm_hcd, false, false); + } + } + + retval = syna_tcm_enable_regulator(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable regulators\n"); + } + + retval = pinctrl_select_state( + tcm_hcd->ts_pinctrl, + tcm_hcd->pinctrl_state_active); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "%s: Failed to select %s pinstate %d\n", + __func__, PINCTRL_STATE_ACTIVE, retval); + } + + if (tcm_hcd->host_download_mode) { +#ifndef WAKEUP_GESTURE + syna_tcm_check_hdl(tcm_hcd); + retval = syna_tcm_wait_hdl(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to wait for completion of download\n"); + goto exit; + } +#endif + } else { + tcm_hcd->enable_irq(tcm_hcd, true, NULL); +#ifdef RESET_ON_RESUME + msleep(RESET_ON_RESUME_DELAY_MS); + goto do_reset; +#endif + } + + tcm_hcd->update_watchdog(tcm_hcd, true); + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + LOGN(tcm_hcd->pdev->dev.parent, + "Application firmware not running\n"); + goto do_reset; + } + + retval = tcm_hcd->sleep(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to exit deep sleep\n"); + goto exit; + } + + retval = syna_tcm_rezero(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to rezero\n"); + goto exit; + } + + goto mod_resume; + +do_reset: + retval = tcm_hcd->reset(tcm_hcd, false, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + goto exit; + } + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + LOGN(tcm_hcd->pdev->dev.parent, + "Application firmware not running\n"); + retval = 0; + goto exit; + } + +mod_resume: + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->resume)) + mod_handler->mod_cb->resume(tcm_hcd); + } + } + + mutex_unlock(&mod_pool.mutex); + + retval = 0; + +exit: + tcm_hcd->in_suspend = false; + + return retval; +} + +static int syna_tcm_suspend(struct device *dev) +{ + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_hcd *tcm_hcd = dev_get_drvdata(dev); + int retval; + + if (tcm_hcd->in_suspend || !tcm_hcd->init_okay) + return 0; + + if (pinctrl_select_state( + tcm_hcd->ts_pinctrl, + tcm_hcd->pinctrl_state_suspend)) + LOGE(tcm_hcd->pdev->dev.parent, + "%s: Failed to select %s pinstate\n", + __func__, PINCTRL_STATE_RELEASE); + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->suspend)) + mod_handler->mod_cb->suspend(tcm_hcd); + } + } + + retval = syna_tcm_enable_regulator(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable regulators\n"); + } + + mutex_unlock(&mod_pool.mutex); + + tcm_hcd->in_suspend = true; + + return retval; +} +#endif + + + +#ifdef CONFIG_DRM + +static int syna_tcm_early_suspend(struct device *dev) +{ +#ifndef WAKEUP_GESTURE + int retval; +#endif + + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_hcd *tcm_hcd = dev_get_drvdata(dev); + + if (tcm_hcd->in_suspend || !tcm_hcd->init_okay) + return 0; + + if (pinctrl_select_state( + tcm_hcd->ts_pinctrl, + tcm_hcd->pinctrl_state_suspend)) + LOGE(tcm_hcd->pdev->dev.parent, + "%s: Failed to select %s pinstate\n", + __func__, PINCTRL_STATE_RELEASE); + + tcm_hcd->update_watchdog(tcm_hcd, false); + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + LOGN(tcm_hcd->pdev->dev.parent, + "Application firmware not running\n"); + return 0; + } + +#ifndef WAKEUP_GESTURE + retval = tcm_hcd->sleep(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enter deep sleep\n"); + return retval; + } +#endif + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->early_suspend)) + mod_handler->mod_cb->early_suspend(tcm_hcd); + } + } + + mutex_unlock(&mod_pool.mutex); + +#ifndef WAKEUP_GESTURE + tcm_hcd->enable_irq(tcm_hcd, false, true); +#endif + + return 0; +} + +static int syna_tcm_fb_notifier_cb(struct notifier_block *nb, + unsigned long action, void *data) +{ + int retval = 0; + int transition; + struct drm_panel_notifier *evdata = data; + struct syna_tcm_hcd *tcm_hcd = + container_of(nb, struct syna_tcm_hcd, fb_notifier); + + if (!evdata) + return 0; + + transition = *(int *)evdata->data; + + if (atomic_read(&tcm_hcd->firmware_flashing) + && transition == DRM_PANEL_BLANK_POWERDOWN) { + retval = wait_event_interruptible_timeout(tcm_hcd->reflash_wq, + !atomic_read(&tcm_hcd->firmware_flashing), + msecs_to_jiffies(RESPONSE_TIMEOUT_MS)); + if (retval == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Timed out waiting for flashing firmware\n"); + atomic_set(&tcm_hcd->firmware_flashing, 0); + return -EIO; + } + } + + if (action == DRM_PANEL_EARLY_EVENT_BLANK && + transition == DRM_PANEL_BLANK_POWERDOWN) + retval = syna_tcm_early_suspend(&tcm_hcd->pdev->dev); + else if (action == DRM_PANEL_EVENT_BLANK) { + if (transition == DRM_PANEL_BLANK_POWERDOWN) { + retval = syna_tcm_suspend(&tcm_hcd->pdev->dev); + tcm_hcd->fb_ready = 0; + } else if (transition == DRM_PANEL_BLANK_UNBLANK) { +#ifndef RESUME_EARLY_UNBLANK + retval = syna_tcm_resume(&tcm_hcd->pdev->dev); + tcm_hcd->fb_ready++; +#endif + } + } else if (action == DRM_PANEL_EARLY_EVENT_BLANK && + transition == DRM_PANEL_BLANK_UNBLANK) { +#ifdef RESUME_EARLY_UNBLANK + retval = syna_tcm_resume(&tcm_hcd->pdev->dev); + tcm_hcd->fb_ready++; +#endif + } + + + return 0; +} + +#elif CONFIG_FB + +static int syna_tcm_early_suspend(struct device *dev) +{ +#ifndef WAKEUP_GESTURE + int retval; +#endif + + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_hcd *tcm_hcd = dev_get_drvdata(dev); + + if (tcm_hcd->in_suspend) + return 0; + + tcm_hcd->update_watchdog(tcm_hcd, false); + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + LOGN(tcm_hcd->pdev->dev.parent, + "Application firmware not running\n"); + return 0; + } + +#ifndef WAKEUP_GESTURE + retval = tcm_hcd->sleep(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enter deep sleep\n"); + return retval; + } +#endif + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->early_suspend)) + mod_handler->mod_cb->early_suspend(tcm_hcd); + } + } + + mutex_unlock(&mod_pool.mutex); + + return 0; +} + +static int syna_tcm_fb_notifier_cb(struct notifier_block *nb, + unsigned long action, void *data) +{ + int retval = 0; + int *transition; + struct fb_event *evdata = data; + struct syna_tcm_hcd *tcm_hcd = + container_of(nb, struct syna_tcm_hcd, fb_notifier); + + if (!evdata || !evdata->data || !tcm_hcd) + return 0; + + transition = (int *)evdata->data; + + if (atomic_read(&tcm_hcd->firmware_flashing) + && *transition == FB_BLANK_POWERDOWN) { + retval = wait_event_interruptible_timeout(tcm_hcd->reflash_wq, + !atomic_read(&tcm_hcd->firmware_flashing), + msecs_to_jiffies(RESPONSE_TIMEOUT_MS)); + if (retval == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Timed out waiting for flashing firmware\n"); + atomic_set(&tcm_hcd->firmware_flashing, 0); + return -EIO; + } + } + + if (action == FB_EARLY_EVENT_BLANK && + *transition == FB_BLANK_POWERDOWN) + retval = syna_tcm_early_suspend(&tcm_hcd->pdev->dev); + else if (action == FB_EVENT_BLANK) { + if (*transition == FB_BLANK_POWERDOWN) { + retval = syna_tcm_suspend(&tcm_hcd->pdev->dev); + tcm_hcd->fb_ready = 0; + } else if (*transition == FB_BLANK_UNBLANK) { +#ifndef RESUME_EARLY_UNBLANK + retval = syna_tcm_resume(&tcm_hcd->pdev->dev); + tcm_hcd->fb_ready++; +#endif + } + } else if (action == FB_EARLY_EVENT_BLANK && + *transition == FB_BLANK_UNBLANK) { +#ifdef RESUME_EARLY_UNBLANK + retval = syna_tcm_resume(&tcm_hcd->pdev->dev); + tcm_hcd->fb_ready++; +#endif + } + + return 0; +} +#endif + +static int synaptics_tcm_pinctrl_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval = 0; + + /* Get pinctrl if target uses pinctrl */ + tcm_hcd->ts_pinctrl = devm_pinctrl_get((tcm_hcd->pdev->dev.parent)); + if (IS_ERR_OR_NULL(tcm_hcd->ts_pinctrl)) { + retval = PTR_ERR(tcm_hcd->ts_pinctrl); + LOGE(tcm_hcd->pdev->dev.parent, + "Target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; + } + + tcm_hcd->pinctrl_state_active + = pinctrl_lookup_state(tcm_hcd->ts_pinctrl, "pmx_ts_active"); + if (IS_ERR_OR_NULL(tcm_hcd->pinctrl_state_active)) { + retval = PTR_ERR(tcm_hcd->pinctrl_state_active); + LOGE(tcm_hcd->pdev->dev.parent, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + tcm_hcd->pinctrl_state_suspend + = pinctrl_lookup_state(tcm_hcd->ts_pinctrl, "pmx_ts_suspend"); + if (IS_ERR_OR_NULL(tcm_hcd->pinctrl_state_suspend)) { + retval = PTR_ERR(tcm_hcd->pinctrl_state_suspend); + LOGE(tcm_hcd->pdev->dev.parent, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + tcm_hcd->pinctrl_state_release + = pinctrl_lookup_state(tcm_hcd->ts_pinctrl, "pmx_ts_release"); + if (IS_ERR_OR_NULL(tcm_hcd->pinctrl_state_release)) { + retval = PTR_ERR(tcm_hcd->pinctrl_state_release); + LOGE(tcm_hcd->pdev->dev.parent, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); + } + + return retval; + +err_pinctrl_lookup: + devm_pinctrl_put(tcm_hcd->ts_pinctrl); +err_pinctrl_get: + tcm_hcd->ts_pinctrl = NULL; + return retval; +} + +static int syna_tcm_probe(struct platform_device *pdev) +{ + int retval; + int idx; + struct syna_tcm_hcd *tcm_hcd; + const struct syna_tcm_board_data *bdata; + const struct syna_tcm_hw_interface *hw_if; + struct drm_panel *active_panel = tcm_get_panel(); + + hw_if = pdev->dev.platform_data; + if (!hw_if) { + LOGE(&pdev->dev, + "Hardware interface not found\n"); + return -ENODEV; + } + + bdata = hw_if->bdata; + if (!bdata) { + LOGE(&pdev->dev, + "Board data not found\n"); + return -ENODEV; + } + + tcm_hcd = kzalloc(sizeof(*tcm_hcd), GFP_KERNEL); + if (!tcm_hcd) { + LOGE(&pdev->dev, + "Failed to allocate memory for tcm_hcd\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, tcm_hcd); + + tcm_hcd->pdev = pdev; + tcm_hcd->hw_if = hw_if; + tcm_hcd->reset = syna_tcm_reset; + tcm_hcd->sleep = syna_tcm_sleep; + tcm_hcd->identify = syna_tcm_identify; + tcm_hcd->enable_irq = syna_tcm_enable_irq; + tcm_hcd->switch_mode = syna_tcm_switch_mode; + tcm_hcd->read_message = syna_tcm_read_message; + tcm_hcd->write_message = syna_tcm_write_message; + tcm_hcd->get_dynamic_config = syna_tcm_get_dynamic_config; + tcm_hcd->set_dynamic_config = syna_tcm_set_dynamic_config; + tcm_hcd->get_data_location = syna_tcm_get_data_location; + + tcm_hcd->rd_chunk_size = RD_CHUNK_SIZE; + tcm_hcd->wr_chunk_size = WR_CHUNK_SIZE; + +#ifdef PREDICTIVE_READING + tcm_hcd->read_length = MIN_READ_LENGTH; +#else + tcm_hcd->read_length = MESSAGE_HEADER_SIZE; +#endif + + tcm_hcd->watchdog.run = RUN_WATCHDOG; + tcm_hcd->update_watchdog = syna_tcm_update_watchdog; + + if (bdata->irq_gpio >= 0) + tcm_hcd->irq = gpio_to_irq(bdata->irq_gpio); + else + tcm_hcd->irq = bdata->irq_gpio; + + mutex_init(&tcm_hcd->extif_mutex); + mutex_init(&tcm_hcd->reset_mutex); + mutex_init(&tcm_hcd->irq_en_mutex); + mutex_init(&tcm_hcd->io_ctrl_mutex); + mutex_init(&tcm_hcd->rw_ctrl_mutex); + mutex_init(&tcm_hcd->command_mutex); + mutex_init(&tcm_hcd->identify_mutex); + + INIT_BUFFER(tcm_hcd->in, false); + INIT_BUFFER(tcm_hcd->out, false); + INIT_BUFFER(tcm_hcd->resp, true); + INIT_BUFFER(tcm_hcd->temp, false); + INIT_BUFFER(tcm_hcd->config, false); + INIT_BUFFER(tcm_hcd->report.buffer, true); + + LOCK_BUFFER(tcm_hcd->in); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->in, + tcm_hcd->read_length + 1); + if (retval < 0) { + LOGE(&pdev->dev, + "Failed to allocate memory for tcm_hcd->in.buf\n"); + UNLOCK_BUFFER(tcm_hcd->in); + goto err_alloc_mem; + } + + UNLOCK_BUFFER(tcm_hcd->in); + + atomic_set(&tcm_hcd->command_status, CMD_IDLE); + + atomic_set(&tcm_hcd->helper.task, HELP_NONE); + + device_init_wakeup(&pdev->dev, 1); + + init_waitqueue_head(&tcm_hcd->hdl_wq); + + init_waitqueue_head(&tcm_hcd->reflash_wq); + atomic_set(&tcm_hcd->firmware_flashing, 0); + + if (!mod_pool.initialized) { + mutex_init(&mod_pool.mutex); + INIT_LIST_HEAD(&mod_pool.list); + mod_pool.initialized = true; + } + + retval = syna_tcm_get_regulator(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get regulators\n"); + goto err_get_regulator; + } + + retval = syna_tcm_enable_regulator(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable regulators\n"); + goto err_enable_regulator; + } + + retval = syna_tcm_config_gpio(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to configure GPIO's\n"); + goto err_config_gpio; + } + + retval = synaptics_tcm_pinctrl_init(tcm_hcd); + if (!retval && tcm_hcd->ts_pinctrl) { + retval = pinctrl_select_state( + tcm_hcd->ts_pinctrl, + tcm_hcd->pinctrl_state_active); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "%s: Failed to select %s pinstate %d\n", + __func__, PINCTRL_STATE_ACTIVE, retval); + } + } + + sysfs_dir = kobject_create_and_add(PLATFORM_DRIVER_NAME, + &pdev->dev.kobj); + if (!sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dir; + } + + tcm_hcd->sysfs_dir = sysfs_dir; + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + retval = sysfs_create_file(tcm_hcd->sysfs_dir, + &(*attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs file\n"); + goto err_sysfs_create_file; + } + } + + tcm_hcd->dynamnic_config_sysfs_dir = + kobject_create_and_add(DYNAMIC_CONFIG_SYSFS_DIR_NAME, + tcm_hcd->sysfs_dir); + if (!tcm_hcd->dynamnic_config_sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create dynamic config sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dynamic_config_dir; + } + + for (idx = 0; idx < ARRAY_SIZE(dynamic_config_attrs); idx++) { + retval = sysfs_create_file(tcm_hcd->dynamnic_config_sysfs_dir, + &(*dynamic_config_attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create dynamic config sysfs file\n"); + goto err_sysfs_create_dynamic_config_file; + } + } + +#ifdef CONFIG_DRM + tcm_hcd->fb_notifier.notifier_call = syna_tcm_fb_notifier_cb; + if (active_panel) { + retval = drm_panel_notifier_register(active_panel, + &tcm_hcd->fb_notifier); + if (retval < 0) { + dev_err(&pdev->dev, + "%s: Failed to register fb notifier client\n", + __func__); + goto err_drm_reg; + } + } + +#elif CONFIG_FB + tcm_hcd->fb_notifier.notifier_call = syna_tcm_fb_notifier_cb; + retval = fb_register_client(&tcm_hcd->fb_notifier); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to register FB notifier client\n"); + } +#endif + + tcm_hcd->notifier_thread = kthread_run(syna_tcm_report_notifier, + tcm_hcd, "syna_tcm_report_notifier"); + if (IS_ERR(tcm_hcd->notifier_thread)) { + retval = PTR_ERR(tcm_hcd->notifier_thread); + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create and run tcm_hcd->notifier_thread\n"); + goto err_create_run_kthread; + } + + tcm_hcd->helper.workqueue = + create_singlethread_workqueue("syna_tcm_helper"); + INIT_WORK(&tcm_hcd->helper.work, syna_tcm_helper_work); + + tcm_hcd->watchdog.workqueue = + create_singlethread_workqueue("syna_tcm_watchdog"); + INIT_DELAYED_WORK(&tcm_hcd->watchdog.work, syna_tcm_watchdog_work); + + tcm_hcd->polling_workqueue = + create_singlethread_workqueue("syna_tcm_polling"); + INIT_DELAYED_WORK(&tcm_hcd->polling_work, syna_tcm_polling_work); + + mod_pool.workqueue = + create_singlethread_workqueue("syna_tcm_module"); + INIT_WORK(&mod_pool.work, syna_tcm_module_work); + mod_pool.tcm_hcd = tcm_hcd; + mod_pool.queue_work = true; + mod_pool.reconstructing = false; + + return 0; + + +err_create_run_kthread: +#ifdef CONFIG_DRM + if (active_panel) + drm_panel_notifier_unregister(active_panel, + &tcm_hcd->fb_notifier); +#elif CONFIG_FB + fb_unregister_client(&tcm_hcd->fb_notifier); +#endif + +err_sysfs_create_dynamic_config_file: + for (idx--; idx >= 0; idx--) { + sysfs_remove_file(tcm_hcd->dynamnic_config_sysfs_dir, + &(*dynamic_config_attrs[idx]).attr); + } + + kobject_put(tcm_hcd->dynamnic_config_sysfs_dir); + + idx = ARRAY_SIZE(attrs); + +err_sysfs_create_dynamic_config_dir: +err_sysfs_create_file: + for (idx--; idx >= 0; idx--) + sysfs_remove_file(tcm_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(tcm_hcd->sysfs_dir); + +err_sysfs_create_dir: + if (bdata->irq_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->irq_gpio, false, 0, 0); + + if (bdata->power_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->power_gpio, false, 0, 0); + + if (bdata->reset_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->reset_gpio, false, 0, 0); + +err_config_gpio: + syna_tcm_enable_regulator(tcm_hcd, false); + +err_enable_regulator: + syna_tcm_get_regulator(tcm_hcd, false); + +err_get_regulator: + device_init_wakeup(&pdev->dev, 0); + +err_alloc_mem: + RELEASE_BUFFER(tcm_hcd->report.buffer); + RELEASE_BUFFER(tcm_hcd->config); + RELEASE_BUFFER(tcm_hcd->temp); + RELEASE_BUFFER(tcm_hcd->resp); + RELEASE_BUFFER(tcm_hcd->out); + RELEASE_BUFFER(tcm_hcd->in); + +err_drm_reg: + kfree(tcm_hcd); + + return retval; +} + +static int syna_tcm_deferred_probe(struct device *dev) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = dev_get_drvdata(dev); + + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable interrupt\n"); + goto err_enable_irq; + } + retval = tcm_hcd->reset(tcm_hcd, false, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + tcm_hcd->init_okay = false; + tcm_hcd->watchdog.run = false; + tcm_hcd->update_watchdog(tcm_hcd, false); + tcm_hcd->enable_irq(tcm_hcd, false, false); +#ifndef KEEP_DRIVER_ON_ERROR + goto err_reset; +#endif + } else { + tcm_hcd->init_okay = true; + tcm_hcd->update_watchdog(tcm_hcd, true); + } + + queue_work(mod_pool.workqueue, &mod_pool.work); + + return 0; +#ifndef KEEP_DRIVER_ON_ERROR +err_reset: +#endif +err_enable_irq: + + return retval; +} + + +static int syna_tcm_remove(struct platform_device *pdev) +{ + int idx; + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_module_handler *tmp_handler; + struct syna_tcm_hcd *tcm_hcd = platform_get_drvdata(pdev); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + struct drm_panel *active_panel = tcm_get_panel(); + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry_safe(mod_handler, tmp_handler, + &mod_pool.list, link) { + if (mod_handler->mod_cb->remove) + mod_handler->mod_cb->remove(tcm_hcd); + list_del(&mod_handler->link); + kfree(mod_handler); + } + } + + mod_pool.queue_work = false; + cancel_work_sync(&mod_pool.work); + flush_workqueue(mod_pool.workqueue); + destroy_workqueue(mod_pool.workqueue); + + mutex_unlock(&mod_pool.mutex); + + if (tcm_hcd->irq_enabled && bdata->irq_gpio >= 0) { + disable_irq(tcm_hcd->irq); + free_irq(tcm_hcd->irq, tcm_hcd); + } + + cancel_delayed_work_sync(&tcm_hcd->polling_work); + flush_workqueue(tcm_hcd->polling_workqueue); + destroy_workqueue(tcm_hcd->polling_workqueue); + + cancel_delayed_work_sync(&tcm_hcd->watchdog.work); + flush_workqueue(tcm_hcd->watchdog.workqueue); + destroy_workqueue(tcm_hcd->watchdog.workqueue); + + cancel_work_sync(&tcm_hcd->helper.work); + flush_workqueue(tcm_hcd->helper.workqueue); + destroy_workqueue(tcm_hcd->helper.workqueue); + + kthread_stop(tcm_hcd->notifier_thread); + +#ifdef CONFIG_DRM + if (active_panel) + drm_panel_notifier_unregister(active_panel, + &tcm_hcd->fb_notifier); +#elif CONFIG_FB + fb_unregister_client(&tcm_hcd->fb_notifier); +#endif + + for (idx = 0; idx < ARRAY_SIZE(dynamic_config_attrs); idx++) { + sysfs_remove_file(tcm_hcd->dynamnic_config_sysfs_dir, + &(*dynamic_config_attrs[idx]).attr); + } + + kobject_put(tcm_hcd->dynamnic_config_sysfs_dir); + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) + sysfs_remove_file(tcm_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(tcm_hcd->sysfs_dir); + + if (bdata->irq_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->irq_gpio, false, 0, 0); + + if (bdata->power_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->power_gpio, false, 0, 0); + + if (bdata->reset_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->reset_gpio, false, 0, 0); + + syna_tcm_enable_regulator(tcm_hcd, false); + + syna_tcm_get_regulator(tcm_hcd, false); + + device_init_wakeup(&pdev->dev, 0); + + RELEASE_BUFFER(tcm_hcd->report.buffer); + RELEASE_BUFFER(tcm_hcd->config); + RELEASE_BUFFER(tcm_hcd->temp); + RELEASE_BUFFER(tcm_hcd->resp); + RELEASE_BUFFER(tcm_hcd->out); + RELEASE_BUFFER(tcm_hcd->in); + + kfree(tcm_hcd); + + return 0; +} + +#ifdef CONFIG_PM +static const struct dev_pm_ops syna_tcm_dev_pm_ops = { +#if !defined(CONFIG_DRM) && !defined(CONFIG_FB) + .suspend = syna_tcm_suspend, + .resume = syna_tcm_resume, +#endif +}; +#endif + +static struct platform_driver syna_tcm_driver = { + .driver = { + .name = PLATFORM_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &syna_tcm_dev_pm_ops, +#endif + }, + .probe = syna_tcm_probe, + .remove = syna_tcm_remove, +}; + +static int __init syna_tcm_module_init(void) +{ + int retval; + + retval = syna_tcm_bus_init(); + if (retval < 0) + return retval; + + return platform_driver_register(&syna_tcm_driver); +} + +static void __exit syna_tcm_module_exit(void) +{ + platform_driver_unregister(&syna_tcm_driver); + + syna_tcm_bus_exit(); +} + +late_initcall(syna_tcm_module_init); +module_exit(syna_tcm_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Touch Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_tcm/synaptics_tcm_core.h b/synaptics_tcm/synaptics_tcm_core.h new file mode 100644 index 0000000000..62f36bdd55 --- /dev/null +++ b/synaptics_tcm/synaptics_tcm_core.h @@ -0,0 +1,684 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2019 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2019 Scott Lin + * + * 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. + */ + +#ifndef _SYNAPTICS_TCM_CORE_H_ +#define _SYNAPTICS_TCM_CORE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_DRM +#include +#elif CONFIG_FB +#include +#include +#endif +#include + +#define SYNAPTICS_TCM_ID_PRODUCT (1 << 0) +#define SYNAPTICS_TCM_ID_VERSION 0x0101 +#define SYNAPTICS_TCM_ID_SUBVERSION 0 + +#define PLATFORM_DRIVER_NAME "synaptics_tcm" + +#define TOUCH_INPUT_NAME "synaptics_tcm_touch" +#define TOUCH_INPUT_PHYS_PATH "synaptics_tcm/touch_input" + +/* #define WAKEUP_GESTURE */ + +#define RD_CHUNK_SIZE 0 /* read length limit in bytes, 0 = unlimited */ +#define WR_CHUNK_SIZE 0 /* write length limit in bytes, 0 = unlimited */ + +#define MESSAGE_HEADER_SIZE 4 +#define MESSAGE_MARKER 0xa5 +#define MESSAGE_PADDING 0x5a + +#define LOGx(func, dev, log, ...) \ + func(dev, "%s: " log, __func__, ##__VA_ARGS__) + +#define LOGy(func, dev, log, ...) \ + func(dev, "%s (line %d): " log, __func__, __LINE__, ##__VA_ARGS__) + +#define LOGD(dev, log, ...) LOGx(dev_dbg, dev, log, ##__VA_ARGS__) +#define LOGI(dev, log, ...) LOGx(dev_info, dev, log, ##__VA_ARGS__) +#define LOGN(dev, log, ...) LOGx(dev_notice, dev, log, ##__VA_ARGS__) +#define LOGW(dev, log, ...) LOGy(dev_warn, dev, log, ##__VA_ARGS__) +#define LOGE(dev, log, ...) LOGy(dev_err, dev, log, ##__VA_ARGS__) + +#define INIT_BUFFER(buffer, is_clone) \ + mutex_init(&buffer.buf_mutex); \ + buffer.clone = is_clone + +#define LOCK_BUFFER(buffer) \ + mutex_lock(&buffer.buf_mutex) + +#define UNLOCK_BUFFER(buffer) \ + mutex_unlock(&buffer.buf_mutex) + +#define RELEASE_BUFFER(buffer) \ + do { \ + if (buffer.clone == false) { \ + kfree(buffer.buf); \ + buffer.buf_size = 0; \ + buffer.data_length = 0; \ + } \ + } while (0) + +#define MAX(a, b) \ + ({__typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; }) + +#define MIN(a, b) \ + ({__typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a < _b ? _a : _b; }) + +#define STR(x) #x + +#define CONCAT(a, b) a##b + +#define SHOW_PROTOTYPE(m_name, a_name) \ +static ssize_t CONCAT(m_name##_sysfs, _##a_name##_show)(struct device *dev, \ + struct device_attribute *attr, char *buf); \ +\ +static struct device_attribute dev_attr_##a_name = \ + __ATTR(a_name, 0444, \ + CONCAT(m_name##_sysfs, _##a_name##_show), \ + syna_tcm_store_error) + +#define STORE_PROTOTYPE(m_name, a_name) \ +static ssize_t CONCAT(m_name##_sysfs, _##a_name##_store)(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count); \ +\ +static struct device_attribute dev_attr_##a_name = \ + __ATTR(a_name, 0220, \ + syna_tcm_show_error, \ + CONCAT(m_name##_sysfs, _##a_name##_store)) + +#define SHOW_STORE_PROTOTYPE(m_name, a_name) \ +static ssize_t CONCAT(m_name##_sysfs, _##a_name##_show)(struct device *dev, \ + struct device_attribute *attr, char *buf); \ +\ +static ssize_t CONCAT(m_name##_sysfs, _##a_name##_store)(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count); \ +\ +static struct device_attribute dev_attr_##a_name = \ + __ATTR(a_name, 0664, \ + CONCAT(m_name##_sysfs, _##a_name##_show), \ + CONCAT(m_name##_sysfs, _##a_name##_store)) + +#define ATTRIFY(a_name) (&dev_attr_##a_name) + +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" + +enum module_type { + TCM_TOUCH = 0, + TCM_DEVICE = 1, + TCM_TESTING = 2, + TCM_REFLASH = 3, + TCM_RECOVERY = 4, + TCM_ZEROFLASH = 5, + TCM_DIAGNOSTICS = 6, + TCM_LAST, +}; + +enum boot_mode { + MODE_APPLICATION = 0x01, + MODE_HOST_DOWNLOAD = 0x02, + MODE_BOOTLOADER = 0x0b, + MODE_TDDI_BOOTLOADER = 0x0c, + MODE_PRODUCTION_TEST = 0x0e, +}; + +enum boot_status { + BOOT_STATUS_OK = 0x00, + BOOT_STATUS_BOOTING = 0x01, + BOOT_STATUS_APP_BAD_DISPLAY_CRC = 0xfc, + BOOT_STATUS_BAD_DISPLAY_CONFIG = 0xfd, + BOOT_STATUS_BAD_APP_FIRMWARE = 0xfe, + BOOT_STATUS_WARM_BOOT = 0xff, +}; + +enum app_status { + APP_STATUS_OK = 0x00, + APP_STATUS_BOOTING = 0x01, + APP_STATUS_UPDATING = 0x02, + APP_STATUS_BAD_APP_CONFIG = 0xff, +}; + +enum firmware_mode { + FW_MODE_BOOTLOADER = 0, + FW_MODE_APPLICATION = 1, + FW_MODE_PRODUCTION_TEST = 2, +}; + +enum dynamic_config_id { + DC_UNKNOWN = 0x00, + DC_NO_DOZE, + DC_DISABLE_NOISE_MITIGATION, + DC_INHIBIT_FREQUENCY_SHIFT, + DC_REQUESTED_FREQUENCY, + DC_DISABLE_HSYNC, + DC_REZERO_ON_EXIT_DEEP_SLEEP, + DC_CHARGER_CONNECTED, + DC_NO_BASELINE_RELAXATION, + DC_IN_WAKEUP_GESTURE_MODE, + DC_STIMULUS_FINGERS, + DC_GRIP_SUPPRESSION_ENABLED, + DC_ENABLE_THICK_GLOVE, + DC_ENABLE_GLOVE, +}; + +enum command { + CMD_NONE = 0x00, + CMD_CONTINUE_WRITE = 0x01, + CMD_IDENTIFY = 0x02, + CMD_RESET = 0x04, + CMD_ENABLE_REPORT = 0x05, + CMD_DISABLE_REPORT = 0x06, + 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, +}; + +enum status_code { + STATUS_IDLE = 0x00, + STATUS_OK = 0x01, + STATUS_BUSY = 0x02, + STATUS_CONTINUED_READ = 0x03, + STATUS_NOT_EXECUTED_IN_DEEP_SLEEP = 0x0b, + STATUS_RECEIVE_BUFFER_OVERFLOW = 0x0c, + STATUS_PREVIOUS_COMMAND_PENDING = 0x0d, + STATUS_NOT_IMPLEMENTED = 0x0e, + STATUS_ERROR = 0x0f, + STATUS_INVALID = 0xff, +}; + +enum report_type { + REPORT_IDENTIFY = 0x10, + REPORT_TOUCH = 0x11, + REPORT_DELTA = 0x12, + REPORT_RAW = 0x13, + REPORT_STATUS = 0x1b, + REPORT_PRINTF = 0x82, + REPORT_HDL = 0xfe, +}; + +enum command_status { + CMD_IDLE = 0, + CMD_BUSY = 1, + CMD_ERROR = -1, +}; + +enum flash_area { + BOOTLOADER = 0, + BOOT_CONFIG, + APP_FIRMWARE, + APP_CONFIG, + DISP_CONFIG, + CUSTOM_OTP, + CUSTOM_LCM, + CUSTOM_OEM, + PPDT, +}; + +enum flash_data { + LCM_DATA = 1, + OEM_DATA, + PPDT_DATA, +}; + +enum helper_task { + HELP_NONE = 0, + HELP_RUN_APPLICATION_FIRMWARE, + HELP_SEND_RESET_NOTIFICATION, +}; + +struct syna_tcm_helper { + atomic_t task; + struct work_struct work; + struct workqueue_struct *workqueue; +}; + +struct syna_tcm_watchdog { + bool run; + unsigned char count; + struct delayed_work work; + struct workqueue_struct *workqueue; +}; + +struct syna_tcm_buffer { + bool clone; + unsigned char *buf; + unsigned int buf_size; + unsigned int data_length; + struct mutex buf_mutex; +}; + +struct syna_tcm_report { + unsigned char id; + struct syna_tcm_buffer buffer; +}; + +struct syna_tcm_identification { + unsigned char version; + unsigned char mode; + unsigned char part_number[16]; + unsigned char build_id[4]; + unsigned char max_write_size[2]; +}; + +struct syna_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]; + 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]; +}; + +struct syna_tcm_app_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[16]; + 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]; +}; + +struct syna_tcm_touch_info { + unsigned char image_2d_scale_factor[4]; + unsigned char image_0d_scale_factor[4]; + unsigned char hybrid_x_scale_factor[4]; + unsigned char hybrid_y_scale_factor[4]; +}; + +struct syna_tcm_message_header { + unsigned char marker; + unsigned char code; + unsigned char length[2]; +}; + +struct syna_tcm_features { + unsigned char byte_0_reserved; + unsigned char byte_1_reserved; + unsigned char dual_firmware:1; + unsigned char byte_2_reserved:7; +} __packed; + +struct syna_tcm_hcd { + pid_t isr_pid; + atomic_t command_status; + atomic_t host_downloading; + atomic_t firmware_flashing; + wait_queue_head_t hdl_wq; + wait_queue_head_t reflash_wq; + int irq; + bool init_okay; + bool do_polling; + bool in_suspend; + bool irq_enabled; + bool host_download_mode; + unsigned char marker; + unsigned char fb_ready; + unsigned char command; + unsigned char async_report_id; + unsigned char status_report_code; + unsigned char response_code; + unsigned int read_length; + unsigned int payload_length; + unsigned int packrat_number; + unsigned int rd_chunk_size; + unsigned int wr_chunk_size; + unsigned int app_status; + struct platform_device *pdev; + struct regulator *pwr_reg; + struct regulator *bus_reg; + struct kobject *sysfs_dir; + struct kobject *dynamnic_config_sysfs_dir; + struct mutex extif_mutex; + struct mutex reset_mutex; + struct mutex irq_en_mutex; + struct mutex io_ctrl_mutex; + struct mutex rw_ctrl_mutex; + struct mutex command_mutex; + struct mutex identify_mutex; + struct delayed_work polling_work; + struct workqueue_struct *polling_workqueue; + struct task_struct *notifier_thread; + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; +#if defined(CONFIG_DRM) || defined(CONFIG_FB) + struct notifier_block fb_notifier; +#endif + struct syna_tcm_buffer in; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_buffer temp; + struct syna_tcm_buffer config; + struct syna_tcm_report report; + struct syna_tcm_app_info app_info; + struct syna_tcm_boot_info boot_info; + struct syna_tcm_touch_info touch_info; + struct syna_tcm_identification id_info; + struct syna_tcm_helper helper; + struct syna_tcm_watchdog watchdog; + struct syna_tcm_features features; + const struct syna_tcm_hw_interface *hw_if; + int (*reset)(struct syna_tcm_hcd *tcm_hcd, bool hw, bool update_wd); + int (*sleep)(struct syna_tcm_hcd *tcm_hcd, bool en); + int (*identify)(struct syna_tcm_hcd *tcm_hcd, bool id); + int (*enable_irq)(struct syna_tcm_hcd *tcm_hcd, bool en, bool ns); + int (*switch_mode)(struct syna_tcm_hcd *tcm_hcd, + enum firmware_mode mode); + int (*read_message)(struct syna_tcm_hcd *tcm_hcd, + unsigned char *in_buf, unsigned int length); + int (*write_message)(struct syna_tcm_hcd *tcm_hcd, + unsigned char command, unsigned char *payload, + unsigned int length, unsigned char **resp_buf, + unsigned int *resp_buf_size, unsigned int *resp_length, + unsigned char *response_code, + unsigned int polling_delay_ms); + int (*get_dynamic_config)(struct syna_tcm_hcd *tcm_hcd, + enum dynamic_config_id id, unsigned short *value); + int (*set_dynamic_config)(struct syna_tcm_hcd *tcm_hcd, + enum dynamic_config_id id, unsigned short value); + int (*get_data_location)(struct syna_tcm_hcd *tcm_hcd, + enum flash_area area, unsigned int *addr, + unsigned int *length); + int (*read_flash_data)(enum flash_area area, bool run_app_firmware, + struct syna_tcm_buffer *output); + void (*report_touch)(void); + void (*update_watchdog)(struct syna_tcm_hcd *tcm_hcd, bool en); +}; + +struct syna_tcm_module_cb { + enum module_type type; + int (*init)(struct syna_tcm_hcd *tcm_hcd); + int (*remove)(struct syna_tcm_hcd *tcm_hcd); + int (*syncbox)(struct syna_tcm_hcd *tcm_hcd); + int (*asyncbox)(struct syna_tcm_hcd *tcm_hcd); + int (*reset)(struct syna_tcm_hcd *tcm_hcd); + int (*suspend)(struct syna_tcm_hcd *tcm_hcd); + int (*resume)(struct syna_tcm_hcd *tcm_hcd); + int (*early_suspend)(struct syna_tcm_hcd *tcm_hcd); +}; + +struct syna_tcm_module_handler { + bool insert; + bool detach; + struct list_head link; + struct syna_tcm_module_cb *mod_cb; +}; + +struct syna_tcm_module_pool { + bool initialized; + bool queue_work; + bool reconstructing; + struct mutex mutex; + struct list_head list; + struct work_struct work; + struct workqueue_struct *workqueue; + struct syna_tcm_hcd *tcm_hcd; +}; + +struct syna_tcm_bus_io { + unsigned char type; + int (*rmi_read)(struct syna_tcm_hcd *tcm_hcd, unsigned short addr, + unsigned char *data, unsigned int length); + int (*rmi_write)(struct syna_tcm_hcd *tcm_hcd, unsigned short addr, + unsigned char *data, unsigned int length); + int (*read)(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length); + int (*write)(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length); +}; + +struct syna_tcm_hw_interface { + struct syna_tcm_board_data *bdata; + const struct syna_tcm_bus_io *bus_io; +}; + +struct drm_panel *tcm_get_panel(void); + +int syna_tcm_bus_init(void); + +void syna_tcm_bus_exit(void); + +int syna_tcm_bus_init_spi(void); + +void syna_tcm_bus_exit_spi(void); + +int syna_tcm_add_module(struct syna_tcm_module_cb *mod_cb, bool insert); + +static inline int syna_tcm_rmi_read(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + return tcm_hcd->hw_if->bus_io->rmi_read(tcm_hcd, addr, data, length); +} + +static inline int syna_tcm_rmi_write(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + return tcm_hcd->hw_if->bus_io->rmi_write(tcm_hcd, addr, data, length); +} + +static inline int syna_tcm_read(struct syna_tcm_hcd *tcm_hcd, + unsigned char *data, unsigned int length) +{ + return tcm_hcd->hw_if->bus_io->read(tcm_hcd, data, length); +} + +static inline int syna_tcm_write(struct syna_tcm_hcd *tcm_hcd, + unsigned char *data, unsigned int length) +{ + return tcm_hcd->hw_if->bus_io->write(tcm_hcd, data, length); +} + +static inline ssize_t syna_tcm_show_error(struct device *dev, + struct device_attribute *attr, char *buf) +{ + pr_err("%s: Attribute not readable\n", + __func__); + + return -EPERM; +} + +static inline ssize_t syna_tcm_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + pr_err("%s: Attribute not writable\n", + __func__); + + return -EPERM; +} + +static inline int secure_memcpy(unsigned char *dest, unsigned int dest_size, + const unsigned char *src, unsigned int src_size, + unsigned int count) +{ + if (dest == NULL || src == NULL) + return -EINVAL; + + if (count > dest_size || count > src_size) { + pr_err("%s: src_size = %d, dest_size = %d, count = %d\n", + __func__, src_size, dest_size, count); + return -EINVAL; + } + + memcpy((void *)dest, (const void *)src, count); + + return 0; +} + +static inline int syna_tcm_realloc_mem(struct syna_tcm_hcd *tcm_hcd, + struct syna_tcm_buffer *buffer, unsigned int size) +{ + int retval; + unsigned char *temp; + + if (size > buffer->buf_size) { + temp = buffer->buf; + + buffer->buf = kmalloc(size, GFP_KERNEL); + if (!(buffer->buf)) { + dev_err(tcm_hcd->pdev->dev.parent, + "%s: Failed to allocate memory\n", + __func__); + kfree(temp); + buffer->buf_size = 0; + return -ENOMEM; + } + + retval = secure_memcpy(buffer->buf, + size, + temp, + buffer->buf_size, + buffer->buf_size); + if (retval < 0) { + dev_err(tcm_hcd->pdev->dev.parent, + "%s: Failed to copy data\n", + __func__); + kfree(temp); + kfree(buffer->buf); + buffer->buf_size = 0; + return retval; + } + + kfree(temp); + buffer->buf_size = size; + } + + return 0; +} + +static inline int syna_tcm_alloc_mem(struct syna_tcm_hcd *tcm_hcd, + struct syna_tcm_buffer *buffer, unsigned int size) +{ + if (size > buffer->buf_size) { + kfree(buffer->buf); + buffer->buf = kmalloc(size, GFP_KERNEL); + if (!(buffer->buf)) { + dev_err(tcm_hcd->pdev->dev.parent, + "%s: Failed to allocate memory\n", + __func__); + dev_err(tcm_hcd->pdev->dev.parent, + "%s: Allocation size = %d\n", + __func__, size); + buffer->buf_size = 0; + buffer->data_length = 0; + return -ENOMEM; + } + buffer->buf_size = size; + } + + memset(buffer->buf, 0x00, buffer->buf_size); + buffer->data_length = 0; + + return 0; +} + +static inline unsigned int le2_to_uint(const unsigned char *src) +{ + return (unsigned int)src[0] + + (unsigned int)src[1] * 0x100; +} + +static inline unsigned int le4_to_uint(const unsigned char *src) +{ + return (unsigned int)src[0] + + (unsigned int)src[1] * 0x100 + + (unsigned int)src[2] * 0x10000 + + (unsigned int)src[3] * 0x1000000; +} + +static inline unsigned int ceil_div(unsigned int dividend, + unsigned int divisor) +{ + return (dividend + divisor - 1) / divisor; +} + +#endif diff --git a/synaptics_tcm/synaptics_tcm_device.c b/synaptics_tcm/synaptics_tcm_device.c new file mode 100644 index 0000000000..963e2994bd --- /dev/null +++ b/synaptics_tcm/synaptics_tcm_device.c @@ -0,0 +1,707 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2019 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2019 Scott Lin + * + * 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. + */ + +#include +#include +#include +#include "synaptics_tcm_core.h" + +#define CHAR_DEVICE_NAME "tcm" + +#define CONCURRENT true + +#define DEVICE_IOC_MAGIC 's' +#define DEVICE_IOC_RESET _IO(DEVICE_IOC_MAGIC, 0) /* 0x00007300 */ +#define DEVICE_IOC_IRQ _IOW(DEVICE_IOC_MAGIC, 1, int) /* 0x40047301 */ +#define DEVICE_IOC_RAW _IOW(DEVICE_IOC_MAGIC, 2, int) /* 0x40047302 */ +#define DEVICE_IOC_CONCURRENT _IOW(DEVICE_IOC_MAGIC, 3, int) /* 0x40047303 */ + +struct device_hcd { + dev_t dev_num; + bool raw_mode; + bool concurrent; + unsigned int ref_count; + struct cdev char_dev; + struct class *class; + struct device *device; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_buffer report; + struct syna_tcm_hcd *tcm_hcd; +}; + +DECLARE_COMPLETION(device_remove_complete); + +static struct device_hcd *device_hcd; + +static int rmidev_major_num; + +static void device_capture_touch_report(unsigned int count) +{ + int retval; + unsigned char id; + unsigned int idx; + unsigned int size; + unsigned char *data; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + static bool report; + static unsigned int offset; + static unsigned int remaining_size; + + if (count < 2) + return; + + data = &device_hcd->resp.buf[0]; + + if (data[0] != MESSAGE_MARKER) + return; + + id = data[1]; + + size = 0; + + LOCK_BUFFER(device_hcd->report); + + switch (id) { + case REPORT_TOUCH: + if (count >= 4) { + remaining_size = le2_to_uint(&data[2]); + } else { + report = false; + goto exit; + } + retval = syna_tcm_alloc_mem(tcm_hcd, + &device_hcd->report, + remaining_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for report.buf\n"); + report = false; + goto exit; + } + idx = 4; + size = count - idx; + offset = 0; + report = true; + break; + case STATUS_CONTINUED_READ: + if (report == false) + goto exit; + if (count >= 2) { + idx = 2; + size = count - idx; + } + break; + default: + goto exit; + } + + if (size) { + size = MIN(size, remaining_size); + retval = secure_memcpy(&device_hcd->report.buf[offset], + device_hcd->report.buf_size - offset, + &data[idx], + count - idx, + size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy touch report data\n"); + report = false; + goto exit; + } else { + offset += size; + remaining_size -= size; + device_hcd->report.data_length += size; + } + } + + if (remaining_size) + goto exit; + + LOCK_BUFFER(tcm_hcd->report.buffer); + + tcm_hcd->report.buffer.buf = device_hcd->report.buf; + tcm_hcd->report.buffer.buf_size = device_hcd->report.buf_size; + tcm_hcd->report.buffer.data_length = device_hcd->report.data_length; + + tcm_hcd->report_touch(); + + UNLOCK_BUFFER(tcm_hcd->report.buffer); + + report = false; + +exit: + UNLOCK_BUFFER(device_hcd->report); +} + +static int device_capture_touch_report_config(unsigned int count) +{ + int retval; + unsigned int size; + unsigned int buf_size; + unsigned char *data; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + if (device_hcd->raw_mode) { + if (count < 3) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid write data\n"); + return -EINVAL; + } + + size = le2_to_uint(&device_hcd->out.buf[1]); + + if (count - 3 < size) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incomplete write data\n"); + return -EINVAL; + } + + if (!size) + return 0; + + data = &device_hcd->out.buf[3]; + buf_size = device_hcd->out.buf_size - 3; + } else { + size = count - 1; + + if (!size) + return 0; + + data = &device_hcd->out.buf[1]; + buf_size = device_hcd->out.buf_size - 1; + } + + LOCK_BUFFER(tcm_hcd->config); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->config, + size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for tcm_hcd->config.buf\n"); + UNLOCK_BUFFER(tcm_hcd->config); + return retval; + } + + retval = secure_memcpy(tcm_hcd->config.buf, + tcm_hcd->config.buf_size, + data, + buf_size, + size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy touch report config data\n"); + UNLOCK_BUFFER(tcm_hcd->config); + return retval; + } + + tcm_hcd->config.data_length = size; + + UNLOCK_BUFFER(tcm_hcd->config); + + return 0; +} + +#ifdef HAVE_UNLOCKED_IOCTL +static long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#else +static int device_ioctl(struct inode *inp, struct file *filp, unsigned int cmd, + unsigned long arg) +#endif +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = 0; + + switch (cmd) { + case DEVICE_IOC_RESET: + retval = tcm_hcd->reset(tcm_hcd, false, true); + break; + case DEVICE_IOC_IRQ: + if (arg == 0) + retval = tcm_hcd->enable_irq(tcm_hcd, false, false); + else if (arg == 1) + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + break; + case DEVICE_IOC_RAW: + if (arg == 0) { + device_hcd->raw_mode = false; + tcm_hcd->update_watchdog(tcm_hcd, true); + } else if (arg == 1) { + device_hcd->raw_mode = true; + tcm_hcd->update_watchdog(tcm_hcd, false); + } + break; + case DEVICE_IOC_CONCURRENT: + if (arg == 0) + device_hcd->concurrent = false; + else if (arg == 1) + device_hcd->concurrent = true; + break; + default: + retval = -ENOTTY; + break; + } + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static loff_t device_llseek(struct file *filp, loff_t off, int whence) +{ + return -EINVAL; +} + +static ssize_t device_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + if (count == 0) + return 0; + + mutex_lock(&tcm_hcd->extif_mutex); + + LOCK_BUFFER(device_hcd->resp); + + if (device_hcd->raw_mode) { + retval = syna_tcm_alloc_mem(tcm_hcd, + &device_hcd->resp, + count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for resp.buf\n"); + UNLOCK_BUFFER(device_hcd->resp); + goto exit; + } + + retval = tcm_hcd->read_message(tcm_hcd, + device_hcd->resp.buf, + count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read message\n"); + UNLOCK_BUFFER(device_hcd->resp); + goto exit; + } + } else { + if (count != device_hcd->resp.data_length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid length information\n"); + UNLOCK_BUFFER(device_hcd->resp); + retval = -EINVAL; + goto exit; + } + } + + if (copy_to_user(buf, device_hcd->resp.buf, count)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy data to user space\n"); + UNLOCK_BUFFER(device_hcd->resp); + retval = -EINVAL; + goto exit; + } + + if (!device_hcd->concurrent) + goto skip_concurrent; + + if (tcm_hcd->report_touch == NULL) { + LOGE(tcm_hcd->pdev->dev.parent, + "Unable to report touch\n"); + device_hcd->concurrent = false; + } + + if (device_hcd->raw_mode) + device_capture_touch_report(count); + +skip_concurrent: + UNLOCK_BUFFER(device_hcd->resp); + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t device_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + if (count == 0) + return 0; + + mutex_lock(&tcm_hcd->extif_mutex); + + LOCK_BUFFER(device_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &device_hcd->out, + count == 1 ? count + 1 : count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for device_hcd->out.buf\n"); + UNLOCK_BUFFER(device_hcd->out); + goto exit; + } + + if (copy_from_user(device_hcd->out.buf, buf, count)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy data from user space\n"); + UNLOCK_BUFFER(device_hcd->out); + retval = -EINVAL; + goto exit; + } + + LOCK_BUFFER(device_hcd->resp); + + if (device_hcd->raw_mode) { + retval = tcm_hcd->write_message(tcm_hcd, + device_hcd->out.buf[0], + &device_hcd->out.buf[1], + count - 1, + NULL, + NULL, + NULL, + NULL, + 0); + } else { + mutex_lock(&tcm_hcd->reset_mutex); + retval = tcm_hcd->write_message(tcm_hcd, + device_hcd->out.buf[0], + &device_hcd->out.buf[1], + count - 1, + &device_hcd->resp.buf, + &device_hcd->resp.buf_size, + &device_hcd->resp.data_length, + NULL, + 0); + mutex_unlock(&tcm_hcd->reset_mutex); + } + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command 0x%02x\n", + device_hcd->out.buf[0]); + UNLOCK_BUFFER(device_hcd->resp); + UNLOCK_BUFFER(device_hcd->out); + goto exit; + } + + if (count && device_hcd->out.buf[0] == CMD_SET_TOUCH_REPORT_CONFIG) { + retval = device_capture_touch_report_config(count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to capture touch report config\n"); + } + } + + UNLOCK_BUFFER(device_hcd->out); + + if (device_hcd->raw_mode) + retval = count; + else + retval = device_hcd->resp.data_length; + + UNLOCK_BUFFER(device_hcd->resp); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static int device_open(struct inode *inp, struct file *filp) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (device_hcd->ref_count < 1) { + device_hcd->ref_count++; + retval = 0; + } else { + retval = -EACCES; + } + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static int device_release(struct inode *inp, struct file *filp) +{ + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (device_hcd->ref_count) + device_hcd->ref_count--; + + mutex_unlock(&tcm_hcd->extif_mutex); + + return 0; +} + +static char *device_devnode(struct device *dev, umode_t *mode) +{ + if (!mode) + return NULL; + + /* S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; */ + *mode = 0666; + + return kasprintf(GFP_KERNEL, "%s/%s", PLATFORM_DRIVER_NAME, + dev_name(dev)); +} + +static int device_create_class(void) +{ + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + if (device_hcd->class != NULL) + return 0; + + device_hcd->class = class_create(THIS_MODULE, PLATFORM_DRIVER_NAME); + + if (IS_ERR(device_hcd->class)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create class\n"); + return -ENODEV; + } + + device_hcd->class->devnode = device_devnode; + + return 0; +} + +static const struct file_operations device_fops = { + .owner = THIS_MODULE, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = device_ioctl, +#ifdef HAVE_COMPAT_IOCTL + .compat_ioctl = device_ioctl, +#endif +#else + .ioctl = device_ioctl, +#endif + .llseek = device_llseek, + .read = device_read, + .write = device_write, + .open = device_open, + .release = device_release, +}; + +static int device_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + dev_t dev_num; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + device_hcd = kzalloc(sizeof(*device_hcd), GFP_KERNEL); + if (!device_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for device_hcd\n"); + return -ENOMEM; + } + + device_hcd->tcm_hcd = tcm_hcd; + + device_hcd->concurrent = CONCURRENT; + + INIT_BUFFER(device_hcd->out, false); + INIT_BUFFER(device_hcd->resp, false); + INIT_BUFFER(device_hcd->report, false); + + if (rmidev_major_num) { + dev_num = MKDEV(rmidev_major_num, 0); + retval = register_chrdev_region(dev_num, 1, + PLATFORM_DRIVER_NAME); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to register char device\n"); + goto err_register_chrdev_region; + } + } else { + retval = alloc_chrdev_region(&dev_num, 0, 1, + PLATFORM_DRIVER_NAME); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate char device\n"); + goto err_alloc_chrdev_region; + } + + rmidev_major_num = MAJOR(dev_num); + } + + device_hcd->dev_num = dev_num; + + cdev_init(&device_hcd->char_dev, &device_fops); + + retval = cdev_add(&device_hcd->char_dev, dev_num, 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add char device\n"); + goto err_add_chardev; + } + + retval = device_create_class(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create class\n"); + goto err_create_class; + } + + device_hcd->device = device_create(device_hcd->class, NULL, + device_hcd->dev_num, NULL, CHAR_DEVICE_NAME"%d", + MINOR(device_hcd->dev_num)); + if (IS_ERR(device_hcd->device)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create device\n"); + retval = -ENODEV; + goto err_create_device; + } + + if (bdata->irq_gpio >= 0) { + retval = gpio_export(bdata->irq_gpio, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to export GPIO\n"); + } else { + retval = gpio_export_link(&tcm_hcd->pdev->dev, + "attn", bdata->irq_gpio); + if (retval < 0) + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to export GPIO link\n"); + } + } + + return 0; + +err_create_device: + class_destroy(device_hcd->class); + +err_create_class: + cdev_del(&device_hcd->char_dev); + +err_add_chardev: + unregister_chrdev_region(dev_num, 1); + +err_alloc_chrdev_region: +err_register_chrdev_region: + RELEASE_BUFFER(device_hcd->report); + RELEASE_BUFFER(device_hcd->resp); + RELEASE_BUFFER(device_hcd->out); + + kfree(device_hcd); + device_hcd = NULL; + + return retval; +} + +static int device_remove(struct syna_tcm_hcd *tcm_hcd) +{ + if (!device_hcd) + goto exit; + + device_destroy(device_hcd->class, device_hcd->dev_num); + + class_destroy(device_hcd->class); + + cdev_del(&device_hcd->char_dev); + + unregister_chrdev_region(device_hcd->dev_num, 1); + + RELEASE_BUFFER(device_hcd->report); + RELEASE_BUFFER(device_hcd->resp); + RELEASE_BUFFER(device_hcd->out); + + kfree(device_hcd); + device_hcd = NULL; + +exit: + complete(&device_remove_complete); + + return 0; +} + +static int device_reset(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!device_hcd) { + retval = device_init(tcm_hcd); + return retval; + } + + return 0; +} + +static struct syna_tcm_module_cb device_module = { + .type = TCM_DEVICE, + .init = device_init, + .remove = device_remove, + .syncbox = NULL, + .asyncbox = NULL, + .reset = device_reset, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init device_module_init(void) +{ + return syna_tcm_add_module(&device_module, true); +} + +static void __exit device_module_exit(void) +{ + syna_tcm_add_module(&device_module, false); + + wait_for_completion(&device_remove_complete); +} + +module_init(device_module_init); +module_exit(device_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Device Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_tcm/synaptics_tcm_diagnostics.c b/synaptics_tcm/synaptics_tcm_diagnostics.c new file mode 100644 index 0000000000..92472e1086 --- /dev/null +++ b/synaptics_tcm/synaptics_tcm_diagnostics.c @@ -0,0 +1,561 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2019 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2019 Scott Lin + * + * 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. + */ + +#include +#include "synaptics_tcm_core.h" + +#define SYSFS_DIR_NAME "diagnostics" + +enum pingpong_state { + PING = 0, + PONG = 1, +}; + +struct diag_hcd { + pid_t pid; + unsigned char report_type; + enum pingpong_state state; + struct kobject *sysfs_dir; + struct siginfo sigio; + struct task_struct *task; + struct syna_tcm_buffer ping; + struct syna_tcm_buffer pong; + struct syna_tcm_hcd *tcm_hcd; +}; + +DECLARE_COMPLETION(diag_remove_complete); + +static struct diag_hcd *diag_hcd; + +STORE_PROTOTYPE(diag, pid); +SHOW_PROTOTYPE(diag, size); +STORE_PROTOTYPE(diag, type); +SHOW_PROTOTYPE(diag, rows); +SHOW_PROTOTYPE(diag, cols); +SHOW_PROTOTYPE(diag, hybrid); +SHOW_PROTOTYPE(diag, buttons); + +static struct device_attribute *attrs[] = { + ATTRIFY(pid), + ATTRIFY(size), + ATTRIFY(type), + ATTRIFY(rows), + ATTRIFY(cols), + ATTRIFY(hybrid), + ATTRIFY(buttons), +}; + +static ssize_t diag_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static struct bin_attribute bin_attr = { + .attr = { + .name = "data", + .mode = 0444, + }, + .size = 0, + .read = diag_sysfs_data_show, +}; + +static ssize_t diag_sysfs_pid_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + if (kstrtouint(buf, 10, &input)) + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + diag_hcd->pid = input; + + if (diag_hcd->pid) { + diag_hcd->task = pid_task(find_vpid(diag_hcd->pid), + PIDTYPE_PID); + if (!diag_hcd->task) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to locate task\n"); + retval = -EINVAL; + goto exit; + } + } + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (diag_hcd->state == PING) { + LOCK_BUFFER(diag_hcd->ping); + + retval = snprintf(buf, PAGE_SIZE, + "%u\n", + diag_hcd->ping.data_length); + + UNLOCK_BUFFER(diag_hcd->ping); + } else { + LOCK_BUFFER(diag_hcd->pong); + + retval = snprintf(buf, PAGE_SIZE, + "%u\n", + diag_hcd->pong.data_length); + + UNLOCK_BUFFER(diag_hcd->pong); + } + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_type_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + if (kstrtouint(buf, 10, &input)) + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + diag_hcd->report_type = (unsigned char)input; + + mutex_unlock(&tcm_hcd->extif_mutex); + + return count; +} + +static ssize_t diag_sysfs_rows_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int rows; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + retval = -ENODEV; + goto exit; + } + + app_info = &tcm_hcd->app_info; + rows = le2_to_uint(app_info->num_of_image_rows); + + retval = snprintf(buf, PAGE_SIZE, "%u\n", rows); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_cols_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int cols; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + retval = -ENODEV; + goto exit; + } + + app_info = &tcm_hcd->app_info; + cols = le2_to_uint(app_info->num_of_image_cols); + + retval = snprintf(buf, PAGE_SIZE, "%u\n", cols); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_hybrid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int hybrid; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + retval = -ENODEV; + goto exit; + } + + app_info = &tcm_hcd->app_info; + hybrid = le2_to_uint(app_info->has_hybrid_data); + + retval = snprintf(buf, PAGE_SIZE, "%u\n", hybrid); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_buttons_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int buttons; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + retval = -ENODEV; + goto exit; + } + + app_info = &tcm_hcd->app_info; + buttons = le2_to_uint(app_info->num_of_buttons); + + retval = snprintf(buf, PAGE_SIZE, "%u\n", buttons); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int readlen; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = 0; + + if (diag_hcd->state == PING) { + LOCK_BUFFER(diag_hcd->ping); + + if (diag_hcd->ping.data_length == 0) { + readlen = 0; + goto exit; + } + + readlen = MIN(count, diag_hcd->ping.data_length - pos); + + if (diag_hcd->ping.data_length) { + retval = secure_memcpy(buf, + count, + &diag_hcd->ping.buf[pos], + diag_hcd->ping.buf_size - pos, + readlen); + } + + UNLOCK_BUFFER(diag_hcd->ping); + } else { + LOCK_BUFFER(diag_hcd->pong); + + if (diag_hcd->pong.data_length == 0) { + readlen = 0; + goto exit; + } + + readlen = MIN(count, diag_hcd->pong.data_length - pos); + + if (diag_hcd->pong.data_length) { + retval = secure_memcpy(buf, + count, + &diag_hcd->pong.buf[pos], + diag_hcd->pong.buf_size - pos, + readlen); + } + + UNLOCK_BUFFER(diag_hcd->pong); + } + +exit: + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy report data\n"); + } else { + retval = readlen; + } + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static void diag_report(void) +{ + int retval; + static enum pingpong_state state = PING; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + if (state == PING) { + LOCK_BUFFER(diag_hcd->ping); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &diag_hcd->ping, + tcm_hcd->report.buffer.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for ping.buf\n"); + UNLOCK_BUFFER(diag_hcd->ping); + return; + } + + retval = secure_memcpy(diag_hcd->ping.buf, + diag_hcd->ping.buf_size, + tcm_hcd->report.buffer.buf, + tcm_hcd->report.buffer.buf_size, + tcm_hcd->report.buffer.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy report data\n"); + UNLOCK_BUFFER(diag_hcd->ping); + return; + } + + diag_hcd->ping.data_length = tcm_hcd->report.buffer.data_length; + + UNLOCK_BUFFER(diag_hcd->ping); + + diag_hcd->state = state; + state = PONG; + } else { + LOCK_BUFFER(diag_hcd->pong); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &diag_hcd->pong, + tcm_hcd->report.buffer.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for pong.buf\n"); + UNLOCK_BUFFER(diag_hcd->pong); + return; + } + + retval = secure_memcpy(diag_hcd->pong.buf, + diag_hcd->pong.buf_size, + tcm_hcd->report.buffer.buf, + tcm_hcd->report.buffer.buf_size, + tcm_hcd->report.buffer.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy report data\n"); + UNLOCK_BUFFER(diag_hcd->pong); + return; + } + + diag_hcd->pong.data_length = tcm_hcd->report.buffer.data_length; + + UNLOCK_BUFFER(diag_hcd->pong); + + diag_hcd->state = state; + state = PING; + } +} + +static int diag_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + int idx; + + diag_hcd = kzalloc(sizeof(*diag_hcd), GFP_KERNEL); + if (!diag_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for diag_hcd\n"); + return -ENOMEM; + } + + diag_hcd->tcm_hcd = tcm_hcd; + diag_hcd->state = PING; + + INIT_BUFFER(diag_hcd->ping, false); + INIT_BUFFER(diag_hcd->pong, false); + + memset(&diag_hcd->sigio, 0x00, sizeof(diag_hcd->sigio)); + diag_hcd->sigio.si_signo = SIGIO; + diag_hcd->sigio.si_code = SI_USER; + + diag_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME, + tcm_hcd->sysfs_dir); + if (!diag_hcd->sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dir; + } + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + retval = sysfs_create_file(diag_hcd->sysfs_dir, + &(*attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs file\n"); + goto err_sysfs_create_file; + } + } + + retval = sysfs_create_bin_file(diag_hcd->sysfs_dir, &bin_attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs bin file\n"); + goto err_sysfs_create_bin_file; + } + + return 0; + +err_sysfs_create_bin_file: +err_sysfs_create_file: + for (idx--; idx >= 0; idx--) + sysfs_remove_file(diag_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(diag_hcd->sysfs_dir); + +err_sysfs_create_dir: + RELEASE_BUFFER(diag_hcd->pong); + RELEASE_BUFFER(diag_hcd->ping); + + kfree(diag_hcd); + diag_hcd = NULL; + + return retval; +} + +static int diag_remove(struct syna_tcm_hcd *tcm_hcd) +{ + int idx; + + if (!diag_hcd) + goto exit; + + sysfs_remove_bin_file(diag_hcd->sysfs_dir, &bin_attr); + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) + sysfs_remove_file(diag_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(diag_hcd->sysfs_dir); + + RELEASE_BUFFER(diag_hcd->pong); + RELEASE_BUFFER(diag_hcd->ping); + + kfree(diag_hcd); + diag_hcd = NULL; + +exit: + complete(&diag_remove_complete); + + return 0; +} + +static int diag_syncbox(struct syna_tcm_hcd *tcm_hcd) +{ + if (!diag_hcd) + return 0; + + if (tcm_hcd->report.id == diag_hcd->report_type) + diag_report(); + + return 0; +} + +static int diag_reset(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!diag_hcd) { + retval = diag_init(tcm_hcd); + return retval; + } + + return 0; +} + +static struct syna_tcm_module_cb diag_module = { + .type = TCM_DIAGNOSTICS, + .init = diag_init, + .remove = diag_remove, + .syncbox = diag_syncbox, + .asyncbox = NULL, + .reset = diag_reset, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init diag_module_init(void) +{ + return syna_tcm_add_module(&diag_module, true); +} + +static void __exit diag_module_exit(void) +{ + syna_tcm_add_module(&diag_module, false); + + wait_for_completion(&diag_remove_complete); +} + +module_init(diag_module_init); +module_exit(diag_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Diagnostics Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_tcm/synaptics_tcm_i2c.c b/synaptics_tcm/synaptics_tcm_i2c.c new file mode 100644 index 0000000000..483002ea12 --- /dev/null +++ b/synaptics_tcm/synaptics_tcm_i2c.c @@ -0,0 +1,523 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2019 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2019 Scott Lin + * + * 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. + */ + +#include +#include +#include "synaptics_tcm_core.h" +#include "linux/moduleparam.h" + +#define XFER_ATTEMPTS 10 + +static unsigned char *buf; + +static unsigned int buf_size; + +static struct syna_tcm_bus_io bus_io; + +static struct syna_tcm_hw_interface hw_if; + +static struct platform_device *syna_tcm_i2c_device; + +static struct drm_panel *active_tcm_panel; + +struct drm_panel *tcm_get_panel(void) +{ + return active_tcm_panel; +} +EXPORT_SYMBOL(tcm_get_panel); + +#ifdef CONFIG_OF +static int parse_dt(struct device *dev, struct syna_tcm_board_data *bdata) +{ + int retval; + struct device_node *np = dev->of_node; + + retval = of_get_named_gpio_flags(np, + "synaptics,irq-gpio", 0, + (enum of_gpio_flags *)&bdata->irq_flags); + if (!gpio_is_valid(retval)) { + if (retval != -EPROBE_DEFER) + dev_err(dev, "Error getting irq_gpio\n"); + return retval; + } + bdata->irq_gpio = retval; + + of_property_read_u32(np, "synaptics,irq-on-state", + &bdata->irq_on_state); + of_property_read_string(np, "synaptics,pwr-reg-name", + &bdata->pwr_reg_name); + of_property_read_string(np, "synaptics,bus-reg-name", + &bdata->bus_reg_name); + of_property_read_string(np, "synaptics,firmware-name", + &bdata->fw_name); + + bdata->power_gpio = of_get_named_gpio_flags(np, + "synaptics,power-gpio", 0, NULL); + + retval = of_property_read_u32(np, "synaptics,power-on-state", + &bdata->power_on_state); + if (retval < 0) { + LOGD(dev, "Failed to read synaptics,power-on-state\n"); + bdata->power_on_state = 0; + } + + retval = of_property_read_u32(np, "synaptics,power-delay-ms", + &bdata->power_delay_ms); + if (retval < 0) { + LOGE(dev, "Failed to read synaptics,power-delay-ms\n"); + return retval; + } + + retval = of_get_named_gpio_flags(np, + "synaptics,reset-gpio", 0, NULL); + if (!gpio_is_valid(retval)) { + if (retval != -EPROBE_DEFER) + dev_err(dev, "Error getting reset gpio\n"); + return retval; + } + bdata->reset_gpio = retval; + + retval = of_property_read_u32(np, "synaptics,reset-on-state", + &bdata->reset_on_state); + if (retval < 0) { + LOGE(dev, "Failed to read synaptics,reset-on-state\n"); + return retval; + } + + retval = of_property_read_u32(np, "synaptics,reset-active-ms", + &bdata->reset_active_ms); + if (retval < 0) { + LOGE(dev, "Failed to read synaptics,reset-active-ms\n"); + return retval; + } + + retval = of_property_read_u32(np, "synaptics,reset-delay-ms", + &bdata->reset_delay_ms); + if (retval < 0) { + LOGE(dev, "Unable to read synaptics,reset-delay-ms\n"); + return retval; + } + + bdata->x_flip = of_property_read_bool(np, "synaptics,x-flip"); + bdata->y_flip = of_property_read_bool(np, "synaptics,y-flip"); + bdata->swap_axes = of_property_read_bool(np, "synaptics,swap-axes"); + + retval = of_property_read_u32(np, "synaptics,ubl-i2c-addr", + &bdata->ubl_i2c_addr); + if (retval < 0) { + LOGE(dev, "Unable to read synaptics,ubl-i2c-addr\n"); + return retval; + } + + bdata->extend_report = of_property_read_bool(np, + "synaptics,extend_report"); + + return 0; +} +#endif + +static int syna_tcm_i2c_alloc_mem(struct syna_tcm_hcd *tcm_hcd, + unsigned int size) +{ + struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent); + + if (size > buf_size) { + if (buf_size) + kfree(buf); + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + LOGE(&i2c->dev, + "Failed to allocate memory for buf\n"); + buf_size = 0; + return -ENOMEM; + } + buf_size = size; + } + + return 0; +} + +static int syna_tcm_i2c_rmi_read(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval = 0; + unsigned char address; + unsigned int attempt; + struct i2c_msg msg[2]; + struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + address = (unsigned char)addr; + + msg[0].addr = bdata->ubl_i2c_addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &address; + + msg[1].addr = bdata->ubl_i2c_addr; + msg[1].flags = I2C_M_RD; + msg[1].len = length; + msg[1].buf = data; + + for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) { + if (i2c_transfer(i2c->adapter, msg, 2) == 2) { + retval = length; + goto exit; + } + + LOGD(&i2c->dev, "Transfer attempt %d times\n", attempt + 1); + + if (attempt + 1 == XFER_ATTEMPTS) { + LOGE(&i2c->dev, "Transfer failed\n"); + retval = -EIO; + goto exit; + } + + msleep(20); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_i2c_rmi_write(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned int attempt; + unsigned int byte_count; + struct i2c_msg msg; + struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + byte_count = length + 1; + + retval = syna_tcm_i2c_alloc_mem(tcm_hcd, byte_count); + if (retval < 0) { + LOGE(&i2c->dev, + "Failed to allocate memory\n"); + goto exit; + } + + buf[0] = (unsigned char)addr; + retval = secure_memcpy(&buf[1], + buf_size - 1, + data, + length, + length); + if (retval < 0) { + LOGE(&i2c->dev, + "Failed to copy write data\n"); + goto exit; + } + + msg.addr = bdata->ubl_i2c_addr; + msg.flags = 0; + msg.len = byte_count; + msg.buf = buf; + + for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) { + if (i2c_transfer(i2c->adapter, &msg, 1) == 1) { + retval = length; + goto exit; + } + + LOGD(&i2c->dev, "Transfer attempt %d times\n", attempt + 1); + + if (attempt + 1 == XFER_ATTEMPTS) { + LOGE(&i2c->dev, "Transfer failed\n"); + retval = -EIO; + goto exit; + } + + msleep(20); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_i2c_read(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length) +{ + int retval = 0; + unsigned int attempt; + struct i2c_msg msg; + struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent); + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + msg.addr = i2c->addr; + msg.flags = I2C_M_RD; + msg.len = length; + msg.buf = data; + + for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) { + if (i2c_transfer(i2c->adapter, &msg, 1) == 1) { + retval = length; + goto exit; + } + + LOGD(&i2c->dev, "Transfer attempt %d times\n", attempt + 1); + + if (attempt + 1 == XFER_ATTEMPTS) { + LOGE(&i2c->dev, "Transfer failed\n"); + retval = -EIO; + goto exit; + } + + msleep(20); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_i2c_write(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length) +{ + int retval = 0; + unsigned int attempt; + struct i2c_msg msg; + struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent); + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + msg.addr = i2c->addr; + msg.flags = 0; + msg.len = length; + msg.buf = data; + + for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) { + if (i2c_transfer(i2c->adapter, &msg, 1) == 1) { + retval = length; + goto exit; + } + + LOGD(&i2c->dev, "Transfer attempt %d times\n", attempt + 1); + + if (attempt + 1 == XFER_ATTEMPTS) { + LOGE(&i2c->dev, "Transfer failed\n"); + retval = -EIO; + goto exit; + } + + msleep(20); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_check_dt(struct device_node *np) +{ + int i; + int count; + struct device_node *node; + struct drm_panel *panel; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) + return 0; + + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + panel = of_drm_find_panel(node); + of_node_put(node); + if (!IS_ERR(panel)) { + active_tcm_panel = panel; + return 0; + } + } + + return PTR_ERR(panel); +} + +static int syna_tcm_check_default_tp(struct device_node *dt, const char *prop) +{ + const char *active_tp; + const char *compatible; + char *start; + int ret; + + ret = of_property_read_string(dt->parent, prop, &active_tp); + if (ret) { + pr_err(" %s:fail to read %s %d\n", __func__, prop, ret); + return -ENODEV; + } + + ret = of_property_read_string(dt, "compatible", &compatible); + if (ret < 0) { + pr_err(" %s:fail to read %s %d\n", __func__, "compatible", ret); + return -ENODEV; + } + + start = strnstr(active_tp, compatible, strlen(active_tp)); + if (start == NULL) { + pr_err(" %s:no match compatible, %s, %s\n", + __func__, compatible, active_tp); + ret = -ENODEV; + } + + return ret; +} + +static int syna_tcm_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *dev_id) +{ + int retval; + struct device_node *dt = i2c->dev.of_node; + + retval = syna_tcm_check_dt(dt); + if (retval == -EPROBE_DEFER) + return retval; + + if (retval) { + if (!syna_tcm_check_default_tp(dt, "qcom,i2c-touch-active")) + retval = -EPROBE_DEFER; + else + retval = -ENODEV; + + return retval; + } + + syna_tcm_i2c_device = platform_device_alloc(PLATFORM_DRIVER_NAME, 0); + if (!syna_tcm_i2c_device) { + LOGE(&i2c->dev, + "Failed to allocate platform device\n"); + return -ENOMEM; + } + +#ifdef CONFIG_OF + hw_if.bdata = devm_kzalloc(&i2c->dev, sizeof(*hw_if.bdata), GFP_KERNEL); + if (!hw_if.bdata) { + LOGE(&i2c->dev, + "Failed to allocate memory for board data\n"); + return -ENOMEM; + } + retval = parse_dt(&i2c->dev, hw_if.bdata); + if (retval < 0) { + LOGE(&i2c->dev, "Failed to parse dt\n"); + return retval; + } +#else + hw_if.bdata = i2c->dev.platform_data; +#endif + + bus_io.type = BUS_I2C; + bus_io.read = syna_tcm_i2c_read; + bus_io.write = syna_tcm_i2c_write; + bus_io.rmi_read = syna_tcm_i2c_rmi_read; + bus_io.rmi_write = syna_tcm_i2c_rmi_write; + + hw_if.bus_io = &bus_io; + + syna_tcm_i2c_device->dev.parent = &i2c->dev; + syna_tcm_i2c_device->dev.platform_data = &hw_if; + + retval = platform_device_add(syna_tcm_i2c_device); + if (retval < 0) { + LOGE(&i2c->dev, + "Failed to add platform device\n"); + return retval; + } + + return 0; +} + +static int syna_tcm_i2c_remove(struct i2c_client *i2c) +{ + syna_tcm_i2c_device->dev.platform_data = NULL; + + platform_device_unregister(syna_tcm_i2c_device); + + return 0; +} + +static const struct i2c_device_id syna_tcm_id_table[] = { + {I2C_MODULE_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, syna_tcm_id_table); + +#ifdef CONFIG_OF +static const struct of_device_id syna_tcm_of_match_table[] = { + { + .compatible = "synaptics,tcm-i2c", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, syna_tcm_of_match_table); +#else +#define syna_tcm_of_match_table NULL +#endif + +static struct i2c_driver syna_tcm_i2c_driver = { + .driver = { + .name = I2C_MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = syna_tcm_of_match_table, + }, + .probe = syna_tcm_i2c_probe, + .remove = syna_tcm_i2c_remove, + .id_table = syna_tcm_id_table, +}; + +int syna_tcm_bus_init(void) +{ + return i2c_add_driver(&syna_tcm_i2c_driver); +} +EXPORT_SYMBOL(syna_tcm_bus_init); + +void syna_tcm_bus_exit(void) +{ + kfree(buf); + + i2c_del_driver(&syna_tcm_i2c_driver); +} +EXPORT_SYMBOL(syna_tcm_bus_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM I2C Bus Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_tcm/synaptics_tcm_recovery.c b/synaptics_tcm/synaptics_tcm_recovery.c new file mode 100644 index 0000000000..3be072faf1 --- /dev/null +++ b/synaptics_tcm/synaptics_tcm_recovery.c @@ -0,0 +1,898 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2019 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2019 Scott Lin + * + * 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. + */ + +#include "synaptics_tcm_core.h" + +#define SET_UP_RECOVERY_MODE true + +#define ENABLE_SYSFS_INTERFACE true + +#define SYSFS_DIR_NAME "recovery" + +#define IHEX_BUF_SIZE (2048 * 1024) + +#define DATA_BUF_SIZE (512 * 1024) + +#define IHEX_RECORD_SIZE 14 + +#define PDT_START_ADDR 0x00e9 + +#define UBL_FN_NUMBER 0x35 + +#define F35_CHUNK_SIZE 16 + +#define F35_CHUNK_SIZE_WORDS 8 + +#define F35_ERASE_ALL_WAIT_MS 5000 + +#define F35_ERASE_ALL_POLL_MS 100 + +#define F35_DATA5_OFFSET 5 + +#define F35_CTRL3_OFFSET 18 + +#define F35_RESET_COMMAND 16 + +#define F35_ERASE_ALL_COMMAND 3 + +#define F35_WRITE_CHUNK_COMMAND 2 + +#define F35_READ_FLASH_STATUS_COMMAND 1 + +struct rmi_pdt_entry { + unsigned char query_base_addr; + unsigned char command_base_addr; + unsigned char control_base_addr; + unsigned char data_base_addr; + unsigned char intr_src_count:3; + unsigned char reserved_1:2; + unsigned char fn_version:2; + unsigned char reserved_2:1; + unsigned char fn_number; +} __packed; + +struct rmi_addr { + unsigned short query_base; + unsigned short command_base; + unsigned short control_base; + unsigned short data_base; +}; + +struct recovery_hcd { + bool set_up_recovery_mode; + unsigned char chunk_buf[F35_CHUNK_SIZE + 3]; + unsigned char out_buf[3]; + unsigned char *ihex_buf; + unsigned char *data_buf; + unsigned int ihex_size; + unsigned int ihex_records; + unsigned int data_entries; + struct kobject *sysfs_dir; + struct rmi_addr f35_addr; + struct syna_tcm_hcd *tcm_hcd; +}; + +DECLARE_COMPLETION(recovery_remove_complete); + +static struct recovery_hcd *recovery_hcd; + +static int recovery_do_recovery(void); + +STORE_PROTOTYPE(recovery, recovery); + +static struct device_attribute *attrs[] = { + ATTRIFY(recovery), +}; + +static ssize_t recovery_sysfs_ihex_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static struct bin_attribute bin_attr = { + .attr = { + .name = "ihex", + .mode = 0220, + }, + .size = 0, + .write = recovery_sysfs_ihex_store, +}; + +static ssize_t recovery_sysfs_recovery_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + if (kstrtouint(buf, 10, &input)) + return -EINVAL; + + if (input == 1) + recovery_hcd->set_up_recovery_mode = true; + else if (input == 2) + recovery_hcd->set_up_recovery_mode = false; + else + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (recovery_hcd->ihex_size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get ihex data\n"); + retval = -EINVAL; + goto exit; + } + + if (recovery_hcd->ihex_size % IHEX_RECORD_SIZE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid ihex data\n"); + retval = -EINVAL; + goto exit; + } + + recovery_hcd->ihex_records = recovery_hcd->ihex_size / IHEX_RECORD_SIZE; + + retval = recovery_do_recovery(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do recovery\n"); + goto exit; + } + + retval = count; + +exit: + recovery_hcd->set_up_recovery_mode = SET_UP_RECOVERY_MODE; + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t recovery_sysfs_ihex_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = secure_memcpy(&recovery_hcd->ihex_buf[pos], + IHEX_BUF_SIZE - pos, + buf, + count, + count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy ihex data\n"); + recovery_hcd->ihex_size = 0; + goto exit; + } + + recovery_hcd->ihex_size = pos + count; + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static int recovery_device_reset(void) +{ + int retval; + unsigned char command; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + command = F35_RESET_COMMAND; + + retval = syna_tcm_rmi_write(tcm_hcd, + recovery_hcd->f35_addr.control_base + F35_CTRL3_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write F$35 command\n"); + return retval; + } + + msleep(bdata->reset_delay_ms); + + return 0; +} + +static int recovery_add_data_entry(unsigned char data) +{ + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + if (recovery_hcd->data_entries >= DATA_BUF_SIZE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Reached data buffer size limit\n"); + return -EINVAL; + } + + recovery_hcd->data_buf[recovery_hcd->data_entries++] = data; + + return 0; +} + +static int recovery_add_padding(unsigned int *words) +{ + int retval; + unsigned int padding; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + padding = (F35_CHUNK_SIZE_WORDS - *words % F35_CHUNK_SIZE_WORDS); + padding %= F35_CHUNK_SIZE_WORDS; + + while (padding) { + retval = recovery_add_data_entry(0xff); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + + retval = recovery_add_data_entry(0xff); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + + (*words)++; + padding--; + } + + return 0; +} + +static int recovery_parse_ihex(void) +{ + int retval; + unsigned char colon; + unsigned char *buf; + unsigned int addr; + unsigned int type; + unsigned int addrl; + unsigned int addrh; + unsigned int data0; + unsigned int data1; + unsigned int count; + unsigned int words; + unsigned int offset; + unsigned int record; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + words = 0; + + offset = 0; + + buf = recovery_hcd->ihex_buf; + + recovery_hcd->data_entries = 0; + + for (record = 0; record < recovery_hcd->ihex_records; record++) { + buf[(record + 1) * IHEX_RECORD_SIZE - 1] = 0x00; + retval = sscanf(&buf[record * IHEX_RECORD_SIZE], + "%c%02x%02x%02x%02x%02x%02x", + &colon, + &count, + &addrh, + &addrl, + &type, + &data0, + &data1); + if (retval != 7) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read ihex record\n"); + return -EINVAL; + } + + if (type == 0x00) { + if ((words % F35_CHUNK_SIZE_WORDS) == 0) { + addr = (addrh << 8) + addrl; + addr += offset; + addr >>= 4; + + retval = recovery_add_data_entry(addr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + + retval = recovery_add_data_entry(addr >> 8); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + } + + retval = recovery_add_data_entry(data0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + + retval = recovery_add_data_entry(data1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + + words++; + } else if (type == 0x02) { + retval = recovery_add_padding(&words); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add padding\n"); + return retval; + } + + offset = (data0 << 8) + data1; + offset <<= 4; + } + } + + retval = recovery_add_padding(&words); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add padding\n"); + return retval; + } + + return 0; +} + +static int recovery_check_status(void) +{ + int retval; + unsigned char status; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + retval = syna_tcm_rmi_read(tcm_hcd, + recovery_hcd->f35_addr.data_base, + &status, + sizeof(status)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read status\n"); + return retval; + } + + status = status & 0x1f; + + if (status != 0x00) { + LOGE(tcm_hcd->pdev->dev.parent, + "Recovery mode status = 0x%02x\n", + status); + return -EINVAL; + } + + return 0; +} + +static int recovery_write_flash(void) +{ + int retval; + unsigned char *data_ptr; + unsigned int chunk_buf_size; + unsigned int chunk_data_size; + unsigned int entries_written; + unsigned int entries_to_write; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + entries_written = 0; + + data_ptr = recovery_hcd->data_buf; + + chunk_buf_size = sizeof(recovery_hcd->chunk_buf); + + chunk_data_size = chunk_buf_size - 1; + + recovery_hcd->chunk_buf[chunk_buf_size - 1] = F35_WRITE_CHUNK_COMMAND; + + while (entries_written < recovery_hcd->data_entries) { + entries_to_write = F35_CHUNK_SIZE + 2; + + retval = secure_memcpy(recovery_hcd->chunk_buf, + chunk_buf_size - 1, + data_ptr, + recovery_hcd->data_entries - entries_written, + entries_to_write); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy chunk data\n"); + return retval; + } + + retval = syna_tcm_rmi_write(tcm_hcd, + recovery_hcd->f35_addr.control_base, + recovery_hcd->chunk_buf, + chunk_buf_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write chunk data\n"); + return retval; + } + + data_ptr += entries_to_write; + entries_written += entries_to_write; + } + + retval = recovery_check_status(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get no error recovery mode status\n"); + return retval; + } + + return 0; +} + +static int recovery_poll_erase_completion(void) +{ + int retval; + unsigned char status; + unsigned char command; + unsigned char data_base; + unsigned int timeout; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + timeout = F35_ERASE_ALL_WAIT_MS; + + data_base = recovery_hcd->f35_addr.data_base; + + do { + command = F35_READ_FLASH_STATUS_COMMAND; + + retval = syna_tcm_rmi_write(tcm_hcd, + recovery_hcd->f35_addr.command_base, + &command, + sizeof(command)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write F$35 command\n"); + return retval; + } + + do { + retval = syna_tcm_rmi_read(tcm_hcd, + recovery_hcd->f35_addr.command_base, + &command, + sizeof(command)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read command status\n"); + return retval; + } + + if (command == 0x00) + break; + + if (timeout == 0) + break; + + msleep(F35_ERASE_ALL_POLL_MS); + timeout -= F35_ERASE_ALL_POLL_MS; + } while (true); + + if (command != 0 && timeout == 0) { + retval = -EINVAL; + goto exit; + } + + retval = syna_tcm_rmi_read(tcm_hcd, + data_base + F35_DATA5_OFFSET, + &status, + sizeof(status)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read flash status\n"); + return retval; + } + + if ((status & 0x01) == 0x00) + break; + + if (timeout == 0) { + retval = -EINVAL; + goto exit; + } + + msleep(F35_ERASE_ALL_POLL_MS); + timeout -= F35_ERASE_ALL_POLL_MS; + } while (true); + + retval = 0; + +exit: + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get erase completion\n"); + } + + return retval; +} + +static int recovery_erase_flash(void) +{ + int retval; + unsigned char command; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + command = F35_ERASE_ALL_COMMAND; + + retval = syna_tcm_rmi_write(tcm_hcd, + recovery_hcd->f35_addr.control_base + F35_CTRL3_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write F$35 command\n"); + return retval; + } + + if (recovery_hcd->f35_addr.command_base) { + retval = recovery_poll_erase_completion(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to wait for erase completion\n"); + return retval; + } + } else { + msleep(F35_ERASE_ALL_WAIT_MS); + } + + retval = recovery_check_status(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get no error recovery mode status\n"); + return retval; + } + + return 0; +} + +static int recovery_set_up_recovery_mode(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + retval = tcm_hcd->identify(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + return retval; + } + + if (tcm_hcd->id_info.mode == MODE_APPLICATION) { + retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_BOOTLOADER); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enter bootloader mode\n"); + return retval; + } + } + + retval = tcm_hcd->write_message(tcm_hcd, + recovery_hcd->out_buf[0], + &recovery_hcd->out_buf[1], + 2, + NULL, + NULL, + NULL, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_REBOOT_TO_ROM_BOOTLOADER)); + return retval; + } + + msleep(bdata->reset_delay_ms); + + return 0; +} + +static int recovery_do_recovery(void) +{ + int retval; + struct rmi_pdt_entry p_entry; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + retval = recovery_parse_ihex(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to parse ihex data\n"); + return retval; + } + + if (recovery_hcd->set_up_recovery_mode) { + retval = recovery_set_up_recovery_mode(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up recovery mode\n"); + return retval; + } + } + + tcm_hcd->update_watchdog(tcm_hcd, false); + + retval = syna_tcm_rmi_read(tcm_hcd, + PDT_START_ADDR, + (unsigned char *)&p_entry, + sizeof(p_entry)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read PDT entry\n"); + return retval; + } + + if (p_entry.fn_number != UBL_FN_NUMBER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to find F$35\n"); + return -ENODEV; + } + + recovery_hcd->f35_addr.query_base = p_entry.query_base_addr; + recovery_hcd->f35_addr.command_base = p_entry.command_base_addr; + recovery_hcd->f35_addr.control_base = p_entry.control_base_addr; + recovery_hcd->f35_addr.data_base = p_entry.data_base_addr; + + LOGN(tcm_hcd->pdev->dev.parent, + "Start of recovery\n"); + + retval = recovery_erase_flash(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase flash\n"); + return retval; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Flash erased\n"); + + retval = recovery_write_flash(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to flash\n"); + return retval; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Flash written\n"); + + retval = recovery_device_reset(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + return retval; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "End of recovery\n"); + + if (recovery_hcd->set_up_recovery_mode) + return 0; + + tcm_hcd->update_watchdog(tcm_hcd, true); + + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable interrupt\n"); + return retval; + } + + retval = tcm_hcd->identify(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + return retval; + } + + if (tcm_hcd->id_info.mode != MODE_APPLICATION) { + retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + return retval; + } + } + + return 0; +} + +static int recovery_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval = 0; + int idx; + + recovery_hcd = kzalloc(sizeof(*recovery_hcd), GFP_KERNEL); + if (!recovery_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for recovery_hcd\n"); + return -ENOMEM; + } + + recovery_hcd->ihex_buf = kzalloc(IHEX_BUF_SIZE, GFP_KERNEL); + if (!recovery_hcd->ihex_buf) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for ihex_buf\n"); + goto err_allocate_ihex_buf; + } + + recovery_hcd->data_buf = kzalloc(DATA_BUF_SIZE, GFP_KERNEL); + if (!recovery_hcd->data_buf) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for data_buf\n"); + goto err_allocate_data_buf; + } + + recovery_hcd->tcm_hcd = tcm_hcd; + + recovery_hcd->set_up_recovery_mode = SET_UP_RECOVERY_MODE; + + recovery_hcd->out_buf[0] = CMD_REBOOT_TO_ROM_BOOTLOADER; + recovery_hcd->out_buf[1] = 0; + recovery_hcd->out_buf[2] = 0; + + if (!ENABLE_SYSFS_INTERFACE) + return 0; + + recovery_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME, + tcm_hcd->sysfs_dir); + if (!recovery_hcd->sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dir; + } + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + retval = sysfs_create_file(recovery_hcd->sysfs_dir, + &(*attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs file\n"); + goto err_sysfs_create_file; + } + } + + retval = sysfs_create_bin_file(recovery_hcd->sysfs_dir, &bin_attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs bin file\n"); + goto err_sysfs_create_bin_file; + } + + return 0; + +err_sysfs_create_bin_file: +err_sysfs_create_file: + for (idx--; idx >= 0; idx--) + sysfs_remove_file(recovery_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(recovery_hcd->sysfs_dir); + +err_sysfs_create_dir: + kfree(recovery_hcd->data_buf); +err_allocate_data_buf: + kfree(recovery_hcd->ihex_buf); +err_allocate_ihex_buf: + kfree(recovery_hcd); + recovery_hcd = NULL; + + return retval; +} + +static int recovery_remove(struct syna_tcm_hcd *tcm_hcd) +{ + int idx; + + if (!recovery_hcd) + goto exit; + + if (ENABLE_SYSFS_INTERFACE) { + sysfs_remove_bin_file(recovery_hcd->sysfs_dir, &bin_attr); + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + sysfs_remove_file(recovery_hcd->sysfs_dir, + &(*attrs[idx]).attr); + } + + kobject_put(recovery_hcd->sysfs_dir); + } + + kfree(recovery_hcd->data_buf); + kfree(recovery_hcd->ihex_buf); + kfree(recovery_hcd); + recovery_hcd = NULL; + +exit: + complete(&recovery_remove_complete); + + return 0; +} + +static int recovery_reset(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!recovery_hcd) { + retval = recovery_init(tcm_hcd); + return retval; + } + + return 0; +} + +static struct syna_tcm_module_cb recovery_module = { + .type = TCM_RECOVERY, + .init = recovery_init, + .remove = recovery_remove, + .syncbox = NULL, + .asyncbox = NULL, + .reset = recovery_reset, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init recovery_module_init(void) +{ + return syna_tcm_add_module(&recovery_module, true); +} + +static void __exit recovery_module_exit(void) +{ + syna_tcm_add_module(&recovery_module, false); + + wait_for_completion(&recovery_remove_complete); +} + +module_init(recovery_module_init); +module_exit(recovery_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Recovery Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_tcm/synaptics_tcm_reflash.c b/synaptics_tcm/synaptics_tcm_reflash.c new file mode 100644 index 0000000000..bfb6e6db17 --- /dev/null +++ b/synaptics_tcm/synaptics_tcm_reflash.c @@ -0,0 +1,2193 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2019 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2019 Scott Lin + * + * 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. + */ + +#include +#include +#include "synaptics_tcm_core.h" + +#define STARTUP_REFLASH + +#define FORCE_REFLASH false + +#define ENABLE_SYSFS_INTERFACE true + +#define SYSFS_DIR_NAME "reflash" + +#define CUSTOM_DIR_NAME "custom" + +#define FW_IMAGE_NAME "synaptics_firmware.img" + +#define BOOT_CONFIG_ID "BOOT_CONFIG" + +#define APP_CODE_ID "APP_CODE" + +#define PROD_TEST_ID "APP_PROD_TEST" + +#define APP_CONFIG_ID "APP_CONFIG" + +#define DISP_CONFIG_ID "DISPLAY" + +#define FB_READY_COUNT 2 + +#define FB_READY_WAIT_MS 100 + +#define FB_READY_TIMEOUT_S 80 + +#define IMAGE_FILE_MAGIC_VALUE 0x4818472b + +#define FLASH_AREA_MAGIC_VALUE 0x7c05e516 + +#define BOOT_CONFIG_SIZE 8 + +#define BOOT_CONFIG_SLOTS 16 + +#define IMAGE_BUF_SIZE (512 * 1024) + +#define ERASE_FLASH_DELAY_MS 500 + +#define WRITE_FLASH_DELAY_MS 20 + +#define REFLASH (1 << 0) + +#define FORCE_UPDATE (1 << 1) + +#define APP_CFG_UPDATE (1 << 2) + +#define DISP_CFG_UPDATE (1 << 3) + +#define BOOT_CFG_UPDATE (1 << 4) + +#define BOOT_CFG_LOCKDOWN (1 << 5) + +#define reflash_write(p_name) \ +static int reflash_write_##p_name(void) \ +{ \ + int retval; \ + unsigned int size; \ + unsigned int flash_addr; \ + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; \ + const unsigned char *data; \ +\ + data = reflash_hcd->image_info.p_name.data; \ + size = reflash_hcd->image_info.p_name.size; \ + flash_addr = reflash_hcd->image_info.p_name.flash_addr; \ +\ + retval = reflash_write_flash(flash_addr, data, size); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to write to flash\n"); \ + return retval; \ + } \ +\ + return 0; \ +} + +#define reflash_erase(p_name) \ +static int reflash_erase_##p_name(void) \ +{ \ + int retval; \ + unsigned int size; \ + unsigned int flash_addr; \ + unsigned int page_start; \ + unsigned int page_count; \ + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; \ +\ + flash_addr = reflash_hcd->image_info.p_name.flash_addr; \ +\ + page_start = flash_addr / reflash_hcd->page_size; \ +\ + size = reflash_hcd->image_info.p_name.size; \ + page_count = ceil_div(size, reflash_hcd->page_size); \ +\ + LOGD(tcm_hcd->pdev->dev.parent, \ + "Page start = %d\n", \ + page_start); \ +\ + LOGD(tcm_hcd->pdev->dev.parent, \ + "Page count = %d\n", \ + page_count); \ +\ + retval = reflash_erase_flash(page_start, page_count); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to erase flash pages\n"); \ + return retval; \ + } \ +\ + return 0; \ +} + +#define reflash_update(p_name) \ +static int reflash_update_##p_name(bool reset) \ +{ \ + int retval; \ + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; \ +\ + retval = reflash_set_up_flash_access(); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to set up flash access\n"); \ + return retval; \ + } \ +\ + tcm_hcd->update_watchdog(tcm_hcd, false); \ +\ + retval = reflash_check_##p_name(); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed "#p_name" partition check\n"); \ + reset = true; \ + goto reset; \ + } \ +\ + retval = reflash_erase_##p_name(); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to erase "#p_name" partition\n"); \ + reset = true; \ + goto reset; \ + } \ +\ + LOGN(tcm_hcd->pdev->dev.parent, \ + "Partition erased ("#p_name")\n"); \ +\ + retval = reflash_write_##p_name(); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to write "#p_name" partition\n"); \ + reset = true; \ + goto reset; \ + } \ +\ + LOGN(tcm_hcd->pdev->dev.parent, \ + "Partition written ("#p_name")\n"); \ +\ + retval = 0; \ +\ +reset: \ + if (!reset) \ + goto exit; \ +\ + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to do reset\n"); \ + } \ +\ +exit: \ + tcm_hcd->update_watchdog(tcm_hcd, true); \ +\ + return retval; \ +} + +#define reflash_show_data() \ +{ \ + LOCK_BUFFER(reflash_hcd->read); \ +\ + readlen = MIN(count, reflash_hcd->read.data_length - pos); \ +\ + retval = secure_memcpy(buf, \ + count, \ + &reflash_hcd->read.buf[pos], \ + reflash_hcd->read.buf_size - pos, \ + readlen); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to copy read data\n"); \ + } else { \ + retval = readlen; \ + } \ +\ + UNLOCK_BUFFER(reflash_hcd->read); \ +} + +enum update_area { + NONE = 0, + FIRMWARE_CONFIG, + CONFIG_ONLY, +}; + +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]; +}; + +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]; +}; + +struct block_data { + const unsigned char *data; + unsigned int size; + unsigned int flash_addr; +}; + +struct image_info { + struct block_data boot_config; + struct block_data app_firmware; + struct block_data prod_test_firmware; + struct block_data app_config; + struct block_data disp_config; +}; + +struct image_header { + unsigned char magic_value[4]; + unsigned char num_of_areas[4]; +}; + +struct boot_config { + union { + unsigned char i2c_address; + struct { + unsigned char cpha:1; + unsigned char cpol:1; + unsigned char word0_b2__7:6; + } __packed; + }; + unsigned char attn_polarity:1; + unsigned char attn_drive:2; + unsigned char attn_pullup:1; + unsigned char word0_b12__14:3; + unsigned char used:1; + unsigned short customer_part_id; + unsigned short boot_timeout; + unsigned short continue_on_reset:1; + unsigned short word3_b1__15:15; +} __packed; + +struct reflash_hcd { + bool force_update; + bool disp_cfg_update; + const unsigned char *image; + unsigned char *image_buf; + unsigned int image_size; + unsigned int page_size; + unsigned int write_block_size; + unsigned int max_write_payload_size; + const struct firmware *fw_entry; + struct mutex reflash_mutex; + struct kobject *sysfs_dir; + struct kobject *custom_dir; + struct work_struct work; + struct workqueue_struct *workqueue; + struct image_info image_info; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_buffer read; + struct syna_tcm_hcd *tcm_hcd; +}; + +DECLARE_COMPLETION(reflash_remove_complete); + +static struct reflash_hcd *reflash_hcd; + +static int reflash_get_fw_image(void); + +static int reflash_read_data(enum flash_area area, bool run_app_firmware, + struct syna_tcm_buffer *output); + +static int reflash_update_custom_otp(const unsigned char *data, + unsigned int offset, unsigned int datalen); + +static int reflash_update_custom_lcm(const unsigned char *data, + unsigned int offset, unsigned int datalen); + +static int reflash_update_custom_oem(const unsigned char *data, + unsigned int offset, unsigned int datalen); + +static int reflash_update_boot_config(bool lock); + +static int reflash_update_app_config(bool reset); + +static int reflash_update_disp_config(bool reset); + +static int reflash_do_reflash(void); + +STORE_PROTOTYPE(reflash, reflash); + +static struct device_attribute *attrs[] = { + ATTRIFY(reflash), +}; + +static ssize_t reflash_sysfs_image_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_lockdown_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_lockdown_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_lcm_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_lcm_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_oem_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_oem_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static struct bin_attribute bin_attrs[] = { + { + .attr = { + .name = "image", + .mode = 0220, + }, + .size = 0, + .write = reflash_sysfs_image_store, + }, + { + .attr = { + .name = "lockdown", + .mode = 0664, + }, + .size = 0, + .read = reflash_sysfs_lockdown_show, + .write = reflash_sysfs_lockdown_store, + }, + { + .attr = { + .name = "lcm", + .mode = 0664, + }, + .size = 0, + .read = reflash_sysfs_lcm_show, + .write = reflash_sysfs_lcm_store, + }, + { + .attr = { + .name = "oem", + .mode = 0664, + }, + .size = 0, + .read = reflash_sysfs_oem_show, + .write = reflash_sysfs_oem_store, + }, +}; + +static ssize_t reflash_sysfs_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (kstrtouint(buf, 10, &input)) + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + pm_stay_awake(&tcm_hcd->pdev->dev); + + mutex_lock(&reflash_hcd->reflash_mutex); + + if (reflash_hcd->image_size != 0) + reflash_hcd->image = reflash_hcd->image_buf; + + reflash_hcd->force_update = input & FORCE_UPDATE ? true : false; + + if (input & REFLASH || input & FORCE_UPDATE) { + retval = reflash_do_reflash(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reflash\n"); + goto exit; + } + } + + if ((input & ~(REFLASH | FORCE_UPDATE)) == 0) { + retval = count; + goto exit; + } + + retval = reflash_get_fw_image(); + if (retval < 0) { + LOGD(tcm_hcd->pdev->dev.parent, + "Failed to get firmware image\n"); + goto exit; + } + + if (input & BOOT_CFG_LOCKDOWN) { + retval = reflash_update_boot_config(true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to lockdown boot config\n"); + goto exit; + } + } else if (input & BOOT_CFG_UPDATE) { + retval = reflash_update_boot_config(false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to update boot config\n"); + goto exit; + } + } + + if (input & REFLASH || input & FORCE_UPDATE) { + retval = count; + goto exit; + } + + if (input & DISP_CFG_UPDATE) { + if (input & APP_CFG_UPDATE) + retval = reflash_update_disp_config(false); + else + retval = reflash_update_disp_config(true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash display config\n"); + goto exit; + } + } + + if (input & APP_CFG_UPDATE) { + retval = reflash_update_app_config(true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash application config\n"); + goto exit; + } + } + + retval = count; + +exit: + if (reflash_hcd->fw_entry) { + release_firmware(reflash_hcd->fw_entry); + reflash_hcd->fw_entry = NULL; + } + + reflash_hcd->image = NULL; + reflash_hcd->image_size = 0; + reflash_hcd->force_update = FORCE_REFLASH; + + mutex_unlock(&reflash_hcd->reflash_mutex); + + pm_relax(&tcm_hcd->pdev->dev); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_image_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = secure_memcpy(&reflash_hcd->image_buf[pos], + IMAGE_BUF_SIZE - pos, + buf, + count, + count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy firmware image data\n"); + reflash_hcd->image_size = 0; + goto exit; + } + + reflash_hcd->image_size = pos + count; + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_lockdown_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int readlen; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_read_data(CUSTOM_OTP, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read lockdown data\n"); + goto exit; + } + + reflash_show_data(); + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_lockdown_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + pm_stay_awake(&tcm_hcd->pdev->dev); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_update_custom_otp(buf, pos, count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to update custom OTP data\n"); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + pm_relax(&tcm_hcd->pdev->dev); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_lcm_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int readlen; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_read_data(CUSTOM_LCM, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read LCM data\n"); + goto exit; + } + + reflash_show_data(); + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_lcm_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + pm_stay_awake(&tcm_hcd->pdev->dev); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_update_custom_lcm(buf, pos, count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to update custom LCM data\n"); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + pm_relax(&tcm_hcd->pdev->dev); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_oem_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int readlen; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_read_data(CUSTOM_OEM, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read OEM data\n"); + goto exit; + } + + reflash_show_data(); + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_oem_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + pm_stay_awake(&tcm_hcd->pdev->dev); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_update_custom_oem(buf, pos, count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to update custom OEM data\n"); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + pm_relax(&tcm_hcd->pdev->dev); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static int reflash_set_up_flash_access(void) +{ + int retval; + unsigned int temp; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = tcm_hcd->identify(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + return retval; + } + + if (tcm_hcd->id_info.mode == MODE_APPLICATION) { + retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_BOOTLOADER); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enter bootloader mode\n"); + return retval; + } + } + + temp = tcm_hcd->boot_info.write_block_size_words; + reflash_hcd->write_block_size = temp * 2; + + temp = le2_to_uint(tcm_hcd->boot_info.erase_page_size_words); + reflash_hcd->page_size = temp * 2; + + temp = le2_to_uint(tcm_hcd->boot_info.max_write_payload_size); + reflash_hcd->max_write_payload_size = temp; + + LOGD(tcm_hcd->pdev->dev.parent, + "Write block size = %d\n", + reflash_hcd->write_block_size); + + LOGD(tcm_hcd->pdev->dev.parent, + "Page size = %d\n", + reflash_hcd->page_size); + + LOGD(tcm_hcd->pdev->dev.parent, + "Max write payload size = %d\n", + reflash_hcd->max_write_payload_size); + + if (reflash_hcd->write_block_size > (tcm_hcd->wr_chunk_size - 5)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Write size greater than available chunk space\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_parse_fw_image(void) +{ + 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 image_info *image_info; + struct area_descriptor *descriptor; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + const unsigned char *image; + const unsigned char *content; + + image = reflash_hcd->image; + image_info = &reflash_hcd->image_info; + header = (struct image_header *)image; + + reflash_hcd->disp_cfg_update = false; + + magic_value = le4_to_uint(header->magic_value); + if (magic_value != IMAGE_FILE_MAGIC_VALUE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid image file magic value\n"); + return -EINVAL; + } + + memset(image_info, 0x00, sizeof(*image_info)); + + offset = sizeof(*header); + num_of_areas = le4_to_uint(header->num_of_areas); + + for (idx = 0; idx < num_of_areas; idx++) { + addr = le4_to_uint(image + offset); + descriptor = (struct area_descriptor *)(image + addr); + offset += 4; + + magic_value = le4_to_uint(descriptor->magic_value); + if (magic_value != FLASH_AREA_MAGIC_VALUE) + continue; + + length = le4_to_uint(descriptor->length); + content = (unsigned char *)descriptor + sizeof(*descriptor); + flash_addr = le4_to_uint(descriptor->flash_addr_words) * 2; + checksum = le4_to_uint(descriptor->checksum); + + if (!memcmp((char *)descriptor->id_string, + BOOT_CONFIG_ID, + strlen(BOOT_CONFIG_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Boot config checksum error\n"); + return -EINVAL; + } + image_info->boot_config.size = length; + image_info->boot_config.data = content; + image_info->boot_config.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Boot config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Boot config flash address = 0x%08x\n", + flash_addr); + } else if (!memcmp((char *)descriptor->id_string, + APP_CODE_ID, + strlen(APP_CODE_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "APP firmware checksum error\n"); + return -EINVAL; + } + image_info->app_firmware.size = length; + image_info->app_firmware.data = content; + image_info->app_firmware.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Application firmware size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Application firmware flash address = 0x%08x\n", + flash_addr); + } else if (!memcmp((char *)descriptor->id_string, + PROD_TEST_ID, + strlen(PROD_TEST_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Production test checksum error\n"); + return -EINVAL; + } + image_info->prod_test_firmware.size = length; + image_info->prod_test_firmware.data = content; + image_info->prod_test_firmware.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Production test firmware size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Production test flash address = 0x%08x\n", + flash_addr); + } else if (!memcmp((char *)descriptor->id_string, + APP_CONFIG_ID, + strlen(APP_CONFIG_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application config checksum error\n"); + return -EINVAL; + } + image_info->app_config.size = length; + image_info->app_config.data = content; + image_info->app_config.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Application config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Application config flash address = 0x%08x\n", + flash_addr); + } else if (!memcmp((char *)descriptor->id_string, + DISP_CONFIG_ID, + strlen(DISP_CONFIG_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Display config checksum error\n"); + return -EINVAL; + } + reflash_hcd->disp_cfg_update = true; + image_info->disp_config.size = length; + image_info->disp_config.data = content; + image_info->disp_config.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Display config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Display config flash address = 0x%08x\n", + flash_addr); + } + } + + return 0; +} + +static int reflash_get_fw_image(void) +{ + int retval; + const char *fw_name; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (bdata->fw_name) + fw_name = bdata->fw_name; + else + fw_name = FW_IMAGE_NAME; + + if (reflash_hcd->image == NULL) { + retval = request_firmware(&reflash_hcd->fw_entry, fw_name, + tcm_hcd->pdev->dev.parent); + if (retval < 0) { + LOGD(tcm_hcd->pdev->dev.parent, + "Failed to request %s\n", + fw_name); + return retval; + } + + LOGD(tcm_hcd->pdev->dev.parent, + "Firmware image size = %d\n", + (unsigned int)reflash_hcd->fw_entry->size); + + reflash_hcd->image = reflash_hcd->fw_entry->data; + reflash_hcd->image_size = reflash_hcd->fw_entry->size; + } + + retval = reflash_parse_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to parse firmware image\n"); + return retval; + } + + return 0; +} + +static enum update_area reflash_compare_id_info(void) +{ + enum update_area update_area; + 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; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + const unsigned char *app_config_data; + + update_area = NONE; + + if (reflash_hcd->image_info.app_config.size < sizeof(*header)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid application config in image file\n"); + goto exit; + } + + app_config_data = reflash_hcd->image_info.app_config.data; + header = (struct app_config_header *)app_config_data; + + if (reflash_hcd->force_update) { + update_area = FIRMWARE_CONFIG; + goto exit; + } + + if (tcm_hcd->id_info.mode != MODE_APPLICATION) { + update_area = FIRMWARE_CONFIG; + goto exit; + } + + image_fw_id = le4_to_uint(header->build_id); + device_fw_id = tcm_hcd->packrat_number; + + if (image_fw_id > device_fw_id) { + LOGN(tcm_hcd->pdev->dev.parent, + "Image firmware ID newer than device firmware ID\n"); + update_area = FIRMWARE_CONFIG; + goto exit; + } else if (image_fw_id < device_fw_id) { + LOGN(tcm_hcd->pdev->dev.parent, + "Image firmware ID older than device firmware ID\n"); + update_area = NONE; + goto exit; + } + + image_config_id = header->customer_config_id; + device_config_id = tcm_hcd->app_info.customer_config_id; + + for (idx = 0; idx < 16; idx++) { + if (image_config_id[idx] > device_config_id[idx]) { + LOGN(tcm_hcd->pdev->dev.parent, + "Image config ID newer than device's ID\n"); + update_area = CONFIG_ONLY; + goto exit; + } else if (image_config_id[idx] < device_config_id[idx]) { + LOGN(tcm_hcd->pdev->dev.parent, + "Image config ID older than device's ID\n"); + update_area = NONE; + goto exit; + } + } + + update_area = NONE; + +exit: + if (update_area == NONE) + LOGD(tcm_hcd->pdev->dev.parent, "No need to do reflash\n"); + else + LOGD(tcm_hcd->pdev->dev.parent, + "Updating %s\n", + update_area == FIRMWARE_CONFIG ? + "firmware and config" : + "config only"); + + return update_area; +} + +static int reflash_read_flash(unsigned int address, unsigned char *data, + unsigned int datalen) +{ + int retval; + unsigned int length_words; + unsigned int flash_addr_words; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + LOCK_BUFFER(reflash_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &reflash_hcd->out, + 6); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for reflash_hcd->out.buf\n"); + UNLOCK_BUFFER(reflash_hcd->out); + return retval; + } + + length_words = datalen / 2; + flash_addr_words = address / 2; + + reflash_hcd->out.buf[0] = (unsigned char)flash_addr_words; + reflash_hcd->out.buf[1] = (unsigned char)(flash_addr_words >> 8); + reflash_hcd->out.buf[2] = (unsigned char)(flash_addr_words >> 16); + reflash_hcd->out.buf[3] = (unsigned char)(flash_addr_words >> 24); + reflash_hcd->out.buf[4] = (unsigned char)length_words; + reflash_hcd->out.buf[5] = (unsigned char)(length_words >> 8); + + LOCK_BUFFER(reflash_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_READ_FLASH, + reflash_hcd->out.buf, + 6, + &reflash_hcd->resp.buf, + &reflash_hcd->resp.buf_size, + &reflash_hcd->resp.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_READ_FLASH)); + UNLOCK_BUFFER(reflash_hcd->resp); + UNLOCK_BUFFER(reflash_hcd->out); + return retval; + } + + UNLOCK_BUFFER(reflash_hcd->out); + + if (reflash_hcd->resp.data_length != datalen) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read requested length\n"); + UNLOCK_BUFFER(reflash_hcd->resp); + return -EIO; + } + + retval = secure_memcpy(data, + datalen, + reflash_hcd->resp.buf, + reflash_hcd->resp.buf_size, + datalen); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy read data\n"); + UNLOCK_BUFFER(reflash_hcd->resp); + return retval; + } + + UNLOCK_BUFFER(reflash_hcd->resp); + + return 0; +} + +static int reflash_read_data(enum flash_area area, bool run_app_firmware, + struct syna_tcm_buffer *output) +{ + int retval; + unsigned int temp; + unsigned int addr; + unsigned int length; + struct syna_tcm_app_info *app_info; + struct syna_tcm_boot_info *boot_info; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + switch (area) { + case CUSTOM_LCM: + case CUSTOM_OEM: + case PPDT: + retval = tcm_hcd->get_data_location(tcm_hcd, + area, + &addr, + &length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get data location\n"); + return retval; + } + break; + default: + break; + } + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + + app_info = &tcm_hcd->app_info; + boot_info = &tcm_hcd->boot_info; + + switch (area) { + case BOOT_CONFIG: + temp = le2_to_uint(boot_info->boot_config_start_block); + addr = temp * reflash_hcd->write_block_size; + length = BOOT_CONFIG_SIZE * BOOT_CONFIG_SLOTS; + break; + case APP_CONFIG: + temp = le2_to_uint(app_info->app_config_start_write_block); + addr = temp * reflash_hcd->write_block_size; + length = le2_to_uint(app_info->app_config_size); + break; + case DISP_CONFIG: + temp = le4_to_uint(boot_info->display_config_start_block); + addr = temp * reflash_hcd->write_block_size; + temp = le2_to_uint(boot_info->display_config_length_blocks); + length = temp * reflash_hcd->write_block_size; + break; + case CUSTOM_OTP: + temp = le2_to_uint(boot_info->custom_otp_start_block); + addr = temp * reflash_hcd->write_block_size; + temp = le2_to_uint(boot_info->custom_otp_length_blocks); + length = temp * reflash_hcd->write_block_size; + break; + case CUSTOM_LCM: + case CUSTOM_OEM: + case PPDT: + addr *= reflash_hcd->write_block_size; + length *= reflash_hcd->write_block_size; + break; + default: + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid data area\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (addr == 0 || length == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Data area unavailable\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + LOCK_BUFFER(reflash_hcd->read); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &reflash_hcd->read, + length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for read.buf\n"); + UNLOCK_BUFFER(reflash_hcd->read); + goto run_app_firmware; + } + + retval = reflash_read_flash(addr, reflash_hcd->read.buf, length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read from flash\n"); + UNLOCK_BUFFER(reflash_hcd->read); + goto run_app_firmware; + } + + reflash_hcd->read.data_length = length; + + if (output != NULL) { + retval = syna_tcm_alloc_mem(tcm_hcd, + output, + length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for output->buf\n"); + UNLOCK_BUFFER(reflash_hcd->read); + goto run_app_firmware; + } + + retval = secure_memcpy(output->buf, + output->buf_size, + reflash_hcd->read.buf, + reflash_hcd->read.buf_size, + length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy read data\n"); + UNLOCK_BUFFER(reflash_hcd->read); + goto run_app_firmware; + } + + output->data_length = length; + } + + UNLOCK_BUFFER(reflash_hcd->read); + + retval = 0; + +run_app_firmware: + if (!run_app_firmware) + goto exit; + + if (tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + } + +exit: + return retval; +} + +static int reflash_check_boot_config(void) +{ + unsigned int temp; + unsigned int image_addr; + unsigned int device_addr; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image_info.boot_config.size < BOOT_CONFIG_SIZE) { + LOGE(tcm_hcd->pdev->dev.parent, + "No valid boot config in image file\n"); + return -EINVAL; + } + + image_addr = reflash_hcd->image_info.boot_config.flash_addr; + + temp = le2_to_uint(tcm_hcd->boot_info.boot_config_start_block); + device_addr = temp * reflash_hcd->write_block_size; + + if (image_addr != device_addr) { + LOGE(tcm_hcd->pdev->dev.parent, + "Flash address mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_check_app_config(void) +{ + unsigned int temp; + unsigned int image_addr; + unsigned int image_size; + unsigned int device_addr; + unsigned int device_size; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image_info.app_config.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No application config in image file\n"); + return -EINVAL; + } + + image_addr = reflash_hcd->image_info.app_config.flash_addr; + image_size = reflash_hcd->image_info.app_config.size; + + temp = le2_to_uint(tcm_hcd->app_info.app_config_start_write_block); + device_addr = temp * reflash_hcd->write_block_size; + device_size = le2_to_uint(tcm_hcd->app_info.app_config_size); + + if (device_addr == 0 && device_size == 0) + return 0; + + if (image_addr != device_addr) { + LOGE(tcm_hcd->pdev->dev.parent, + "Flash address mismatch\n"); + return -EINVAL; + } + + if (image_size != device_size) { + LOGE(tcm_hcd->pdev->dev.parent, + "Config size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_check_disp_config(void) +{ + unsigned int temp; + unsigned int image_addr; + unsigned int image_size; + unsigned int device_addr; + unsigned int device_size; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image_info.disp_config.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No display config in image file\n"); + return -EINVAL; + } + + image_addr = reflash_hcd->image_info.disp_config.flash_addr; + image_size = reflash_hcd->image_info.disp_config.size; + + temp = le4_to_uint(tcm_hcd->boot_info.display_config_start_block); + device_addr = temp * reflash_hcd->write_block_size; + + temp = le2_to_uint(tcm_hcd->boot_info.display_config_length_blocks); + device_size = temp * reflash_hcd->write_block_size; + + if (image_addr != device_addr) { + LOGE(tcm_hcd->pdev->dev.parent, + "Flash address mismatch\n"); + return -EINVAL; + } + + if (image_size != device_size) { + LOGE(tcm_hcd->pdev->dev.parent, + "Config size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_check_prod_test_firmware(void) +{ + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image_info.prod_test_firmware.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No production test firmware in image file\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_check_app_firmware(void) +{ + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image_info.app_firmware.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No application firmware in image file\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_write_flash(unsigned int address, const unsigned char *data, + unsigned int datalen) +{ + 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; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + w_length = tcm_hcd->wr_chunk_size - 5; + + w_length = w_length - (w_length % reflash_hcd->write_block_size); + + w_length = MIN(w_length, reflash_hcd->max_write_payload_size); + + offset = 0; + + remaining_length = datalen; + + LOCK_BUFFER(reflash_hcd->out); + LOCK_BUFFER(reflash_hcd->resp); + + while (remaining_length) { + if (remaining_length > w_length) + xfer_length = w_length; + else + xfer_length = remaining_length; + + retval = syna_tcm_alloc_mem(tcm_hcd, + &reflash_hcd->out, + xfer_length + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for out.buf\n"); + UNLOCK_BUFFER(reflash_hcd->resp); + UNLOCK_BUFFER(reflash_hcd->out); + return retval; + } + + flash_address = address + offset; + block_address = flash_address / reflash_hcd->write_block_size; + reflash_hcd->out.buf[0] = (unsigned char)block_address; + reflash_hcd->out.buf[1] = (unsigned char)(block_address >> 8); + + retval = secure_memcpy(&reflash_hcd->out.buf[2], + reflash_hcd->out.buf_size - 2, + &data[offset], + datalen - offset, + xfer_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy write data\n"); + UNLOCK_BUFFER(reflash_hcd->resp); + UNLOCK_BUFFER(reflash_hcd->out); + return retval; + } + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_WRITE_FLASH, + reflash_hcd->out.buf, + xfer_length + 2, + &reflash_hcd->resp.buf, + &reflash_hcd->resp.buf_size, + &reflash_hcd->resp.data_length, + NULL, + WRITE_FLASH_DELAY_MS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_WRITE_FLASH)); + LOGE(tcm_hcd->pdev->dev.parent, + "Flash address = 0x%08x\n", + flash_address); + LOGE(tcm_hcd->pdev->dev.parent, + "Data length = %d\n", + xfer_length); + UNLOCK_BUFFER(reflash_hcd->resp); + UNLOCK_BUFFER(reflash_hcd->out); + return retval; + } + + offset += xfer_length; + remaining_length -= xfer_length; + } + + UNLOCK_BUFFER(reflash_hcd->resp); + UNLOCK_BUFFER(reflash_hcd->out); + + return 0; +} + +reflash_write(app_config) + +reflash_write(disp_config) + +reflash_write(prod_test_firmware) + +reflash_write(app_firmware) + +static int reflash_erase_flash(unsigned int page_start, unsigned int page_count) +{ + int retval; + unsigned char out_buf[2]; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + out_buf[0] = (unsigned char)page_start; + out_buf[1] = (unsigned char)page_count; + + LOCK_BUFFER(reflash_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_ERASE_FLASH, + out_buf, + sizeof(out_buf), + &reflash_hcd->resp.buf, + &reflash_hcd->resp.buf_size, + &reflash_hcd->resp.data_length, + NULL, + ERASE_FLASH_DELAY_MS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_ERASE_FLASH)); + UNLOCK_BUFFER(reflash_hcd->resp); + return retval; + } + + UNLOCK_BUFFER(reflash_hcd->resp); + + return 0; +} + +reflash_erase(app_config) + +reflash_erase(disp_config) + +reflash_erase(prod_test_firmware) + +reflash_erase(app_firmware) + +static int reflash_update_custom_otp(const unsigned char *data, + unsigned int offset, unsigned int datalen) +{ + int retval; + unsigned int temp; + unsigned int addr; + unsigned int length; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + + tcm_hcd->update_watchdog(tcm_hcd, false); + + temp = le2_to_uint(tcm_hcd->boot_info.custom_otp_start_block); + addr = temp * reflash_hcd->write_block_size; + + temp = le2_to_uint(tcm_hcd->boot_info.custom_otp_length_blocks); + length = temp * reflash_hcd->write_block_size; + + if (addr == 0 || length == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Data area unavailable\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (datalen + offset > length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid data length\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + retval = reflash_write_flash(addr + offset, + data, + datalen); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to flash\n"); + goto run_app_firmware; + } + + retval = 0; + +run_app_firmware: + if (tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + } + + tcm_hcd->update_watchdog(tcm_hcd, true); + + return retval; +} + +static int reflash_update_custom_lcm(const unsigned char *data, + unsigned int offset, unsigned int datalen) +{ + int retval; + unsigned int addr; + unsigned int length; + unsigned int page_start; + unsigned int page_count; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = tcm_hcd->get_data_location(tcm_hcd, + CUSTOM_LCM, + &addr, + &length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get data location\n"); + return retval; + } + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + + tcm_hcd->update_watchdog(tcm_hcd, false); + + addr *= reflash_hcd->write_block_size; + length *= reflash_hcd->write_block_size; + + if (addr == 0 || length == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Data area unavailable\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (datalen + offset > length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid data length\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (offset == 0) { + page_start = addr / reflash_hcd->page_size; + + page_count = ceil_div(length, reflash_hcd->page_size); + + retval = reflash_erase_flash(page_start, page_count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase flash pages\n"); + goto run_app_firmware; + } + } + + retval = reflash_write_flash(addr + offset, + data, + datalen); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to flash\n"); + goto run_app_firmware; + } + + retval = 0; + +run_app_firmware: + if (tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + } + + tcm_hcd->update_watchdog(tcm_hcd, true); + + return retval; +} + +static int reflash_update_custom_oem(const unsigned char *data, + unsigned int offset, unsigned int datalen) +{ + int retval; + unsigned int addr; + unsigned int length; + unsigned int page_start; + unsigned int page_count; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = tcm_hcd->get_data_location(tcm_hcd, + CUSTOM_OEM, + &addr, + &length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get data location\n"); + return retval; + } + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + + tcm_hcd->update_watchdog(tcm_hcd, false); + + addr *= reflash_hcd->write_block_size; + length *= reflash_hcd->write_block_size; + + if (addr == 0 || length == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Data area unavailable\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (datalen + offset > length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid data length\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (offset == 0) { + page_start = addr / reflash_hcd->page_size; + + page_count = ceil_div(length, reflash_hcd->page_size); + + retval = reflash_erase_flash(page_start, page_count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase flash pages\n"); + goto run_app_firmware; + } + } + + retval = reflash_write_flash(addr + offset, + data, + datalen); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to flash\n"); + goto run_app_firmware; + } + + retval = 0; + +run_app_firmware: + if (tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + } + + tcm_hcd->update_watchdog(tcm_hcd, true); + + return retval; +} + +static int reflash_update_boot_config(bool lock) +{ + int retval; + unsigned char slot_used; + unsigned int idx; + unsigned int addr = 0; + struct boot_config *data; + struct boot_config *last_slot; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + + tcm_hcd->update_watchdog(tcm_hcd, false); + + retval = reflash_check_boot_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed boot_config partition check\n"); + goto reset; + } + + retval = reflash_read_data(BOOT_CONFIG, false, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read boot config\n"); + goto reset; + } + + LOCK_BUFFER(reflash_hcd->read); + + data = (struct boot_config *)reflash_hcd->read.buf; + last_slot = data + (BOOT_CONFIG_SLOTS - 1); + slot_used = tcm_hcd->id_info.mode == MODE_TDDI_BOOTLOADER ? 0 : 1; + + if (last_slot->used == slot_used) { + LOGE(tcm_hcd->pdev->dev.parent, + "Boot config already locked down\n"); + UNLOCK_BUFFER(reflash_hcd->read); + goto reset; + } + + if (lock) { + idx = BOOT_CONFIG_SLOTS - 1; + } else { + for (idx = 0; idx < BOOT_CONFIG_SLOTS; idx++) { + if (data->used == slot_used) { + data++; + continue; + } else { + break; + } + } + } + + UNLOCK_BUFFER(reflash_hcd->read); + + if (idx == BOOT_CONFIG_SLOTS) { + LOGE(tcm_hcd->pdev->dev.parent, + "No free boot config slot available\n"); + goto reset; + } + + addr += idx * BOOT_CONFIG_SIZE; + + retval = reflash_write_flash(addr, + reflash_hcd->image_info.boot_config.data, + BOOT_CONFIG_SIZE); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to flash\n"); + goto reset; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Slot %d updated with new boot config\n", + idx); + + retval = 0; + +reset: + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + + tcm_hcd->update_watchdog(tcm_hcd, true); + + return retval; +} + +reflash_update(app_config) + +reflash_update(disp_config) + +reflash_update(prod_test_firmware) + +reflash_update(app_firmware) + +static int reflash_do_reflash(void) +{ + int retval; + enum update_area update_area; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = reflash_get_fw_image(); + if (retval < 0) { + LOGD(tcm_hcd->pdev->dev.parent, + "Failed to get firmware image\n"); + goto exit; + } + + LOGD(tcm_hcd->pdev->dev.parent, + "Start of reflash\n"); + + atomic_set(&tcm_hcd->firmware_flashing, 1); + + update_area = reflash_compare_id_info(); + + switch (update_area) { + case FIRMWARE_CONFIG: + retval = reflash_update_app_firmware(false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash application firmware\n"); + goto exit; + } + memset(&tcm_hcd->app_info, 0x00, sizeof(tcm_hcd->app_info)); + if (tcm_hcd->features.dual_firmware) { + retval = reflash_update_prod_test_firmware(false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash production test\n"); + goto exit; + } + } + case CONFIG_ONLY: + if (reflash_hcd->disp_cfg_update) { + retval = reflash_update_disp_config(false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash display config\n"); + goto exit; + } + } + retval = reflash_update_app_config(true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash application config\n"); + goto exit; + } + break; + case NONE: + default: + break; + } + + LOGD(tcm_hcd->pdev->dev.parent, + "End of reflash\n"); + + retval = 0; + +exit: + if (reflash_hcd->fw_entry) { + release_firmware(reflash_hcd->fw_entry); + reflash_hcd->fw_entry = NULL; + reflash_hcd->image = NULL; + reflash_hcd->image_size = 0; + } + + atomic_set(&tcm_hcd->firmware_flashing, 0); + wake_up_interruptible(&tcm_hcd->reflash_wq); + return retval; +} + +#ifdef STARTUP_REFLASH +static void reflash_startup_work(struct work_struct *work) +{ + int retval; +#if defined(CONFIG_DRM) || defined(CONFIG_FB) + unsigned int timeout; +#endif + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + +#if defined(CONFIG_DRM) || defined(CONFIG_FB) + timeout = FB_READY_TIMEOUT_S * 1000 / FB_READY_WAIT_MS; + + while (tcm_hcd->fb_ready != FB_READY_COUNT - 1) { + if (timeout == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Timed out waiting for FB ready\n"); + return; + } + msleep(FB_READY_WAIT_MS); + timeout--; + } +#endif + + pm_stay_awake(&tcm_hcd->pdev->dev); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_do_reflash(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reflash\n"); + } + + mutex_unlock(&reflash_hcd->reflash_mutex); + + pm_relax(&tcm_hcd->pdev->dev); +} +#endif + +static int reflash_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval = 0; + int idx; + + reflash_hcd = kzalloc(sizeof(*reflash_hcd), GFP_KERNEL); + if (!reflash_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for reflash_hcd\n"); + return -ENOMEM; + } + + reflash_hcd->image_buf = kzalloc(IMAGE_BUF_SIZE, GFP_KERNEL); + if (!reflash_hcd->image_buf) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for image_buf\n"); + goto err_allocate_memory; + } + + reflash_hcd->tcm_hcd = tcm_hcd; + + reflash_hcd->force_update = FORCE_REFLASH; + + mutex_init(&reflash_hcd->reflash_mutex); + + INIT_BUFFER(reflash_hcd->out, false); + INIT_BUFFER(reflash_hcd->resp, false); + INIT_BUFFER(reflash_hcd->read, false); + +#ifdef STARTUP_REFLASH + reflash_hcd->workqueue = + create_singlethread_workqueue("syna_tcm_reflash"); + INIT_WORK(&reflash_hcd->work, reflash_startup_work); + queue_work(reflash_hcd->workqueue, &reflash_hcd->work); +#endif + + if (!ENABLE_SYSFS_INTERFACE) + return 0; + + reflash_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME, + tcm_hcd->sysfs_dir); + if (!reflash_hcd->sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dir; + } + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + retval = sysfs_create_file(reflash_hcd->sysfs_dir, + &(*attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs file\n"); + goto err_sysfs_create_file; + } + } + + retval = sysfs_create_bin_file(reflash_hcd->sysfs_dir, &bin_attrs[0]); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs bin file\n"); + goto err_sysfs_create_bin_file; + } + + reflash_hcd->custom_dir = kobject_create_and_add(CUSTOM_DIR_NAME, + reflash_hcd->sysfs_dir); + if (!reflash_hcd->custom_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create custom sysfs directory\n"); + retval = -EINVAL; + goto err_custom_sysfs_create_dir; + } + + for (idx = 1; idx < ARRAY_SIZE(bin_attrs); idx++) { + retval = sysfs_create_bin_file(reflash_hcd->custom_dir, + &bin_attrs[idx]); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs bin file\n"); + goto err_custom_sysfs_create_bin_file; + } + } + + tcm_hcd->read_flash_data = reflash_read_data; + + return 0; + +err_custom_sysfs_create_bin_file: + for (idx--; idx > 0; idx--) + sysfs_remove_bin_file(reflash_hcd->custom_dir, &bin_attrs[idx]); + + kobject_put(reflash_hcd->custom_dir); + + idx = ARRAY_SIZE(attrs); + +err_custom_sysfs_create_dir: + sysfs_remove_bin_file(reflash_hcd->sysfs_dir, &bin_attrs[0]); + +err_sysfs_create_bin_file: +err_sysfs_create_file: + for (idx--; idx >= 0; idx--) + sysfs_remove_file(reflash_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(reflash_hcd->sysfs_dir); + +err_sysfs_create_dir: +err_allocate_memory: + kfree(reflash_hcd->image_buf); + + RELEASE_BUFFER(reflash_hcd->read); + RELEASE_BUFFER(reflash_hcd->resp); + RELEASE_BUFFER(reflash_hcd->out); + + kfree(reflash_hcd); + reflash_hcd = NULL; + + return retval; +} + +static int reflash_remove(struct syna_tcm_hcd *tcm_hcd) +{ + int idx; + + if (!reflash_hcd) + goto exit; + + tcm_hcd->read_flash_data = NULL; + + if (ENABLE_SYSFS_INTERFACE) { + for (idx = 1; idx < ARRAY_SIZE(bin_attrs); idx++) { + sysfs_remove_bin_file(reflash_hcd->custom_dir, + &bin_attrs[idx]); + } + + kobject_put(reflash_hcd->custom_dir); + + sysfs_remove_bin_file(reflash_hcd->sysfs_dir, &bin_attrs[0]); + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + sysfs_remove_file(reflash_hcd->sysfs_dir, + &(*attrs[idx]).attr); + } + + kobject_put(reflash_hcd->sysfs_dir); + } + +#ifdef STARTUP_REFLASH + cancel_work_sync(&reflash_hcd->work); + flush_workqueue(reflash_hcd->workqueue); + destroy_workqueue(reflash_hcd->workqueue); +#endif + + kfree(reflash_hcd->image_buf); + + RELEASE_BUFFER(reflash_hcd->read); + RELEASE_BUFFER(reflash_hcd->resp); + RELEASE_BUFFER(reflash_hcd->out); + + kfree(reflash_hcd); + reflash_hcd = NULL; + +exit: + complete(&reflash_remove_complete); + + return 0; +} + +static int reflash_reset(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!reflash_hcd) { + retval = reflash_init(tcm_hcd); + return retval; + } + + return 0; +} + +static struct syna_tcm_module_cb reflash_module = { + .type = TCM_REFLASH, + .init = reflash_init, + .remove = reflash_remove, + .syncbox = NULL, + .asyncbox = NULL, + .reset = reflash_reset, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init reflash_module_init(void) +{ + return syna_tcm_add_module(&reflash_module, true); +} + +static void __exit reflash_module_exit(void) +{ + syna_tcm_add_module(&reflash_module, false); + + wait_for_completion(&reflash_remove_complete); +} + +module_init(reflash_module_init); +module_exit(reflash_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Reflash Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_tcm/synaptics_tcm_spi.c b/synaptics_tcm/synaptics_tcm_spi.c new file mode 100644 index 0000000000..ee8751ac52 --- /dev/null +++ b/synaptics_tcm/synaptics_tcm_spi.c @@ -0,0 +1,670 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2019 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2019 Scott Lin + * + * 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. + */ + +#include +#include +#include "synaptics_tcm_core.h" + +static unsigned char *buf; + +static unsigned int buf_size; + +static struct spi_transfer *xfer; + +static struct syna_tcm_bus_io bus_io; + +static struct syna_tcm_hw_interface hw_if; + +static struct platform_device *syna_tcm_spi_device; + +#ifdef CONFIG_OF +static int parse_dt(struct device *dev, struct syna_tcm_board_data *bdata) +{ + int retval; + u32 value; + struct property *prop; + struct device_node *np = dev->of_node; + const char *name; + + prop = of_find_property(np, "synaptics,irq-gpio", NULL); + if (prop && prop->length) { + bdata->irq_gpio = of_get_named_gpio_flags(np, + "synaptics,irq-gpio", 0, + (enum of_gpio_flags *)&bdata->irq_flags); + } else { + bdata->irq_gpio = -1; + } + + retval = of_property_read_u32(np, "synaptics,irq-on-state", &value); + if (retval < 0) + bdata->irq_on_state = 0; + else + bdata->irq_on_state = value; + + retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name); + if (retval < 0) + bdata->pwr_reg_name = NULL; + else + bdata->pwr_reg_name = name; + + retval = of_property_read_string(np, "synaptics,bus-reg-name", &name); + if (retval < 0) + bdata->bus_reg_name = NULL; + else + bdata->bus_reg_name = name; + + prop = of_find_property(np, "synaptics,power-gpio", NULL); + if (prop && prop->length) { + bdata->power_gpio = of_get_named_gpio_flags(np, + "synaptics,power-gpio", 0, NULL); + } else { + bdata->power_gpio = -1; + } + + prop = of_find_property(np, "synaptics,power-on-state", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-on-state", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,power-on-state\n"); + return retval; + } + bdata->power_on_state = value; + } else { + bdata->power_on_state = 0; + } + + prop = of_find_property(np, "synaptics,power-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-delay-ms", + &value); + if (retval < 0) { + LOGE(dev, "Failed to read synaptics,power-delay-ms\n"); + return retval; + } + bdata->power_delay_ms = value; + } else { + bdata->power_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-gpio", NULL); + if (prop && prop->length) { + bdata->reset_gpio = of_get_named_gpio_flags(np, + "synaptics,reset-gpio", 0, NULL); + } else { + bdata->reset_gpio = -1; + } + + prop = of_find_property(np, "synaptics,reset-on-state", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-on-state", + &value); + if (retval < 0) { + LOGE(dev, "Failed to read synaptics,reset-on-state\n"); + return retval; + } + bdata->reset_on_state = value; + } else { + bdata->reset_on_state = 0; + } + + prop = of_find_property(np, "synaptics,reset-active-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-active-ms", + &value); + if (retval < 0) { + LOGE(dev, "Failed to read synaptics,reset-active-ms\n"); + return retval; + } + bdata->reset_active_ms = value; + } else { + bdata->reset_active_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-delay-ms", + &value); + if (retval < 0) { + LOGE(dev, "Unable to read synaptics,reset-delay-ms\n"); + return retval; + } + bdata->reset_delay_ms = value; + } else { + bdata->reset_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,x-flip", NULL); + bdata->x_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,y-flip", NULL); + bdata->y_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,swap-axes", NULL); + bdata->swap_axes = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,byte-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,byte-delay-us", + &value); + if (retval < 0) { + LOGE(dev, "Unable to read synaptics,byte-delay-us\n"); + return retval; + } + bdata->byte_delay_us = value; + } else { + bdata->byte_delay_us = 0; + } + + prop = of_find_property(np, "synaptics,block-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,block-delay-us", + &value); + if (retval < 0) { + LOGE(dev, "Unable to read synaptics,block-delay-us\n"); + return retval; + } + bdata->block_delay_us = value; + } else { + bdata->block_delay_us = 0; + } + + prop = of_find_property(np, "synaptics,spi-mode", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,spi-mode", + &value); + if (retval < 0) { + LOGE(dev, "Unable to read synaptics,spi-mode\n"); + return retval; + } + bdata->spi_mode = value; + + } else { + bdata->spi_mode = 0; + } + + prop = of_find_property(np, "synaptics,ubl-max-freq", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,ubl-max-freq", + &value); + if (retval < 0) { + LOGE(dev, "Unable to read synaptics,ubl-max-freq\n"); + return retval; + } + bdata->ubl_max_freq = value; + } else { + bdata->ubl_max_freq = 0; + } + + prop = of_find_property(np, "synaptics,ubl-byte-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,ubl-byte-delay-us", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,ubl-byte-delay-us\n"); + return retval; + } + bdata->ubl_byte_delay_us = value; + } else { + bdata->ubl_byte_delay_us = 0; + } + + return 0; +} +#endif + +static int syna_tcm_spi_alloc_mem(struct syna_tcm_hcd *tcm_hcd, + unsigned int count, unsigned int size) +{ + static unsigned int xfer_count; + struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent); + + if (count > xfer_count) { + kfree(xfer); + xfer = kcalloc(count, sizeof(*xfer), GFP_KERNEL); + if (!xfer) { + LOGE(&spi->dev, + "Failed to allocate memory for xfer\n"); + xfer_count = 0; + return -ENOMEM; + } + xfer_count = count; + } else { + memset(xfer, 0, count * sizeof(*xfer)); + } + + if (size > buf_size) { + if (buf_size) + kfree(buf); + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + LOGE(&spi->dev, + "Failed to allocate memory for buf\n"); + buf_size = 0; + return -ENOMEM; + } + buf_size = size; + } + + return 0; +} + +static int syna_tcm_spi_rmi_read(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned int idx; + unsigned int mode; + unsigned int byte_count; + struct spi_message msg; + struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + spi_message_init(&msg); + + byte_count = length + 2; + + if (bdata->ubl_byte_delay_us == 0) + retval = syna_tcm_spi_alloc_mem(tcm_hcd, 2, byte_count); + else + retval = syna_tcm_spi_alloc_mem(tcm_hcd, byte_count, 3); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to allocate memory\n"); + goto exit; + } + + buf[0] = (unsigned char)(addr >> 8) | 0x80; + buf[1] = (unsigned char)addr; + + if (bdata->ubl_byte_delay_us == 0) { + xfer[0].len = 2; + xfer[0].tx_buf = buf; + xfer[0].speed_hz = bdata->ubl_max_freq; + spi_message_add_tail(&xfer[0], &msg); + memset(&buf[2], 0xff, length); + xfer[1].len = length; + xfer[1].tx_buf = &buf[2]; + xfer[1].rx_buf = data; + if (bdata->block_delay_us) + xfer[1].delay_usecs = bdata->block_delay_us; + xfer[1].speed_hz = bdata->ubl_max_freq; + spi_message_add_tail(&xfer[1], &msg); + } else { + buf[2] = 0xff; + for (idx = 0; idx < byte_count; idx++) { + xfer[idx].len = 1; + if (idx < 2) { + xfer[idx].tx_buf = &buf[idx]; + } else { + xfer[idx].tx_buf = &buf[2]; + xfer[idx].rx_buf = &data[idx - 2]; + } + xfer[idx].delay_usecs = bdata->ubl_byte_delay_us; + if (bdata->block_delay_us && (idx == byte_count - 1)) + xfer[idx].delay_usecs = bdata->block_delay_us; + xfer[idx].speed_hz = bdata->ubl_max_freq; + spi_message_add_tail(&xfer[idx], &msg); + } + } + + mode = spi->mode; + spi->mode = SPI_MODE_3; + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = length; + } else { + LOGE(&spi->dev, + "Failed to complete SPI transfer, error = %d\n", + retval); + } + + spi->mode = mode; + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_spi_rmi_write(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned int mode; + unsigned int byte_count; + struct spi_message msg; + struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + spi_message_init(&msg); + + byte_count = length + 2; + + retval = syna_tcm_spi_alloc_mem(tcm_hcd, 1, byte_count); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to allocate memory\n"); + goto exit; + } + + buf[0] = (unsigned char)(addr >> 8) & ~0x80; + buf[1] = (unsigned char)addr; + retval = secure_memcpy(&buf[2], + buf_size - 2, + data, + length, + length); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to copy write data\n"); + goto exit; + } + + xfer[0].len = byte_count; + xfer[0].tx_buf = buf; + if (bdata->block_delay_us) + xfer[0].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[0], &msg); + + mode = spi->mode; + spi->mode = SPI_MODE_3; + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = length; + } else { + LOGE(&spi->dev, + "Failed to complete SPI transfer, error = %d\n", + retval); + } + + spi->mode = mode; + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_spi_read(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length) +{ + int retval; + unsigned int idx; + struct spi_message msg; + struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + spi_message_init(&msg); + + if (bdata->byte_delay_us == 0) + retval = syna_tcm_spi_alloc_mem(tcm_hcd, 1, length); + else + retval = syna_tcm_spi_alloc_mem(tcm_hcd, length, 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory\n"); + goto exit; + } + + if (bdata->byte_delay_us == 0) { + memset(buf, 0xff, length); + xfer[0].len = length; + xfer[0].tx_buf = buf; + xfer[0].rx_buf = data; + if (bdata->block_delay_us) + xfer[0].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[0], &msg); + } else { + buf[0] = 0xff; + for (idx = 0; idx < length; idx++) { + xfer[idx].len = 1; + xfer[idx].tx_buf = buf; + xfer[idx].rx_buf = &data[idx]; + xfer[idx].delay_usecs = bdata->byte_delay_us; + if (bdata->block_delay_us && (idx == length - 1)) + xfer[idx].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[idx], &msg); + } + } + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = length; + } else { + LOGE(&spi->dev, + "Failed to complete SPI transfer, error = %d\n", + retval); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_spi_write(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length) +{ + int retval; + unsigned int idx; + struct spi_message msg; + struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + spi_message_init(&msg); + + if (bdata->byte_delay_us == 0) + retval = syna_tcm_spi_alloc_mem(tcm_hcd, 1, 0); + else + retval = syna_tcm_spi_alloc_mem(tcm_hcd, length, 0); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to allocate memory\n"); + goto exit; + } + + if (bdata->byte_delay_us == 0) { + xfer[0].len = length; + xfer[0].tx_buf = data; + if (bdata->block_delay_us) + xfer[0].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[0], &msg); + } else { + for (idx = 0; idx < length; idx++) { + xfer[idx].len = 1; + xfer[idx].tx_buf = &data[idx]; + xfer[idx].delay_usecs = bdata->byte_delay_us; + if (bdata->block_delay_us && (idx == length - 1)) + xfer[idx].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[idx], &msg); + } + } + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = length; + } else { + LOGE(&spi->dev, + "Failed to complete SPI transfer, error = %d\n", + retval); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_spi_probe(struct spi_device *spi) +{ + int retval; + + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { + LOGE(&spi->dev, + "Full duplex not supported by host\n"); + return -EIO; + } + + syna_tcm_spi_device = platform_device_alloc(PLATFORM_DRIVER_NAME, 0); + if (!syna_tcm_spi_device) { + LOGE(&spi->dev, + "Failed to allocate platform device\n"); + return -ENOMEM; + } + +#ifdef CONFIG_OF + hw_if.bdata = devm_kzalloc(&spi->dev, sizeof(*hw_if.bdata), GFP_KERNEL); + if (!hw_if.bdata) { + LOGE(&spi->dev, + "Failed to allocate memory for board data\n"); + return -ENOMEM; + } + parse_dt(&spi->dev, hw_if.bdata); +#else + hw_if.bdata = spi->dev.platform_data; +#endif + + switch (hw_if.bdata->spi_mode) { + case 0: + spi->mode = SPI_MODE_0; + break; + case 1: + spi->mode = SPI_MODE_1; + break; + case 2: + spi->mode = SPI_MODE_2; + break; + case 3: + spi->mode = SPI_MODE_3; + break; + } + + bus_io.type = BUS_SPI; + bus_io.read = syna_tcm_spi_read; + bus_io.write = syna_tcm_spi_write; + bus_io.rmi_read = syna_tcm_spi_rmi_read; + bus_io.rmi_write = syna_tcm_spi_rmi_write; + + hw_if.bus_io = &bus_io; + + spi->bits_per_word = 8; + + retval = spi_setup(spi); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to set up SPI protocol driver\n"); + return retval; + } + + syna_tcm_spi_device->dev.parent = &spi->dev; + syna_tcm_spi_device->dev.platform_data = &hw_if; + + retval = platform_device_add(syna_tcm_spi_device); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to add platform device\n"); + return retval; + } + + return 0; +} + +static int syna_tcm_spi_remove(struct spi_device *spi) +{ + syna_tcm_spi_device->dev.platform_data = NULL; + + platform_device_unregister(syna_tcm_spi_device); + + return 0; +} + +static const struct spi_device_id syna_tcm_id_table[] = { + {SPI_MODULE_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(spi, syna_tcm_id_table); + +#ifdef CONFIG_OF +static const struct of_device_id syna_tcm_of_match_table[] = { + { + .compatible = "synaptics,tcm-spi", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, syna_tcm_of_match_table); +#else +#define syna_tcm_of_match_table NULL +#endif + +static struct spi_driver syna_tcm_spi_driver = { + .driver = { + .name = SPI_MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = syna_tcm_of_match_table, + }, + .probe = syna_tcm_spi_probe, + .remove = syna_tcm_spi_remove, + .id_table = syna_tcm_id_table, +}; + +int syna_tcm_bus_init_spi(void) +{ + return spi_register_driver(&syna_tcm_spi_driver); +} +EXPORT_SYMBOL(syna_tcm_bus_init_spi); + +void syna_tcm_bus_exit_spi(void) +{ + kfree(buf); + + kfree(xfer); + + spi_unregister_driver(&syna_tcm_spi_driver); +} +EXPORT_SYMBOL(syna_tcm_bus_exit_spi); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM SPI Bus Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_tcm/synaptics_tcm_testing.c b/synaptics_tcm/synaptics_tcm_testing.c new file mode 100644 index 0000000000..b1921f779c --- /dev/null +++ b/synaptics_tcm/synaptics_tcm_testing.c @@ -0,0 +1,1938 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2019 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2019 Scott Lin + * + * 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. + */ + +#include +#include "synaptics_tcm_core.h" +#include "synaptics_tcm_testing.h" + +#define SYSFS_DIR_NAME "testing" + +#define REPORT_TIMEOUT_MS 500 + +#define testing_sysfs_show(t_name) \ +static ssize_t testing_sysfs_##t_name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + int retval; \ + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; \ +\ + mutex_lock(&tcm_hcd->extif_mutex); \ +\ + retval = testing_##t_name(); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to do "#t_name" test\n"); \ + goto exit; \ + } \ +\ + retval = snprintf(buf, PAGE_SIZE, \ + "%s\n", \ + testing_hcd->result ? "Passed" : "Failed"); \ +\ +exit: \ + mutex_unlock(&tcm_hcd->extif_mutex); \ +\ + return retval; \ +} + +enum test_code { + TEST_TRX_TRX_SHORTS = 0, + TEST_TRX_SENSOR_OPENS = 1, + TEST_TRX_GROUND_SHORTS = 2, + TEST_DYNAMIC_RANGE = 7, + TEST_OPEN_SHORT_DETECTOR = 8, + TEST_NOISE = 10, + TEST_PT11 = 11, + TEST_PT12 = 12, + TEST_PT13 = 13, + TEST_DYNAMIC_RANGE_DOZE = 14, + TEST_NOISE_DOZE = 15, +}; + +struct testing_hcd { + bool result; + unsigned char report_type; + unsigned int report_index; + unsigned int num_of_reports; + struct kobject *sysfs_dir; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_buffer report; + struct syna_tcm_buffer process; + struct syna_tcm_buffer output; + struct syna_tcm_hcd *tcm_hcd; + int (*collect_reports)(enum report_type report_type, + unsigned int num_of_reports); +}; + +DECLARE_COMPLETION(report_complete); + +DECLARE_COMPLETION(testing_remove_complete); + +static struct testing_hcd *testing_hcd; + +static int testing_dynamic_range(void); + +static int testing_dynamic_range_lpwg(void); + +static int testing_dynamic_range_doze(void); + +static int testing_noise(void); + +static int testing_noise_lpwg(void); + +static int testing_noise_doze(void); + +static int testing_open_short_detector(void); + +static int testing_pt11(void); + +static int testing_pt12(void); + +static int testing_pt13(void); + +static int testing_reset_open(void); + +static int testing_lockdown(void); + +static int testing_trx(enum test_code test_code); + +SHOW_PROTOTYPE(testing, dynamic_range); +SHOW_PROTOTYPE(testing, dynamic_range_lpwg); +SHOW_PROTOTYPE(testing, dynamic_range_doze); +SHOW_PROTOTYPE(testing, noise); +SHOW_PROTOTYPE(testing, noise_lpwg); +SHOW_PROTOTYPE(testing, noise_doze); +SHOW_PROTOTYPE(testing, open_short_detector); +SHOW_PROTOTYPE(testing, pt11); +SHOW_PROTOTYPE(testing, pt12); +SHOW_PROTOTYPE(testing, pt13); +SHOW_PROTOTYPE(testing, reset_open); +SHOW_PROTOTYPE(testing, lockdown); +SHOW_PROTOTYPE(testing, trx_trx_shorts); +SHOW_PROTOTYPE(testing, trx_sensor_opens); +SHOW_PROTOTYPE(testing, trx_ground_shorts); +SHOW_PROTOTYPE(testing, size); + +static struct device_attribute *attrs[] = { + ATTRIFY(dynamic_range), + ATTRIFY(dynamic_range_lpwg), + ATTRIFY(dynamic_range_doze), + ATTRIFY(noise), + ATTRIFY(noise_lpwg), + ATTRIFY(noise_doze), + ATTRIFY(open_short_detector), + ATTRIFY(pt11), + ATTRIFY(pt12), + ATTRIFY(pt13), + ATTRIFY(reset_open), + ATTRIFY(lockdown), + ATTRIFY(trx_trx_shorts), + ATTRIFY(trx_sensor_opens), + ATTRIFY(trx_ground_shorts), + ATTRIFY(size), +}; + +static ssize_t testing_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static struct bin_attribute bin_attr = { + .attr = { + .name = "data", + .mode = 0444, + }, + .size = 0, + .read = testing_sysfs_data_show, +}; + +testing_sysfs_show(dynamic_range) + +testing_sysfs_show(dynamic_range_lpwg) + +testing_sysfs_show(dynamic_range_doze) + +testing_sysfs_show(noise) + +testing_sysfs_show(noise_lpwg) + +testing_sysfs_show(noise_doze) + +testing_sysfs_show(open_short_detector) + +testing_sysfs_show(pt11) + +testing_sysfs_show(pt12) + +testing_sysfs_show(pt13) + +testing_sysfs_show(reset_open) + +testing_sysfs_show(lockdown) + +static ssize_t testing_sysfs_trx_trx_shorts_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = testing_trx(TEST_TRX_TRX_SHORTS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do TRX-TRX shorts test\n"); + goto exit; + } + + retval = snprintf(buf, PAGE_SIZE, + "%s\n", + testing_hcd->result ? "Passed" : "Failed"); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t testing_sysfs_trx_sensor_opens_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = testing_trx(TEST_TRX_SENSOR_OPENS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do TRX-sensor opens test\n"); + goto exit; + } + + retval = snprintf(buf, PAGE_SIZE, + "%s\n", + testing_hcd->result ? "Passed" : "Failed"); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t testing_sysfs_trx_ground_shorts_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = testing_trx(TEST_TRX_GROUND_SHORTS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do TRX-ground shorts test\n"); + goto exit; + } + + retval = snprintf(buf, PAGE_SIZE, + "%s\n", + testing_hcd->result ? "Passed" : "Failed"); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t testing_sysfs_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + LOCK_BUFFER(testing_hcd->output); + + retval = snprintf(buf, PAGE_SIZE, + "%u\n", + testing_hcd->output.data_length); + + UNLOCK_BUFFER(testing_hcd->output); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t testing_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int readlen; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + LOCK_BUFFER(testing_hcd->output); + + readlen = MIN(count, testing_hcd->output.data_length - pos); + + retval = secure_memcpy(buf, + count, + &testing_hcd->output.buf[pos], + testing_hcd->output.buf_size - pos, + readlen); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy report data\n"); + } else { + retval = readlen; + } + + UNLOCK_BUFFER(testing_hcd->output); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static int testing_run_prod_test_item(enum test_code test_code) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + if (tcm_hcd->features.dual_firmware && + tcm_hcd->id_info.mode != MODE_PRODUCTION_TEST) { + retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_PRODUCTION_TEST); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run production test firmware\n"); + return retval; + } + } else if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application firmware not running\n"); + return -ENODEV; + } + + LOCK_BUFFER(testing_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->out, + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd->out.buf\n"); + UNLOCK_BUFFER(testing_hcd->out); + return retval; + } + + testing_hcd->out.buf[0] = test_code; + + LOCK_BUFFER(testing_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_PRODUCTION_TEST, + testing_hcd->out.buf, + 1, + &testing_hcd->resp.buf, + &testing_hcd->resp.buf_size, + &testing_hcd->resp.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_PRODUCTION_TEST)); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + return retval; + } + + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + + return 0; +} + +static int testing_collect_reports(enum report_type report_type, + unsigned int num_of_reports) +{ + int retval; + bool completed; + unsigned int timeout; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + testing_hcd->report_index = 0; + testing_hcd->report_type = report_type; + testing_hcd->num_of_reports = num_of_reports; + + reinit_completion(&report_complete); + + LOCK_BUFFER(testing_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->out, + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd->out.buf\n"); + UNLOCK_BUFFER(testing_hcd->out); + goto exit; + } + + testing_hcd->out.buf[0] = testing_hcd->report_type; + + LOCK_BUFFER(testing_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_ENABLE_REPORT, + testing_hcd->out.buf, + 1, + &testing_hcd->resp.buf, + &testing_hcd->resp.buf_size, + &testing_hcd->resp.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_ENABLE_REPORT)); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + goto exit; + } + + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + + completed = false; + timeout = REPORT_TIMEOUT_MS * num_of_reports; + + retval = wait_for_completion_timeout(&report_complete, + msecs_to_jiffies(timeout)); + if (retval == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Timed out waiting for report collection\n"); + } else { + completed = true; + } + + LOCK_BUFFER(testing_hcd->out); + + testing_hcd->out.buf[0] = testing_hcd->report_type; + + LOCK_BUFFER(testing_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_DISABLE_REPORT, + testing_hcd->out.buf, + 1, + &testing_hcd->resp.buf, + &testing_hcd->resp.buf_size, + &testing_hcd->resp.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_DISABLE_REPORT)); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + goto exit; + } + + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + + if (completed) + retval = 0; + else + retval = -EIO; + +exit: + testing_hcd->report_type = 0; + + return retval; +} + +static void testing_get_frame_size_words(unsigned int *size, bool image_only) +{ + unsigned int rows; + unsigned int cols; + unsigned int hybrid; + unsigned int buttons; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + hybrid = le2_to_uint(app_info->has_hybrid_data); + buttons = le2_to_uint(app_info->num_of_buttons); + + *size = rows * cols; + + if (!image_only) { + if (hybrid) + *size += rows + cols; + *size += buttons; + } +} + +static void testing_doze_frame_output(unsigned int rows, unsigned int cols) +{ + int retval; + unsigned int data_size; + unsigned int header_size; + unsigned int output_size; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + header_size = 2; + + data_size = rows * cols; + + if (le2_to_uint(app_info->num_of_buttons)) + data_size++; + + output_size = header_size + data_size * 2; + + LOCK_BUFFER(testing_hcd->output); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->output, + output_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd->output.buf\n"); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + testing_hcd->output.buf[0] = rows; + testing_hcd->output.buf[1] = cols; + + output_size = header_size; + + LOCK_BUFFER(testing_hcd->resp); + + retval = secure_memcpy(testing_hcd->output.buf + header_size, + testing_hcd->output.buf_size - header_size, + testing_hcd->resp.buf, + testing_hcd->resp.buf_size, + testing_hcd->resp.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy test data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + output_size += testing_hcd->resp.data_length; + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_hcd->output.data_length = output_size; + + UNLOCK_BUFFER(testing_hcd->output); +} + +static void testing_standard_frame_output(bool image_only) +{ + int retval; + unsigned int data_size; + unsigned int header_size; + unsigned int output_size; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + testing_get_frame_size_words(&data_size, image_only); + + header_size = sizeof(app_info->num_of_buttons) + + sizeof(app_info->num_of_image_rows) + + sizeof(app_info->num_of_image_cols) + + sizeof(app_info->has_hybrid_data); + + output_size = header_size + data_size * 2; + + LOCK_BUFFER(testing_hcd->output); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->output, + output_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd->output.buf\n"); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + retval = secure_memcpy(testing_hcd->output.buf, + testing_hcd->output.buf_size, + &app_info->num_of_buttons[0], + header_size, + header_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy header data\n"); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + output_size = header_size; + + LOCK_BUFFER(testing_hcd->resp); + + retval = secure_memcpy(testing_hcd->output.buf + header_size, + testing_hcd->output.buf_size - header_size, + testing_hcd->resp.buf, + testing_hcd->resp.buf_size, + testing_hcd->resp.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy test data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + output_size += testing_hcd->resp.data_length; + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_hcd->output.data_length = output_size; + + UNLOCK_BUFFER(testing_hcd->output); +} + +static int testing_dynamic_range_doze(void) +{ + int retval; + unsigned char *buf; + unsigned int idx; + unsigned int row; + unsigned int col; + unsigned int data; + unsigned int rows; + unsigned int cols; + unsigned int data_size; + unsigned int limits_rows; + unsigned int limits_cols; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + cols = le2_to_uint(app_info->num_of_image_cols); + + retval = testing_run_prod_test_item(TEST_DYNAMIC_RANGE_DOZE); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + data_size = testing_hcd->resp.data_length / 2; + + if (le2_to_uint(app_info->num_of_buttons)) + data_size--; + + if (data_size % cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid max number of rows per burst\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + rows = data_size / cols; + + limits_rows = ARRAY_SIZE(drt_hi_limits); + limits_cols = ARRAY_SIZE(drt_hi_limits[0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = ARRAY_SIZE(drt_lo_limits); + limits_cols = ARRAY_SIZE(drt_lo_limits[0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + idx = 0; + buf = testing_hcd->resp.buf; + testing_hcd->result = true; + + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + data = le2_to_uint(&buf[idx * 2]); + if (data > drt_hi_limits[row][col] || + data < drt_lo_limits[row][col]) { + testing_hcd->result = false; + break; + } + idx++; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_doze_frame_output(rows, cols); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static int testing_dynamic_range_lpwg(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_IN_WAKEUP_GESTURE_MODE, + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable wakeup gesture mode\n"); + return retval; + } + + retval = testing_dynamic_range(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do dynamic range test\n"); + return retval; + } + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_IN_WAKEUP_GESTURE_MODE, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable wakeup gesture mode\n"); + return retval; + } + + return 0; +} + +static int testing_dynamic_range(void) +{ + int retval; + unsigned char *buf; + unsigned int idx; + unsigned int row; + unsigned int col; + unsigned int data; + unsigned int rows; + unsigned int cols; + unsigned int limits_rows; + unsigned int limits_cols; + unsigned int frame_size_words; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + + testing_get_frame_size_words(&frame_size_words, false); + + retval = testing_run_prod_test_item(TEST_DYNAMIC_RANGE); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + if (frame_size_words != testing_hcd->resp.data_length / 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Frame size mismatch\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = ARRAY_SIZE(drt_hi_limits); + limits_cols = ARRAY_SIZE(drt_hi_limits[0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = ARRAY_SIZE(drt_lo_limits); + limits_cols = ARRAY_SIZE(drt_lo_limits[0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + idx = 0; + buf = testing_hcd->resp.buf; + testing_hcd->result = true; + + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + data = le2_to_uint(&buf[idx * 2]); + if (data > drt_hi_limits[row][col] || + data < drt_lo_limits[row][col]) { + testing_hcd->result = false; + break; + } + idx++; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_standard_frame_output(false); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static int testing_noise_doze(void) +{ + int retval; + short data; + unsigned char *buf; + unsigned int idx; + unsigned int row; + unsigned int col; + unsigned int rows; + unsigned int cols; + unsigned int data_size; + unsigned int limits_rows; + unsigned int limits_cols; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + cols = le2_to_uint(app_info->num_of_image_cols); + + retval = testing_run_prod_test_item(TEST_NOISE_DOZE); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + data_size = testing_hcd->resp.data_length / 2; + + if (le2_to_uint(app_info->num_of_buttons)) + data_size--; + + if (data_size % cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid max number of rows per burst\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + rows = data_size / cols; + + limits_rows = ARRAY_SIZE(noise_limits); + limits_cols = ARRAY_SIZE(noise_limits[0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + idx = 0; + buf = testing_hcd->resp.buf; + testing_hcd->result = true; + + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + data = (short)le2_to_uint(&buf[idx * 2]); + if (data > noise_limits[row][col]) { + testing_hcd->result = false; + break; + } + idx++; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_doze_frame_output(rows, cols); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static int testing_noise_lpwg(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_IN_WAKEUP_GESTURE_MODE, + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable wakeup gesture mode\n"); + return retval; + } + + retval = testing_noise(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do noise test\n"); + return retval; + } + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_IN_WAKEUP_GESTURE_MODE, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable wakeup gesture mode\n"); + return retval; + } + + return 0; +} + +static int testing_noise(void) +{ + int retval; + short data; + unsigned char *buf; + unsigned int idx; + unsigned int row; + unsigned int col; + unsigned int rows; + unsigned int cols; + unsigned int limits_rows; + unsigned int limits_cols; + unsigned int frame_size_words; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + + testing_get_frame_size_words(&frame_size_words, false); + + retval = testing_run_prod_test_item(TEST_NOISE); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + if (frame_size_words != testing_hcd->resp.data_length / 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Frame size mismatch\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = ARRAY_SIZE(noise_limits); + limits_cols = ARRAY_SIZE(noise_limits[0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + idx = 0; + buf = testing_hcd->resp.buf; + testing_hcd->result = true; + + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + data = (short)le2_to_uint(&buf[idx * 2]); + if (data > noise_limits[row][col]) { + testing_hcd->result = false; + break; + } + idx++; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_standard_frame_output(false); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static void testing_open_short_detector_output(void) +{ + int retval; + unsigned int rows; + unsigned int cols; + unsigned int data_size; + unsigned int header_size; + unsigned int output_size; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + data_size = (rows * cols + 7) / 8; + + header_size = sizeof(app_info->num_of_buttons) + + sizeof(app_info->num_of_image_rows) + + sizeof(app_info->num_of_image_cols) + + sizeof(app_info->has_hybrid_data); + + output_size = header_size + data_size * 2; + + LOCK_BUFFER(testing_hcd->output); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->output, + output_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for output.buf\n"); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + retval = secure_memcpy(testing_hcd->output.buf, + testing_hcd->output.buf_size, + &app_info->num_of_buttons[0], + header_size, + header_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy header data\n"); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + output_size = header_size; + + LOCK_BUFFER(testing_hcd->resp); + + retval = secure_memcpy(testing_hcd->output.buf + header_size, + testing_hcd->output.buf_size - header_size, + testing_hcd->resp.buf, + testing_hcd->resp.buf_size, + testing_hcd->resp.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy test data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + output_size += testing_hcd->resp.data_length; + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_hcd->output.data_length = output_size; + + UNLOCK_BUFFER(testing_hcd->output); +} + +static int testing_open_short_detector(void) +{ + int retval; + unsigned int bit; + unsigned int byte; + unsigned int row; + unsigned int col; + unsigned int rows; + unsigned int cols; + unsigned int data_size; + unsigned char *data; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + data_size = (rows * cols + 7) / 8; + + retval = testing_run_prod_test_item(TEST_OPEN_SHORT_DETECTOR); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + if (data_size * 2 != testing_hcd->resp.data_length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Data size mismatch\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + testing_hcd->result = true; + + bit = 0; + byte = 0; + data = &testing_hcd->resp.buf[0]; + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + if (data[byte] & (1 << bit)) { + testing_hcd->result = false; + break; + } + if (bit++ > 7) { + bit = 0; + byte++; + } + } + } + + if (testing_hcd->result == true) { + bit = 0; + byte = 0; + data = &testing_hcd->resp.buf[data_size]; + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + if (data[byte] & (1 << bit)) { + testing_hcd->result = false; + break; + } + if (bit++ > 7) { + bit = 0; + byte++; + } + } + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_open_short_detector_output(); + + retval = 0; + +exit: + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + + return retval; +} + +static int testing_pt11(void) +{ + int retval; + short data; + unsigned char *buf; + unsigned int idx; + unsigned int row; + unsigned int col; + unsigned int rows; + unsigned int cols; + unsigned int limits_rows; + unsigned int limits_cols; + unsigned int image_size_words; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + + testing_get_frame_size_words(&image_size_words, true); + + retval = testing_run_prod_test_item(TEST_PT11); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + if (image_size_words != testing_hcd->resp.data_length / 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Image size mismatch\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = ARRAY_SIZE(pt11_hi_limits); + limits_cols = ARRAY_SIZE(pt11_hi_limits[0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = ARRAY_SIZE(pt11_lo_limits); + limits_cols = ARRAY_SIZE(pt11_lo_limits[0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + idx = 0; + buf = testing_hcd->resp.buf; + testing_hcd->result = true; + + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + data = (short)le2_to_uint(&buf[idx * 2]); + if (data > pt11_hi_limits[row][col] || + data < pt11_lo_limits[row][col]) { + testing_hcd->result = false; + break; + } + idx++; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_standard_frame_output(true); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static int testing_pt12(void) +{ + int retval; + short data; + unsigned char *buf; + unsigned int idx; + unsigned int row; + unsigned int col; + unsigned int rows; + unsigned int cols; + unsigned int limits_rows; + unsigned int limits_cols; + unsigned int image_size_words; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + + testing_get_frame_size_words(&image_size_words, true); + + retval = testing_run_prod_test_item(TEST_PT12); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + if (image_size_words != testing_hcd->resp.data_length / 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Image size mismatch\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = ARRAY_SIZE(pt12_limits); + limits_cols = ARRAY_SIZE(pt12_limits[0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + idx = 0; + buf = testing_hcd->resp.buf; + testing_hcd->result = true; + + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + data = (short)le2_to_uint(&buf[idx * 2]); + if (data < pt12_limits[row][col]) { + testing_hcd->result = false; + break; + } + idx++; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_standard_frame_output(true); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static int testing_pt13(void) +{ + int retval; + short data; + unsigned char *buf; + unsigned int idx; + unsigned int row; + unsigned int col; + unsigned int rows; + unsigned int cols; + unsigned int limits_rows; + unsigned int limits_cols; + unsigned int image_size_words; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + + testing_get_frame_size_words(&image_size_words, true); + + retval = testing_run_prod_test_item(TEST_PT13); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + if (image_size_words != testing_hcd->resp.data_length / 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Image size mismatch\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = ARRAY_SIZE(pt13_limits); + limits_cols = ARRAY_SIZE(pt13_limits[0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + idx = 0; + buf = testing_hcd->resp.buf; + testing_hcd->result = true; + + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + data = (short)le2_to_uint(&buf[idx * 2]); + if (data < pt13_limits[row][col]) { + testing_hcd->result = false; + break; + } + idx++; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_standard_frame_output(true); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static int testing_reset_open(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (bdata->reset_gpio < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Hardware reset unavailable\n"); + return -EINVAL; + } + + mutex_lock(&tcm_hcd->reset_mutex); + + tcm_hcd->update_watchdog(tcm_hcd, false); + + gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + gpio_set_value(bdata->reset_gpio, !bdata->reset_on_state); + msleep(bdata->reset_delay_ms); + + tcm_hcd->update_watchdog(tcm_hcd, true); + + mutex_unlock(&tcm_hcd->reset_mutex); + + if (tcm_hcd->id_info.mode == MODE_APPLICATION) { + retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_BOOTLOADER); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enter bootloader mode\n"); + return retval; + } + } else { + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto run_app_firmware; + } + } + + if (tcm_hcd->boot_info.last_reset_reason == reset_open_limit) + testing_hcd->result = true; + else + testing_hcd->result = false; + + retval = 0; + +run_app_firmware: + if (tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + } + + return retval; +} + +static void testing_lockdown_output(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + LOCK_BUFFER(testing_hcd->output); + LOCK_BUFFER(testing_hcd->resp); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->output, + testing_hcd->resp.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for output.buf\n"); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + retval = secure_memcpy(testing_hcd->output.buf, + testing_hcd->output.buf_size, + testing_hcd->resp.buf, + testing_hcd->resp.buf_size, + testing_hcd->resp.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy test data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + testing_hcd->output.data_length = testing_hcd->resp.data_length; + + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); +} + +static int testing_lockdown(void) +{ + int retval; + unsigned int idx; + unsigned int lockdown_size; + unsigned int limits_size; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + if (tcm_hcd->read_flash_data == NULL) { + LOGE(tcm_hcd->pdev->dev.parent, + "Unable to read from flash\n"); + return -EINVAL; + } + + LOCK_BUFFER(testing_hcd->resp); + + retval = tcm_hcd->read_flash_data(CUSTOM_OTP, true, &testing_hcd->resp); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read lockdown data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + return retval; + } + + lockdown_size = testing_hcd->resp.data_length; + + limits_size = sizeof(lockdown_limits) / sizeof(*lockdown_limits); + + if (lockdown_size != limits_size) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + return -EINVAL; + } + + testing_hcd->result = true; + + for (idx = 0; idx < lockdown_size; idx++) { + if (testing_hcd->resp.buf[idx] != lockdown_limits[idx]) { + testing_hcd->result = false; + break; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_lockdown_output(); + + return 0; +} + +static void testing_trx_output(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + LOCK_BUFFER(testing_hcd->output); + LOCK_BUFFER(testing_hcd->resp); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->output, + testing_hcd->resp.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for output.buf\n"); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + retval = secure_memcpy(testing_hcd->output.buf, + testing_hcd->output.buf_size, + testing_hcd->resp.buf, + testing_hcd->resp.buf_size, + testing_hcd->resp.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy test data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + testing_hcd->output.data_length = testing_hcd->resp.data_length; + + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); +} + +static int testing_trx(enum test_code test_code) +{ + int retval; + unsigned char pass_vector; + unsigned int idx; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + switch (test_code) { + case TEST_TRX_TRX_SHORTS: + case TEST_TRX_GROUND_SHORTS: + pass_vector = 0xff; + break; + case TEST_TRX_SENSOR_OPENS: + pass_vector = 0x00; + break; + default: + return -EINVAL; + } + + retval = testing_run_prod_test_item(test_code); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + testing_hcd->result = true; + + for (idx = 0; idx < testing_hcd->resp.data_length; idx++) { + if (testing_hcd->resp.buf[idx] != pass_vector) { + testing_hcd->result = false; + break; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_trx_output(); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static void testing_report(void) +{ + int retval; + unsigned int offset; + unsigned int report_size; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + report_size = tcm_hcd->report.buffer.data_length; + + LOCK_BUFFER(testing_hcd->report); + + if (testing_hcd->report_index == 0) { + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->report, + report_size * testing_hcd->num_of_reports); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for report.buf\n"); + UNLOCK_BUFFER(testing_hcd->report); + return; + } + } + + if (testing_hcd->report_index < testing_hcd->num_of_reports) { + offset = report_size * testing_hcd->report_index; + + retval = secure_memcpy(testing_hcd->report.buf + offset, + testing_hcd->report.buf_size - offset, + tcm_hcd->report.buffer.buf, + tcm_hcd->report.buffer.buf_size, + tcm_hcd->report.buffer.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy report data\n"); + UNLOCK_BUFFER(testing_hcd->report); + return; + } + + testing_hcd->report_index++; + testing_hcd->report.data_length += report_size; + } + + UNLOCK_BUFFER(testing_hcd->report); + + if (testing_hcd->report_index == testing_hcd->num_of_reports) + complete(&report_complete); +} + +static int testing_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + int idx; + + testing_hcd = kzalloc(sizeof(*testing_hcd), GFP_KERNEL); + if (!testing_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd\n"); + return -ENOMEM; + } + + testing_hcd->tcm_hcd = tcm_hcd; + + testing_hcd->collect_reports = testing_collect_reports; + + INIT_BUFFER(testing_hcd->out, false); + INIT_BUFFER(testing_hcd->resp, false); + INIT_BUFFER(testing_hcd->report, false); + INIT_BUFFER(testing_hcd->process, false); + INIT_BUFFER(testing_hcd->output, false); + + testing_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME, + tcm_hcd->sysfs_dir); + if (!testing_hcd->sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dir; + } + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + retval = sysfs_create_file(testing_hcd->sysfs_dir, + &(*attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs file\n"); + goto err_sysfs_create_file; + } + } + + retval = sysfs_create_bin_file(testing_hcd->sysfs_dir, &bin_attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs bin file\n"); + goto err_sysfs_create_bin_file; + } + + return 0; + +err_sysfs_create_bin_file: +err_sysfs_create_file: + for (idx--; idx >= 0; idx--) + sysfs_remove_file(testing_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(testing_hcd->sysfs_dir); + +err_sysfs_create_dir: + RELEASE_BUFFER(testing_hcd->output); + RELEASE_BUFFER(testing_hcd->process); + RELEASE_BUFFER(testing_hcd->report); + RELEASE_BUFFER(testing_hcd->resp); + RELEASE_BUFFER(testing_hcd->out); + + kfree(testing_hcd); + testing_hcd = NULL; + + return retval; +} + +static int testing_remove(struct syna_tcm_hcd *tcm_hcd) +{ + int idx; + + if (!testing_hcd) + goto exit; + + sysfs_remove_bin_file(testing_hcd->sysfs_dir, &bin_attr); + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) + sysfs_remove_file(testing_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(testing_hcd->sysfs_dir); + + RELEASE_BUFFER(testing_hcd->output); + RELEASE_BUFFER(testing_hcd->process); + RELEASE_BUFFER(testing_hcd->report); + RELEASE_BUFFER(testing_hcd->resp); + RELEASE_BUFFER(testing_hcd->out); + + kfree(testing_hcd); + testing_hcd = NULL; + +exit: + complete(&testing_remove_complete); + + return 0; +} + +static int testing_reset(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!testing_hcd) { + retval = testing_init(tcm_hcd); + return retval; + } + + return 0; +} + +static int testing_syncbox(struct syna_tcm_hcd *tcm_hcd) +{ + if (!testing_hcd) + return 0; + + if (tcm_hcd->report.id == testing_hcd->report_type) + testing_report(); + + return 0; +} + +static struct syna_tcm_module_cb testing_module = { + .type = TCM_TESTING, + .init = testing_init, + .remove = testing_remove, + .syncbox = testing_syncbox, + .asyncbox = NULL, + .reset = testing_reset, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init testing_module_init(void) +{ + return syna_tcm_add_module(&testing_module, true); +} + +static void __exit testing_module_exit(void) +{ + syna_tcm_add_module(&testing_module, false); + + wait_for_completion(&testing_remove_complete); +} + +module_init(testing_module_init); +module_exit(testing_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Testing Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_tcm/synaptics_tcm_testing.h b/synaptics_tcm/synaptics_tcm_testing.h new file mode 100644 index 0000000000..c5a39a5896 --- /dev/null +++ b/synaptics_tcm/synaptics_tcm_testing.h @@ -0,0 +1,85 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2019 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2019 Scott Lin + * + * 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. + */ + +#ifndef _SYNAPTICS_TCM_TESTING_H_ +#define _SYNAPTICS_TCM_TESTING_H_ + +static const unsigned short drt_hi_limits[32][32] = { + {0}, +}; + +static const unsigned short drt_lo_limits[32][32] = { + {0,}, +}; + +static const unsigned short noise_limits[32][32] = { + {0,}, +}; + +static const short pt11_hi_limits[32][32] = { + {0,}, +}; + +static const short pt11_lo_limits[32][32] = { + {0,}, +}; + +static const short pt12_limits[32][32] = { + {0,}, +}; + +static const short pt13_limits[32][32] = { + {0,}, +}; + +static const unsigned char lockdown_limits[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char reset_open_limit = 0x13; + +#endif diff --git a/synaptics_tcm/synaptics_tcm_touch.c b/synaptics_tcm/synaptics_tcm_touch.c new file mode 100644 index 0000000000..fbc3b28a02 --- /dev/null +++ b/synaptics_tcm/synaptics_tcm_touch.c @@ -0,0 +1,1272 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2019 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2019 Scott Lin + * + * 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. + */ + +#include +#include +#include "synaptics_tcm_core.h" + +#define TYPE_B_PROTOCOL + +#define USE_DEFAULT_TOUCH_REPORT_CONFIG + +#define TOUCH_REPORT_CONFIG_SIZE 128 + +enum touch_status { + LIFT = 0, + FINGER = 1, + GLOVED_FINGER = 2, + NOP = -1, +}; + +enum touch_report_code { + TOUCH_END = 0, + TOUCH_FOREACH_ACTIVE_OBJECT, + TOUCH_FOREACH_OBJECT, + TOUCH_FOREACH_END, + TOUCH_PAD_TO_NEXT_BYTE, + TOUCH_TIMESTAMP, + TOUCH_OBJECT_N_INDEX, + TOUCH_OBJECT_N_CLASSIFICATION, + TOUCH_OBJECT_N_X_POSITION, + TOUCH_OBJECT_N_Y_POSITION, + TOUCH_OBJECT_N_Z, + TOUCH_OBJECT_N_X_WIDTH, + TOUCH_OBJECT_N_Y_WIDTH, + TOUCH_OBJECT_N_TX_POSITION_TIXELS, + TOUCH_OBJECT_N_RX_POSITION_TIXELS, + TOUCH_0D_BUTTONS_STATE, + TOUCH_GESTURE_DOUBLE_TAP, + TOUCH_FRAME_RATE, + TOUCH_POWER_IM, + TOUCH_CID_IM, + TOUCH_RAIL_IM, + TOUCH_CID_VARIANCE_IM, + TOUCH_NSM_FREQUENCY, + TOUCH_NSM_STATE, + TOUCH_NUM_OF_ACTIVE_OBJECTS, + TOUCH_NUM_OF_CPU_CYCLES_USED_SINCE_LAST_FRAME, + TOUCH_TUNING_GAUSSIAN_WIDTHS = 0x80, + TOUCH_TUNING_SMALL_OBJECT_PARAMS, + TOUCH_TUNING_0D_BUTTONS_VARIANCE, +}; + +struct object_data { + 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 input_params { + unsigned int max_x; + unsigned int max_y; + unsigned int max_objects; +}; + +struct touch_data { + struct object_data *object_data; + unsigned int timestamp; + unsigned int buttons_state; + unsigned int gesture_double_tap; + 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_active_objects; + unsigned int num_of_cpu_cycles; +}; + +struct touch_hcd { + bool irq_wake; + bool report_touch; + bool suspend_touch; + unsigned char *prev_status; + unsigned int max_x; + unsigned int max_y; + unsigned int max_objects; + struct mutex report_mutex; + struct input_dev *input_dev; + struct touch_data touch_data; + struct input_params input_params; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_hcd *tcm_hcd; +}; + +DECLARE_COMPLETION(touch_remove_complete); + +static struct touch_hcd *touch_hcd; + +/** + * touch_free_objects() - Free all touch objects + * + * Report finger lift events to the input subsystem for all touch objects. + */ +static void touch_free_objects(void) +{ +#ifdef TYPE_B_PROTOCOL + unsigned int idx; +#endif + + if (touch_hcd->input_dev == NULL) + return; + + mutex_lock(&touch_hcd->report_mutex); + +#ifdef TYPE_B_PROTOCOL + for (idx = 0; idx < touch_hcd->max_objects; idx++) { + input_mt_slot(touch_hcd->input_dev, idx); + input_mt_report_slot_state(touch_hcd->input_dev, + MT_TOOL_FINGER, 0); + } +#endif + input_report_key(touch_hcd->input_dev, + BTN_TOUCH, 0); + input_report_key(touch_hcd->input_dev, + BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(touch_hcd->input_dev); +#endif + input_sync(touch_hcd->input_dev); + + mutex_unlock(&touch_hcd->report_mutex); +} + +/** + * touch_get_report_data() - Retrieve data from touch report + * + * Retrieve data from the touch report based on the bit offset and bit length + * information from the touch report configuration. + */ +static int touch_get_report_data(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; + unsigned char *touch_report; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + + if (bits == 0 || bits > 32) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid number of bits\n"); + return -EINVAL; + } + + if (offset + bits > tcm_hcd->report.buffer.data_length * 8) { + *data = 0; + return 0; + } + + touch_report = tcm_hcd->report.buffer.buf; + + output_data = 0; + remaining_bits = bits; + + bit_offset = offset % 8; + byte_offset = offset / 8; + + while (remaining_bits) { + byte_data = touch_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; +} + +/** + * touch_parse_report() - Parse touch report + * + * Traverse through the touch report configuration and parse the touch report + * generated by the device accordingly to retrieve the touch data. + */ +static int touch_parse_report(void) +{ + 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 report_size; + unsigned int config_size; + unsigned char *config_data; + struct touch_data *touch_data; + struct object_data *object_data; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + static unsigned int end_of_foreach; + + touch_data = &touch_hcd->touch_data; + object_data = touch_hcd->touch_data.object_data; + + config_data = tcm_hcd->config.buf; + config_size = tcm_hcd->config.data_length; + + report_size = tcm_hcd->report.buffer.data_length; + + size = sizeof(*object_data) * touch_hcd->max_objects; + memset(touch_hcd->touch_data.object_data, 0x00, size); + + num_of_active_objects = false; + + idx = 0; + offset = 0; + objects = 0; + while (idx < config_size) { + code = config_data[idx++]; + switch (code) { + case TOUCH_END: + goto exit; + case TOUCH_FOREACH_ACTIVE_OBJECT: + obj = 0; + next = idx; + active_only = true; + break; + case TOUCH_FOREACH_OBJECT: + obj = 0; + next = idx; + active_only = false; + break; + case TOUCH_FOREACH_END: + end_of_foreach = idx; + if (active_only) { + if (num_of_active_objects) { + objects++; + if (objects < active_objects) + idx = next; + } else if (offset < report_size * 8) { + idx = next; + } + } else { + obj++; + if (obj < touch_hcd->max_objects) + idx = next; + } + break; + case TOUCH_PAD_TO_NEXT_BYTE: + offset = ceil_div(offset, 8) * 8; + break; + case TOUCH_TIMESTAMP: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get timestamp\n"); + return retval; + } + touch_data->timestamp = data; + offset += bits; + break; + case TOUCH_OBJECT_N_INDEX: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &obj); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object index\n"); + return retval; + } + offset += bits; + break; + case TOUCH_OBJECT_N_CLASSIFICATION: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get classification data\n"); + return retval; + } + object_data[obj].status = data; + offset += bits; + break; + case TOUCH_OBJECT_N_X_POSITION: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object x position\n"); + return retval; + } + object_data[obj].x_pos = data; + offset += bits; + break; + case TOUCH_OBJECT_N_Y_POSITION: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object y position\n"); + return retval; + } + object_data[obj].y_pos = data; + offset += bits; + break; + case TOUCH_OBJECT_N_Z: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object z\n"); + return retval; + } + object_data[obj].z = data; + offset += bits; + break; + case TOUCH_OBJECT_N_X_WIDTH: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object x width\n"); + return retval; + } + object_data[obj].x_width = data; + offset += bits; + break; + case TOUCH_OBJECT_N_Y_WIDTH: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object y width\n"); + return retval; + } + object_data[obj].y_width = data; + offset += bits; + break; + case TOUCH_OBJECT_N_TX_POSITION_TIXELS: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object tx position\n"); + return retval; + } + object_data[obj].tx_pos = data; + offset += bits; + break; + case TOUCH_OBJECT_N_RX_POSITION_TIXELS: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object rx position\n"); + return retval; + } + object_data[obj].rx_pos = data; + offset += bits; + break; + case TOUCH_0D_BUTTONS_STATE: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get 0D buttons state\n"); + return retval; + } + touch_data->buttons_state = data; + offset += bits; + break; + case TOUCH_GESTURE_DOUBLE_TAP: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get gesture double tap\n"); + return retval; + } + touch_data->gesture_double_tap = data; + offset += bits; + break; + case TOUCH_FRAME_RATE: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get frame rate\n"); + return retval; + } + touch_data->frame_rate = data; + offset += bits; + break; + case TOUCH_POWER_IM: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get power IM\n"); + return retval; + } + touch_data->power_im = data; + offset += bits; + break; + case TOUCH_CID_IM: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get CID IM\n"); + return retval; + } + touch_data->cid_im = data; + offset += bits; + break; + case TOUCH_RAIL_IM: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get rail IM\n"); + return retval; + } + touch_data->rail_im = data; + offset += bits; + break; + case TOUCH_CID_VARIANCE_IM: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get CID variance IM\n"); + return retval; + } + touch_data->cid_variance_im = data; + offset += bits; + break; + case TOUCH_NSM_FREQUENCY: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get NSM frequency\n"); + return retval; + } + touch_data->nsm_frequency = data; + offset += bits; + break; + case TOUCH_NSM_STATE: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get NSM state\n"); + return retval; + } + touch_data->nsm_state = data; + offset += bits; + break; + case TOUCH_NUM_OF_ACTIVE_OBJECTS: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get number of 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) + idx = end_of_foreach; + break; + case TOUCH_NUM_OF_CPU_CYCLES_USED_SINCE_LAST_FRAME: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get number of CPU cycles\n"); + return retval; + } + touch_data->num_of_cpu_cycles = data; + offset += bits; + break; + case TOUCH_TUNING_GAUSSIAN_WIDTHS: + bits = config_data[idx++]; + offset += bits; + break; + case TOUCH_TUNING_SMALL_OBJECT_PARAMS: + bits = config_data[idx++]; + offset += bits; + break; + case TOUCH_TUNING_0D_BUTTONS_VARIANCE: + bits = config_data[idx++]; + offset += bits; + break; + } + } + +exit: + return 0; +} + +/** + * touch_report() - Report touch events + * + * Retrieve data from the touch report generated by the device and report touch + * events to the input subsystem. + */ +static void touch_report(void) +{ + int retval; + unsigned int idx; + unsigned int x; + unsigned int y; + unsigned int temp; + unsigned int status; + unsigned int touch_count; + struct touch_data *touch_data; + struct object_data *object_data; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (!touch_hcd->report_touch) + return; + + if (touch_hcd->input_dev == NULL) + return; + + mutex_lock(&touch_hcd->report_mutex); + + retval = touch_parse_report(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to parse touch report\n"); + goto exit; + } + + touch_data = &touch_hcd->touch_data; + object_data = touch_hcd->touch_data.object_data; + +#ifdef WAKEUP_GESTURE + if (touch_data->gesture_double_tap && tcm_hcd->in_suspend) { + input_report_key(touch_hcd->input_dev, KEY_WAKEUP, 1); + input_sync(touch_hcd->input_dev); + input_report_key(touch_hcd->input_dev, KEY_WAKEUP, 0); + input_sync(touch_hcd->input_dev); + } +#endif + + if (tcm_hcd->in_suspend) + goto exit; + + touch_count = 0; + + for (idx = 0; idx < touch_hcd->max_objects; idx++) { + if (touch_hcd->prev_status[idx] == LIFT && + object_data[idx].status == LIFT) + status = NOP; + else + status = object_data[idx].status; + + switch (status) { + case LIFT: +#ifdef TYPE_B_PROTOCOL + input_mt_slot(touch_hcd->input_dev, idx); + input_mt_report_slot_state(touch_hcd->input_dev, + MT_TOOL_FINGER, 0); +#endif + break; + case FINGER: + case GLOVED_FINGER: + x = object_data[idx].x_pos; + y = object_data[idx].y_pos; + if (bdata->swap_axes) { + temp = x; + x = y; + y = temp; + } + if (bdata->x_flip) + x = touch_hcd->input_params.max_x - x; + if (bdata->y_flip) + y = touch_hcd->input_params.max_y - y; +#ifdef TYPE_B_PROTOCOL + input_mt_slot(touch_hcd->input_dev, idx); + input_mt_report_slot_state(touch_hcd->input_dev, + MT_TOOL_FINGER, 1); +#endif + input_report_key(touch_hcd->input_dev, + BTN_TOUCH, 1); + input_report_key(touch_hcd->input_dev, + BTN_TOOL_FINGER, 1); + input_report_abs(touch_hcd->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(touch_hcd->input_dev, + ABS_MT_POSITION_Y, y); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(touch_hcd->input_dev); +#endif + LOGD(tcm_hcd->pdev->dev.parent, + "Finger %d: x = %d, y = %d\n", + idx, x, y); + touch_count++; + break; + default: + break; + } + + touch_hcd->prev_status[idx] = object_data[idx].status; + } + + if (touch_count == 0) { + input_report_key(touch_hcd->input_dev, + BTN_TOUCH, 0); + input_report_key(touch_hcd->input_dev, + BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(touch_hcd->input_dev); +#endif + } + + input_sync(touch_hcd->input_dev); + +exit: + mutex_unlock(&touch_hcd->report_mutex); +} + +/** + * touch_set_input_params() - Set input parameters + * + * Set the input parameters of the input device based on the information + * retrieved from the application information packet. In addition, set up an + * array for tracking the status of touch objects. + */ +static int touch_set_input_params(void) +{ + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + + input_set_abs_params(touch_hcd->input_dev, + ABS_MT_POSITION_X, 0, touch_hcd->max_x, 0, 0); + input_set_abs_params(touch_hcd->input_dev, + ABS_MT_POSITION_Y, 0, touch_hcd->max_y, 0, 0); + + input_mt_init_slots(touch_hcd->input_dev, touch_hcd->max_objects, + INPUT_MT_DIRECT); + + touch_hcd->input_params.max_x = touch_hcd->max_x; + touch_hcd->input_params.max_y = touch_hcd->max_y; + touch_hcd->input_params.max_objects = touch_hcd->max_objects; + + if (touch_hcd->max_objects == 0) + return 0; + + kfree(touch_hcd->prev_status); + touch_hcd->prev_status = kzalloc(touch_hcd->max_objects, GFP_KERNEL); + if (!touch_hcd->prev_status) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for prev_status\n"); + return -ENOMEM; + } + + return 0; +} + +/** + * touch_get_input_params() - Get input parameters + * + * Retrieve the input parameters to register with the input subsystem for + * the input device from the application information packet. In addition, + * the touch report configuration is retrieved and stored. + */ +static int touch_get_input_params(void) +{ + int retval; + unsigned int temp; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + app_info = &tcm_hcd->app_info; + touch_hcd->max_x = le2_to_uint(app_info->max_x); + touch_hcd->max_y = le2_to_uint(app_info->max_y); + touch_hcd->max_objects = le2_to_uint(app_info->max_objects); + + if (bdata->swap_axes) { + temp = touch_hcd->max_x; + touch_hcd->max_x = touch_hcd->max_y; + touch_hcd->max_y = temp; + } + + LOCK_BUFFER(tcm_hcd->config); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_TOUCH_REPORT_CONFIG, + NULL, + 0, + &tcm_hcd->config.buf, + &tcm_hcd->config.buf_size, + &tcm_hcd->config.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_TOUCH_REPORT_CONFIG)); + UNLOCK_BUFFER(tcm_hcd->config); + return retval; + } + + UNLOCK_BUFFER(tcm_hcd->config); + + return 0; +} + +/** + * touch_set_input_dev() - Set up input device + * + * Allocate an input device, configure the input device based on the particular + * input events to be reported, and register the input device with the input + * subsystem. + */ +static int touch_set_input_dev(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + + touch_hcd->input_dev = input_allocate_device(); + if (touch_hcd->input_dev == NULL) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate input device\n"); + return -ENODEV; + } + + touch_hcd->input_dev->name = TOUCH_INPUT_NAME; + touch_hcd->input_dev->phys = TOUCH_INPUT_PHYS_PATH; + touch_hcd->input_dev->id.product = SYNAPTICS_TCM_ID_PRODUCT; + touch_hcd->input_dev->id.version = SYNAPTICS_TCM_ID_VERSION; + touch_hcd->input_dev->dev.parent = tcm_hcd->pdev->dev.parent; + input_set_drvdata(touch_hcd->input_dev, tcm_hcd); + + set_bit(EV_SYN, touch_hcd->input_dev->evbit); + set_bit(EV_KEY, touch_hcd->input_dev->evbit); + set_bit(EV_ABS, touch_hcd->input_dev->evbit); + set_bit(BTN_TOUCH, touch_hcd->input_dev->keybit); + set_bit(BTN_TOOL_FINGER, touch_hcd->input_dev->keybit); +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, touch_hcd->input_dev->propbit); +#endif + +#ifdef WAKEUP_GESTURE + set_bit(KEY_WAKEUP, touch_hcd->input_dev->keybit); + input_set_capability(touch_hcd->input_dev, EV_KEY, KEY_WAKEUP); +#endif + + retval = touch_set_input_params(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set input parameters\n"); + input_free_device(touch_hcd->input_dev); + touch_hcd->input_dev = NULL; + return retval; + } + + retval = input_register_device(touch_hcd->input_dev); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to register input device\n"); + input_free_device(touch_hcd->input_dev); + touch_hcd->input_dev = NULL; + return retval; + } + + return 0; +} + +/** + * touch_set_report_config() - Set touch report configuration + * + * Send the SET_TOUCH_REPORT_CONFIG command to configure the format and content + * of the touch report. + */ +static int touch_set_report_config(void) +{ + int retval; + unsigned int idx; + unsigned int length; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (!bdata->extend_report) + return 0; + + app_info = &tcm_hcd->app_info; + length = le2_to_uint(app_info->max_touch_report_config_size); + + if (length < TOUCH_REPORT_CONFIG_SIZE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid maximum touch report config size\n"); + return -EINVAL; + } + + LOCK_BUFFER(touch_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &touch_hcd->out, + length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for touch_hcd->out.buf\n"); + UNLOCK_BUFFER(touch_hcd->out); + return retval; + } + + idx = 0; +#ifdef WAKEUP_GESTURE + touch_hcd->out.buf[idx++] = TOUCH_GESTURE_DOUBLE_TAP; + touch_hcd->out.buf[idx++] = 8; +#endif + touch_hcd->out.buf[idx++] = TOUCH_FOREACH_ACTIVE_OBJECT; + touch_hcd->out.buf[idx++] = TOUCH_OBJECT_N_INDEX; + touch_hcd->out.buf[idx++] = 4; + touch_hcd->out.buf[idx++] = TOUCH_OBJECT_N_CLASSIFICATION; + touch_hcd->out.buf[idx++] = 4; + touch_hcd->out.buf[idx++] = TOUCH_OBJECT_N_X_POSITION; + touch_hcd->out.buf[idx++] = 12; + touch_hcd->out.buf[idx++] = TOUCH_OBJECT_N_Y_POSITION; + touch_hcd->out.buf[idx++] = 12; + touch_hcd->out.buf[idx++] = TOUCH_FOREACH_END; + touch_hcd->out.buf[idx++] = TOUCH_END; + + LOCK_BUFFER(touch_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_SET_TOUCH_REPORT_CONFIG, + touch_hcd->out.buf, + length, + &touch_hcd->resp.buf, + &touch_hcd->resp.buf_size, + &touch_hcd->resp.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_SET_TOUCH_REPORT_CONFIG)); + UNLOCK_BUFFER(touch_hcd->resp); + UNLOCK_BUFFER(touch_hcd->out); + return retval; + } + + UNLOCK_BUFFER(touch_hcd->resp); + UNLOCK_BUFFER(touch_hcd->out); + + return 0; +} + +/** + * touch_check_input_params() - Check input parameters + * + * Check if any of the input parameters registered with the input subsystem for + * the input device has changed. + */ +static int touch_check_input_params(void) +{ + unsigned int size; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + + if (touch_hcd->max_x == 0 && touch_hcd->max_y == 0) + return 0; + + if (touch_hcd->input_params.max_objects != touch_hcd->max_objects) { + kfree(touch_hcd->touch_data.object_data); + size = sizeof(*touch_hcd->touch_data.object_data); + size *= touch_hcd->max_objects; + touch_hcd->touch_data.object_data = kzalloc(size, GFP_KERNEL); + if (!touch_hcd->touch_data.object_data) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for object_data\n"); + return -ENOMEM; + } + return 1; + } + + if (touch_hcd->input_params.max_x != touch_hcd->max_x) + return 1; + + if (touch_hcd->input_params.max_y != touch_hcd->max_y) + return 1; + + return 0; +} + +/** + * touch_set_input_reporting() - Configure touch report and set up new input + * device if necessary + * + * After a device reset event, configure the touch report and set up a new input + * device if any of the input parameters has changed after the device reset. + */ +static int touch_set_input_reporting(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + LOGN(tcm_hcd->pdev->dev.parent, + "Application firmware not running\n"); + return 0; + } + + touch_hcd->report_touch = false; + + touch_free_objects(); + + mutex_lock(&touch_hcd->report_mutex); + + retval = touch_set_report_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set report config\n"); + goto exit; + } + + retval = touch_get_input_params(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get input parameters\n"); + goto exit; + } + + retval = touch_check_input_params(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to check input parameters\n"); + goto exit; + } else if (retval == 0) { + LOGD(tcm_hcd->pdev->dev.parent, + "Input parameters unchanged\n"); + goto exit; + } + + if (touch_hcd->input_dev != NULL) { + input_unregister_device(touch_hcd->input_dev); + touch_hcd->input_dev = NULL; + } + + retval = touch_set_input_dev(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up input device\n"); + goto exit; + } + +exit: + mutex_unlock(&touch_hcd->report_mutex); + + touch_hcd->report_touch = retval < 0 ? false : true; + + return retval; +} + +static int touch_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + touch_hcd = kzalloc(sizeof(*touch_hcd), GFP_KERNEL); + if (!touch_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for touch_hcd\n"); + return -ENOMEM; + } + + touch_hcd->tcm_hcd = tcm_hcd; + + mutex_init(&touch_hcd->report_mutex); + + INIT_BUFFER(touch_hcd->out, false); + INIT_BUFFER(touch_hcd->resp, false); + + retval = touch_set_input_reporting(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up input reporting\n"); + goto err_set_input_reporting; + } + + tcm_hcd->report_touch = touch_report; + + return 0; + +err_set_input_reporting: + kfree(touch_hcd->touch_data.object_data); + kfree(touch_hcd->prev_status); + + RELEASE_BUFFER(touch_hcd->resp); + RELEASE_BUFFER(touch_hcd->out); + + kfree(touch_hcd); + touch_hcd = NULL; + + return retval; +} + +static int touch_remove(struct syna_tcm_hcd *tcm_hcd) +{ + if (!touch_hcd) + goto exit; + + tcm_hcd->report_touch = NULL; + + if (touch_hcd->input_dev) + input_unregister_device(touch_hcd->input_dev); + + kfree(touch_hcd->touch_data.object_data); + kfree(touch_hcd->prev_status); + + RELEASE_BUFFER(touch_hcd->resp); + RELEASE_BUFFER(touch_hcd->out); + + kfree(touch_hcd); + touch_hcd = NULL; + +exit: + complete(&touch_remove_complete); + + return 0; +} + +static int touch_syncbox(struct syna_tcm_hcd *tcm_hcd) +{ + if (!touch_hcd) + return 0; + + switch (tcm_hcd->report.id) { + case REPORT_IDENTIFY: + touch_free_objects(); + break; + case REPORT_TOUCH: + if (!touch_hcd->suspend_touch) + touch_report(); + break; + default: + break; + } + + return 0; +} + +static int touch_asyncbox(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!touch_hcd) + return 0; + + switch (tcm_hcd->async_report_id) { + case REPORT_IDENTIFY: + if (tcm_hcd->id_info.mode != MODE_APPLICATION) + break; + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + return retval; + } + retval = touch_set_input_reporting(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up input reporting\n"); + return retval; + } + break; + default: + break; + } + + return 0; +} + +static int touch_reset(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!touch_hcd) { + retval = touch_init(tcm_hcd); + return retval; + } + + if (tcm_hcd->id_info.mode == MODE_APPLICATION) { + retval = touch_set_input_reporting(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up input reporting\n"); + return retval; + } + } + + return 0; +} + +static int touch_early_suspend(struct syna_tcm_hcd *tcm_hcd) +{ + if (!touch_hcd) + return 0; + +#ifdef WAKEUP_GESTURE + touch_hcd->suspend_touch = false; +#else + touch_hcd->suspend_touch = true; +#endif + + touch_free_objects(); + + return 0; +} + +static int touch_suspend(struct syna_tcm_hcd *tcm_hcd) +{ +#ifdef WAKEUP_GESTURE + int retval; +#endif + + if (!touch_hcd) + return 0; + + touch_hcd->suspend_touch = true; + + touch_free_objects(); + +#ifdef WAKEUP_GESTURE + if (!touch_hcd->irq_wake) { + enable_irq_wake(tcm_hcd->irq); + touch_hcd->irq_wake = true; + } + + touch_hcd->suspend_touch = false; + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_IN_WAKEUP_GESTURE_MODE, + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable wakeup gesture mode\n"); + return retval; + } +#endif + + return 0; +} + +static int touch_resume(struct syna_tcm_hcd *tcm_hcd) +{ +#ifdef WAKEUP_GESTURE + int retval; +#endif + + if (!touch_hcd) + return 0; + + touch_hcd->suspend_touch = false; + +#ifdef WAKEUP_GESTURE + if (touch_hcd->irq_wake) { + disable_irq_wake(tcm_hcd->irq); + touch_hcd->irq_wake = false; + } + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_IN_WAKEUP_GESTURE_MODE, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable wakeup gesture mode\n"); + return retval; + } +#endif + + return 0; +} + +static struct syna_tcm_module_cb touch_module = { + .type = TCM_TOUCH, + .init = touch_init, + .remove = touch_remove, + .syncbox = touch_syncbox, + .asyncbox = touch_asyncbox, + .reset = touch_reset, + .suspend = touch_suspend, + .resume = touch_resume, + .early_suspend = touch_early_suspend, +}; + +static int __init touch_module_init(void) +{ + return syna_tcm_add_module(&touch_module, true); +} + +static void __exit touch_module_exit(void) +{ + syna_tcm_add_module(&touch_module, false); + + wait_for_completion(&touch_remove_complete); +} + +module_init(touch_module_init); +module_exit(touch_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Touch Module"); +MODULE_LICENSE("GPL v2"); diff --git a/synaptics_tcm/synaptics_tcm_zeroflash.c b/synaptics_tcm/synaptics_tcm_zeroflash.c new file mode 100644 index 0000000000..5e8295477c --- /dev/null +++ b/synaptics_tcm/synaptics_tcm_zeroflash.c @@ -0,0 +1,1012 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2019 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2019 Scott Lin + * + * 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. + */ + +#include +#include +#include +#include "synaptics_tcm_core.h" + +#define FW_IMAGE_NAME "synaptics/hdl_firmware.img" + +#define BOOT_CONFIG_ID "BOOT_CONFIG" + +#define F35_APP_CODE_ID "F35_APP_CODE" + +#define APP_CONFIG_ID "APP_CONFIG" + +#define DISP_CONFIG_ID "DISPLAY" + +#define IMAGE_FILE_MAGIC_VALUE 0x4818472b + +#define FLASH_AREA_MAGIC_VALUE 0x7c05e516 + +#define PDT_START_ADDR 0x00e9 + +#define PDT_END_ADDR 0x00ee + +#define UBL_FN_NUMBER 0x35 + +#define F35_CTRL3_OFFSET 18 + +#define F35_CTRL7_OFFSET 22 + +#define F35_WRITE_FW_TO_PMEM_COMMAND 4 + +#define RESET_TO_HDL_DELAY_MS 12 + +#define DOWNLOAD_RETRY_COUNT 10 + +enum f35_error_code { + SUCCESS = 0, + UNKNOWN_FLASH_PRESENT, + MAGIC_NUMBER_NOT_PRESENT, + INVALID_BLOCK_NUMBER, + BLOCK_NOT_ERASED, + NO_FLASH_PRESENT, + CHECKSUM_FAILURE, + WRITE_FAILURE, + INVALID_COMMAND, + IN_DEBUG_MODE, + INVALID_HEADER, + REQUESTING_FIRMWARE, + INVALID_CONFIGURATION, + DISABLE_BLOCK_PROTECT_FAILURE, +}; + +enum config_download { + HDL_INVALID = 0, + HDL_TOUCH_CONFIG, + HDL_DISPLAY_CONFIG, + HDL_DISPLAY_CONFIG_TO_RAM, +}; + +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]; +}; + +struct block_data { + const unsigned char *data; + unsigned int size; + unsigned int flash_addr; +}; + +struct image_info { + unsigned int packrat_number; + struct block_data boot_config; + struct block_data app_firmware; + struct block_data app_config; + struct block_data disp_config; +}; + +struct image_header { + unsigned char magic_value[4]; + unsigned char num_of_areas[4]; +}; + +struct rmi_f35_query { + unsigned char version:4; + unsigned char has_debug_mode:1; + unsigned char has_data5:1; + unsigned char has_query1:1; + unsigned char has_query2:1; + unsigned char chunk_size; + unsigned char has_ctrl7:1; + unsigned char has_host_download:1; + unsigned char has_spi_master:1; + unsigned char advanced_recovery_mode:1; + unsigned char reserved:4; +} __packed; + +struct rmi_f35_data { + unsigned char error_code:5; + unsigned char recovery_mode_forced:1; + unsigned char nvm_programmed:1; + unsigned char in_recovery:1; +} __packed; + +struct rmi_pdt_entry { + unsigned char query_base_addr; + unsigned char command_base_addr; + unsigned char control_base_addr; + unsigned char data_base_addr; + unsigned char intr_src_count:3; + unsigned char reserved_1:2; + unsigned char fn_version:2; + unsigned char reserved_2:1; + unsigned char fn_number; +} __packed; + +struct rmi_addr { + unsigned short query_base; + unsigned short command_base; + unsigned short control_base; + unsigned short data_base; +}; + +struct firmware_status { + unsigned short invalid_static_config:1; + unsigned short need_disp_config:1; + unsigned short need_app_config:1; + unsigned short hdl_version:4; + unsigned short reserved:9; +} __packed; + +struct zeroflash_hcd { + bool has_hdl; + bool f35_ready; + const unsigned char *image; + unsigned char *buf; + const struct firmware *fw_entry; + struct work_struct config_work; + struct work_struct firmware_work; + struct workqueue_struct *workqueue; + struct rmi_addr f35_addr; + struct image_info image_info; + struct firmware_status fw_status; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_hcd *tcm_hcd; +}; + +DECLARE_COMPLETION(zeroflash_remove_complete); + +static struct zeroflash_hcd *zeroflash_hcd; + +static int zeroflash_check_uboot(void) +{ + int retval; + unsigned char fn_number; + struct rmi_f35_query query; + struct rmi_pdt_entry p_entry; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + + retval = syna_tcm_rmi_read(tcm_hcd, + PDT_END_ADDR, + &fn_number, + sizeof(fn_number)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read RMI function number\n"); + return retval; + } + + LOGD(tcm_hcd->pdev->dev.parent, + "Found F$%02x\n", + fn_number); + + if (fn_number != UBL_FN_NUMBER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to find F$35\n"); + return -ENODEV; + } + + if (zeroflash_hcd->f35_ready) + return 0; + + retval = syna_tcm_rmi_read(tcm_hcd, + PDT_START_ADDR, + (unsigned char *)&p_entry, + sizeof(p_entry)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read PDT entry\n"); + return retval; + } + + zeroflash_hcd->f35_addr.query_base = p_entry.query_base_addr; + zeroflash_hcd->f35_addr.command_base = p_entry.command_base_addr; + zeroflash_hcd->f35_addr.control_base = p_entry.control_base_addr; + zeroflash_hcd->f35_addr.data_base = p_entry.data_base_addr; + + retval = syna_tcm_rmi_read(tcm_hcd, + zeroflash_hcd->f35_addr.query_base, + (unsigned char *)&query, + sizeof(query)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read F$35 query\n"); + return retval; + } + + zeroflash_hcd->f35_ready = true; + + if (query.has_query2 && query.has_ctrl7 && query.has_host_download) { + zeroflash_hcd->has_hdl = true; + } else { + LOGE(tcm_hcd->pdev->dev.parent, + "Host download not supported\n"); + zeroflash_hcd->has_hdl = false; + return -ENODEV; + } + + return 0; +} + +static int zeroflash_parse_fw_image(void) +{ + 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 image_info *image_info; + struct area_descriptor *descriptor; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + const unsigned char *image; + const unsigned char *content; + + image = zeroflash_hcd->image; + image_info = &zeroflash_hcd->image_info; + header = (struct image_header *)image; + + magic_value = le4_to_uint(header->magic_value); + if (magic_value != IMAGE_FILE_MAGIC_VALUE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid image file magic value\n"); + return -EINVAL; + } + + memset(image_info, 0x00, sizeof(*image_info)); + + offset = sizeof(*header); + num_of_areas = le4_to_uint(header->num_of_areas); + + for (idx = 0; idx < num_of_areas; idx++) { + addr = le4_to_uint(image + offset); + descriptor = (struct area_descriptor *)(image + addr); + offset += 4; + + magic_value = le4_to_uint(descriptor->magic_value); + if (magic_value != FLASH_AREA_MAGIC_VALUE) + continue; + + length = le4_to_uint(descriptor->length); + content = (unsigned char *)descriptor + sizeof(*descriptor); + flash_addr = le4_to_uint(descriptor->flash_addr_words) * 2; + checksum = le4_to_uint(descriptor->checksum); + + if (!memcmp((char *)descriptor->id_string, + BOOT_CONFIG_ID, + strlen(BOOT_CONFIG_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Boot config checksum error\n"); + return -EINVAL; + } + image_info->boot_config.size = length; + image_info->boot_config.data = content; + image_info->boot_config.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Boot config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Boot config flash address = 0x%08x\n", + flash_addr); + } else if (!memcmp((char *)descriptor->id_string, + F35_APP_CODE_ID, + strlen(F35_APP_CODE_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "APP firmware checksum error\n"); + return -EINVAL; + } + image_info->app_firmware.size = length; + image_info->app_firmware.data = content; + image_info->app_firmware.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Application firmware size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Application firmware flash address = 0x%08x\n", + flash_addr); + } else if (!memcmp((char *)descriptor->id_string, + APP_CONFIG_ID, + strlen(APP_CONFIG_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application config checksum error\n"); + return -EINVAL; + } + image_info->app_config.size = length; + image_info->app_config.data = content; + image_info->app_config.flash_addr = flash_addr; + image_info->packrat_number = le4_to_uint(&content[14]); + LOGD(tcm_hcd->pdev->dev.parent, + "Application config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Application config flash address = 0x%08x\n", + flash_addr); + } else if (!memcmp((char *)descriptor->id_string, + DISP_CONFIG_ID, + strlen(DISP_CONFIG_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Display config checksum error\n"); + return -EINVAL; + } + image_info->disp_config.size = length; + image_info->disp_config.data = content; + image_info->disp_config.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Display config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Display config flash address = 0x%08x\n", + flash_addr); + } + } + + return 0; +} + +static int zeroflash_get_fw_image(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + + if (zeroflash_hcd->fw_entry != NULL) + return 0; + + do { + retval = request_firmware(&zeroflash_hcd->fw_entry, + FW_IMAGE_NAME, + tcm_hcd->pdev->dev.parent); + if (retval < 0) { + LOGD(tcm_hcd->pdev->dev.parent, + "Failed to request %s\n", + FW_IMAGE_NAME); + msleep(100); + } else { + break; + } + } while (1); + + LOGD(tcm_hcd->pdev->dev.parent, + "Firmware image size = %d\n", + (unsigned int)zeroflash_hcd->fw_entry->size); + + zeroflash_hcd->image = zeroflash_hcd->fw_entry->data; + + retval = zeroflash_parse_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to parse firmware image\n"); + release_firmware(zeroflash_hcd->fw_entry); + zeroflash_hcd->fw_entry = NULL; + zeroflash_hcd->image = NULL; + return retval; + } + + return 0; +} + +static void zeroflash_download_config(void) +{ + struct firmware_status *fw_status; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + + fw_status = &zeroflash_hcd->fw_status; + + if (!fw_status->need_app_config && !fw_status->need_disp_config) { + if (atomic_read(&tcm_hcd->helper.task) == HELP_NONE) { + atomic_set(&tcm_hcd->helper.task, + HELP_SEND_RESET_NOTIFICATION); + queue_work(tcm_hcd->helper.workqueue, + &tcm_hcd->helper.work); + } + atomic_set(&tcm_hcd->host_downloading, 0); + return; + } + + queue_work(zeroflash_hcd->workqueue, &zeroflash_hcd->config_work); +} + +static void zeroflash_download_firmware(void) +{ + queue_work(zeroflash_hcd->workqueue, &zeroflash_hcd->firmware_work); +} + +static int zeroflash_download_disp_config(void) +{ + int retval; + unsigned char response_code; + struct image_info *image_info; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + static unsigned int retry_count; + + LOGN(tcm_hcd->pdev->dev.parent, + "Downloading display config\n"); + + image_info = &zeroflash_hcd->image_info; + + if (image_info->disp_config.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No display config in image file\n"); + return -EINVAL; + } + + LOCK_BUFFER(zeroflash_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &zeroflash_hcd->out, + image_info->disp_config.size + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for out.buf\n"); + goto unlock_out; + } + + switch (zeroflash_hcd->fw_status.hdl_version) { + case 0: + zeroflash_hcd->out.buf[0] = 1; + break; + case 1: + zeroflash_hcd->out.buf[0] = 2; + break; + default: + retval = -EINVAL; + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid HDL version (%d)\n", + zeroflash_hcd->fw_status.hdl_version); + goto unlock_out; + } + + zeroflash_hcd->out.buf[1] = HDL_DISPLAY_CONFIG; + + retval = secure_memcpy(&zeroflash_hcd->out.buf[2], + zeroflash_hcd->out.buf_size - 2, + image_info->disp_config.data, + image_info->disp_config.size, + image_info->disp_config.size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy display config data\n"); + goto unlock_out; + } + + zeroflash_hcd->out.data_length = image_info->disp_config.size + 2; + + LOCK_BUFFER(zeroflash_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_DOWNLOAD_CONFIG, + zeroflash_hcd->out.buf, + zeroflash_hcd->out.data_length, + &zeroflash_hcd->resp.buf, + &zeroflash_hcd->resp.buf_size, + &zeroflash_hcd->resp.data_length, + &response_code, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_DOWNLOAD_CONFIG)); + if (response_code != STATUS_ERROR) + goto unlock_resp; + retry_count++; + if (DOWNLOAD_RETRY_COUNT && retry_count > DOWNLOAD_RETRY_COUNT) + goto unlock_resp; + } else { + retry_count = 0; + } + + retval = secure_memcpy((unsigned char *)&zeroflash_hcd->fw_status, + sizeof(zeroflash_hcd->fw_status), + zeroflash_hcd->resp.buf, + zeroflash_hcd->resp.buf_size, + sizeof(zeroflash_hcd->fw_status)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy firmware status\n"); + goto unlock_resp; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Display config downloaded\n"); + + retval = 0; + +unlock_resp: + UNLOCK_BUFFER(zeroflash_hcd->resp); + +unlock_out: + UNLOCK_BUFFER(zeroflash_hcd->out); + + return retval; +} + +static int zeroflash_download_app_config(void) +{ + int retval; + unsigned char padding; + unsigned char response_code; + struct image_info *image_info; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + static unsigned int retry_count; + + LOGN(tcm_hcd->pdev->dev.parent, + "Downloading application config\n"); + + image_info = &zeroflash_hcd->image_info; + + if (image_info->app_config.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No application config in image file\n"); + return -EINVAL; + } + + padding = image_info->app_config.size % 8; + if (padding) + padding = 8 - padding; + + LOCK_BUFFER(zeroflash_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &zeroflash_hcd->out, + image_info->app_config.size + 2 + padding); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for out.buf\n"); + goto unlock_out; + } + + switch (zeroflash_hcd->fw_status.hdl_version) { + case 0: + zeroflash_hcd->out.buf[0] = 1; + break; + case 1: + zeroflash_hcd->out.buf[0] = 2; + break; + default: + retval = -EINVAL; + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid HDL version (%d)\n", + zeroflash_hcd->fw_status.hdl_version); + goto unlock_out; + } + + zeroflash_hcd->out.buf[1] = HDL_TOUCH_CONFIG; + + retval = secure_memcpy(&zeroflash_hcd->out.buf[2], + zeroflash_hcd->out.buf_size - 2, + image_info->app_config.data, + image_info->app_config.size, + image_info->app_config.size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy application config data\n"); + goto unlock_out; + } + + zeroflash_hcd->out.data_length = image_info->app_config.size + 2; + zeroflash_hcd->out.data_length += padding; + + LOCK_BUFFER(zeroflash_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_DOWNLOAD_CONFIG, + zeroflash_hcd->out.buf, + zeroflash_hcd->out.data_length, + &zeroflash_hcd->resp.buf, + &zeroflash_hcd->resp.buf_size, + &zeroflash_hcd->resp.data_length, + &response_code, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_DOWNLOAD_CONFIG)); + if (response_code != STATUS_ERROR) + goto unlock_resp; + retry_count++; + if (DOWNLOAD_RETRY_COUNT && retry_count > DOWNLOAD_RETRY_COUNT) + goto unlock_resp; + } else { + retry_count = 0; + } + + retval = secure_memcpy((unsigned char *)&zeroflash_hcd->fw_status, + sizeof(zeroflash_hcd->fw_status), + zeroflash_hcd->resp.buf, + zeroflash_hcd->resp.buf_size, + sizeof(zeroflash_hcd->fw_status)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy firmware status\n"); + goto unlock_resp; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Application config downloaded\n"); + + retval = 0; + +unlock_resp: + UNLOCK_BUFFER(zeroflash_hcd->resp); + +unlock_out: + UNLOCK_BUFFER(zeroflash_hcd->out); + + return retval; +} + +static void zeroflash_download_config_work(struct work_struct *work) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + + retval = zeroflash_get_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get firmware image\n"); + return; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Start of config download\n"); + + if (zeroflash_hcd->fw_status.need_app_config) { + retval = zeroflash_download_app_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to download application config\n"); + return; + } + goto exit; + } + + if (zeroflash_hcd->fw_status.need_disp_config) { + retval = zeroflash_download_disp_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to download display config\n"); + return; + } + goto exit; + } + +exit: + LOGN(tcm_hcd->pdev->dev.parent, + "End of config download\n"); + + zeroflash_download_config(); +} + +static int zeroflash_download_app_fw(void) +{ + int retval; + unsigned char command; + struct image_info *image_info; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; +#if RESET_TO_HDL_DELAY_MS + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; +#endif + + LOGN(tcm_hcd->pdev->dev.parent, + "Downloading application firmware\n"); + + image_info = &zeroflash_hcd->image_info; + + if (image_info->app_firmware.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No application firmware in image file\n"); + return -EINVAL; + } + + LOCK_BUFFER(zeroflash_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &zeroflash_hcd->out, + image_info->app_firmware.size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for out.buf\n"); + UNLOCK_BUFFER(zeroflash_hcd->out); + return retval; + } + + retval = secure_memcpy(zeroflash_hcd->out.buf, + zeroflash_hcd->out.buf_size, + image_info->app_firmware.data, + image_info->app_firmware.size, + image_info->app_firmware.size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy application firmware data\n"); + UNLOCK_BUFFER(zeroflash_hcd->out); + return retval; + } + + zeroflash_hcd->out.data_length = image_info->app_firmware.size; + + command = F35_WRITE_FW_TO_PMEM_COMMAND; + +#if RESET_TO_HDL_DELAY_MS + gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + gpio_set_value(bdata->reset_gpio, !bdata->reset_on_state); + msleep(RESET_TO_HDL_DELAY_MS); +#endif + + retval = syna_tcm_rmi_write(tcm_hcd, + zeroflash_hcd->f35_addr.control_base + F35_CTRL3_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write F$35 command\n"); + UNLOCK_BUFFER(zeroflash_hcd->out); + return retval; + } + + retval = syna_tcm_rmi_write(tcm_hcd, + zeroflash_hcd->f35_addr.control_base + F35_CTRL7_OFFSET, + zeroflash_hcd->out.buf, + zeroflash_hcd->out.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write application firmware data\n"); + UNLOCK_BUFFER(zeroflash_hcd->out); + return retval; + } + + UNLOCK_BUFFER(zeroflash_hcd->out); + + LOGN(tcm_hcd->pdev->dev.parent, + "Application firmware downloaded\n"); + + return 0; +} + +static void zeroflash_download_firmware_work(struct work_struct *work) +{ + int retval; + struct rmi_f35_data data; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + static unsigned int retry_count; + + retval = zeroflash_check_uboot(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Microbootloader support unavailable\n"); + goto exit; + } + + atomic_set(&tcm_hcd->host_downloading, 1); + + retval = syna_tcm_rmi_read(tcm_hcd, + zeroflash_hcd->f35_addr.data_base, + (unsigned char *)&data, + sizeof(data)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read F$35 data\n"); + goto exit; + } + + if (data.error_code != REQUESTING_FIRMWARE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Microbootloader error code = 0x%02x\n", + data.error_code); + if (data.error_code != CHECKSUM_FAILURE) { + retval = -EIO; + goto exit; + } else { + retry_count++; + } + } else { + retry_count = 0; + } + + retval = zeroflash_get_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get firmware image\n"); + goto exit; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Start of firmware download\n"); + + retval = zeroflash_download_app_fw(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to download application firmware\n"); + goto exit; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "End of firmware download\n"); + +exit: + if (retval < 0) + retry_count++; + + if (DOWNLOAD_RETRY_COUNT && retry_count > DOWNLOAD_RETRY_COUNT) { + retval = tcm_hcd->enable_irq(tcm_hcd, false, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable interrupt\n"); + } + } else { + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable interrupt\n"); + } + } +} + +static int zeroflash_init(struct syna_tcm_hcd *tcm_hcd) +{ + zeroflash_hcd = kzalloc(sizeof(*zeroflash_hcd), GFP_KERNEL); + if (!zeroflash_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for zeroflash_hcd\n"); + return -ENOMEM; + } + + zeroflash_hcd->tcm_hcd = tcm_hcd; + + INIT_BUFFER(zeroflash_hcd->out, false); + INIT_BUFFER(zeroflash_hcd->resp, false); + + zeroflash_hcd->workqueue = + create_singlethread_workqueue("syna_tcm_zeroflash"); + INIT_WORK(&zeroflash_hcd->config_work, + zeroflash_download_config_work); + INIT_WORK(&zeroflash_hcd->firmware_work, + zeroflash_download_firmware_work); + + if (tcm_hcd->init_okay == false && + tcm_hcd->hw_if->bus_io->type == BUS_SPI) + zeroflash_download_firmware(); + + return 0; +} + +static int zeroflash_remove(struct syna_tcm_hcd *tcm_hcd) +{ + if (!zeroflash_hcd) + goto exit; + + if (zeroflash_hcd->fw_entry) + release_firmware(zeroflash_hcd->fw_entry); + + cancel_work_sync(&zeroflash_hcd->config_work); + cancel_work_sync(&zeroflash_hcd->firmware_work); + flush_workqueue(zeroflash_hcd->workqueue); + destroy_workqueue(zeroflash_hcd->workqueue); + + RELEASE_BUFFER(zeroflash_hcd->resp); + RELEASE_BUFFER(zeroflash_hcd->out); + + kfree(zeroflash_hcd); + zeroflash_hcd = NULL; + +exit: + complete(&zeroflash_remove_complete); + + return 0; +} + +static int zeroflash_syncbox(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *fw_status; + + if (!zeroflash_hcd) + return 0; + + switch (tcm_hcd->report.id) { + case REPORT_STATUS: + fw_status = (unsigned char *)&zeroflash_hcd->fw_status; + retval = secure_memcpy(fw_status, + sizeof(zeroflash_hcd->fw_status), + tcm_hcd->report.buffer.buf, + tcm_hcd->report.buffer.buf_size, + sizeof(zeroflash_hcd->fw_status)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy firmware status\n"); + return retval; + } + zeroflash_download_config(); + break; + case REPORT_HDL: + retval = tcm_hcd->enable_irq(tcm_hcd, false, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable interrupt\n"); + return retval; + } + zeroflash_download_firmware(); + break; + default: + break; + } + + return 0; +} + +static int zeroflash_reset(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!zeroflash_hcd) { + retval = zeroflash_init(tcm_hcd); + return retval; + } + + return 0; +} + +static struct syna_tcm_module_cb zeroflash_module = { + .type = TCM_ZEROFLASH, + .init = zeroflash_init, + .remove = zeroflash_remove, + .syncbox = zeroflash_syncbox, + .asyncbox = NULL, + .reset = zeroflash_reset, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init zeroflash_module_init(void) +{ + return syna_tcm_add_module(&zeroflash_module, true); +} + +static void __exit zeroflash_module_exit(void) +{ + syna_tcm_add_module(&zeroflash_module, false); + + wait_for_completion(&zeroflash_remove_complete); +} + +module_init(zeroflash_module_init); +module_exit(zeroflash_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Zeroflash Module"); +MODULE_LICENSE("GPL v2"); From 7b9f8fe0cd66b1015151a5b453c658cf5094a661 Mon Sep 17 00:00:00 2001 From: Raghu Dudda Papanna Date: Fri, 12 Aug 2022 21:30:01 +0530 Subject: [PATCH 032/170] touch: Add synaptics_tcm touch driver for khaje Add synaptic touch module and register DRM Panel notifier. Change-Id: Id8d83c359dec25daedf1e09735e4cba6cd3bd236 Signed-off-by: Raghu Dudda Papanna --- Android.mk | 10 ++++ Kbuild | 15 ++++++ config/gki_khajetouch.conf | 5 ++ config/gki_khajetouchconf.h | 10 ++++ synaptics_tcm/synaptics_tcm_core.c | 77 ++++++++++++++++++++--------- synaptics_tcm/synaptics_tcm_core.h | 4 +- synaptics_tcm/synaptics_tcm_touch.c | 13 ++--- touch_driver_board.mk | 3 +- touch_driver_product.mk | 3 +- 9 files changed, 106 insertions(+), 34 deletions(-) create mode 100644 config/gki_khajetouch.conf create mode 100644 config/gki_khajetouchconf.h diff --git a/Android.mk b/Android.mk index 8f87eb8d51..9dd4750598 100644 --- a/Android.mk +++ b/Android.mk @@ -66,5 +66,15 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := synaptics_tcm_ts.ko + LOCAL_MODULE_KBUILD_NAME := synaptics_tcm_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### endif # DLKM check endif diff --git a/Kbuild b/Kbuild index fabdb54b34..d1e5878e0c 100644 --- a/Kbuild +++ b/Kbuild @@ -11,6 +11,11 @@ endif LINUX_INC += -include $(TOUCH_ROOT)/config/gki_kalamatouchconf.h #endif +#ifeq ($(CONFIG_ARCH_KHAJE), y) + include $(TOUCH_ROOT)/config/gki_khajetouch.conf + LINUX_INC += -include $(TOUCH_ROOT)/config/gki_khajetouchconf.h +#endif + LINUX_INC += -Iinclude/linux \ -Iinclude/linux/drm \ -Iinclude/linux/gunyah \ @@ -121,4 +126,14 @@ ifeq ($(CONFIG_TOUCHSCREEN_DUMMY), y) obj-$(CONFIG_MSM_TOUCH) += dummy_ts.o endif +ifeq ($(CONFIG_TOUCHSCREEN_SYNAPTICS_TCM), y) + synaptics_tcm_ts-y := \ + ./synaptics_tcm/synaptics_tcm_core.o \ + ./synaptics_tcm/synaptics_tcm_i2c.o \ + ./synaptics_tcm/synaptics_tcm_touch.o + + obj-$(CONFIG_MSM_TOUCH) += synaptics_tcm_ts.o + +endif + CDEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/config/gki_khajetouch.conf b/config/gki_khajetouch.conf new file mode 100644 index 0000000000..366a02322a --- /dev/null +++ b/config/gki_khajetouch.conf @@ -0,0 +1,5 @@ +export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM=y +export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C=y +export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE=y +export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH=y +export CONFIG_MSM_TOUCH=m diff --git a/config/gki_khajetouchconf.h b/config/gki_khajetouchconf.h new file mode 100644 index 0000000000..f79af6a910 --- /dev/null +++ b/config/gki_khajetouchconf.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM 1 +#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C 1 +#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE 1 +#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH 1 + diff --git a/synaptics_tcm/synaptics_tcm_core.c b/synaptics_tcm/synaptics_tcm_core.c index f095624ddf..f030032b7e 100644 --- a/synaptics_tcm/synaptics_tcm_core.c +++ b/synaptics_tcm/synaptics_tcm_core.c @@ -207,6 +207,9 @@ static struct device_attribute *dynamic_config_attrs[] = { ATTRIFY(enable_glove), }; +extern int touch_module_init(void); +extern void touch_module_exit(void); + static ssize_t syna_tcm_sysfs_info_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -3044,7 +3047,7 @@ static int syna_tcm_suspend(struct device *dev) #ifdef CONFIG_DRM - +#if 0 static int syna_tcm_early_suspend(struct device *dev) { #ifndef WAKEUP_GESTURE @@ -3101,21 +3104,51 @@ static int syna_tcm_early_suspend(struct device *dev) return 0; } +#endif -static int syna_tcm_fb_notifier_cb(struct notifier_block *nb, - unsigned long action, void *data) +static void syna_tcm_fb_notifier_cb(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, + void *pvt_data) { int retval = 0; - int transition; - struct drm_panel_notifier *evdata = data; - struct syna_tcm_hcd *tcm_hcd = - container_of(nb, struct syna_tcm_hcd, fb_notifier); + struct syna_tcm_hcd *tcm_hcd = (struct syna_tcm_hcd *) pvt_data; - if (!evdata) - return 0; - - transition = *(int *)evdata->data; + if (!notification) { + pr_err("Invalid notification\n"); + return; + } + pr_debug("Notification type:%d, early_trigger:%d", + notification->notif_type, + notification->notif_data.early_trigger); + switch (notification->notif_type) { + case DRM_PANEL_EVENT_UNBLANK: + if (notification->notif_data.early_trigger) + pr_debug("resume notification pre commit\n"); + else + retval = syna_tcm_resume(&tcm_hcd->pdev->dev); + break; + case DRM_PANEL_EVENT_BLANK: + if (notification->notif_data.early_trigger) { + retval = syna_tcm_suspend(&tcm_hcd->pdev->dev); + } else { + pr_debug("suspend notification post commit\n"); + } + break; + case DRM_PANEL_EVENT_BLANK_LP: + pr_debug("received lp event\n"); + break; + case DRM_PANEL_EVENT_FPS_CHANGE: + pr_debug("Received fps change old fps:%d new fps:%d\n", + notification->notif_data.old_fps, + notification->notif_data.new_fps); + break; + default: + pr_debug("notification not serviced :%d\n", + notification->notif_type); + break; + } +#if 0 if (atomic_read(&tcm_hcd->firmware_flashing) && transition == DRM_PANEL_BLANK_POWERDOWN) { retval = wait_event_interruptible_timeout(tcm_hcd->reflash_wq, @@ -3149,9 +3182,8 @@ static int syna_tcm_fb_notifier_cb(struct notifier_block *nb, tcm_hcd->fb_ready++; #endif } +#endif - - return 0; } #elif CONFIG_FB @@ -3313,6 +3345,7 @@ static int syna_tcm_probe(struct platform_device *pdev) const struct syna_tcm_board_data *bdata; const struct syna_tcm_hw_interface *hw_if; struct drm_panel *active_panel = tcm_get_panel(); + void *cookie; hw_if = pdev->dev.platform_data; if (!hw_if) { @@ -3488,16 +3521,17 @@ static int syna_tcm_probe(struct platform_device *pdev) } #ifdef CONFIG_DRM - tcm_hcd->fb_notifier.notifier_call = syna_tcm_fb_notifier_cb; if (active_panel) { - retval = drm_panel_notifier_register(active_panel, - &tcm_hcd->fb_notifier); - if (retval < 0) { + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, active_panel, + &syna_tcm_fb_notifier_cb, tcm_hcd); + if (!cookie) { dev_err(&pdev->dev, "%s: Failed to register fb notifier client\n", __func__); goto err_drm_reg; } + tcm_hcd->notifier_cookie = cookie; } #elif CONFIG_FB @@ -3536,15 +3570,14 @@ static int syna_tcm_probe(struct platform_device *pdev) mod_pool.tcm_hcd = tcm_hcd; mod_pool.queue_work = true; mod_pool.reconstructing = false; - + touch_module_init(); return 0; err_create_run_kthread: #ifdef CONFIG_DRM if (active_panel) - drm_panel_notifier_unregister(active_panel, - &tcm_hcd->fb_notifier); + panel_event_notifier_unregister(tcm_hcd->notifier_cookie); #elif CONFIG_FB fb_unregister_client(&tcm_hcd->fb_notifier); #endif @@ -3647,6 +3680,7 @@ static int syna_tcm_remove(struct platform_device *pdev) const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; struct drm_panel *active_panel = tcm_get_panel(); + touch_module_exit(); mutex_lock(&mod_pool.mutex); if (!list_empty(&mod_pool.list)) { @@ -3687,8 +3721,7 @@ static int syna_tcm_remove(struct platform_device *pdev) #ifdef CONFIG_DRM if (active_panel) - drm_panel_notifier_unregister(active_panel, - &tcm_hcd->fb_notifier); + panel_event_notifier_unregister(tcm_hcd->notifier_cookie); #elif CONFIG_FB fb_unregister_client(&tcm_hcd->fb_notifier); #endif diff --git a/synaptics_tcm/synaptics_tcm_core.h b/synaptics_tcm/synaptics_tcm_core.h index 62f36bdd55..b13a5c01fc 100644 --- a/synaptics_tcm/synaptics_tcm_core.h +++ b/synaptics_tcm/synaptics_tcm_core.h @@ -40,9 +40,10 @@ #include #include #include -#include +#include "synaptics_tcm.h" #ifdef CONFIG_DRM #include +#include #elif CONFIG_FB #include #include @@ -434,6 +435,7 @@ struct syna_tcm_hcd { #if defined(CONFIG_DRM) || defined(CONFIG_FB) struct notifier_block fb_notifier; #endif + void *notifier_cookie; struct syna_tcm_buffer in; struct syna_tcm_buffer out; struct syna_tcm_buffer resp; diff --git a/synaptics_tcm/synaptics_tcm_touch.c b/synaptics_tcm/synaptics_tcm_touch.c index fbc3b28a02..b4b6b75965 100644 --- a/synaptics_tcm/synaptics_tcm_touch.c +++ b/synaptics_tcm/synaptics_tcm_touch.c @@ -1252,21 +1252,16 @@ static struct syna_tcm_module_cb touch_module = { .early_suspend = touch_early_suspend, }; -static int __init touch_module_init(void) + int touch_module_init(void) { return syna_tcm_add_module(&touch_module, true); } - -static void __exit touch_module_exit(void) +EXPORT_SYMBOL(touch_module_init); + void touch_module_exit(void) { syna_tcm_add_module(&touch_module, false); wait_for_completion(&touch_remove_complete); } +EXPORT_SYMBOL(touch_module_exit); -module_init(touch_module_init); -module_exit(touch_module_exit); - -MODULE_AUTHOR("Synaptics, Inc."); -MODULE_DESCRIPTION("Synaptics TCM Touch Module"); -MODULE_LICENSE("GPL v2"); diff --git a/touch_driver_board.mk b/touch_driver_board.mk index 4b7ee0fcd3..635cfd7e43 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -10,7 +10,8 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) ifeq ($(call is-board-platform-in-list,$(TARGET_BOARD_PLATFORM)),true) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ - $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ + $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko endif endif endif diff --git a/touch_driver_product.mk b/touch_driver_product.mk index 6af71d5338..20d33042c9 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -8,5 +8,6 @@ endif ifeq ($(TOUCH_DLKM_ENABLE), true) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ - $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ + $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko endif From 17541541714f4ec80bb038b8665e7d82e5b5d4cf Mon Sep 17 00:00:00 2001 From: gary-rad-ic Date: Wed, 3 Aug 2022 17:50:29 +0530 Subject: [PATCH 033/170] touch: add Raydium I2C touchscreen driver add support to enable Raydium RM6D030 I2C touchscreen driver Signed-off-by: gary-rad-ic Git-commit: 014e8a5066b2b04cacaf83fdae80abd840c2f5f6 Git-repo: https://github.com/gary-rad-ic/wearable-touch-driver/tree/RM6D030 Change-Id: Icd877f7f6787c5da14347240f08522b886581b1b Signed-off-by: Surya Teja Kudiri --- raydium/Config.h | 54 + raydium/Makefile | 4 + raydium/chip_raydium/f303_ic_control.c | 516 ++ raydium/chip_raydium/f303_ic_control.h | 31 + raydium/chip_raydium/f303_ic_reg.h | 281 + raydium/chip_raydium/f303_ic_test.c | 2319 +++++++ raydium/chip_raydium/f303_ic_test.h | 65 + raydium/chip_raydium/ic_drv_global.c | 127 + raydium/chip_raydium/ic_drv_global.h | 190 + raydium/chip_raydium/ic_drv_interface.c | 229 + raydium/chip_raydium/ic_drv_interface.h | 211 + raydium/drv_interface.c | 437 ++ raydium/drv_interface.h | 86 + raydium/rad_fw_image_30.h | 7757 +++++++++++++++++++++++ raydium/raydium_driver.c | 2122 +++++++ raydium/raydium_driver.h | 432 ++ raydium/raydium_fw_update.c | 1135 ++++ raydium/raydium_selftest.c | 597 ++ raydium/raydium_selftest.h | 34 + raydium/raydium_sysfs.c | 1454 +++++ raydium/tpselftest_30.h | 54 + 21 files changed, 18135 insertions(+) create mode 100644 raydium/Config.h create mode 100644 raydium/Makefile create mode 100644 raydium/chip_raydium/f303_ic_control.c create mode 100644 raydium/chip_raydium/f303_ic_control.h create mode 100644 raydium/chip_raydium/f303_ic_reg.h create mode 100644 raydium/chip_raydium/f303_ic_test.c create mode 100644 raydium/chip_raydium/f303_ic_test.h create mode 100644 raydium/chip_raydium/ic_drv_global.c create mode 100644 raydium/chip_raydium/ic_drv_global.h create mode 100644 raydium/chip_raydium/ic_drv_interface.c create mode 100644 raydium/chip_raydium/ic_drv_interface.h create mode 100644 raydium/drv_interface.c create mode 100644 raydium/drv_interface.h create mode 100644 raydium/rad_fw_image_30.h create mode 100644 raydium/raydium_driver.c create mode 100644 raydium/raydium_driver.h create mode 100644 raydium/raydium_fw_update.c create mode 100644 raydium/raydium_selftest.c create mode 100644 raydium/raydium_selftest.h create mode 100644 raydium/raydium_sysfs.c create mode 100644 raydium/tpselftest_30.h diff --git a/raydium/Config.h b/raydium/Config.h new file mode 100644 index 0000000000..46357eec69 --- /dev/null +++ b/raydium/Config.h @@ -0,0 +1,54 @@ +/* config.h + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + +#ifndef __CONFIG_H +#define __CONFIG_H + + +/***************************************************************************** +** GLOBAL MARCO DEFINITION +******************************************************************************/ +#define MINIMUM(x, y) (((x) < (y)) ? (x) : (y)) // Compares two parameters, return minimum. +#define MAXIMUM(x, y) (((x) > (y)) ? (x) : (y)) // Compares two parameters, return maximum. +#define ABS(x) ((x) >= 0 ? (x) : -(x)) // return absolute value + +/***************************************************************************** +** GLOBAL FUNCTIONAL DEFINITION +******************************************************************************/ + +//Bootloader +#define BOOTLOADER 1 //1: with bootloader, 0: without bootloader +#define SELFTEST 1 //1: For System Selftest, 0:For Dongle Open/Short Tool +#if SELFTEST +#define SELFTEST_3X 1 +#define SELFTEST_2X 0 +#else +#define SELFTEST_3X 0 +#define SELFTEST_2X 0 +#endif +#define ENABLE_TEST_TIME_MEASURMENT 1 +#define ENABLE_TEST_TIME_MEASURMENT_CC 0 +#define ENABLE_WDT 0 +#define ENABLE_TEST_RSU_DATA_SHOW 0 +#define ENABLE_AUO_VERIFY_LOG 0 +#define ENABLE_CONTROL_OPENSHORT_WDT 0 +#define ENABLE_TEST_GPIO_MEASURMENT 0 +#endif /* end __CONFIG_H */ +/***************************************************************************** +** End Of File +******************************************************************************/ diff --git a/raydium/Makefile b/raydium/Makefile new file mode 100644 index 0000000000..f8be53f2de --- /dev/null +++ b/raydium/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for the touchscreen raydium drivers. +# +obj-$(CONFIG_TOUCHSCREEN_RM_TS) += raydium_driver.o raydium_sysfs.o raydium_fw_update.o drv_interface.o raydium_selftest.o chip_raydium/ic_drv_global.o chip_raydium/ic_drv_interface.o chip_raydium/f303_ic_control.o chip_raydium/f303_ic_test.o diff --git a/raydium/chip_raydium/f303_ic_control.c b/raydium/chip_raydium/f303_ic_control.c new file mode 100644 index 0000000000..8c5a79a6a7 --- /dev/null +++ b/raydium/chip_raydium/f303_ic_control.c @@ -0,0 +1,516 @@ +/* f303_ic_control.c + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#endif + +#include "drv_interface.h" +#include "ic_drv_global.h" +#include "ic_drv_interface.h" +#include "f303_ic_control.h" +#include "f303_ic_reg.h" + +unsigned char check_dev_id_3x(unsigned short u16_dev_id) +{ + unsigned int u32_read; + + if (handle_ic_read(REG_FLASHCTL_DEVID_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + if (((u32_read & 0xFFFF0000) >> 16) == u16_dev_id) { + g_u16_dev_id = DEVICE_ID_3X; + return SUCCESS; + } + DEBUGOUT("Device ID NG! 0x%x:0x%x\r\n", ((u32_read & 0xFFFF0000) >> 16), u16_dev_id); + return ERROR; +} + +unsigned char check_dev_sub_version_3x(unsigned char u8_version) +{ + unsigned int u32_read; + + if (handle_ic_read(REG_FLASHCTL_DEVID_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + DEBUGOUT("Device Sub Version 0x%x\r\n", u32_read); + if ((u32_read & 0x000000FF) == u8_version) + return SUCCESS; + DEBUGOUT("Device Sub Version NG! 0x%x:0x%x\r\n", (u32_read & 0x000000FF), u8_version); + return ERROR; +} + +unsigned char enable_ic_block_3x(void) +{ + unsigned int u32_read; + + if (handle_ic_read(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_read |= 0xc8000000; + /*u32_read |= (BLKEN_FIC_RB_EN | BLKEN_GPIO_RB_EN | BLKEN_SYS_RB_EN);*/ + if (handle_ic_write(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + if (handle_ic_read(REG_SYSCON_MISCIER_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_read |= 0x00000404; + /*u32_read |= (MISCIER_RB_MCU_INTO | MISCIER_RB_MCU_INT_EN);*/ + if (handle_ic_write(REG_SYSCON_MISCIER_ADDR, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + return SUCCESS; +} + +unsigned char disable_i2c_deglitch_3x(void) +{ + unsigned int u32_buf = 0; + unsigned char u8_retry = 3, u8_comfirm_time = 3; + unsigned char u8_check = 0, u8_i = 0; + unsigned int u32_i2c_deglitch = I2CENG_AUTO_I2C_DGF_MODE2 | I2CENG_DGF_OSC_AUTOSEL | I2CENG_AUTO_I2C_DGF_MODE | I2CENG_I2CS_DGFEN_DLY_NUM(6); + + /*check I2C mode*/ + while (u8_retry--) { + u32_buf = 0; + handle_ic_read(REG_FLASHCTL_DEVID_ADDR, 4, (unsigned char *)(&u32_buf), g_u8_drv_interface, I2C_WORD_MODE); + if ((u32_buf & 0xFFFF0000) == 0xF3030000) + u8_check++; + else + break; + } + if (u8_check == 3) { + if (!g_u8_mute_i2c_err_log) + DEBUGOUT("PDA2 OK\r\n"); + return SUCCESS; + } + + u8_retry = 100; + while (u8_retry--) { + if (handle_ic_write(REG_I2CENG_ADDR, 4, (unsigned char *)(&u32_i2c_deglitch), I2C_PDA_MODE, I2C_WORD_MODE) == ERROR) { + /*DEBUGOUT("[disable_i2c_deglitch_3x] handle_ic_write I2C NG!\r\n");*/ + if (!g_u8_mute_i2c_err_log) + DEBUGOUT("[DI2CDG]-W"); + continue; + } + + u8_check = 0; + for (u8_i = 0; u8_i < u8_comfirm_time; u8_i++) { + /*check I2C*/ + u32_buf = 0; + if (handle_ic_read(REG_I2CENG_ADDR, 4, (unsigned char *)(&u32_buf), I2C_PDA_MODE, I2C_WORD_MODE) == ERROR) { + /*DEBUGOUT("[disable_i2c_deglitch_3x] 2.handle_ic_read I2C NG!\r\n");*/ + if (!g_u8_mute_i2c_err_log) + DEBUGOUT("[DI2CDG]-R"); + break; + } + + if (u32_buf == u32_i2c_deglitch) + u8_check++; + else + break; + } + + if (u8_check == u8_comfirm_time) + break; + } + if (!g_u8_mute_i2c_err_log) + DEBUGOUT("\r\n"); + + if (u8_retry == 0) + return ERROR; + + u32_buf = GPIO_DEGLITCH_EN(3); + if (handle_ic_write(REG_GPIO_DEGLITCH_ENABLE, 4, (unsigned char *)(&u32_buf), I2C_PDA_MODE, I2C_WORD_MODE) == ERROR) { + DEBUGOUT("[DI2CDG_3x] 3.handle_ic_write I2C NG!\r\n"); + return ERROR; + } + + /*Enable PDA2*/ + u32_buf = PDA2CTL_PDA2_EN | PDA2CTL_SIE2; + if (handle_ic_write(REG_PDA2CTL_ADDR, 4, (unsigned char *)(&u32_buf), I2C_PDA_MODE, I2C_WORD_MODE) == ERROR) { + DEBUGOUT("[DI2CDG_3x] 4.i2c_write_pda I2C NG!\r\n"); + return ERROR; + } + + /*Disable PDA*/ +#if SELFTEST +#ifdef __KERNEL__ + raydium_i2c_pda_set_address(RAYDIUM_PDA_I2CREG, DISABLE); +#else + sysfs_mode_control(ENABLE_PDA2); +#endif +#else + i2c_set_pda_address(I2C_EID, 0x500006, I2C_PDA2_WORD_MODE); +#endif + return SUCCESS; +} + +unsigned char stop_mcu_3x(unsigned char u8_is_tp_reset) +{ + unsigned short u16_time_out = 100; + unsigned int u32_read_data; + unsigned int u32_write = 0; + + g_u8_mute_i2c_err_log = TRUE; + if (u8_is_tp_reset) { + gpio_touch_hw_reset(); + delay_ms(35); + if (disable_i2c_deglitch_3x() == ERROR) { + DEBUGOUT("[%s] 2.DI2CDG NG!\r\n", __func__); + goto EXIT_ERROR; + } + } + + /*DEBUGOUT("[stop_mcu_3x] 1\r\n");*/ + + /*Stop MCU*/ + /*memset(wData, 0, sizeof(wData));*/ + u32_write = (MCU_HOLD | SKIP_LOAD); + if (handle_ic_write(REG_FLASHCTL_FLASH_STATE_REG_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + goto EXIT_ERROR; + + u32_write = BLKRST_SW_RST; + if (handle_ic_write(REG_SYSCON_BLKRST_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + goto EXIT_ERROR; + delay_ms(20); + + if (disable_i2c_deglitch_3x() == ERROR) { + /*DEBUGOUT("[stop_mcu_3x] 3.disable_i2c_deglitch_3x NG!\r\n");*/ + DEBUGOUT("[%s] 3.DI2CDG NG!\r\n", __func__); + goto EXIT_ERROR; + } + /*DEBUGOUT("[stop_mcu_3x] 2\r\n");*/ + + if (handle_ic_read(REG_FLASHCTL_FLASH_STATE_REG_ADDR, 4, (unsigned char *)(&u32_read_data), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) { + DEBUGOUT("[%s] 4.Flash State NG!\r\n", __func__); + goto EXIT_ERROR; + } + + while ((u32_read_data & MCU_HOLD_STATUS) == 0 && u16_time_out-- > 0) { + delay_ms(10); + if (handle_ic_read(REG_FLASHCTL_FLASH_STATE_REG_ADDR, 4, (unsigned char *)(&u32_read_data), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + goto EXIT_ERROR; + +#if ENABLE_WDT + Chip_WWDT_Feed(LPC_WWDT);/* reload the Watchdog timer*/ +#endif + } + + DEBUGOUT("Stop MCU=0x%X(0x%x)(%d)!!\r\n", u32_read_data, (u32_read_data & MCU_HOLD_STATUS), u16_time_out); + + if ((u32_read_data & MCU_HOLD_STATUS) == 0) { + DEBUGOUT("[%s] 4.STOP MCU NG!\r\n", __func__); + goto EXIT_ERROR; + } + + g_u8_mute_i2c_err_log = FALSE; + return SUCCESS; + +EXIT_ERROR: + g_u8_mute_i2c_err_log = FALSE; + return ERROR; +} + +unsigned char hardware_reset_3x(unsigned char u8_enable_ic_block) +{ + unsigned char u8_time_out = 200; + + DEBUGOUT("HW Reseting...\r\n"); + + gpio_touch_hw_reset(); + delay_ms(100); + + g_u8_mute_i2c_err_log = TRUE; + if (disable_i2c_deglitch_3x() == ERROR) { + /*DEBUGOUT("[hardware_reset_3x] disable_i2c_deglitch_3x NG!\r\n");*/ + DEBUGOUT("[%s] DI2CDG NG!\r\n, __func__"); + g_u8_mute_i2c_err_log = FALSE; + return ERROR; + } + g_u8_mute_i2c_err_log = FALSE; + + if (u8_enable_ic_block) { + if (enable_ic_block_3x() == ERROR) { + DEBUGOUT("HW Reset NG!!\r\n"); + return ERROR; + } + } + while (u8_time_out--) { + if (gpio_touch_int_pin_state_access()) + break; + if (u8_time_out == 0) + return ERROR; + delay_ms(1); + } + return SUCCESS; +} + +unsigned char set_fw_system_cmd_3x(unsigned int u32_sysm_cmd) +{ + unsigned char u8_time_out = 100, u8_value; + + if (handle_ic_write(FW_SYS_CMD_ADDR, 4, (unsigned char *)&u32_sysm_cmd, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /* Wait Test Command ready*/ + while (--u8_time_out) { + delay_ms(1); + if (handle_ic_read(FW_SYS_CMD_ADDR, 1, &u8_value, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + else if (u8_value == 0) + break; + } + + if (u8_time_out == 0) + return ERROR; + + return SUCCESS; +} + +unsigned char wait_fw_state_3x(unsigned int u32_addr, unsigned int u32_state, unsigned short u16_delay, unsigned short u16_retry) +{ + unsigned int u32_read_data = 0; + + do { + if (handle_ic_read(u32_addr, 4, (unsigned char *)(&u32_read_data), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + delay_ms(u16_delay); + u16_retry--; + +#if ENABLE_WDT + /* reload the Watchdog timer*/ + Chip_WWDT_Feed(LPC_WWDT); +#endif + } while ((u32_read_data != u32_state) && (u16_retry != 0)); + + if (u32_read_data != u32_state) + return ERROR; + return SUCCESS; +} + +unsigned char wait_T2D_done_state_3x(unsigned int u32_addr, unsigned short u16_delay, unsigned short u16_retry) +{ + unsigned int u32_read_data = 0; + + do { + if (handle_ic_read(u32_addr, 4, (unsigned char *)(&u32_read_data), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + /*DEBUGOUT("[T2DW] ready 0x%x\r\n", u32_read_data);*/ + + delay_ms(u16_delay); + u16_retry--; + } while (((u32_read_data & 0x01) == 0) && (u16_retry != 0)); + + if ((u32_read_data & 0x01) == 0) + return ERROR; + return SUCCESS; +} + +unsigned char WriteToDriver_3x(unsigned char *p_u8_data, unsigned char u8_data_length) +{ + unsigned int u32_write = 0; + + + if (p_u8_data != NULL) { + + if (p_u8_data[0] == 0xFE) { + g_u8_PAGE_ADDR = p_u8_data[1]; + return SUCCESS; + } + + if (p_u8_data[0] == 0x35) { + u32_write = ((0x00 << 24) | (0x00 << 16) | (p_u8_data[0] << 8) | g_u8_PAGE_ADDR); + if (handle_ic_write(REG_T2D_CONFIG_2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + u32_write = 0x0000015A; + if (handle_ic_write(REG_T2D_CONFIG_3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + } + + if (u8_data_length == 1) /*cmd mode*/ + u32_write = ((0x00 << 24) | (p_u8_data[1] << 16) | (p_u8_data[0] << 8) | g_u8_PAGE_ADDR); + else if (u8_data_length == 2) + u32_write = ((0x01 << 24) | (p_u8_data[1] << 16) | (p_u8_data[0] << 8) | g_u8_PAGE_ADDR); + else if (u8_data_length == 5) { + /* 2A 2B*/ + + u32_write = ((0x01 << 24) | (p_u8_data[1] << 16) | (p_u8_data[0] << 8) | 0x00); + if (handle_ic_write(REG_T2D_CONFIG_2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + u32_write = 0x0000015A; + if (handle_ic_write(REG_T2D_CONFIG_3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_write = ((0x01 << 24) | (p_u8_data[2] << 16) | (p_u8_data[0] << 8) | 0x01); + if (handle_ic_write(REG_T2D_CONFIG_2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + u32_write = 0x0000015A; + if (handle_ic_write(REG_T2D_CONFIG_3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_write = ((0x01 << 24) | (p_u8_data[3] << 16) | (p_u8_data[0] << 8) | 0x02); + if (handle_ic_write(REG_T2D_CONFIG_2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + u32_write = 0x0000015A; + if (handle_ic_write(REG_T2D_CONFIG_3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_write = ((0x01 << 24) | (p_u8_data[4] << 16) | (p_u8_data[0] << 8) | 0x03); + if (handle_ic_write(REG_T2D_CONFIG_2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + u32_write = 0x0000015A; + if (handle_ic_write(REG_T2D_CONFIG_3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + return SUCCESS; + } + + if (handle_ic_write(REG_T2D_CONFIG_2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + u32_write = 0x0000015A; + if (handle_ic_write(REG_T2D_CONFIG_3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + } + return SUCCESS; +} + +unsigned char ReadFromDriver_3x(unsigned char *p_u8_addr, unsigned char u8_read_len, unsigned char *p_u8_output_buf) +{ + unsigned int u32_write = 0; + + if ((p_u8_addr[0] == 0xFE) || (p_u8_addr[0] == 0xFF) || (u8_read_len > 1)) { + DEBUGOUT("[%s] no use\r\n", __func__); + return FALSE; + } + + u32_write = ((p_u8_addr[0] << 24) | (g_u8_PAGE_ADDR << 16) | (0x01 << 8) | 0x82); + DEBUGOUT("read address 0x%x\r\n", u32_write); + if (handle_ic_write(REG_T2D_R_CONFIG_1, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + if (handle_ic_read(REG_T2D_R_CONFIG_2, 4, p_u8_output_buf, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + DEBUGOUT("read data 0x%x\r\n", p_u8_output_buf[0]); + return TRUE; + +} + +unsigned char WriteDriverByTouchMode(const unsigned char *p_u8_data, uint16_t u16DataLength) +{ + unsigned short TotalDataLen = (p_u8_data[3] << 8) + p_u8_data[4] - 7;/*Valid data not include 7 byte header*/ + unsigned short u16_i = 0/*, u8_j = 0*/; + unsigned char u8_CurCmdLen = 0; + unsigned int u32_write = 0; + +/* + * unsigned char u8_read_buf[4]; + * signed char i8_retry_cnt = 3; + * unsigned char u8_error_flag = 0; + */ + + for (u16_i = 0; u16_i < (u16DataLength - 7) && u16_i < sizeof(g_u8_data_buf); u16_i++) + g_u8_data_buf[u16_i] = p_u8_data[u16_i + 7]; + if (p_u8_data[0] == 0xF3 && p_u8_data[1] == 0x01) { + /*Check Command type*/ + DEBUGOUT("Write Key\r\n"); + u32_write = 0x015AFABC; + if (handle_ic_write(REG_T2D_CONFIG_2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + u32_write = 0x0000015A; + if (handle_ic_write(REG_T2D_CONFIG_3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + if (p_u8_data[2] == 0x01 || + p_u8_data[2] == 0x10) { /*SPI short multiple command*/ + for (u16_i = 0; u16_i < TotalDataLen; u16_i += (u8_CurCmdLen + 1)) { + u8_CurCmdLen = g_u8_data_buf[u16_i]; + +/* + * DEBUGOUT(" i = %d\r\n", u16_i); + * if (u8_CurCmdLen == 2) + * DEBUGOUT("-Before Parse Data[%d] = 0x%02X, 0x%02X, 0x%02X, %d\r\n", u16_i, g_u8_data_buf[u16_i], g_u8_data_buf[u16_i + 1], g_u8_data_buf[u16_i + 2], u8_CurCmdLen); + * else if (u8_CurCmdLen == 3) + * DEBUGOUT("-Before Parse Data[%d] = 0x%02X, 0x%02X, 0x%02X, 0x%02X, %d\r\n", u16_i, g_u8_data_buf[u16_i], g_u8_data_buf[u16_i + 1], g_u8_data_buf[u16_i + 2], g_u8_data_b uf[u16_i + 3], u8_CurCmdLen); + * else + * DEBUGOUT("-Before Parse Data[%d] = 0x%02X, 0x%02X, %d\r\n", u16_i, g_u8_data_buf[u16_i], g_u8_data_buf[u16_i + 1], u8_CurCmdLen); + */ + + /*Check is Delay command*/ + if (g_u8_data_buf[u16_i + 1] != 0xFF) { /*Check Command ID*/ + + if (wait_T2D_done_state_3x(0x50001220, 1, 100) == ERROR) { + DEBUGOUT("[T2DW] Check T2D idle Fail\r\n"); + return ERROR; + } + + WriteToDriver_3x((unsigned char *)&g_u8_data_buf[u16_i + 1], u8_CurCmdLen); + /*delay_ms(1);*/ +/* + * i8_retry_cnt = 3; + * while (i8_retry_cnt) { + * u8_error_flag = 0; + * if (ReadFromDriver_3x((unsigned char *)&g_u8_data_buf[u16_i + 1], u8_CurCmdLen - 1 , u8_read_buf)) { + * for (u8_j = 0 ; u8_j < u8_CurCmdLen - 1 ; u8_j++) { + * if (g_u8_data_buf[u16_i + 2 + u8_j] != u8_read_buf[u8_j]) { + * u8_error_flag = 1; + * DEBUGOUT("write address:%x\r\n", g_u8_data_buf[u16_i + 1]); + * DEBUGOUT("Data:%d\r\n", g_u8_data_buf[u16_i + 2 + u8_j]); + * DEBUGOUT("g_u8_spi_cmd_read_buf[%d]:%x\r\n", u8_j, u8_read_buf[u8_j]); + * } + * } + * if (u8_error_flag == 0) + * break; + * if (u8_error_flag == 1) + * i8_retry_cnt--; + * if (i8_retry_cnt == 0) { + * DEBUGOUT("i8_retry_cnt error!\r\n"); + * return ERROR; + * } + * } else + * break; + * } + */ + } else { + DEBUGOUT("Delay\r\n"); + delay_ms(g_u8_data_buf[u16_i + 2]); + } + } + } else { + DEBUGOUT("[%s] command type not support\r\n", __func__); + return ERROR; + } + u32_write = 0x0100FABC; + if (handle_ic_write(REG_T2D_CONFIG_2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + u32_write = 0x0000015A; + if (handle_ic_write(REG_T2D_CONFIG_3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + } + return SUCCESS; +} + diff --git a/raydium/chip_raydium/f303_ic_control.h b/raydium/chip_raydium/f303_ic_control.h new file mode 100644 index 0000000000..834122a2a7 --- /dev/null +++ b/raydium/chip_raydium/f303_ic_control.h @@ -0,0 +1,31 @@ +/* f303_ic_control.h + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + +#include "ic_drv_global.h" + +#define HEADER_LENGTH (4)//length + checksum + +extern unsigned char enable_ic_block_3x(void); +extern unsigned char stop_mcu_3x(unsigned char u8_is_tp_reset); +extern unsigned char hardware_reset_3x(unsigned char u8_enable_ic_block); +extern unsigned char check_dev_id_3x(unsigned short u16_dev_id); +extern unsigned char check_dev_sub_version_3x(unsigned char u8_version); +extern unsigned char set_fw_system_cmd_3x(unsigned int u32_sysm_cmd); +extern unsigned char wait_fw_state_3x(unsigned int u32_addr, unsigned int u32_state, unsigned short u16_delay, unsigned short u16_retry); +extern unsigned char disable_i2c_deglitch_3x(void); +extern unsigned char WriteDriverByTouchMode(const unsigned char *p_u8_data, uint16_t u16DataLength); diff --git a/raydium/chip_raydium/f303_ic_reg.h b/raydium/chip_raydium/f303_ic_reg.h new file mode 100644 index 0000000000..9c4ca329f8 --- /dev/null +++ b/raydium/chip_raydium/f303_ic_reg.h @@ -0,0 +1,281 @@ +/* f303_ic_reg.h + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + + // ['h5000_0900], [32'hFFFF_FFFF], PRAM_LOCK +#define I2CTB_LOCK (0x00000001<<6) // [0] (R/W) I2C Table lock +#define BOTLR_LOCK (0x00000001<<5) // [0] (R/W) Boot loader lock +#define USEFW_LOCK (0x00000001<<4) // [0] (R/W) User fw lock +#define CONFIG_LOCK (0x00000001<<3) // [0] (R/W) Configuration lock +#define COMP_LOCK (0x00000001<<2) // [0] (R/W) compensation lock +#define BASEL_LOCK (0x00000001<<1) // [0] (R/W) baseline lock +#define INICO_LOCK (0x00000001<<0) // [0] (R/W) Initial code lock + +// ['h5000_0904], [32'h0000_0000], Program RAM store type, PRAM_STORE_TYPE +#define BOTLR_AREA (0x00000001<<5) // [0] (R/W) Boot loader area +#define USEFW_AREA (0x00000001<<4) // [0] (R/W) User fw area +#define CONFIG_AREA (0x00000001<<3) // [0] (R/W) Configuration area +#define COMP_AREA (0x00000001<<2) // [0] (R/W) compensation area +#define BASEL_AREA (0x00000001<<1) // [0] (R/W) baseline area +#define INICO_AREA (0x00000001<<0) // [0] (R/W) Initial code area + +// ['h5000_0918], [32'h0000_0000],Flash state control register ,FLASH_STATE_REG +#define BLDR_FINISH (0x00000001<<14) // [0] (R) MCU_HOLD status +#define MCU_HOLD_STATUS (0x00000001<<13) // [0] (R) MCU_HOLD status +#define BOOT_REGION (0x00000001<<12) // [0] (R) boot region index +#define BL_CRC_CHK (0x00000001<<11) // [0] (R/W) boot-loader area CRC check +#define FW_CRC_CHK (0x00000001<<10) // [0] (R/W) user FW CRC check +#define PARA_CRC_CHK (0x00000001<<9) // [0] (R/W) parameter area CRC check +#define COMP_CRC_CHK (0x00000001<<8) // [0] (R/W) compensation area CRC check +#define BASELINE_CRC_CHK (0x00000001<<7) // [0] (R/W) baseline area CRC check +#define INITIAL_CRC_CHK (0x00000001<<6) // [0] (R/W) initial code CRC check +#define MCU_HOLD (0x00000001<<5) // [0] (R/W) MCU hold +#define SKIP_LOAD (0x00000001<<4) // [0] (R/W) Skip all load flash action +#define FW_INICO_ERR (0x00000001<<3) // [0] (R) FW_INICO_ERR +#define FW_CG_ERR (0x00000001<<2) // [0] (R) cc bl CRC error +#define FW_CRC_ERR (0x00000001<<1) // [0] (R) FW_CRC_ERR +#define BL0_CRC_ERR (0x00000001<<0) // [0] (R) BL0_CRC_ERR + +// ['h5000_0934], [32'h0000_0000], FLASH Lock and Key Register (main) (FLKEY1) +#define FLKEY1_LOCK (0x00000000<<0) // [7:0] (R/W) FLASH Lock and Key1 Register +#define FLKEY1_KEY (0x000000A5<<0) // [7:0] (R/W) FLASH Unlock and Key1 Register:0xA5 + +// ['h5000_0938], [32'h0000_0000], FLASH Lock and Key Register (information) (FLKEY2) +#define FLKEY2_LOCK (0x00000000<<0) // [7:0] (R/W) FLASH Lock and Key3 Register +#define FLKEY2_KEY (0x000000D7<<0) // [7:0] (R/W) FLASH Unlock and Key3 Register:0xD7 + +//#define MAX_SENSING_PIN_NUM 30 + +// ['h5000_0610], [32'h0106_0300], I2C eng Register (I2CENG) +#define REG_I2CENG_ADDR 0x50000610 +#define I2CENG_AUTO_I2C_DGF_MODE2 (0x00000001<<26) // [26:26] (R/W) auto_i2c_dgf_mode2,, 1: switch def_en after next posedge SCL_dgf, 0: switch def_en control auto_i2c_dgf_mode (bit[24]). +#define I2CENG_DGF_OSC_AUTOSEL (0x00000001<<25) // [25:25] (R/W) dgf_osc_autosel,, 0: TP OSC control, 1: TP OSC/DRIVER OSC control +#define I2CENG_AUTO_I2C_DGF_MODE (0x00000001<<24) // [24:24] (R/W) dgf_en signal delay timing select 0: switch def_en after STOP signal 1: switch def_en after next posedge SCL. +#define I2CENG_I2CS_DGFEN_DLY_NUM(u7x) ((u7x&0x0000007F)<<16) // [22:16] (R/W) dgf_en signal delay number (clock by system clock ). +#define I2CENG_I2CS_DEGFIR_NUM(u7x) ((u7x&0x0000007F)<<8) // [14:8] (R/W) I2C Pad (SCL/SDA) deglitch filter number. +#define I2CENG_RB_MANUAL_I2C_DGF (0x00000001<<7) // [7:7] (R/W) I2C Pad (SCL/SDA) deglitch filter manual mode control 1: Enable 0: Disable. +#define I2CENG_RB_I2C_DGF_EN (0x00000001<<6) // [6:6] (R/W) I2C Pad (SCL/SDA) deglitch filter enable control register when rb_manual_i2c_dgf =1, 1: Enable 0: Disable. +#define I2CENG_FIRST_DAT_SEL(u2x) ((u2x&0x00000003)<<0) // [1:0] (R/W) First data request select for PDA2 read. 0: at 2nd SCL; 1: at 3th SCL; 2:at 4th SCL; 3:at 5th SCL. + +#define REG_I2C_I2CFLASHPRO 0x50000624 + +//['h5000_0628], [32'h0000_0000], PDA2 Control Register (PDA2CTL) +#define REG_PDA2CTL_ADDR 0x50000628 +#define PDA2CTL_PDA2_EN (0x00000001UL<<2) // [2:2] (R/W) PDA2 enable bit. 1: enable 0: disable. +#define PDA2CTL_SIE2 (0x00000001<<1) // [1:1] (R/W) SIE2 enable register . 1: enable 0: mask. +#define PDA2CTL_SI2 (0x00000001<<0) // [0:0] (R/W) SI2 interrupt flag (write 1 to clear). + + +//['h5000_0E1C], [32'h0000_0000], GPIO deglitch enable(GPIO_DEGLITCH) +#define REG_GPIO_DEGLITCH_ENABLE 0x50000E1C +#define GPIO_PULLH_EN(u2x) ((u2x&0x00000003)<<0) // [3:2] (R/W) 1: enable pull-high of GPIO, 0: disable pull-high of GPIO +#define GPIO_DEGLITCH_EN(u2x) ((u2x&0x00000003)<<0) // [1:0] (R/W) 1: enable deglitch function of GPIO, 0: disable deglitch function of GPIO + +#define REG_SYSCON_BLKEN_ADDR 0x40000000 +#define REG_SYSCON_BLKRST_ADDR 0x40000004 +//#define REG_SYSCON_MISCIER_ADDR 0x40000014 + +#define REG_T2D_CONFIG_1 0x5000145c +#define REG_T2D_CONFIG_2 0x50001460 +#define REG_T2D_CONFIG_3 0x50001464 +#define REG_T2D_R_CONFIG_1 0x50001468 +#define REG_T2D_R_CONFIG_2 0x5000146C + +#define MCU_HOLD (0x00000001<<5) // [0] (R/W) MCU hold +#define SKIP_LOAD (0x00000001<<4) // [0] (R/W) Skip all load flash action +#define BLKRST_SW_RST (0x00000001<<0) // [0] (R/W) 1: Software reset, all digital block will be reset +#define MCU_HOLD_STATUS (0x00000001<<13) // [0] (R) MCU_HOLD status +#define FLH_RELEASE_PD (0x00000001<<5) // [0] (R/W) Release from deep power down mode +#define BL_CRC_CHK (0x00000001<<11) // [0] (R/W) boot-loader area CRC check + +/* Base addresses */ +#define RM_PRAM_BASE (0x00000000UL) // Program, AHB +#define RM_RAM_BASE (0x20000000UL) // SRAM, AHB +#define RM_AHB_BASE (0x40000000UL) // Peripheral, AHB +#define RM_APB_BASE (0x50000000UL) // Peripheral, APB + +//#define FW_SYS_CMD_ADDR 0x20000288 +#define FW_FT_CMD_ADDR 0x20000289 +#define FW_FT_ARG0_ADDR 0x2000028A +#define FW_FT_ARG1_ADDR 0x2000028C +//#define FW_FT_IMG_ADDR 0x2000019C +//#define FW_TP_SEQ_NUM_ADDR 0x20000290 + +#define SYS_CMD_FUNC_DIS_BS_UPDATE 0x20 +#define DIS_BASELINE_UPDATE 0x00010000 + +#define SYS_CMD_DO_BL_CAL 0x5A +#define SYS_CMD_READ_CAL_FLAG 0x5B +#define SYS_CMD_DO_CC_CAL 0x5C +#define SYS_CMD_CAL_WAIT 0x5D + +#define SYS_CMD_WAKEUP_GESTURE_ENABLE 0x40 +#define SYS_CMD_WAKEUP_GESTURE_DISABLE 0x41 + +#define SYS_CMD_FT_GET_DSP_NS_PARAM 0x60 +#define SYS_CMD_FT_FUN_FLAG 0x62 +#define SYS_CMD_FT_DC_DISABLE 0x010000 +#define SYS_CMD_FT_DIG_GAIN_ENABLE 0x020000 +#define SYS_CMD_FT_TEST_LOG_EN 0x800000 + +#define FW_FT_CHANNEL_X_ADDR (PRAM_PARA_START + 24) +#define FW_FT_CHANNEL_Y_ADDR (PRAM_PARA_START + 25) +#define FW_FT_PIN_ADDR (PRAM_PARA_START + 27) +#define FW_FT_PWR_MODE_ADDR (PRAM_PARA_START + 26) +#define FW_FT_FW_VERSION (PRAM_PARA_START + 4) +#define FW_FT_SRAM_FW_VERSION 0x200006E0 + + +#define BOOT_SYNC_DATA_ADDR 0x20000200 +#define BOOT_MAIN_STATE_ADDR 0x20000204 +#define BOOT_NORMAL_STATE_ADDR 0x20000208 +#define BOOT_BURNING_STATE_ADDR 0x2000020C +#define BOOT_CMD_TYPE_ADDR 0x20000210 +#define BOOT_RET_DATA_ADDR 0x20000214 +#define BOOT_TEST_MODE_ADDR 0x20000218 + +#define FLASH_OFFSET (0x7800) +#define PRAM_BASELINE_LENGTH (0x130) +#define PRAM_COMP_LENGTH (0x304) +#define PRAM_DIS_INIT_LENGTH (0x80) +#define PRAM_PARA_LENGTH (0x174) +#define PRAM_FW_LENGTH (0x7300) +#define PRAM_BOOT_LENGTH (0x800) + +#define PRAM_BOOT_START (0x0000) +#define PRAM_DIS_INIT_START (0x7F80) +#define PRAM_COMP_START (0x7C78) +#define PRAM_PARA_START (0x7B00) +#define PRAM_FW_START (0x0800) +#define PRAM_RESERVE_START (0x0800) +#define PRAM_PARA_DC_THD_ADDR (PRAM_PARA_START + 136) +#define PRAM_CC_TABLE_ADDR (0x7F78) + +#define PRAM_BASEINE_START (0x6CCC) +#define PRAM_BOOT_CRC_LENGTH (PRAM_BOOT_LENGTH - HEADER_LENGTH) +#define PRAM_FW_CRC_START (0x6B5C) +#define PRAM_FW_CRC_LENGTH (PRAM_FW_LENGTH + PRAM_PARA_LENGTH) //0x7474 +#define PRAM_CB_CRC_START (0x6DFC) +#define PRAM_CB_CRC_LENGTH (PRAM_COMP_LENGTH + PRAM_BASELINE_LENGTH) + + +#define FT_RAWDATA1_SHORT_BUF_ADDR 0x200002E4 //((at(0x200002E4))); +#define FT_RAWDATA2_OPEN_BUF_ADDR (FT_RAWDATA1_SHORT_BUF_ADDR+100) //((at(0x20000348))); +#define FT_RAWDATA3_CC_BUF_ADDR (FT_RAWDATA2_OPEN_BUF_ADDR+100) //((at(0x200003AC))); +#define FT_UC_BUF_ADDR (FT_RAWDATA3_CC_BUF_ADDR+100) //((at(0x20000410))); + +#define FT_OPEN_BL_BUF_ADDR (FT_UC_BUF_ADDR+100) //((at(0x20000474))); +#define FT_TEST_RESULT_BUF_ADDR (FT_OPEN_BL_BUF_ADDR+100) //((at(0x200004D8))); +#define FT_TEST_ITEM_RESULT (FT_TEST_RESULT_BUF_ADDR+50+2) //((at(0x2000050C))); +#define FT_TEST_INFO_ADDR 0x20000674//FT_IMG2PIN_BUF_ADDR+72//((at(0x20000674))); +#define FT_TEST_THD_ADDR (FT_TEST_INFO_ADDR+16) //((at(0x20000684))); +#define FT_TEST_PARA_ADDR (FT_TEST_THD_ADDR+36) //((at(0x200006A8))); + + +#define SRAM_FT_RAWDATA_3_CC_ADDR (RM_RAM_BASE + 0x0000074C) //0x2000074C +#define SRAM_FT_UC_CC_ADDR (RM_RAM_BASE + 0x000006EC) //0x200006EC + +#define FLASH_NORMAL_FW_FW_VERSION_ADDR (FW_FT_FW_VERSION) +#define FLASH_NORMAL_FW_CUST_VERSION_ADDR (PRAM_PARA_START + 10) +#define FLASH_TEST_FW_FW_VERSION_ADDR (FW_FT_FW_VERSION + FLASH_OFFSET) +#define FLASH_NORMAL_FW_CC_TABLE_ADDR 0x9300 + +#define REG_FLASHCTL_FLASH_PRAM_LOCK 0X50000900 +#define REG_FLASHCTL_FLASH_PRAM_STORE_TYPE 0X50000904 +#define REG_FLASHCTL_FLASH_PRAM_ADDR 0X50000908 +#define REG_FLASHCTL_FLASH_PRAM_LENGTH 0X5000090C +#define REG_FLASHCTL_FLASH_ADDR 0X50000910 +#define REG_FLASHCTL_FLASH_ISPCTL 0X50000914 +#define REG_FLASHCTL_FLASH_STATE_REG_ADDR 0x50000918 +#define REG_FLASHCTL_FLASH_FLKEY1 0x50000934 +#define REG_FLASHCTL_FLASH_FLKEY2 0x50000938 +#define REG_FLASHCTL_FLASH_DATA 0x5000093C +#define REG_FLASHCTL_FLASH_ENG3 0x5000094C +#define REG_FLASHCTL_FLASH_PRGCHKSUM_ADDR 0x50000974 +#define REG_FLASHCTL_FLASH_PRGCHKSUM_RESULT 0x50000978 +#define REG_FLASHCTL_DEVID_ADDR 0x500009BC + +#define REG_SPI_SLAVE_SPIFLASHPRO 0x50000524 + +#define PRAM_ADDR_CC_INFO 0x00007F78 + +#define RAM_WRITE_TEST_ADDR1 0x50000950 +#define RAM_WRITE_TEST_ADDR2 0x50000B10 +#define RAM_WRITE_TEST_ADDR3 0x50000B00 +#define RAM_READ_TEST_ADDR1 0x50000954 +#define RAM_READ_TEST_ADDR2 0x50000B04 +#define RAM_READ_TEST_ADDR3 0x50000B08 + +#define FT_UPDATE 0x01 +#define FT_BASELINE_SF 0x02 +#define FT_BASELINE_PS 0x42 +#define FT_COMPENSATION_SF 0x04 +#define FT_COMPENSATION_PS 0x44 +#define FT_RAWDATA_W_BL_SF 0x08 +#define FT_RAWDATA_W_BL_PS 0x48 +#define FT_RAWDATA_WO_BL_SF 0x10 +#define FT_RAWDATA_WO_BL_PS 0x50 +#define FT_ALG_RAWDATA 0x20 +#define FT_PS_SEL 0x40 +#define FT_DEBUG_MESSAGE 0x80 +#define FT_UPDATE_CASE 0xFE + +#define FT_STATUS_PURE_RAW 0x01 +#define FT_STATUS_PURE_AF_DC 0x02 + +typedef enum { + CRC_CHECK_FAIL = 0x80, + CRC_CHECK_PASS = 0x81, + WAIT_TEST_MODE = 0x82, + PARTITION_CRC = 0xA0, + SET_ADDR_READY = 0xA1, + SET_ADDR_FAIL = 0xA2, + WRT_PRAM_DATA = 0xA3, + WRT_PRAM_FAIL = 0xA4, + WAIT_WRT_ACK = 0xA5, + WAIT_ACK_FAIL = 0xA6, + GET_WRT_ACK = 0xA7, + GET_WRT_UNLOCK = 0xA8, + GET_UNLOCK_FAIL = 0xA9, +} I2C_SYNC_CMD; + +typedef enum { + MAIN_STATE_NORMAL_MODE = 0, + MAIN_STATE_BURNING_MODE, + MAIN_STATE_FIRWARE_MODE, +} TCH_BOOTLOADER_STATE; + +typedef enum { + BURNING_STATE_INIT = 0, + BURNING_CHECK_ADDR, + BURNING_UNLOCK_PRAM, + BURNING_WRT_PRAM, + BURNING_WRT_FLASH_PREPARE, + BURNING_WRT_FLASH_EXCUTE, + BURNING_WRT_FLASH_FINISH, + BURNING_STATE_HALT, +} BL_BURNING_STATE; + +typedef enum { + NORMAL_STATE_CHECK = 0, + NORMAL_CRC_CALC, + NORMAL_CRC_NOTIFY, + NORMAL_FW_CRC, + NORMAL_MODE_CHANGE, + NORMAL_MODE_IDLE, +} BL_NORMAL_STATE; + diff --git a/raydium/chip_raydium/f303_ic_test.c b/raydium/chip_raydium/f303_ic_test.c new file mode 100644 index 0000000000..ad471bb49f --- /dev/null +++ b/raydium/chip_raydium/f303_ic_test.c @@ -0,0 +1,2319 @@ +/* f303_ic_test.c + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#endif + +#define RM_F303_MAX_STR_LENGTH 8 +#include "drv_interface.h" +#include "ic_drv_global.h" +#include "ic_drv_interface.h" +#include "f303_ic_control.h" +#include "f303_ic_test.h" +#include "f303_ic_reg.h" +#if !SELFTEST_3X +#include "ic_drv_global_ft.h" +#include "f303_ic_test_ft.h" +#include "ic_drv_interface_ft.h" +#endif + +STATUS ft_test_panel_model_check_3x(unsigned short u16_version); + +#if !SELFTEST_3X +STATUS burn_data_log_to_flash_3x(void) +{ + unsigned short u16_pram_start_addr = 0x1000; + unsigned char u8_read_buf[4], u8_fw_version[4]; + unsigned short u16_index = 0, u16_retry = 0; + unsigned int u32_read = 0, u32_flash_header_start_addr = 0xA000, u32_header_size = 0x400; + unsigned int u32_write = 0; + unsigned char *p_data = (unsigned char *)g_i16_raw_data_frame_buffer; + + if (g_u8_drv_interface == SPI_INTERFACE || g_u8_test_log_burn_times > 3) { + /*flashless*/ + DEBUGOUT("return burn test log\r\n"); + return SUCCESS; + } + + memset(g_i16_raw_data_frame_buffer, 0x0, 1024); + memset(u8_fw_version, 0x0, sizeof(u8_fw_version)); + hardware_reset_3x(false); + /*read info*/ + /*wearable_ic_test_read_info();*/ + + /*read FW Version*/ + if (handle_ic_read(FW_FT_SRAM_FW_VERSION, 4, u8_fw_version, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + + /* -------------*/ + /*read Header*/ + if (handle_ic_read(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_read |= 0x40; + if (handle_ic_write(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_write = 0x00000000; + if (handle_ic_write(REG_I2C_I2CFLASHPRO, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /*Release flash power down mode*/ + u32_write = 0x28; + if (handle_ic_write(REG_FLASHCTL_FLASH_ISPCTL, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + + /*----write data-----*/ + memset(p_data, 0xFF, 1024); + + if (!stop_mcu_3x(0)) { + DEBUGOUT("Stop MCU NG\r\n"); + return ERROR; + } + + u32_write = 0x00000000; + if (handle_ic_write(REG_I2C_I2CFLASHPRO, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /*Unlock PRAM*/ + u32_write = 0x00000000; + if (handle_ic_write(REG_FLASHCTL_FLASH_PRAM_LOCK, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /* unlock key*/ + u32_write = FLKEY2_KEY; + if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_write = FLKEY1_KEY; + if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY1, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_write = FLKEY1_LOCK; + if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY1, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_write = FLKEY1_KEY; + if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY1, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_write = FLKEY2_LOCK; + if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /*clear Pram*/ + u16_index = 0; + while (u16_index < 1024) { + if (handle_ic_write(u16_pram_start_addr + u16_index, 64, p_data, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u16_index += 64; + } + + + /*DEBUGOUT("[u8_fw_version]:%X\r\n", u8_fw_version);*/ + /*DEBUGOUT("[g_u32_wearable_test_result]:%X\r\n", g_u32_wearable_test_result);*/ + + /*fill data*/ + + memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp)); + test_log_raw_data_remap_3x(g_i16_raw_data_1_short_buf, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD); + memcpy(p_data, g_u16_raw_data_tmp, 96); + memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp)); + test_log_raw_data_remap_3x(g_i16_raw_data_2_open_buf, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD); + memcpy((p_data + 96), g_u16_raw_data_tmp, 96); + memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp)); + test_log_raw_data_remap_3x((short *)g_u16_raw_data3_cc_buf, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD); + memcpy((p_data + 192), g_u16_raw_data_tmp, 96); + memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp)); + test_log_raw_data_remap_3x(g_i16_noise_peek_raw_data, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD); + memcpy((p_data + 288), g_u16_raw_data_tmp, 96); + memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp)); + test_log_raw_data_remap_3x(g_i16_display_pattern_avg_raw_data, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD); + memcpy((p_data + 384), g_u16_raw_data_tmp, 96); + memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp)); + test_log_raw_data_remap_3x(g_i16_display_pattern_no_dc_avg_raw_data, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD); + memcpy((p_data + 480), g_u16_raw_data_tmp, 96); + memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp)); + test_log_raw_data_remap_3x(g_i16_display_pattern_avg_raw_data_s2, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD); + memcpy((p_data + 576), g_u16_raw_data_tmp, 96); + memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp)); + test_log_raw_data_remap_3x(g_i16_display_pattern_no_dc_avg_raw_data_s2, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD); + memcpy((p_data + 672), g_u16_raw_data_tmp, 96); + memset(g_u16_raw_data_tmp, 0, sizeof(g_u16_raw_data_tmp)); + test_log_raw_data_remap_3x((short *)g_u16_uc_buf, (short *)g_u16_raw_data_tmp, DATA_REMAP_TO_SENSOR_PAD); + memcpy((p_data + 768), g_u16_raw_data_tmp, 96); + + /*---write data section---*/ + u16_index = 0; + while (u16_index < 1024) { + if (handle_ic_write(u16_pram_start_addr + u16_index, 64, (p_data + u16_index), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u16_index += 64; + } + + + u32_write = u16_pram_start_addr; + if (handle_ic_write(REG_FLASHCTL_FLASH_PRAM_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_write = 0x300; + if (handle_ic_write(REG_FLASHCTL_FLASH_PRAM_LENGTH, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + + DEBUGOUT("g_st_test_info.u8_station_id:%x\r\n", g_st_test_info.u8_station_id); + DEBUGOUT("u8_burn_times:%d\r\n", g_u8_test_log_burn_times); + + DEBUGOUT("ADDR:%x\r\n", u32_flash_header_start_addr + u32_header_size + (g_st_test_info.u8_station_id - 1) * 4096 + + (g_u8_test_log_burn_times - 1) * 0x400); + + /*flash addr , wrtie data section*/ + u32_write = u32_flash_header_start_addr + u32_header_size + (g_st_test_info.u8_station_id - 1) * 4096 + + (g_u8_test_log_burn_times - 1) * 0x400; + if (handle_ic_write(REG_FLASHCTL_FLASH_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /*write trigger*/ + u32_write = 0x4; + if (handle_ic_write(REG_FLASHCTL_FLASH_ISPCTL, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /* check read trigger finished*/ + u16_retry = 1000; + memset(u8_read_buf, 0, sizeof(u8_read_buf)); + if (handle_ic_read(REG_FLASHCTL_FLASH_ISPCTL, 4, u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + + while ((u8_read_buf[0] & 0x08) == 0 && u16_retry > 0) { + if (handle_ic_read(REG_FLASHCTL_FLASH_ISPCTL, 4, u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + delay_ms(1); + u16_retry--; + } + if (u16_retry == 0) { + DEBUGOUT("Write Trigger Error\r\n"); + return ERROR; + } + + /*---end write data section---*/ + + hardware_reset_3x(TRUE); + + return SUCCESS; +} + +STATUS burn_header_log_to_flash_3x(bool is_test_finish, bool is_in_header_data_page) +{ + + unsigned short u16_pram_start_addr = 0x1000; + unsigned char *p_header_data = (unsigned char *)g_u32_save_config; + unsigned char u8_write_buf[64], u8_fw_version[4], u8_read_buf[4], u8_burn_full_times = 20, u8_count_1 = 0, u8_test_info[16]; + unsigned short u16_index = 0, u16_retry = 0; + unsigned int u32_read = 0, u32_flash_header_start_addr = 0xA000 + (g_st_test_info.u8_station_id - 1) * 4096 + g_u8_test_log_burn_times / 5 * 0x100; + unsigned int u32_write = 0, u32_temp = 0; + bool b_is_first_read = true; + + DEBUGOUT("[u32_flash_header_start_addr ]:%d\r\n", u32_flash_header_start_addr); + /*DEBUGOUT("[is_in_header_data_page ]:%d\r\n", is_in_header_data_page);*/ + if (!is_test_finish && is_in_header_data_page) + memset(g_u32_save_config, 0, sizeof(g_u32_save_config)); + + if (is_in_header_data_page) + u32_flash_header_start_addr = 0xA000 + (g_st_test_info.u8_station_id - 1) * 4096 + g_u8_test_log_burn_times / 5 * 0x100; + else + u32_flash_header_start_addr = 0xA000 + (g_st_test_info.u8_station_id - 1) * 4096; + + if (g_u8_drv_interface == SPI_INTERFACE || g_u8_test_log_burn_times > 20) + /*flashless*/ + return SUCCESS; + + + + memset(u8_write_buf, 0x0, sizeof(u8_write_buf)); + /*memset(g_u8_header_buf, 0x0 , sizeof(g_u8_header_buf));*/ + memset(u8_fw_version, 0x0, sizeof(u8_fw_version)); + + hardware_reset_3x(false); + /*read info*/ + + + + + + if (read_flash_data(g_u32_dongle_flash_ini_addr, 16) != ERROR) + memcpy(&u8_test_info, g_u8_data_buf, sizeof(u8_test_info)); + + + + + + /*read FW Version*/ + if (handle_ic_read(FW_FT_SRAM_FW_VERSION, 4, u8_fw_version, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + + /* -------------*/ + /*read Header*/ + if (handle_ic_read(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_read |= 0x40; + if (handle_ic_write(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + + u32_write = 0x00000000; + if (handle_ic_write(REG_I2C_I2CFLASHPRO, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /*Release flash power down mode*/ + u32_write = 0x28; + if (handle_ic_write(REG_FLASHCTL_FLASH_ISPCTL, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + DEBUGOUT("[header addr!]:%X\r\n", u32_flash_header_start_addr); + + while (u16_index < 256) { + /*write flash addr*/ + + + u32_write = u32_flash_header_start_addr + u16_index; + if (handle_ic_write(REG_FLASHCTL_FLASH_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + + /*Set Read trigger, Flh_read_trg[bit 6]*/ + u32_write = 0x40; + if (handle_ic_write(REG_FLASHCTL_FLASH_ISPCTL, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /* check read trigger finished*/ + u16_retry = 1000; + memset(u8_read_buf, 0, sizeof(u8_read_buf)); + + while ((u8_read_buf[0] & 0x40) != 0 && u16_retry > 0) { + if (handle_ic_read(REG_FLASHCTL_FLASH_ISPCTL, 4, u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + delay_ms(1); + u16_retry--; + } + if (u16_retry == 0) + return ERROR; + + /*Read Flash Data*/ + if (handle_ic_read(REG_FLASHCTL_FLASH_DATA, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + /*memcpy(&g_u8_header_buf[0] , &u32_read , 4);*/ + + + if (b_is_first_read && !is_in_header_data_page) { + + u32_temp = u32_read; + while (u32_temp) { + u8_count_1++; + u32_temp = (u32_temp - 1) & u32_temp; + } + /*DEBUGOUT("u32_temp:%d\r\n", 32 - u8_count_1);*/ + + + if (32 - u8_count_1 >= u8_burn_full_times) { + g_u8_test_log_burn_times = 32 - u8_count_1 + 1; + DEBUGOUT("Burn Full times\r\n"); + hardware_reset_3x(TRUE); + return SUCCESS; + } + g_u8_test_log_burn_times = 32 - u8_count_1 + 1; + /*count how many times has been tested , each test would right shift one bit.*/ + u32_read = u32_read >> 1; + DEBUGOUT("Burn times: %d\r\n", g_u8_test_log_burn_times); + memcpy(&p_header_data[0], &u32_read, 4); + b_is_first_read = false; + + } else + memcpy(&p_header_data[u16_index], &u32_read, 4); + u16_index += 4; + + + } + + + + + +/* u16_auo_jig_cmd = g_u16_panel_jig_set_test_items;*/ +/* fill data*/ + if (g_u8_test_log_burn_times <= 5 || is_in_header_data_page) { + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48], &u8_test_info, 8); + /*if ((g_st_test_info.u16_ft_eng_item & IC_TEST_ENG_ITEMS_PANEL_TEST_JIG))*/ + /* memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 4] , &u16_auo_jig_cmd , 2);*/ + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 8], &g_st_test_thd.i16_ft_test_open_lower_thd, 2); + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 10], &g_st_test_thd.i16_ft_test_short_upper_thd, 2); + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 12], &g_st_test_thd.i16_ft_test_single_cc_lower_thd, 2); + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 14], &g_st_test_thd.i16_ft_test_uniformity_cc_upper_thd, 2); + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 16], &g_st_test_thd.i16_ft_test_uniformity_cc_lower_thd, 2); + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 18], &g_st_test_thd.i16_ft_test_panel_test_1_thd, 2); + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 20], &g_st_test_thd.i16_ft_test_panel_test_3_thd, 2); + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 22], &g_st_test_thd.i16_ft_test_panel_test_2_thd, 2); + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 24], &g_st_test_thd.i16_ft_test_panel_test_2_s2_thd, 2); + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 26], &g_st_test_para_resv.u32_normal_fw_version, 4); + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 30], &g_st_test_para_resv.u32_test_fw_version, 4); + if (is_test_finish) { /*after testing, burn the result to flash*/ + DEBUGOUT("g_u8_channel_x: %d\r\n", g_u8_channel_x); + DEBUGOUT("g_u8_channel_y: %d\r\n", g_u8_channel_y); + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 34], &g_u32_wearable_test_result, 4); + p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 37] = p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 34] ^ p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 37]; + p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 34] = p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 34] ^ p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 37]; + p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 37] = p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 34] ^ p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 37]; + p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 36] = p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 35] ^ p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 36]; + p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 35] = p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 35] ^ p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 36]; + p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 36] = p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 35] ^ p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 36]; + + + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 38], u8_fw_version, 4); + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 42], &g_u8_channel_x, 1); + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 43], &g_u8_channel_y, 1); + } +/* DEBUGOUT("g_u8_test_date0: %d\r\n", g_u8_test_date[0]); + * DEBUGOUT("g_u8_test_date: %d\r\n", g_u8_test_date[1]); + * DEBUGOUT("g_u8_test_date: %d\r\n", g_u8_test_date[2]); + * DEBUGOUT("g_u8_test_date: %d\r\n", g_u8_test_date[3]); + */ + memcpy(&p_header_data[8 + ((g_u8_test_log_burn_times - 1) % 5) * 48 + 44], &g_u8_test_date, 4); + } + /*----write data-----*/ + + if (!stop_mcu_3x(0)) { + DEBUGOUT("Stop MCU NG\r\n"); + return ERROR; + } + + u32_write = 0x00000000; + if (handle_ic_write(REG_I2C_I2CFLASHPRO, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /*Unlock PRAM*/ + u32_write = 0x00000000; + if (handle_ic_write(REG_FLASHCTL_FLASH_PRAM_LOCK, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /* unlock key*/ + u32_write = FLKEY2_KEY; + if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_write = FLKEY1_KEY; + if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY1, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_write = FLKEY1_LOCK; + if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY1, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_write = FLKEY1_KEY; + if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY1, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_write = FLKEY2_LOCK; + if (handle_ic_write(REG_FLASHCTL_FLASH_FLKEY2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /*clear Pram*/ + u16_index = 0; + while (u16_index < 256) { /*totally 688(768) + 256 header bytes*/ + if (handle_ic_write(u16_pram_start_addr + u16_index, 64, u8_write_buf, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u16_index += 64; + } + + /*---write header section---*/ + u16_index = 0; + while (u16_index < 256) { /*totally 256 bytes*/ + if (handle_ic_write(u16_pram_start_addr + u16_index, 64, &p_header_data[u16_index], g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u16_index += 64; + } + + u32_write = u16_pram_start_addr; + if (handle_ic_write(REG_FLASHCTL_FLASH_PRAM_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_write = 0x00; + if (handle_ic_write(REG_FLASHCTL_FLASH_PRAM_LENGTH, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /*flash addr , wrtie data section*/ + u32_write = u32_flash_header_start_addr; + if (handle_ic_write(REG_FLASHCTL_FLASH_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /*write trigger*/ + u32_write = 0x4; + if (handle_ic_write(REG_FLASHCTL_FLASH_ISPCTL, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /* check read trigger finished*/ + u16_retry = 1000; + memset(u8_read_buf, 0, sizeof(u8_read_buf)); + if (handle_ic_read(REG_FLASHCTL_FLASH_ISPCTL, 4, u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + while ((u8_read_buf[0] & 0x08) != 0 && u16_retry > 0) { + if (handle_ic_read(REG_FLASHCTL_FLASH_ISPCTL, 4, u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + delay_ms(1); + u16_retry--; + } + if (u16_retry == 0) + return ERROR; + + /*memset(g_u8_test_date , 0 , sizeof(g_u8_test_date)); */ + /*clear date*/ + /*---end write header section---*/ + hardware_reset_3x(TRUE); + DEBUGOUT("End burn header\r\n"); + return SUCCESS; +} + +STATUS turn_on_flash_3x(void) +{ + unsigned int u32_read = 0; + + /*Turn on Flash*/ + if (handle_ic_write(REG_I2C_I2CFLASHPRO, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_read = FLH_RELEASE_PD; + if (handle_ic_write(REG_FLASHCTL_FLASH_ISPCTL, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + return SUCCESS; +} + +STATUS read_fpc_flash_3x(unsigned int u32_addr, unsigned int *p_u32_data) +{ + unsigned int u32_read; + + /*Turn on Flash*/ + if (turn_on_flash_3x() == ERROR) + return ERROR; + + if (handle_ic_read(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_read |= 0x40; + if (handle_ic_write(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_read = 0x00000000; + if (handle_ic_write(REG_I2C_I2CFLASHPRO, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + if (handle_ic_write(REG_FLASHCTL_FLASH_ADDR, 4, (unsigned char *)&u32_addr, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + u32_read = 0x40; + if (handle_ic_write(REG_FLASHCTL_FLASH_ISPCTL, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + delay_ms(1); + + if (handle_ic_read(REG_FLASHCTL_FLASH_DATA, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + *p_u32_data = u32_read; + return SUCCESS; +} + +STATUS set_test_info_thd_para_3x(void) +{ + unsigned char u8_read_buf[96], u8_i; + unsigned char *p_u8_input_buf; + + if (handle_ic_write(FT_TEST_INFO_ADDR, sizeof(g_st_test_info), (unsigned char *)&g_st_test_info, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + if (handle_ic_read(FT_TEST_INFO_ADDR, sizeof(g_st_test_info), (unsigned char *)u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + + p_u8_input_buf = (unsigned char *)&g_st_test_info; + for (u8_i = 0; u8_i < sizeof(g_st_test_info); u8_i++) { + if (p_u8_input_buf[u8_i] != u8_read_buf[u8_i]) { + DEBUGOUT("Set INFO NG:[%d]=%d->%d\r\n", u8_i, p_u8_input_buf[u8_i], u8_read_buf[u8_i]); + return ERROR; + } + } + + if (handle_ic_write(FT_TEST_THD_ADDR, sizeof(g_st_test_thd), (unsigned char *)&g_st_test_thd, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + if (handle_ic_read(FT_TEST_THD_ADDR, sizeof(g_st_test_thd), (unsigned char *)u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + p_u8_input_buf = (unsigned char *)&g_st_test_thd; + for (u8_i = 0; u8_i < sizeof(g_st_test_thd); u8_i++) { + if (p_u8_input_buf[u8_i] != u8_read_buf[u8_i]) { + DEBUGOUT("Set THD NG:[%d]=%d->%d\r\n", u8_i, p_u8_input_buf[u8_i], u8_read_buf[u8_i]); + return ERROR; + } + } + + if (handle_ic_write(FT_TEST_PARA_ADDR, sizeof(g_st_test_para_resv), (unsigned char *)&g_st_test_para_resv, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + if (handle_ic_read(FT_TEST_PARA_ADDR, sizeof(g_st_test_para_resv), (unsigned char *)u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + p_u8_input_buf = (unsigned char *)&g_st_test_para_resv; + for (u8_i = 0; u8_i < sizeof(g_st_test_para_resv); u8_i++) { + if (p_u8_input_buf[u8_i] != u8_read_buf[u8_i]) { + DEBUGOUT("Set Para NG:[%d]=%d->%d\r\n", u8_i, p_u8_input_buf[u8_i], u8_read_buf[u8_i]); + return ERROR; + } + } + + if (handle_ic_write(SRAM_FT_UC_CC_ADDR, 96, (unsigned char *)g_u16_uc_golden_cc_buf, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + if (handle_ic_read(SRAM_FT_UC_CC_ADDR, 96, (unsigned char *)u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + p_u8_input_buf = (unsigned char *)g_u16_uc_golden_cc_buf; + for (u8_i = 0; u8_i < 96; u8_i++) { + if (p_u8_input_buf[u8_i] != u8_read_buf[u8_i]) { + DEBUGOUT("Set PRAM_FT_FW_GOLDEN_CC_ADDR NG:[%d]=%d->%d\r\n", u8_i, p_u8_input_buf[u8_i], u8_read_buf[u8_i]); + return ERROR; + } + } +/*DEBUGOUT("g_u16_uc_golden_cc_buf[0]=%X,g_u16_uc_golden_cc_buf[1]=%X,g_u16_uc_golden_cc_buf[2]=%X,g_u16_uc_golden_cc_buf[3]=%X,g_u16_uc_golden_cc_buf[4]=%X,g_u16_uc_golden_cc_buf[5]=%X\r\n",g_u16_uc_golden_cc_buf[0],g_u16_uc_golden_cc_buf[1],g_u16_uc_golden_cc_buf[2],g_u16_uc_golden_cc_buf[3],g_u16_uc_golden_cc_buf[4],g_u16_uc_golden_cc_buf[5]); + * DEBUGOUT("u8_read_buf[0]=%X,u8_read_buf[1]=%X,u8_read_buf[2]=%X,u8_read_buf[3]=%X,u8_read_buf[4]=%X,u8_read_buf[5]=%X\r\n",u8_read_buf[0],u8_read_buf[1],u8_read_buf[2],u8_read_buf[3],u8_read_buf[4],u8_read_buf[5]); + */ + + if (handle_ic_write(SRAM_FT_RAWDATA_3_CC_ADDR, 96, (unsigned char *)g_u16_raw_data3_golden_cc_buf, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + if (handle_ic_read(SRAM_FT_RAWDATA_3_CC_ADDR, 96, (unsigned char *)u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + p_u8_input_buf = (unsigned char *)g_u16_raw_data3_golden_cc_buf; + for (u8_i = 0; u8_i < 96; u8_i++) { + if (p_u8_input_buf[u8_i] != u8_read_buf[u8_i]) { + DEBUGOUT("Set PRAM_FT_OPEN_GOLDEN_CC_ADDR NG:[%d]=%d->%d\r\n", u8_i, p_u8_input_buf[u8_i], u8_read_buf[u8_i]); + return ERROR; + } + } + + return SUCCESS; +} + +void dump_image_data_3x(short *p_image_buf, unsigned char u8_remap) +{ + short *p_i16_buf = (short *)g_u16_raw_data_tmp; + unsigned char u8_i, u8_j; + + memset(p_i16_buf, 0, MAX_IMAGE_BUFFER_SIZE * 2); + if (u8_remap) { + for (u8_i = 0; u8_i < MAX_IMAGE_BUFFER_SIZE; u8_i++) { + if (g_u8_wearable_pin_map[u8_i] != F303_NA_P) + p_i16_buf[g_u8_wearable_pin_map[u8_i]] = p_image_buf[u8_i]; + } + } else + memcpy(p_i16_buf, p_image_buf, MAX_IMAGE_BUFFER_SIZE << 1); + + for (u8_j = 0; u8_j < g_u8_channel_y; u8_j++) { + for (u8_i = 0; u8_i < g_u8_channel_x; u8_i++) + DEBUGOUT("%4d, ", p_i16_buf[u8_i + u8_j * g_u8_channel_x]); + DEBUGOUT("\r\n"); + } +} + +void dump_image_hex_data_3x(short *p_image_buf) +{ + + short *p_i16_buf = (short *)g_u16_raw_data_tmp; + unsigned char u8_i, u8_j; + + memset(p_i16_buf, 0, MAX_IMAGE_BUFFER_SIZE * 2); + for (u8_i = 0; u8_i < MAX_IMAGE_BUFFER_SIZE; u8_i++) { + if (g_u8_wearable_pin_map[u8_i] != F303_NA_P) + p_i16_buf[g_u8_wearable_pin_map[u8_i]] = p_image_buf[u8_i]; + } + + for (u8_j = 0; u8_j < g_u8_channel_y; u8_j++) { + for (u8_i = 0; u8_i < g_u8_channel_x; u8_i++) + DEBUGOUT("%4X, ", (p_i16_buf[u8_i + u8_j * g_u8_channel_x] & 0x7ff)); + DEBUGOUT("\r\n"); + } +} + +STATUS check_test_fw_status_3x(unsigned char u8_target_status, unsigned char *p_u8_result) +{ + unsigned int u32_read; + + if (handle_ic_read(FW_FT_ARG1_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + if ((u32_read & FT_TEST_STUATUS_PATTERN) == FT_TEST_STUATUS_PATTERN) { + if (((unsigned char)(u32_read & 0x00FF)) == u8_target_status) + *p_u8_result = 1; + else { + DEBUGOUT("[CTFS] Test STATUS NG!! (%x:%x)\r\n", u8_target_status, u32_read); + *p_u8_result = 0; + return ERROR; + } + DEBUGOUT("[CTFS] Test FW STATUS (%x:%x)\r\n", u8_target_status, u32_read); + } else { + DEBUGOUT("[CTFS] Test STATUS Not Ready!! (%x:%x)\r\n", u8_target_status, u32_read); + *p_u8_result = 0; + } + return SUCCESS; +} + +STATUS ft_test_do_fw_test_3x(unsigned short u16_test_items) +{ + + unsigned char u8_write_data[5], u8_result; + unsigned int u32_read_data; + short i16_time_out = 0; +#if ENABLE_TEST_TIME_MEASURMENT + unsigned int u32_fun_time = 0; + + u32_fun_time = get_system_time(); + DEBUGOUT("ft_test_do_fw_test Start Time= %d\r\n", u32_fun_time); +#endif + + g_u32_wearable_test_result &= ~WEARABLE_FT_TEST_RESULT_TEST_INIT_NG; + + if ((u16_test_items & (IC_TEST_ITEMS_OPEN | IC_TEST_ITEMS_SHORT | IC_TEST_ITEMS_UC | IC_TEST_ITEMS_UB)) == 0) + return SUCCESS; + memset(u8_write_data, 0, sizeof(u8_write_data)); + u8_write_data[1] = FT_CMD_DO_FT_TEST | 0x80; + u8_write_data[2] = (unsigned char) u16_test_items; + u8_write_data[3] = (unsigned char)(u16_test_items >> 8); + + DEBUGOUT("Star FW Test!!\r\n"); + + if (handle_ic_write(FW_SYS_CMD_ADDR, 4, u8_write_data, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /*Wait test fw finish INT */ + /*Wait 20 Sec*/ + i16_time_out = 1000; + + while (i16_time_out--) { + delay_ms(20); +#if ENABLE_WDT + Chip_WWDT_Feed(LPC_WWDT);/* reload the Watchdog timer*/ +#endif + if (!gpio_touch_int_pin_state_access() || (i16_time_out < 10)) { + if (check_test_fw_status_3x(FT_CMD_DO_FT_TEST, &u8_result) == ERROR) { + DEBUGOUT("[FWT] Wait Test CMD NG (0x%x:%d)\r\n", FT_CMD_DO_FT_TEST, i16_time_out); + return ERROR; + } else if (u8_result == TRUE) { + break; + } + } + } + + if (handle_ic_read(FW_SYS_CMD_ADDR, 4, (unsigned char *)(&u32_read_data), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + DEBUGOUT("[FWT]SysCMD1:0x%x (%d)\r\n", u32_read_data, i16_time_out); + + if (i16_time_out == IC_TEST_TIME_OUT) { + DEBUGOUT("[FWT] Wait Test CMD Timeout!! (0x%x)\r\n", FT_CMD_DO_FT_TEST); + return ERROR; + } + +#if ENABLE_TEST_TIME_MEASURMENT + DEBUGOUT("[FWT] FW Test Time(%d)\r\n", get_system_time() - u32_fun_time); +#endif + + return SUCCESS; +} +STATUS enter_fw_test_mode_3x(void) +{ + unsigned char u8_result = 0; + short i16_time_out = 0; + +#if ENABLE_TEST_TIME_MEASURMENT + unsigned int u32_fun_time = 0; + + u32_fun_time = get_system_time(); +#endif + + g_u32_wearable_test_result &= ~WEARABLE_FT_TEST_RESULT_TEST_INIT_NG; + + DEBUGOUT("[EFTM] Start\r\n"); + + if ((g_st_test_info.u16_ft_test_item & (IC_TEST_ITEMS_OPEN | IC_TEST_ITEMS_SHORT | + IC_TEST_ITEMS_UC | IC_TEST_ITEMS_UB)) == 0) { + return SUCCESS; + } + + g_u8_is_normal_fw = FALSE; + + i16_time_out = 300; + u8_result = 0; + while (i16_time_out--) { + if (!gpio_touch_int_pin_state_access() || (i16_time_out < 20)) { + if (check_test_fw_status_3x(FT_CMD_INIT, &u8_result) == ERROR) + goto IC_INIT_NG; + else if (u8_result == TRUE) + break; + } + delay_ms(5); + } + + if (i16_time_out == IC_TEST_TIME_OUT) { + DEBUGOUT("[EFTM] FW State Check NG\r\n"); + goto IC_INIT_NG; + } + + DEBUGOUT("[EFTM] Enter FT mode\r\n"); + + if (set_test_info_thd_para_3x() == ERROR) { + DEBUGOUT("[RUPI] Set test info, thd, para NG!\r\n"); + return ERROR; + } + +#if ENABLE_TEST_TIME_MEASURMENT + DEBUGOUT("[EFTM] (%d)\r\n", get_system_time() - u32_fun_time); +#endif + return SUCCESS; + +IC_INIT_NG: + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_TEST_INIT_NG; + return ERROR; + +} + +STATUS ft_test_read_used_pin_infor_3x(unsigned char *p_u8_infor) +{ + /*unsigned char u8_i;*/ + unsigned char u8_r_buf[2]; + + /*get Pin remap*/ + if (handle_ic_read(FW_FT_PIN_ADDR, 48, p_u8_infor, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) { + DEBUGOUT("[RUPI] NG!\r\n"); + return ERROR; + } +/* + * for (u8_i = 0; u8_i < 36; u8_i++) { + * DEBUGOUT("Pin[%d] remap=%d\r\n", u8_i, p_u8_infor[u8_i]); + * } + */ + + if (handle_ic_read(FW_FT_CHANNEL_X_ADDR, 2, u8_r_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) { + DEBUGOUT("[RUPI] NG!\r\n"); + return ERROR; + } + g_u8_channel_x = u8_r_buf[0]; + g_u8_channel_y = u8_r_buf[1]; + DEBUGOUT("[RUPI] X:%d,Y:%d\r\n", g_u8_channel_x, g_u8_channel_y); + + + return SUCCESS; +} + +STATUS ft_test_ctrl_mbist_fun_3x(unsigned char u8_enable) +{ + unsigned int u32_read; + + if (u8_enable) { + if (handle_ic_read(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) { + DEBUGOUT("Data RAM Test NG !!!\r\n"); + return ERROR; + } + + u32_read |= (1 << 29); + if (handle_ic_write(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) { + DEBUGOUT("Data RAM Test NG !!!\r\n"); + return ERROR; + } + } else { + if (handle_ic_read(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) { + DEBUGOUT("Data RAM Test NG !!!\r\n"); + return ERROR; + } + + u32_read &= ~(1 << 29); + if (handle_ic_write(REG_SYSCON_BLKEN_ADDR, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) { + DEBUGOUT("Data RAM Test NG !!!\r\n"); + return ERROR; + } + } + return SUCCESS; +} + +/* + * STATUS ft_test_ram_test_3x(unsigned char u8_is_stop_mcu) + * { + * unsigned int u32_read; + * unsigned char u8_retry_times = 2; + * unsigned int u32_write = 0; + + * if ((g_st_test_info.u16_ft_test_info_1) == WEARBLE_FT_PRAM_SYSTEM_NG_CASE) { + * g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_PRAM_NG; + * return ERROR; + * } + * + * RETRY_RAM_TEST: + * + * if (u8_is_stop_mcu) { + * stop_mcu_3x(TRUE); + * } + * + * if (ft_test_ctrl_mbist_fun_3x(ENABLE) == ERROR) { + * goto EXIT_ERROR; + * } + * + * u32_write = 0x04000080; + * if (handle_ic_write(RAM_WRITE_TEST_ADDR1, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) { + * goto EXIT_ERROR; + * } + * + * u32_write = 0x00FE90FE; + * if (handle_ic_write(RAM_WRITE_TEST_ADDR2, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) { + * goto EXIT_ERROR; + * } + * + * u32_write = 0x0A800080; + * if (handle_ic_write(RAM_WRITE_TEST_ADDR3, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) { + * goto EXIT_ERROR; + * } + * + * delay_ms(5); + * + * if (handle_ic_read(RAM_READ_TEST_ADDR1, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) { + * goto EXIT_ERROR; + * } + * + * if ((u32_read & 0xFE00) != 0) { + * DEBUGOUT("RAM Test NG 954[0x%x]!!!\r\n", u32_read); + * goto EXIT_ERROR; + * } + * + * if (handle_ic_read(RAM_READ_TEST_ADDR2, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) { + * goto EXIT_ERROR; + * } + * + * if (u32_read != 0x19007F00) { + * DEBUGOUT("RAM Test NG B04[0x%x]!!!\r\n", u32_read); + * goto EXIT_ERROR; + * } + * + * if (handle_ic_read(RAM_READ_TEST_ADDR3, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) { + * goto EXIT_ERROR; + * } + * + * if (u32_read != 0x40007F00) { + * DEBUGOUT("RAM Test NG B08[0x%x]!!!\r\n", u32_read); + * goto EXIT_ERROR; + * } + * + * if (ft_test_ctrl_mbist_fun_3x(DISABLE) == ERROR) { + * goto EXIT_ERROR; + * } else { + * DEBUGOUT("RAM Test Pass\r\n"); + * return SUCCESS; + * } + * + * EXIT_ERROR: + * if ((u8_retry_times-- > 0)) { + * goto RETRY_RAM_TEST; + * } + * + * ft_test_ctrl_mbist_fun_3x(DISABLE); + * g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_PRAM_NG; + * DEBUGOUT("PRAM Test NG !!!\r\n"); + * return ERROR; + * } + */ +STATUS ft_test_ram_test_3x(unsigned char u8_is_stop_mcu) +{ + unsigned int u32_read; + unsigned char u8_retry_times = 2, u8_check_time = 8; + unsigned int u32_write = 0; + unsigned int u32_addr = 0; + unsigned short u16_retry, u16DataBufLength; + + if ((g_st_test_info.u16_ft_test_info_1) == WEARBLE_FT_PRAM_SYSTEM_NG_CASE) { + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_PRAM_NG; + return ERROR; + } + +RETRY_RAM_TEST: + +#if ENABLE_WDT + /* reload the Watchdog timer*/ + Chip_WWDT_Feed(LPC_WWDT); +#endif + + if (u8_is_stop_mcu) + stop_mcu_3x(TRUE); + + u32_write = (USEFW_LOCK | CONFIG_LOCK | COMP_LOCK | BASEL_LOCK | INICO_LOCK); + if (handle_ic_write(REG_FLASHCTL_FLASH_PRAM_LOCK, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + goto EXIT_ERROR; + + /* i2c tx buffer size = 0x38 (56)*/ + u16DataBufLength = 0x38; + u32_addr = 0; + while (u32_addr < u16DataBufLength) { + g_u8_data_buf[u32_addr] = 0xFF; + g_u8_data_buf[u32_addr + 1] = 0x00; + g_u8_data_buf[u32_addr + 2] = 0xAA; + g_u8_data_buf[u32_addr + 3] = 0x55; + u32_addr += 4; + } + u32_addr = PRAM_BOOT_START; + + while (u32_addr < PRAM_BOOT_LENGTH) { + if ((u32_addr + u16DataBufLength) > PRAM_BOOT_LENGTH) + u16DataBufLength = PRAM_BOOT_LENGTH - u32_addr; + + if (handle_ic_write(u32_addr, u16DataBufLength, g_u8_data_buf, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + goto EXIT_ERROR; + + u32_addr += u16DataBufLength; + +#if ENABLE_WDT + /* reload the Watchdog timer*/ + Chip_WWDT_Feed(LPC_WWDT); +#endif + } + + gpio_touch_reset_pin_control(FALSE);/*Low*/ + delay_ms(1); + gpio_touch_reset_pin_control(TRUE);/*High*/ + delay_ms(25); + + g_u8_mute_i2c_err_log = TRUE; + + u16_retry = 150; + do { +#if ENABLE_WDT + /* reload the Watchdog timer*/ + Chip_WWDT_Feed(LPC_WWDT); +#endif + + delay_ms(35); + if (handle_ic_read(REG_FLASHCTL_FLASH_STATE_REG_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + goto EXIT_ERROR; + + /*delay_ms(2);*/ + u16_retry--; + } while (((u32_read & BLDR_FINISH) != BLDR_FINISH) && (u16_retry != 0)); + + g_u8_mute_i2c_err_log = FALSE; + + if ((u32_read & BLDR_FINISH) != BLDR_FINISH) { + g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_PRAM_NG; + DEBUGOUT("PRAM Test NG !!!\r\n"); + return ERROR; + } + + if ((u8_check_time-- > 0)) + goto RETRY_RAM_TEST; + + DEBUGOUT("RAM Test Pass\r\n"); + return SUCCESS; + +EXIT_ERROR: + if ((u8_retry_times-- > 0)) { + u8_check_time = 4; + goto RETRY_RAM_TEST; + } + + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_PRAM_NG; + DEBUGOUT("PRAM Test NG !!!\r\n"); + + g_u8_mute_i2c_err_log = FALSE; + + return ERROR; +} + +STATUS ft_test_connect_test_3x(void) +{ + unsigned char u8_retry = IC_TEST_RETRY_TIME; + unsigned int u32_w_buf, u32_r_buf; + unsigned int u32_test_addr = RM_DATAMEM0_BASE; + char i8_str[8]; + + if ((g_st_test_info.u16_ft_test_info_1) == WEARBLE_FT_I2C_SYSTEM_NG_CASE) { + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_I2C_NG; + return ERROR; + } + +RETRY_CONNECT_TEST: + + if (g_u8_drv_interface == SPI_INTERFACE) + snprintf(i8_str, RM_F303_MAX_STR_LENGTH, "SPI"); + else + snprintf(i8_str, RM_F303_MAX_STR_LENGTH, "I2C"); + + u32_w_buf = 0x55aa00ff; + + if (handle_ic_write(u32_test_addr, 4, (unsigned char *)&u32_w_buf, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) { + DEBUGOUT("%s Test Write NG!!\r\n", i8_str); + goto NG_CASE; + } + + if (handle_ic_read(u32_test_addr, 4, (unsigned char *)&u32_r_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) { + DEBUGOUT("%s Test Read NG!!\r\n", i8_str); + goto NG_CASE; + } + + + if (u32_w_buf != u32_r_buf) { + DEBUGOUT("%s Test Compare NG [%d], W=0x%x,R=0x%x\r\n", i8_str, 0, u32_w_buf, u32_r_buf); + goto NG_CASE; + } + + +#if ENABLE_WDT + Chip_WWDT_Feed(LPC_WWDT);/* reload the Watchdog timer*/ +#endif + + + DEBUGOUT("%s Test Pass\r\n", i8_str); + return SUCCESS; + +NG_CASE: + if (--u8_retry > 0) { + DEBUGOUT("%s Test Retry=%d\r\n", i8_str, u8_retry); + if (hardware_reset_3x(TRUE) == ERROR) + goto NG_CASE2; + /*Stop MCU*/ + stop_mcu_3x(FALSE); + goto RETRY_CONNECT_TEST; + } + +NG_CASE2: + + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_I2C_NG; + DEBUGOUT("%s Test NG !!!\r\n", i8_str); + return ERROR; +} + +STATUS ft_test_reset_pin_test_3x(void) +{ + unsigned int u32_write = 0; + + if ((g_st_test_info.u16_ft_test_info_1) == WEARBLE_FT_RESET_PIN_SYSTEM_NG_CASE) { + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_RESET_NG; + return ERROR; + } + + u32_write = 0x00000404; + if (handle_ic_write(REG_SYSCON_MISCIER_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + if (!gpio_touch_int_pin_state_access()) { /* check INT in high state*/ + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_INT_NG; + return ERROR; + } + gpio_touch_reset_pin_control(FALSE);/*Low*/ + delay_ms(1); + if (gpio_touch_int_pin_state_access()) { + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_RESET_NG; + DEBUGOUT("Reset Pin Test NG(Not High) !!!\r\n"); + return ERROR; + } + gpio_touch_reset_pin_control(TRUE);/*High*/ + delay_ms(25); + DEBUGOUT("Reset Pin Test Pass !!!\r\n"); + return SUCCESS; +} + +STATUS ft_test_panel_model_check_3x(unsigned short u16_version) +{ + unsigned int u32_read; + unsigned short u16_model_version; + + if (handle_ic_read(FW_FT_SRAM_FW_VERSION, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + u16_model_version = (u32_read & 0x0000FFFF); + DEBUGOUT("Panel Model Test! 0x%08X:0x%08X\r\n", u32_read, u16_version); + DEBUGOUT("Panel Model 0x%08X\r\n", u16_model_version); + if (u16_model_version == u16_version) + return SUCCESS; + + DEBUGOUT("Panel Model NG! 0x%x:0x%x\r\n", u16_model_version, u16_version); + return ERROR; +} + +void ft_raw_data_checksum_cal_3x(unsigned short *u16_buffer) +{ + unsigned char u8_i; + + u16_buffer[48] = 0x55AA; + u16_buffer[49] = 0; + for (u8_i = 0; u8_i < (MAX_SENSING_PIN_NUM - 1); u8_i++) + u16_buffer[49] += u16_buffer[u8_i]; + + DEBUGOUT("[RDCSA] %x:%x\r\n", u16_buffer[48], u16_buffer[49]); +} + +void ft_test_result_checksum_cal_3x(unsigned char *u8_buffer) +{ + unsigned char u8_i; + + u8_buffer[48] = 0x5A; + u8_buffer[49] = 0; + for (u8_i = 0; u8_i < (MAX_SENSING_PIN_NUM - 1); u8_i++) + u8_buffer[49] += u8_buffer[u8_i]; + + DEBUGOUT("[TRCSA] %x:%x\r\n", u8_buffer[48], u8_buffer[49]); +} + +STATUS ft_raw_data_checksum_check_3x(unsigned short *u16_buffer) +{ + unsigned char u8_i; + unsigned short u16_sum = 0; + + if (u16_buffer[48] != 0x55AA) { + DEBUGOUT("u16_buffer[34]:%x\r\n", u16_buffer[48]); + DEBUGOUT("[RDCSC] Pattern NG! [0x%x](0x%x)\r\n", (int)u16_buffer, u16_buffer[48]); + return ERROR; + } + + /*u16_buffer[35]=0;*/ + for (u8_i = 0; u8_i < (MAX_SENSING_PIN_NUM - 1); u8_i++) + u16_sum += u16_buffer[u8_i]; + + if (u16_buffer[49] != u16_sum) { + DEBUGOUT("[RDCSC] Check SUM NG! [0x%x](0x%x:0x%x)\r\n", (int)u16_buffer, u16_sum, u16_buffer[49]); + return ERROR; + } + + DEBUGOUT("[RDCSC] PASS (0x%x:0x%x)\r\n", u16_buffer[48], u16_buffer[49]); + + return SUCCESS; +} + +STATUS ft_test_result_checksum_check_3x(unsigned char *u8_buffer) +{ + unsigned char u8_i; + unsigned char u8_sum = 0; + + if (u8_buffer[48] != 0x5A) { + DEBUGOUT("[TRCSC] Pattern NG! [0x%x](0x%x:0x%x)\r\n", (int)u8_buffer, u8_buffer[48], u8_buffer[49]); + return ERROR; + } + + /*u32_buffer[35]=0;*/ + for (u8_i = 0; u8_i < (MAX_SENSING_PIN_NUM - 1); u8_i++) + u8_sum += u8_buffer[u8_i]; + + if (u8_buffer[49] != u8_sum) { + DEBUGOUT("[TRCSC] Check SUM NG! [0x%x](0x%x:0x%x)\r\n", (int)u8_buffer, u8_sum, u8_buffer[49]); + return ERROR; + } + + DEBUGOUT("[TRCSC] PASS (0x%x:0x%x)\r\n", u8_buffer[48], u8_buffer[49]); + + return SUCCESS; +} + +STATUS baseline_update_control_3x(bool b_enable_baseline_update) +{ + unsigned int u32_write_buf; + unsigned char u8_read; + short i16_time_out = 100; + + if (b_enable_baseline_update) { + u32_write_buf = (SYS_CMD_FUNC_DIS_BS_UPDATE); + DEBUGOUT("[BUC] Enable Baseline update\r\n"); + } else { + u32_write_buf = (DIS_BASELINE_UPDATE | SYS_CMD_FUNC_DIS_BS_UPDATE); + DEBUGOUT("[BUC] Disable Baseline update\r\n"); + } + + if (handle_ic_write(FW_SYS_CMD_ADDR, 4, (unsigned char *)&u32_write_buf, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + delay_ms(5); + while (i16_time_out--) { + if (handle_ic_read(FW_SYS_CMD_ADDR, 1, &u8_read, g_u8_drv_interface, I2C_BYTE_MODE) == SUCCESS) { + if (u8_read == 0x00) + break; + } else { + DEBUGOUT("[BUC] I2C Read NG\r\n"); + return ERROR; + } + delay_ms(2); + } + + if (i16_time_out == IC_TEST_TIME_OUT) { + DEBUGOUT("[BUC] Baseline Update Control (%d) NG\r\n", b_enable_baseline_update); + return ERROR; + } + + return SUCCESS; +} + +STATUS wait_fw_init_ready_3x(void) +{ + unsigned short u16_time_out; + unsigned char u8_r_buf[2]; + + u16_time_out = 100; + /*Check FW Ready ?*/ + do { + if (handle_ic_read(FW_FT_ARG0_ADDR, 2, u8_r_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + DEBUGOUT("FW Flag = 0x%x,0x%x\r\n", u8_r_buf[0], u8_r_buf[1]); + if (u8_r_buf[0] == 0xAA && u8_r_buf[1] == 0x55) + return SUCCESS; + delay_ms(2); + + } while (u16_time_out-- > 0); + + return ERROR; +} + + +STATUS enter_normal_fw_3x(void) +{ + + unsigned char u8_pattern_noise_only = 0; +#if ENABLE_TEST_TIME_MEASURMENT + unsigned int u32_fun_time = 0; + + u32_fun_time = get_system_time(); +#endif + if (g_u8_is_normal_fw) + return SUCCESS; + +#if !SELFTEST_3X + /*Set INT Falling Triggle*/ + gpio_touch_int_trigger_control(FALSE); +#endif + + g_u32_wearable_test_result &= ~WEARABLE_FT_TEST_RESULT_NORMAL_FW_RESET_NG; + + if (g_u8_drv_interface == SPI_INTERFACE) { + if (hardware_reset_3x(TRUE) == ERROR) { + DEBUGOUT("[ENF]No INT, no OTP!!\r\n"); + g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_NORMAL_FW_RESET_NG; + return ERROR; + } + +#if !SELFTEST_3X + if (burn_pram_from_dongle_flash_3x() == ERROR) + return ERROR; +#endif + } else { + if ((g_st_test_info.u16_ft_test_item & ~(IC_TEST_ITEMS_SYSTEM | IC_TEST_ITEMS_PANEL_TEST_2)) == FALSE) { + u8_pattern_noise_only = TRUE; + DEBUGOUT("Panel Test!!!\r\n"); + } + + if (!u8_pattern_noise_only) { + /*PRAM Test*/ + if (ft_test_ram_test_3x(1) == ERROR) + return ERROR; + } + + if (hardware_reset_3x(FALSE) == ERROR) { + DEBUGOUT("[ENF]No INT, no OTP!!\r\n"); + g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_NORMAL_FW_RESET_NG; + return ERROR; + } + } + + if (wait_fw_init_ready_3x() == ERROR) + return ERROR; + + if (ft_test_read_used_pin_infor_3x(g_u8_wearable_pin_map) == ERROR) + return ERROR; + +#if ENABLE_TEST_TIME_MEASURMENT + DEBUGOUT("[ENF] (%d)\r\n", get_system_time() - u32_fun_time); +#endif + + g_u8_is_normal_fw = TRUE; + return SUCCESS; +} + +STATUS check_cc_bl_flag_3x(void) +{ + unsigned char u8_read_buf[4]; + unsigned char u8_is_cc_ready = 0; + unsigned char u8_fw_version[4]; + short i16_time_out; + + DEBUGOUT("[CCBF] Check CC BL Flag\r\n"); + + if (handle_ic_read(FW_FT_FW_VERSION, 4, u8_fw_version, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + + i16_time_out = 20; + + while (i16_time_out--) { + if (!u8_is_cc_ready) { + if (handle_ic_read(PRAM_ADDR_CC_INFO, 4, u8_read_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + if ((u8_read_buf[0] >= 1) && (u8_read_buf[2] == u8_fw_version[2]) && (u8_read_buf[3] == u8_fw_version[3])) + u8_is_cc_ready = 1; + } + + if (u8_is_cc_ready) + break; + delay_ms(1); + } + + /*Check flag*/ + if (i16_time_out == IC_TEST_TIME_OUT) { + DEBUGOUT("CC Flag=%d NG!!\r\n", u8_read_buf[0]); + DEBUGOUT("FW Ver=%X.%X:%X.%X NG!!\r\n", u8_fw_version[2], u8_fw_version[3], u8_read_buf[2], u8_read_buf[3]); + return ERROR; + } + + return SUCCESS; +} + +STATUS burn_cc_to_ic_flash_3x(void) +{ + short i16_time_out = 1000; + unsigned int u32_cc_table; + unsigned char u8_fw_value[4]; + unsigned int u32_write = 0; +#if SELFTEST_3X + unsigned int u32_read; +#endif + +#if ENABLE_TEST_TIME_MEASURMENT_CC + DEBUGOUT("[BCTF] Before Switch to Bootloader\r\n"); + g_u32_spend_time = get_system_time(); +#endif + +#if !SELFTEST /*210708 add, for fw use bootloader block*/ + if (stop_mcu_3x(0) == ERROR) + return ERROR; + + /*WRT boot-loader to PRAM first*/ + u32_write = (USEFW_LOCK | CONFIG_LOCK | COMP_LOCK | BASEL_LOCK); + if (handle_ic_write(REG_FLASHCTL_FLASH_PRAM_LOCK, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /*read flash data and write to pram (Bootloader)*/ + if (handle_spiFlash_to_pram(PRAM_BOOT_START, PRAM_BOOT_LENGTH, PRAM_BOOT_START) == ERROR) { + DEBUGOUT("confirm crc error!!\r\n"); + return ERROR; + } + + /*check pram bootloader crc*/ + if (check_pram_crc32_3x(PRAM_BOOT_START, PRAM_BOOT_CRC_LENGTH) == ERROR) { + DEBUGOUT("confirm bootloader crc error!!\r\n"); + return ERROR; + } + + /*read flash data and write to pram (Initial_code)*/ + if (handle_spiFlash_to_pram(PRAM_DIS_INIT_START, PRAM_DIS_INIT_LENGTH, PRAM_DIS_INIT_START) == ERROR) { + DEBUGOUT("confirm crc error!!\r\n"); + return ERROR; + } + + /*check pram Initial code crc*/ + if (check_pram_crc32_3x(PRAM_DIS_INIT_START, 0x7C) == ERROR) { + DEBUGOUT("confirm init crc error!!\r\n"); + return ERROR; + } +#endif + + /*Set Skip_Load = 1*/ + u32_write = (BL_CRC_CHK | SKIP_LOAD); + if (handle_ic_write(REG_FLASHCTL_FLASH_STATE_REG_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /*wait sw rst finish*/ + u32_write = BLKRST_SW_RST; + if (handle_ic_write(REG_SYSCON_BLKRST_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + delay_ms(2); + + if (wait_fw_state_3x(BOOT_RET_DATA_ADDR, WAIT_TEST_MODE, 1, i16_time_out) == ERROR) { + DEBUGOUT("[BCTF] Check Burn CC & BL Fail\r\n"); + return ERROR; + } + +#if ENABLE_TEST_TIME_MEASURMENT_CC + DEBUGOUT("[BCTF] End of Switch to Bootloader = %d\r\n", get_system_time() - g_u32_spend_time); + g_u32_spend_time = get_system_time(); + DEBUGOUT("TICK=%d\r\n", get_system_time()); +#endif + +#if SELFTEST_3X + if (sysfs_burn_cc_bl()) { + /*Read Flash CC Table*/ + if (read_fpc_flash_3x(FLASH_NORMAL_FW_CC_TABLE_ADDR, &u32_cc_table) == ERROR) + return ERROR; + DEBUGOUT("Flash Do CC FW Version=0x%04X\r\n", (u32_cc_table & 0xFFFF0000) >> 16); + DEBUGOUT("Flash Do CC Flag=0x%X\r\n", (u32_cc_table & 0x0000FFFF)); + g_u32_fw_cc_version = u32_cc_table; + + /*Read PRAM FW Version*/ + if (handle_ic_read(FW_FT_SRAM_FW_VERSION, 4, u8_fw_value, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + DEBUGOUT("SRAM FW Version=0x%03x,0x%03x\r\n", u8_fw_value[3], u8_fw_value[2]); + + if (((u32_cc_table & 0xFF000000) >> 24) != u8_fw_value[3] || ((u32_cc_table & 0x00FF0000) >> 16) != u8_fw_value[2]) { + DEBUGOUT("Flash CC Table FW Version is not match!\r\n"); + if (handle_ic_read(REG_FLASHCTL_FLASH_STATE_REG_ADDR, 4, (unsigned char *)(&u32_read), g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + DEBUGOUT("read 5000 0918 error\r\n"); + DEBUGOUT("Flash 0x50000918=0x%08X\r\n", u32_read); + return ERROR; + } + + return SUCCESS; + } else + return ERROR; +#else + if (burn_to_ic_flash_3x(COMP_AREA) == ERROR) { + DEBUGOUT("[BCTF] Burn CC & BL Fail\r\n"); + return ERROR; + } + + u32_write = 0x00000000; + if (handle_ic_write(BOOT_TEST_MODE_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /* clr sync_data(0x20000200) = 0 as finish*/ + u32_write = 0x00000000; + if (handle_ic_write(BOOT_SYNC_DATA_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + delay_ms(10); + + if (wait_fw_state_3x(REG_FLASHCTL_FLASH_STATE_REG_ADDR, 0x4000, 2, i16_time_out) == ERROR) { + DEBUGOUT("[BCTF] Check Burn CC & BL Fail\r\n"); + return ERROR; + } + +#if ENABLE_AUO_VERIFY_LOG + if (read_fpc_flash_3x((unsigned int)(FLASH_FW_START + PRAM_FW_LENGTH - 4), &u32_i2c_read_data)) + DEBUGOUT("Normal FW CRC=0x%08X\r\n", u32_i2c_read_data); +#endif + +#if ENABLE_TEST_TIME_MEASURMENT_CC + DEBUGOUT("BCTF] Burn to flash finish[Tick=%d]!\r\n", get_system_time() - g_u32_spend_time); +#endif + + /*Read Flash CC Table*/ + if (read_fpc_flash_3x(FLASH_NORMAL_FW_CC_TABLE_ADDR, &u32_cc_table) == ERROR) + return ERROR; + DEBUGOUT("Flash Do CC FW Version=0x%04X\r\n", (u32_cc_table & 0xFFFF0000) >> 16); + DEBUGOUT("Flash Do CC Flag=0x%X\r\n", (u32_cc_table & 0x0000FFFF)); + + /*Read PRAM FW Version*/ + if (handle_ic_read(FW_FT_SRAM_FW_VERSION, 4, u8_fw_value, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + DEBUGOUT("SRAM FW Version=0x%03x,0x%03x\r\n", u8_fw_value[3], u8_fw_value[2]); + + if (((u32_cc_table & 0xFF000000) >> 24) != u8_fw_value[3] || ((u32_cc_table & 0x00FF0000) >> 16) != u8_fw_value[2]) { + DEBUGOUT("Flash CC Table FW Version is not match!\r\n"); + return ERROR; + } + + + return SUCCESS; +#endif +} + +STATUS do_calibration_3x(unsigned char u8_do_calibration_cmd, unsigned char u8_burn_flash) +{ + unsigned char u8_value[4]; + short u8_time_out = 100; + unsigned char u8_retry = IC_TEST_RETRY_TIME; + + g_u32_wearable_test_result &= ~(WEARABLE_FT_TEST_RESULT_CC_CALIBRATION_NG | WEARABLE_FT_TEST_RESULT_BURN_CC_NG); + + if (u8_do_calibration_cmd == TRUE) { + +#if ENABLE_TEST_TIME_MEASURMENT_CC + DEBUGOUT("[DOCC] Start do calibration (%d)\r\n", get_system_time()); + g_u32_spend_time = get_system_time(); +#endif + while (u8_retry--) { + u8_value[0] = SYS_CMD_DO_CC_CAL; + if (handle_ic_write(FW_SYS_CMD_ADDR, 1, u8_value, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) /*Trigger Calibration*/ + return ERROR; + + u8_time_out = 400; + + /* Wait calibration ready*/ + while (u8_time_out--) { + delay_ms(10);/*20*/ + if (handle_ic_read(FW_SYS_CMD_ADDR, 4, u8_value, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + return ERROR; + else if (u8_value[0] == 0) + break; +#if ENABLE_WDT + Chip_WWDT_Feed(LPC_WWDT);/* reload the Watchdog timer*/ +#endif + } + + if (u8_time_out == IC_TEST_TIME_OUT) { + if (u8_retry) { + DEBUGOUT("[DOCC] Do CC Retry (%d,%d)", u8_retry, u8_value[0]); + hardware_reset_3x(TRUE); +#if !SELFTEST_3X + if (g_u8_drv_interface == SPI_INTERFACE) { + if (burn_pram_from_dongle_flash_3x() == ERROR) + return ERROR; + } +#endif + delay_ms(20); + continue; + } else { + g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_CC_CALIBRATION_NG; + DEBUGOUT("[DOCC] SYS CMD CC NG\r\n"); + return ERROR; + } + } else { + if (check_cc_bl_flag_3x() == ERROR) { + if (u8_retry) { + DEBUGOUT("[DOCC] CC Flag Retry (%d)", u8_retry); + hardware_reset_3x(TRUE); +#if !SELFTEST_3X + if (g_u8_drv_interface == SPI_INTERFACE) { + if (burn_pram_from_dongle_flash_3x() == ERROR) + return ERROR; + } +#endif + delay_ms(20); + continue; + } else { + g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_CC_CALIBRATION_NG; + DEBUGOUT("[DOCC] CC Flag NG\r\n"); + return ERROR; + } + } else /*Calibration OK*/ + break; + } + } +#if ENABLE_TEST_TIME_MEASURMENT_CC + DEBUGOUT("[DOCC] Do CC Finish! [%d:%d]\r\n", get_system_time() - g_u32_spend_time, u8_time_out); + g_u32_spend_time = get_system_time(); +#endif + } + + if (u8_burn_flash == FALSE) { + DEBUGOUT("[DOCC] Do Calibration Finish \r\n"); + return SUCCESS; + } + + if (burn_cc_to_ic_flash_3x() == ERROR) { + DEBUGOUT("[DOCC] Burn to flash NG!\r\n"); + hardware_reset_3x(TRUE); + g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_BURN_CC_NG; + return ERROR; + } + +#if ENABLE_TEST_TIME_MEASURMENT_CC + DEBUGOUT("[DOCC] Burn CC Finish (%d)\r\n", get_system_time() - g_u32_spend_time); +#endif + + DEBUGOUT("[DOCC] Burn CC Finish \r\n"); + return SUCCESS; +} + +STATUS hw_int_pin_Test_3x(void) +{ + unsigned int u32_write = 0; + + if ((g_st_test_info.u16_ft_test_info_1) == WEARBLE_FT_INT_SYSTEM_NG_CASE) { + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_INT_NG; + return ERROR; + } + + /*Trigger INT to Low*/ + u32_write = 0x00000004; + if (handle_ic_write(REG_SYSCON_MISCIER_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /*Read Pin state*/ + if (!gpio_touch_int_pin_state_access()) { + /*DEBUGOUT("INT is LOW\r\n");*/ + } else { + DEBUGOUT("INT Test NG!\r\n"); + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_INT_NG; + return ERROR; + } + + /*Trigger INT to High*/ + u32_write = 0x00000404; + if (handle_ic_write(REG_SYSCON_MISCIER_ADDR, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) + return ERROR; + + /*Read Pin state*/ + if (gpio_touch_int_pin_state_access()) { + /*DEBUGOUT("INT is High\r\n");*/ + } else { + DEBUGOUT("INT Test NG!\r\n"); + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_INT_NG; + return ERROR; + } + gpio_touch_int_access(TRUE); + DEBUGOUT("INT Test Pass\r\n"); + return SUCCESS; +} + +STATUS read_test_fw_data_3x(unsigned short u16_test_items) +{ + unsigned char u8_retry = IC_TEST_RETRY_TIME; + unsigned int u32_test_item_result; +#if ENABLE_TEST_TIME_MEASURMENT + unsigned int u32_fun_time = 0; + + u32_fun_time = get_system_time(); +#endif + + g_u32_wearable_test_result &= ~(WEARABLE_FT_TEST_RESULT_GET_DATA_NG | WEARABLE_FT_TEST_RESULT_OPEN_NG + | WEARABLE_FT_TEST_RESULT_SHORT_NG | WEARABLE_FT_TEST_RESULT_UB_NG + | WEARABLE_FT_TEST_RESULT_UC_NG | WEARABLE_FT_TEST_RESULT_SINGLE_CC_OPEN_NG + | WEARABLE_FT_TEST_RESULT_SINGLE_CC_SHORT_NG); + if ((g_st_test_info.u16_ft_test_item & (IC_TEST_ITEMS_OPEN | IC_TEST_ITEMS_SHORT | + IC_TEST_ITEMS_UC | IC_TEST_ITEMS_UB)) == 0) { + return SUCCESS; + } + +RETRY: + memset(g_i16_raw_data_1_short_buf, 0, sizeof(g_i16_raw_data_1_short_buf)); + memset(g_i16_raw_data_2_open_buf, 0, sizeof(g_i16_raw_data_2_open_buf)); + memset(g_u16_raw_data3_cc_buf, 0, sizeof(g_u16_raw_data3_cc_buf)); + memset(g_u16_uc_buf, 0, sizeof(g_u16_uc_buf)); + memset(g_u8_wearable_pin_map, F303_NA_P, sizeof(g_u8_wearable_pin_map)); + + if (handle_ic_read(FT_RAWDATA1_SHORT_BUF_ADDR, MAX_SENSING_PIN_NUM * 2, (unsigned char *)g_i16_raw_data_1_short_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + goto ERROR_EXIT; + + + if (handle_ic_read(FT_RAWDATA2_OPEN_BUF_ADDR, MAX_SENSING_PIN_NUM * 2, (unsigned char *)g_i16_raw_data_2_open_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + goto ERROR_EXIT; + + + if (handle_ic_read(FT_RAWDATA3_CC_BUF_ADDR, MAX_SENSING_PIN_NUM * 2, (unsigned char *)(g_u16_raw_data3_cc_buf), g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + goto ERROR_EXIT; + + if (handle_ic_read(FT_UC_BUF_ADDR, MAX_SENSING_PIN_NUM * 2, (unsigned char *)g_u16_uc_buf, g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + goto ERROR_EXIT; + + if (handle_ic_read(FT_TEST_RESULT_BUF_ADDR, MAX_SENSING_PIN_NUM, (unsigned char *)(g_u8_test_result), g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + goto ERROR_EXIT; + + if (handle_ic_read(FT_TEST_ITEM_RESULT, 4, (unsigned char *)(&u32_test_item_result), g_u8_drv_interface, I2C_BYTE_MODE) == ERROR) + goto ERROR_EXIT; + + if (ft_raw_data_checksum_check_3x((unsigned short *)g_i16_raw_data_1_short_buf) == ERROR) + goto ERROR_EXIT; + + if (ft_raw_data_checksum_check_3x((unsigned short *)g_i16_raw_data_2_open_buf) == ERROR) + goto ERROR_EXIT; + + if (ft_raw_data_checksum_check_3x((unsigned short *)g_u16_raw_data3_cc_buf) == ERROR) + goto ERROR_EXIT; + + if (ft_raw_data_checksum_check_3x(g_u16_uc_buf) == ERROR) + goto ERROR_EXIT; + + if (ft_test_result_checksum_check_3x(g_u8_test_result) == ERROR) + goto ERROR_EXIT; + + if (ft_test_read_used_pin_infor_3x(g_u8_wearable_pin_map) == ERROR) + goto ERROR_EXIT; + +#if ENABLE_TEST_RSU_DATA_SHOW + DEBUGOUT("Slow Data:\r\n"); + dump_image_data_3x(g_i16_raw_data_1_short_buf, TRUE); + DEBUGOUT("Quick Data:\r\n"); + dump_image_data_3x(g_i16_raw_data_2_open_buf, TRUE); + DEBUGOUT("P CC:\r\n"); + dump_image_hex_data_3x((short *)g_u16_raw_data3_cc_buf); + DEBUGOUT("Open Golden CC:\r\n"); + dump_image_hex_data_3x((short *)g_u16_raw_data3_golden_cc_buf); + DEBUGOUT("UC:\r\n"); + dump_image_data_3x((short *)g_u16_uc_buf, TRUE); + DEBUGOUT("Golden UC:\r\n"); + dump_image_data_3x((short *)g_u16_uc_golden_cc_buf, TRUE); + DEBUGOUT("test item result:0x%x\r\n", u32_test_item_result); +#endif + + if (g_u8_print_debug_msg & PRINT_DEBUG_MSG_TYPE_4) { + DEBUGOUT("Slow Data:\r\n"); + dump_image_data_3x(g_i16_raw_data_1_short_buf, TRUE); + DEBUGOUT("Quick Data:\r\n"); + dump_image_data_3x(g_i16_raw_data_2_open_buf, TRUE); + DEBUGOUT("P CC:\r\n"); + dump_image_data_3x((short *)g_u16_raw_data3_cc_buf, TRUE); + + } + if (g_u8_print_debug_msg & PRINT_DEBUG_MSG_TYPE_5) { + DEBUGOUT("UC Data:\r\n"); + dump_image_data_3x((short *)g_u16_uc_buf, TRUE); + } + +#if ENABLE_TEST_TIME_MEASURMENT + DEBUGOUT("#read_test_data end=%d\r\n", get_system_time() - u32_fun_time); +#endif + g_u32_wearable_test_result |= u32_test_item_result; + DEBUGOUT("Read Test FW Result Finish\r\n"); + return SUCCESS; + +ERROR_EXIT: + if (u8_retry) { + u8_retry--; + DEBUGOUT("Read Test FW Result Retry:%d", u8_retry); + goto RETRY; + } + + g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_GET_DATA_NG; + DEBUGOUT("Read Test FW Result NG\r\n"); + return ERROR; +} + +void test_item_message_3x(void) +{ + unsigned short u16_test_item = g_st_test_info.u16_ft_test_item; + + DEBUGOUT("================================\r\n"); + DEBUGOUT("Enter 3x IC Test,(%d)\r\n", get_system_time()); + DEBUGOUT("IC Test Items=0x%X\r\n", u16_test_item); + + if (u16_test_item & IC_TEST_ITEMS_SYSTEM) + DEBUGOUT("Enable System Test\r\n"); + + if (u16_test_item & IC_TEST_ITEMS_OPEN) + DEBUGOUT("Enable Open Test\r\n"); + + if (u16_test_item & IC_TEST_ITEMS_SHORT) + DEBUGOUT("Enable Short Test\r\n"); + + if (u16_test_item & IC_TEST_ITEMS_UC) + DEBUGOUT("Enable Uniformity CC test\r\n"); + + if (u16_test_item & IC_TEST_ITEMS_UB) + DEBUGOUT("Enable Uniformity BL test\r\n"); + + if (u16_test_item & IC_TEST_ITEMS_BURN_FW) + DEBUGOUT("Enable Burn FW\r\n"); + + if (u16_test_item & IC_TEST_ITEMS_BURN_CC) + DEBUGOUT("Enable Burn CC\r\n"); +} + +STATUS burn_cc_3x(unsigned short u16_test_items) +{ + unsigned char u8_retry = IC_TEST_RETRY_TIME; +#if ENABLE_AUO_VERIFY_LOG + unsigned int u32_flash_crc = 0; +#endif +#if ENABLE_TEST_TIME_MEASURMENT + unsigned int u32_fun_time = 0; + + u32_fun_time = get_system_time(); +#endif + g_u32_wearable_test_result &= ~(WEARABLE_FT_TEST_RESULT_BURN_CC_NG | WEARABLE_FT_TEST_RESULT_NORMAL_FW_NG + | WEARABLE_FT_TEST_RESULT_CC_CALIBRATION_NG); + + if ((g_st_test_info.u16_ft_test_info_1) == WEARBLE_FT_BURN_CC_SYSTEM_NG_CASE) { + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_BURN_CC_NG; + return ERROR; + } + + if ((g_st_test_info.u16_ft_test_item & (IC_TEST_ITEMS_BURN_CC)) == 0) { + g_u32_wearable_test_result &= ~WEARABLE_FT_TEST_RESULT_NORMAL_FW_RESET_NG; + return SUCCESS; + } + + while (u8_retry--) { + if (enter_normal_fw_3x() == ERROR) { + if (u8_retry && !(g_u32_wearable_test_result & WEARABLE_FT_TEST_RESULT_PRAM_NG)) + continue; + else { + g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_NORMAL_FW_NG; + DEBUGOUT("Enter Normal FW NG\r\n"); + return ERROR; + } + } + /*Do CC*/ +#if ENABLE_WDT + Chip_WWDT_Feed(LPC_WWDT);/* reload the Watchdog timer*/ +#endif + + DEBUGOUT("[BC] Burn CC Start!!\r\n"); + if (g_u8_drv_interface == SPI_INTERFACE) { + /*flashless, bypass*/ + DEBUGOUT("[BC]Flashless Burn CC ByPass!!\r\n"); + return SUCCESS; + } + /*Burn CC to Flash*/ + if (do_calibration_3x(TRUE, TRUE) == SUCCESS) { + DEBUGOUT("[BC] Burn CC Pass!!(%d)\r\n", u8_retry); + break; + } + if (u8_retry) + continue; + else { + DEBUGOUT("[BC] Burn CC NG!!\r\n"); + return ERROR; + } + } + +#if ENABLE_TEST_TIME_MEASURMENT + DEBUGOUT("[BC] Burn CC Time(%d)\r\n", get_system_time() - u32_fun_time); +#endif + + return SUCCESS; +} + +/*Use dongle ext.Flash to read test fw and write to pram*/ +STATUS load_test_fw_3x(void) +{ + STATUS u8_ret = ERROR; +#if ENABLE_TEST_TIME_MEASURMENT + unsigned int u32_fun_time = 0; + + u32_fun_time = get_system_time(); + DEBUGOUT("load_test_fw Start Time= %d\r\n", u32_fun_time); +#endif + g_u32_wearable_test_result &= ~(WEARABLE_FT_TEST_RESULT_LOAD_TESTFW_NG); + + if (g_st_test_info.u16_ft_test_info_1 == WEARBLE_FT_LOAD_TEST_FW_SYSTEM_NG_CASE) { + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_LOAD_TESTFW_NG; + return ERROR; + } + + if ((g_st_test_info.u16_ft_test_item & (IC_TEST_ITEMS_OPEN | IC_TEST_ITEMS_SHORT | IC_TEST_ITEMS_UC | + IC_TEST_ITEMS_UB)) == 0) + return SUCCESS; + + DEBUGOUT("[LTFW] start load test fw\r\n"); +#if SELFTEST_3X + u8_ret = raydium_upgrade_test_fw_3x(0); +#else + u8_ret = load_test_fw_ft_3x(); +#endif + DEBUGOUT("[LTFW] Load test fw finish!!\r\n"); +#if ENABLE_TEST_TIME_MEASURMENT + DEBUGOUT("load_test_fw Spend Time= %d\r\n", get_system_time() - u32_fun_time); +#endif + + return u8_ret; +} +/* + * STATUS check_ext_flash_fw_version(void) + * { + * unsigned int u32_addr, u32_normal_fw_version, u32_test_fw_version; + * unsigned short u16_data_length = 0; + * u16_data_length = 4; + * + * + * //get normal FW version from ext flash// + * u32_addr = FLASH_NORMAL_FW_FW_VERSION_ADDR; + * if (read_flash_data(u32_addr, u16_data_length) == ERROR) { + * DEBUGOUT("Read Ext Flash NG [0x%08X] \r\n", u32_addr); + * return ERROR; + * } + * u32_normal_fw_version = 0; + * u32_normal_fw_version |= g_u8_data_buf[0]; + * u32_normal_fw_version |= g_u8_data_buf[1] << 8; + * u32_normal_fw_version |= g_u8_data_buf[2] << 16; + * u32_normal_fw_version |= g_u8_data_buf[3] << 24; + * //DEBUGOUT("SPI Read_addr = 0x%08X, u16DataLength = %d \r\n", u32_normal_fw_version, u16_data_length); + */ + /* Test FW Version*/ + u32_addr = FLASH_TEST_FW_FW_VERSION_ADDR; + if (read_flash_data(u32_addr, u16_data_length) == ERROR) { + DEBUGOUT("Read Ext Flash NG [0x%08X] \r\n", u32_addr); + return ERROR; + } + u32_test_fw_version = 0; + u32_test_fw_version |= g_u8_data_buf[0]; + u32_test_fw_version |= g_u8_data_buf[1] << 8; + u32_test_fw_version |= g_u8_data_buf[2] << 16; + u32_test_fw_version |= g_u8_data_buf[3] << 24; + /*DEBUGOUT("SPI Read_addr = 0x%08X, u16DataLength = %d \r\n", u32_test_fw_version, u16_data_length);*/ + if (g_st_test_para_resv.u32_normal_fw_version != u32_normal_fw_version + || g_st_test_para_resv.u32_test_fw_version != u32_test_fw_version) { + DEBUGOUT("INI FW Version NG ,0x%08X VS 0x%08X \r\n", g_st_test_para_resv.u32_normal_fw_version, u32_normal_fw_version); + DEBUGOUT("INI Test FW Version NG ,0x%08X VS 0x%08X \r\n", g_st_test_para_resv.u32_test_fw_version, u32_test_fw_version); + return ERROR; + } + + return SUCCESS; +} +#endif +STATUS system_test_3x(void) +{ + STATUS u8_test_result = SUCCESS; + unsigned short u16_panel_mode; +#if ENABLE_TEST_TIME_MEASURMENT + unsigned int u32_fun_time; +#endif + + /*Clear Test Item Test result*/ + g_u32_wearable_test_result &= ~(WEARABLE_FT_TEST_RESULT_I2C_NG | WEARABLE_FT_TEST_RESULT_INT_NG + | WEARABLE_FT_TEST_RESULT_RESET_NG | WEARABLE_FT_TEST_RESULT_PRAM_NG + | WEARABLE_FT_TEST_RESULT_IC_SUB_VERSION_NG | WEARABLE_FT_TEST_RESULT_IC_FW_VERIFY_NG); + + if ((g_st_test_info.u16_ft_test_item & IC_TEST_ITEMS_SYSTEM) == FALSE) + return SUCCESS; + + if (enable_ic_block_3x() == ERROR) { + DEBUGOUT("enable_ic_block NG!!!\r\n"); + /*return ERROR;*/ + } + + stop_mcu_3x(1); + +/* + * Check Dongle Ext Flash FW/Test FW version PK INI + * + * if (check_ext_flash_fw_version() == ERROR) { + * u8_test_result = ERROR; + * g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_IC_FW_VERIFY_NG; + * goto ERROR_EXIT; + * } + */ + +#if ENABLE_TEST_TIME_MEASURMENT + u32_fun_time = get_system_time(); +#endif + /*I2C or SPI Test*/ + if (ft_test_connect_test_3x() == ERROR) { + u8_test_result = ERROR; + goto ERROR_EXIT; + } +#if ENABLE_TEST_TIME_MEASURMENT + DEBUGOUT("Interface test Spend Time= %d\r\n", get_system_time() - u32_fun_time); +#endif + + if ((g_st_test_info.u16_ft_eng_item & IC_TEST_ENG_ITEMS_RESV_12)) { + DEBUGOUT("INI Model = 0x%08X\r\n", g_st_test_para_resv.u32_normal_fw_version); + u16_panel_mode = (g_st_test_para_resv.u32_normal_fw_version & 0x0000FFFF); + if (ft_test_panel_model_check_3x(u16_panel_mode) == ERROR) { + u8_test_result = ERROR; + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_IC_FW_VERIFY_NG; + goto ERROR_EXIT; + } + } + +#if ENABLE_TEST_TIME_MEASURMENT + u32_fun_time = get_system_time(); +#endif + /*INT Pin Test*/ + if (hw_int_pin_Test_3x() == ERROR) { + u8_test_result = ERROR; + goto ERROR_EXIT; + } +#if ENABLE_TEST_TIME_MEASURMENT + DEBUGOUT("INT Spend Time= %d\r\n", get_system_time() - u32_fun_time); +#endif + +#if ENABLE_TEST_TIME_MEASURMENT + u32_fun_time = get_system_time(); +#endif + +/* + * RAM Test + * if (ft_test_ram_test_3x(0) == ERROR) { + * u8_test_result = ERROR; + * goto ERROR_EXIT; + * } + */ + +#if ENABLE_TEST_TIME_MEASURMENT + DEBUGOUT("RAM Spend Time= %d\r\n", get_system_time() - u32_fun_time); +#endif + + if ((g_st_test_info.u16_ft_eng_item & IC_TEST_ENG_ITEMS_RESV_11)) { + if (check_dev_sub_version_3x(g_st_test_para_resv.u8_para_resv_0[21]) == ERROR) { + u8_test_result = ERROR; + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_IC_SUB_VERSION_NG; + goto ERROR_EXIT; + } + } + + + +#if ENABLE_TEST_TIME_MEASURMENT + u32_fun_time = get_system_time(); +#endif + /*Reset Pin Test*/ + if (ft_test_reset_pin_test_3x() == ERROR) { + u8_test_result = ERROR; + goto ERROR_EXIT; + } + +#if ENABLE_TEST_TIME_MEASURMENT + DEBUGOUT("Reset Spend Time= %d\r\n", get_system_time() - u32_fun_time); +#endif + +ERROR_EXIT: + + if (u8_test_result == ERROR) + DEBUGOUT("System Test NG!!\r\n"); + else + DEBUGOUT("System Test PASS\r\n"); + + return u8_test_result; +} + +void do_ic_test_3x(void) +{ + unsigned short u16_test_items; + +#if ENABLE_TEST_TIME_MEASURMENT + unsigned char u8_ic_test_state; +#endif + +#if ENABLE_TEST_TIME_MEASURMENT + unsigned int u32_fun_time = 0; +#endif + unsigned int u32_read_data = 0; +#if ENABLE_CONTROL_OPENSHORT_WDT + Chip_WWDT_SetTimeOut(LPC_WWDT, (WDT_OSC / 10) * 150); +#endif + + g_u32_wearable_test_result &= ~(WEARABLE_FT_TEST_RESULT_POWER_ON_NG | WEARABLE_FT_TEST_RESULT_IC_VERSION_NG + | WEARABLE_FT_TEST_RESULT_MCU_HOLD_NG | WEARABLE_FT_TEST_RESULT_EXT_FLASH_EMPTY_NG + | WEARABLE_FT_TEST_RESULT_FLASH_ID_NG | WEARABLE_FT_TEST_RESULT_NORMAL_FW_VER_NG + | WEARABLE_FT_TEST_RESULT_PANEL_TEST_3_NG | WEARABLE_FT_TEST_RESULT_CB_NG | WEARABLE_FT_TEST_RESULT_TEST_FW_VER_NG + | WEARABLE_FT_TEST_RESULT_AUO_JIG_NG); + + if (!wearable_ic_test_init()) { + g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_TEST_INIT_NG; + g_u8_ic_test_state = IC_TEST_EXIT; + return; + } + + test_item_message_3x(); +#if !SELFTEST_3X + if ((g_st_test_info.u16_ft_eng_item & IC_TEST_ENG_ITEMS_RESV_16)) { + + + + DEBUGOUT("TEST STATUS= %d\r\n", g_u8_panel_jig_test_status); + + DEBUGOUT("[burn_header_log_to_flash]\r\n"); + if (!b_is_auo_jig_testing) { + b_is_auo_jig_testing = true; + g_u8_test_log_burn_times = 0; + if (!burn_header_log_to_flash_3x(FALSE, FALSE)) + DEBUGOUT("[burn_header_log_to_flash_3x ERROR!]\r\n"); + if (g_u8_test_log_burn_times > 5) { + if (!burn_header_log_to_flash_3x(FALSE, TRUE)) + DEBUGOUT("[burn_header_log_to_flash_3x ERROR!]\r\n"); + } + } + } +#endif + if (g_st_test_info.u16_ft_test_item == 0) + return; +/* + * if (g_st_test_info.u8_device_id != 2) { + * g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_IC_VERSION_NG; + * return; + * } + */ + + /*handle_display_write(g_u8_display_pattern_sleep_to_active, sizeof(g_u8_display_pattern_sleep_to_active));*/ + handle_ic_read(0x50001100, 4, (unsigned char *)(&u32_read_data), g_u8_drv_interface, I2C_WORD_MODE); + DEBUGOUT("display mode 0x%x\r\n", u32_read_data); + + g_u8_ic_test_state = IC_TEST_INIT; + + u16_test_items = g_st_test_info.u16_ft_test_item; + + g_u32_wearable_test_result &= ~(WEARABLE_FT_TEST_RESULT_POWER_ON_NG | WEARABLE_FT_TEST_RESULT_IC_VERSION_NG + | WEARABLE_FT_TEST_RESULT_MCU_HOLD_NG | WEARABLE_FT_TEST_RESULT_EXT_FLASH_EMPTY_NG + | WEARABLE_FT_TEST_RESULT_FLASH_ID_NG | WEARABLE_FT_TEST_RESULT_NORMAL_FW_VER_NG + | WEARABLE_FT_TEST_RESULT_PANEL_TEST_3_NG | WEARABLE_FT_TEST_RESULT_CB_NG | WEARABLE_FT_TEST_RESULT_TEST_FW_VER_NG + + | WEARABLE_FT_TEST_RESULT_AUO_JIG_NG); + + + while (u16_test_items) { +#if ENABLE_TEST_TIME_MEASURMENT + u8_ic_test_state = g_u8_ic_test_state; + g_u32_spend_time = get_system_time(); +#endif + + switch (g_u8_ic_test_state) { + case IC_TEST_INIT: +#if ENABLE_TEST_TIME_MEASURMENT + u32_fun_time = get_system_time(); +#endif + g_u8_ic_test_state = IC_TEST_SYSTEM; +#if ENABLE_TEST_TIME_MEASURMENT + DEBUGOUT("IC_TEST_INIT Spend Time= %d\r\n", get_system_time() - u32_fun_time); +#endif + + break; + case IC_TEST_SYSTEM: + if (system_test_3x() == SUCCESS || + (g_st_test_info.u16_ft_eng_item & IC_TEST_ENG_ITEMS_TEST_ALL)) { + + g_u8_ic_test_state = IC_TEST_BURN_FW; + } else { + g_u8_ic_test_state = IC_TEST_EXIT; + } + break; + case IC_TEST_BURN_FW: + if (burn_fw_3x() == ERROR) { + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_BURN_FW_NG; + g_u8_ic_test_state = IC_TEST_EXIT; + } else { + g_u8_ic_test_state = IC_TEST_LOAD_TEST_FW; + } + break; + case IC_TEST_LOAD_TEST_FW: + if (load_test_fw_3x() == ERROR) { + DEBUGOUT("Load Test FW NG!!\r\n"); + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_LOAD_TESTFW_NG; + g_u8_ic_test_state = IC_TEST_EXIT; + } else { + g_u8_ic_test_state = IC_TEST_ENTER_FW_TEST_MODE; + } + break; + case IC_TEST_ENTER_FW_TEST_MODE: + if (enter_fw_test_mode_3x() == ERROR) { + DEBUGOUT("Enter FW Test Mode NG!!\r\n"); + g_u8_ic_test_state = IC_TEST_EXIT; + } else { + g_u8_ic_test_state = IC_TEST_OPEN_SHORT; + } + break; + case IC_TEST_OPEN_SHORT: + if (ft_test_do_fw_test_3x(u16_test_items) == SUCCESS) { + DEBUGOUT("FW Test Finish! \r\n"); + g_u8_ic_test_state = IC_TEST_READ_TEST_FW_DATA; + } else { + DEBUGOUT("FW Test NG!!\r\n"); + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_TEST_INIT_NG; + g_u8_ic_test_state = IC_TEST_EXIT; + } + break; + case IC_TEST_READ_TEST_FW_DATA: + if (read_test_fw_data_3x(u16_test_items) == SUCCESS) { + g_u8_ic_test_state = IC_TEST_BURN_CC; + } else { + DEBUGOUT("Read Test FW Data NG!!\r\n"); + g_u8_ic_test_state = IC_TEST_EXIT; + } + break; + case IC_TEST_BURN_CC: + if (burn_cc_3x(u16_test_items) == ERROR) { + DEBUGOUT("Burn CC NG!!\r\n"); + g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_BURN_CC_NG; + g_u8_ic_test_state = IC_TEST_PANEL_PATTERN_TEST; + } else { + g_u8_ic_test_state = IC_TEST_PANEL_PATTERN_TEST; + } + break; + case IC_TEST_PANEL_PATTERN_TEST: +#if ENABLE_TEST_GPIO_MEASURMENT + Chip_GPIO_SetPinOutHigh(LPC_GPIO, DONGLE_GPIO_PORT_1, DONGLE_GPIO_PIN_10); + delay_ms(1); + Chip_GPIO_SetPinOutLow(LPC_GPIO, DONGLE_GPIO_PORT_1, DONGLE_GPIO_PIN_10); +#endif + DEBUGOUT("B g_u32_wearable_test_result = 0x%08x\r\n", g_u32_wearable_test_result); + +#if !SELFTEST_3X + do_ic_panel_test_3x(); +#endif + g_u8_ic_test_state = IC_TEST_EXIT; + break; + case IC_TEST_EXIT: +#if ENABLE_TEST_TIME_MEASURMENT + u32_fun_time = get_system_time(); + DEBUGOUT("Final action Start Time= %d\r\n", u32_fun_time); +#endif + DEBUGOUT("g_u32_wearable_test_result = 0x%08x\r\n", g_u32_wearable_test_result); + DEBUGOUT("Exit IC Test!\r\n"); +#if !SELFTEST_3X + gpio_touch_int_trigger_control(FALSE); +#endif + + g_u8_ic_test_state = IC_TEST_INIT; + if ((g_st_test_info.u16_ft_test_item & (IC_TEST_ITEMS_OPEN | IC_TEST_ITEMS_SHORT)) == 0) { + ft_raw_data_checksum_cal_3x((unsigned short *)g_i16_raw_data_1_short_buf); + ft_raw_data_checksum_cal_3x((unsigned short *)g_i16_raw_data_2_open_buf); + ft_raw_data_checksum_cal_3x(g_u16_raw_data3_cc_buf); + } + ft_raw_data_checksum_cal_3x(g_u16_uc_buf); + + ft_raw_data_checksum_cal_3x((unsigned short *)g_u16_raw_data3_golden_cc_buf); + ft_raw_data_checksum_cal_3x((unsigned short *)g_u16_uc_golden_cc_buf); + ft_test_result_checksum_cal_3x(g_u8_test_result); +#if !SELFTEST_3X + DEBUGOUT("g_u8_panel_jig_test_status= %d\r\n", g_u8_panel_jig_test_status); + if (g_u8_panel_jig_test_status == STATUS_DATA_PANEL_JIG_FINISH && (g_st_test_info.u16_ft_eng_item & IC_TEST_ENG_ITEMS_ENABLE_BURN_TEST_LOG)) { + b_is_auo_jig_testing = false; + burn_header_log_to_flash_3x(TRUE, TRUE); + memset(g_u32_save_config, 0, sizeof(g_u32_save_config)); + if (!burn_data_log_to_flash_3x()) + DEBUGOUT("[burn_log_to_flash ERROR!]\r\n"); + } +#endif +/* + * if (g_u8_panel_jig_test_status == STATUS_DATA_PANEL_JIG_FINISH && (g_st_test_info.u16_ft_eng_item & IC_TEST_ENG_ITEMS_ENABLE_BURN_TEST_LOG)) { + * if (!burn_log_to_flash_3x()) + * DEBUGOUT("[burn_log_to_flash ERROR!]\r\n"); + * } + */ + +#if ENABLE_CONTROL_OPENSHORT_WDT + /*Chip_WWDT_SetTimeOut(LPC_WWDT, (WDT_OSC / 10) * 50);*/ +#endif + u16_test_items = 0; +#if ENABLE_TEST_TIME_MEASURMENT + DEBUGOUT("Final action Spend Time= %d\r\n", get_system_time() - u32_fun_time); +#endif + break; + } + +#if !SELFTEST_3X + update_dongle_test_status(g_u8_ic_test_state); +#endif + +#if ENABLE_WDT + /* reload the Watchdog timer*/ + Chip_WWDT_Feed(LPC_WWDT); +#endif + +#if !SELFTEST_3X + if (g_u8_ic_power_on_ng) { + g_u32_wearable_test_result |= WEARABLE_FT_TEST_RESULT_POWER_ON_NG; + g_u8_ic_test_state = IC_TEST_EXIT; + } +#endif + +#if ENABLE_TEST_TIME_MEASURMENT + DEBUGOUT("Test Item [%d] End (%d)\r\n", u8_ic_test_state, get_system_time() - g_u32_spend_time); +#endif + } /*while(u16_test_items)*/ +} + + diff --git a/raydium/chip_raydium/f303_ic_test.h b/raydium/chip_raydium/f303_ic_test.h new file mode 100644 index 0000000000..5ce4db850d --- /dev/null +++ b/raydium/chip_raydium/f303_ic_test.h @@ -0,0 +1,65 @@ +/* f303_ic_test.h + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + +#include "ic_drv_global.h" + +/**************************************************************************** + * Defined Const Value + *****************************************************************************/ +#define FT_CMD_DO_FT_TEST 0x14 + +extern STATUS turn_on_flash_3x(void); +extern STATUS read_fpc_flash_3x(unsigned int u32_addr, unsigned int *p_u32_data); +extern STATUS set_test_info_thd_para_3x(void); +extern STATUS check_test_fw_status_3x(unsigned char u8_target_status, unsigned char *p_u8_result); +extern STATUS ft_test_do_fw_test_3x(unsigned short u16_test_items); +extern STATUS enter_fw_test_mode_3x(void); +extern STATUS system_test_3x(void); +extern STATUS ft_test_ctrl_mbist_fun_3x(unsigned char u8_enable); +extern STATUS ft_test_ram_test_3x(unsigned char u8_is_stop_mcu); +extern STATUS ft_test_connect_test_3x(void); +extern STATUS ft_test_reset_pin_test_3x(void); +extern STATUS ft_raw_data_checksum_check_3x(unsigned short *u16_buffer); +extern STATUS ft_test_result_checksum_check_3x(unsigned char *u8_buffer); +extern STATUS burn_cc_to_ic_flash_3x(void); +extern STATUS check_cc_bl_flag_3x(void); +extern STATUS read_test_fw_data_3x(unsigned short u16_test_items); +extern STATUS load_test_fw_3x(void); + +extern void dump_image_data_3x(short *p_image_buf, unsigned char u8_remap); +extern void dump_image_hex_data_3x(short *p_image_buf); +extern STATUS ft_test_read_used_pin_infor_3x(unsigned char *p_u8_infor); +extern void ft_raw_data_checksum_cal_3x(unsigned short *u16_buffer); +extern void ft_test_result_checksum_cal_3x(unsigned char *u8_buffer); +extern STATUS baseline_update_control_3x(bool b_enable_baseline_update); +extern STATUS enter_normal_fw_3x(void); +extern STATUS do_calibration_3x(unsigned char u8_do_calibration_cmd, unsigned char u8_burn_flash); +extern STATUS hw_int_pin_Test_3x(void); +extern void test_item_message_3x(void); +extern STATUS burn_cc_3x(unsigned short u16_test_items); +extern void do_ic_test_3x(void); +extern STATUS burn_data_log_to_flash_3x(void); +extern STATUS burn_header_log_to_flash_3x(bool is_test_finish, bool is_in_header_data_page); + +//-----------------------------extern FT function ------------------------------------------ +extern STATUS load_test_fw_ft_3x(void); +extern void do_ic_panel_test_3x(void); +extern STATUS burn_fw_3x(void); +extern STATUS burn_to_ic_flash_3x(unsigned char u8_type); +extern unsigned char notify_panel_jig_start_test_3x(unsigned char u8_cmd); + diff --git a/raydium/chip_raydium/ic_drv_global.c b/raydium/chip_raydium/ic_drv_global.c new file mode 100644 index 0000000000..6de61b4698 --- /dev/null +++ b/raydium/chip_raydium/ic_drv_global.c @@ -0,0 +1,127 @@ +/* i2c_drv_global.c + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + +#include +#include "ic_drv_global.h" +#include "ic_drv_interface.h" +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "drv_interface.h" + + + +#if !SELFTEST +#include "usb.h" +#include "Descriptors.h" +#endif +/* + ***************************************************************************** + ** + ** Declared Global Variable + ** + ***************************************************************************** + ********************* Global ********************* + */ + + +unsigned short g_u16_dev_id; + +unsigned char g_u8_drv_interface; + +volatile unsigned char g_u8_gpio_irq_trigger; + +unsigned char g_u8_raw_data_buffer[MAX_IMAGE_BUFFER_SIZE * 2]; +unsigned short g_u16_raw_data_tmp[MAX_IMAGE_BUFFER_SIZE]; + +// IC Test +short g_i16_raw_data_1_short_buf[MAX_SENSING_PIN_NUM]; +short g_i16_raw_data_2_open_buf[MAX_SENSING_PIN_NUM]; +unsigned short g_u16_raw_data3_cc_buf[MAX_SENSING_PIN_NUM]; +unsigned short g_u16_uc_buf[MAX_SENSING_PIN_NUM];//CC +unsigned short g_u16_raw_data3_golden_cc_buf[MAX_SENSING_PIN_NUM]; +unsigned short g_u16_uc_golden_cc_buf[MAX_SENSING_PIN_NUM]; + +unsigned int g_u32_test_result[MAX_SENSING_PIN_NUM]; // each node test result (bit[0]:open ng, bit[1]:short ng, bit[2]:uniformity ng..etc) +unsigned char g_u8_test_result[MAX_SENSING_PIN_NUM]; + + +unsigned int g_u32_wearable_test_result; +unsigned char g_u8_wearable_pin_map[MAX_IMAGE_BUFFER_SIZE]; + + +unsigned char g_u8_data_buf[DATA_BUFFER_SIZE]; +unsigned char g_u8_mipi_read_buf[56]; + +unsigned char g_u8_channel_x; +unsigned char g_u8_channel_y; + +unsigned char g_u8_is_normal_fw; + +volatile unsigned short g_u16_test_items_host_cmd; +unsigned short g_u16_test_items_tool_cmd; +unsigned char g_u8_ic_power_on_ng; +char g_i8_test_baseline_msg[30]; +volatile unsigned short g_u16_panel_jig_set_test_items; +bool b_is_auo_jig_testing; + +#if ENABLE_TEST_TIME_MEASURMENT || ENABLE_TEST_TIME_MEASURMENT_CC +unsigned int g_u32_spend_time; +#endif + +unsigned int g_u32_fw_cc_version; +unsigned char g_u8_print_debug_msg; +unsigned char g_u8_display_interface; +unsigned char g_u8_PAGE_ADDR; + +int g_u32_dongle_flash_ini_addr;// 0xF000 +int g_u32_ini_threshold_addr;// DONGLE_FLASH_INI_ADDR + 16 +int g_u32_ini_para_addr;// INI_THRESHOLD_ADDR + 36 +//int u32_ini_raw_data_2_bl_addr;// INI_PARA_ADDR+48 +int g_u32_ini_raw_data_3_cc_addr;// INI_RAW_DATA2_BL_ADDR+72 +int g_u32_ini_uc_cc_addr;// INI_RAW_DATA_3_CC_ADDR + 72// INI_RAW_DATA2_ADDR + +int g_u32_initial_code_start_addr; + +void ic_drv_init(void) +{ + g_u16_test_items_host_cmd = 0xFFFF; + g_u16_test_items_tool_cmd = 0; + g_u16_dev_id = DEVICE_ID_3X; + g_u8_print_debug_msg = 0; + g_u8_display_interface = DISPLAY_TOUCH_2_DRIVER_MODE; + b_is_auo_jig_testing = false; +#if !SELFTEST + g_u8_ic_power_on_ng = FALSE; +#endif +} + +/* + ***************************************************************************** + ** + * End Of File + ****************************************************************************** + */ diff --git a/raydium/chip_raydium/ic_drv_global.h b/raydium/chip_raydium/ic_drv_global.h new file mode 100644 index 0000000000..ae9e884deb --- /dev/null +++ b/raydium/chip_raydium/ic_drv_global.h @@ -0,0 +1,190 @@ +/* i2c_drv_global.h + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + +#ifndef _DRVGLOBAL_H_ +#define _DRVGLOBAL_H_ +#include +#ifdef __KERNEL__ +#include +#else +#include +#endif + +#if (SELFTEST_2X) +#include "f302_ic_control.h" +#endif + +#if !SELFTEST +#include "f302_ic_control.h" +#include "f303_ic_control.h" +#include "usb.h" +#endif +//********* Basic Parameter Definition ***********// +/***************************************************************************** +** +** Declared Global Variable +** +*****************************************************************************/ + + +#define MAX_IMAGE_BUFFER_SIZE 64 +#define MAX_SENSING_PIN_NUM 50 // (wearable) +#define F302_MAX_IMAGE_BUFFER_SIZE 36 +#define DATA_BUFFER_SIZE (0x100) + +#define F302_DONGLE_FLASH_INI_ADDR 0xF000 +#define F302_INI_THRESHOLD_ADDR (F302_DONGLE_FLASH_INI_ADDR + 16) +#define F302_INI_PARA_ADDR (F302_INI_THRESHOLD_ADDR + 36) +#define F302_INI_RAW_DATA2_BL_ADDR (F302_INI_PARA_ADDR + 48) +#define F302_INI_RAW_DATA_3_CC_ADDR (F302_INI_RAW_DATA2_BL_ADDR + 72) +#define F302_INI_UC_CC_ADDR (F302_INI_RAW_DATA_3_CC_ADDR + 72) // INI_RAW_DATA2_ADDR +#define F302_NA_P 36 + +#define F303_DONGLE_FLASH_INI_ADDR 0x10000 +#define F303_INI_THRESHOLD_ADDR (F303_DONGLE_FLASH_INI_ADDR + 16) +#define F303_INI_PARA_ADDR (F303_INI_THRESHOLD_ADDR + 36) +#define F303_INI_RAW_DATA2_BL_ADDR (F303_INI_PARA_ADDR + 48) +#define F303_INI_RAW_DATA_3_CC_ADDR (F303_INI_RAW_DATA2_BL_ADDR + 128) +#define F303_INI_UC_CC_ADDR (F303_INI_RAW_DATA_3_CC_ADDR + 128) // INI_RAW_DATA2_ADDR +#define F303_MAX_SENSING_PIN_NUM 48 +#define F303_NA_P 65 + +#define F302_INITIAL_CODE_START_ADDR 0xF400 +#define F303_INITIAL_CODE_START_ADDR 0x10400 + +#define PRINT_DEBUG_MSG_TYPE_1 0x01 +#define PRINT_DEBUG_MSG_TYPE_2 0x02 +#define PRINT_DEBUG_MSG_TYPE_3 0x04 +#define PRINT_DEBUG_MSG_TYPE_4 0x08 +#define PRINT_DEBUG_MSG_TYPE_5 0x10 +#define PRINT_DEBUG_MSG_TYPE_6 0x20 +#define PRINT_DEBUG_MSG_TYPE_7 0x40 +#define PRINT_DEBUG_MSG_TYPE_8 0x80 + +#define FW_SYS_CMD_ADDR 0x20000288 + +#define SYS_CMD_TP_MANUAL_MODE 0x34 +#define SYS_CMD_DO_BL_CAL 0x5A +#define SYS_CMD_DO_CC_CAL 0x5C + +#define FW_TP_SEQ_NUM_ADDR 0x20000290 +#define FW_TP_POINT_DATA_ADDR 0x20000294 +#define REG_SYSCON_MISCIER_ADDR 0x40000014 +#define FW_FT_IMG_ADDR 0x2000019C +#define RM_DATAMEM0_BASE 0x20000000 + +#define F303_DRAM_FT_DBG_START 0x2000004C + +//======================= Basic Hardware Define ============================== + + +//==================== End of Basic Hardware Define ========================== + +#if SELFTEST + +#ifdef __KERNEL__ +typedef enum { + error = -1, + success = 1 +} STATUS; + +#else +typedef enum { + ERROR = 0, + SUCCESS = 1 +} STATUS; +typedef enum { + DISABLE = 0, + ENABLE = 1 +} FunctionalState; + +typedef enum { + false = 0, + true = 1 +} bool; +#endif +#endif + +//========== Basic Parameter Information ========== +//********************* Global *********************// + +// Global variable for dongle +extern unsigned char g_u8_drv_interface; + +extern volatile unsigned char g_u8_gpio_irq_trigger; +extern unsigned short g_u16_dev_id; + + +extern unsigned char g_u8_raw_data_buffer[MAX_IMAGE_BUFFER_SIZE * 2]; +extern unsigned short g_u16_raw_data_tmp[MAX_IMAGE_BUFFER_SIZE]; + +extern short g_i16_raw_data_1_short_buf[MAX_SENSING_PIN_NUM]; +extern short g_i16_raw_data_2_open_buf[MAX_SENSING_PIN_NUM]; +extern unsigned short g_u16_raw_data3_cc_buf[MAX_SENSING_PIN_NUM]; +extern unsigned short g_u16_uc_buf[MAX_SENSING_PIN_NUM]; + + +extern unsigned short g_u16_raw_data3_golden_cc_buf[MAX_SENSING_PIN_NUM]; +extern unsigned short g_u16_uc_golden_cc_buf[MAX_SENSING_PIN_NUM]; +extern unsigned int g_u32_test_result[MAX_SENSING_PIN_NUM]; // each node test result (open ng, short ng, uniformity ng..etc) +extern unsigned char g_u8_wearable_pin_map[MAX_IMAGE_BUFFER_SIZE]; +extern unsigned char g_u8_test_result[MAX_SENSING_PIN_NUM]; + +extern volatile unsigned short g_u16_test_items_host_cmd; +extern unsigned short g_u16_test_items_tool_cmd; +extern volatile unsigned short g_u16_panel_jig_set_test_items; + +extern unsigned int g_u32_wearable_test_result; +extern unsigned char g_u8_channel_x; +extern unsigned char g_u8_channel_y; +extern unsigned char g_u8_is_normal_fw; +extern unsigned char g_u8_data_buf[DATA_BUFFER_SIZE]; +extern unsigned char g_u8_mipi_read_buf[56]; + +extern unsigned int g_u32_fw_cc_version; +extern unsigned char g_u8_print_debug_msg; + +extern char g_i8_test_baseline_msg[30]; +extern bool b_is_auo_jig_testing; +#if ENABLE_TEST_TIME_MEASURMENT || ENABLE_TEST_TIME_MEASURMENT_CC +extern unsigned int g_u32_spend_time; +#endif + + +#if !SELFTEST +extern unsigned char g_u8_ic_power_on_ng; +#endif + +extern unsigned char g_u8_display_interface; +extern unsigned char g_u8_PAGE_ADDR; + +extern int g_u32_dongle_flash_ini_addr;// 0xF000 +extern int g_u32_ini_threshold_addr;// DONGLE_FLASH_INI_ADDR + 16 +extern int g_u32_ini_para_addr;// INI_THRESHOLD_ADDR + 36 +//int u32_ini_raw_data_2_bl_addr;// INI_PARA_ADDR+48 +extern int g_u32_ini_raw_data_3_cc_addr;// INI_RAW_DATA2_BL_ADDR+72 +extern int g_u32_ini_uc_cc_addr;// INI_RAW_DATA_3_CC_ADDR + 72// INI_RAW_DATA2_ADDR + +extern int g_u32_initial_code_start_addr; +extern void ic_drv_init(void); + +#endif /* end _DRVGLOBAL_H_*/ + +/****************************************************************************** +** End Of File +******************************************************************************/ diff --git a/raydium/chip_raydium/ic_drv_interface.c b/raydium/chip_raydium/ic_drv_interface.c new file mode 100644 index 0000000000..663e54d6b3 --- /dev/null +++ b/raydium/chip_raydium/ic_drv_interface.c @@ -0,0 +1,229 @@ +/* i2c_drv_interface.c + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#endif +#include "ic_drv_interface.h" +#include "drv_interface.h" +#include "ic_drv_global.h" +#if SELFTEST_2X +#include "f302_ic_control.h" +#include "f302_ic_test.h" +#endif +#if SELFTEST_3X +#include "f303_ic_control.h" +#include "f303_ic_test.h" +#endif +#if !SELFTEST +#include "f302_ic_test.h" +#include "f302_ic_test_ft.h" +#include "f303_ic_test.h" +#include "f303_ic_test_ft.h" +#include "ic_drv_global_ft.h" +#include "main.h" +#endif + +st_test_threshold g_st_test_thd; +st_test_info g_st_test_info; +st_test_para_resv g_st_test_para_resv; +unsigned char g_u8_ic_test_state; + +STATUS wearable_ic_test_read_info(void) +{ + if (read_flash_data(g_u32_dongle_flash_ini_addr, 16) != ERROR) { + memcpy(&g_st_test_info, g_u8_data_buf, sizeof(g_st_test_info)); + } else { + DEBUGOUT("Read Flash Data ERROR\r\n"); + return ERROR; + } + + return SUCCESS; +} + +STATUS wearable_ic_test_info_init(void) +{ +#if SELFTEST + g_u16_test_items_host_cmd = IC_TEST_ITEMS_SYSTEM | IC_TEST_ITEMS_OPEN | IC_TEST_ITEMS_SHORT | IC_TEST_ITEMS_UC | IC_TEST_ITEMS_UB | IC_TEST_ITEMS_BURN_CC; +#endif + + if (!wearable_ic_test_read_info()) { + DEBUGOUT("Read Info ERROR\r\n"); + return ERROR; + } + + if (g_u16_test_items_host_cmd != 0) + g_st_test_info.u16_ft_test_item &= g_u16_test_items_host_cmd; + g_u16_panel_jig_set_test_items |= g_st_test_info.u16_ft_test_item; + + DEBUGOUT("TestItem:0x%x:0x%x\r\n", g_st_test_info.u16_ft_test_item, g_st_test_info.u16_ft_eng_item); + DEBUGOUT("g_u16_panel_jig_set_test_items:0x%x\r\n", g_u16_panel_jig_set_test_items); + if (read_flash_data(g_u32_ini_threshold_addr, 36)) { + memcpy(&g_st_test_thd, g_u8_data_buf, sizeof(g_st_test_thd)); + } else { + DEBUGOUT("Read THD Data ERROR\r\n"); + return ERROR; + } + + DEBUGOUT("THD:\r\n%d,%d,%d,%d,\r\n%d,%d,%d,%d,\r\n%d,%d,%d,%d\r\n", + g_st_test_thd.i16_ft_test_open_lower_thd, + g_st_test_thd.i16_ft_test_short_upper_thd, + g_st_test_thd.i16_ft_test_short_lower_thd, + g_st_test_thd.i16_ft_test_single_cc_upper_thd, + g_st_test_thd.i16_ft_test_single_cc_lower_thd, + g_st_test_thd.i16_ft_test_uniformity_bl_upper_thd, + g_st_test_thd.i16_ft_test_uniformity_bl_lower_thd, + g_st_test_thd.i16_ft_test_uniformity_cc_upper_thd, + g_st_test_thd.i16_ft_test_uniformity_cc_lower_thd, + g_st_test_thd.i16_ft_test_panel_test_1_thd, + g_st_test_thd.i16_ft_test_panel_test_3_thd, + g_st_test_thd.i16_ft_test_panel_test_2_thd); + + if (read_flash_data(g_u32_ini_para_addr, 48)) { + memcpy(&g_st_test_para_resv, g_u8_data_buf, sizeof(g_st_test_para_resv)); + } else { + DEBUGOUT("Read INI Para ERROR\r\n"); + return ERROR; + } +/* + * DEBUGOUT(" g_st_test_para_resv.u32_normal_fw_version = %X ,g_st_test_para_resv.u32_test_fw_version= %X \r\n", + * g_st_test_para_resv.u32_normal_fw_version, + * g_st_test_para_resv.u32_test_fw_version + * ); + */ + + if (g_u16_dev_id == DEVICE_ID_3X) { + if (read_flash_data(g_u32_ini_raw_data_3_cc_addr, 128)) { + memcpy(g_u16_raw_data3_golden_cc_buf, g_u8_data_buf, sizeof(g_u16_raw_data3_golden_cc_buf)); + } else { + DEBUGOUT("read raw data 3 cc ERROR\r\n"); + return ERROR; + } +/* + * DEBUGOUT(" g_u16_raw_data3_golden_cc_buf[0] = %d,g_u16_raw_data3_golden_cc_buf[1 =%d g_u16_raw_data3_golden_cc_buf[2]=%d,\r\n", + * g_u16_raw_data3_golden_cc_buf[0], + * g_u16_raw_data3_golden_cc_buf[1], + * g_u16_raw_data3_golden_cc_buf[2] + * ); + */ + if (read_flash_data(g_u32_ini_uc_cc_addr, 128)) { + memcpy(g_u16_uc_golden_cc_buf, g_u8_data_buf, sizeof(g_u16_uc_golden_cc_buf)); + } else { + DEBUGOUT("read uc cc ERROR\r\n"); + return ERROR; + } + } else if (g_u16_dev_id == DEVICE_ID_2X) { + if (read_flash_data(g_u32_ini_raw_data_3_cc_addr, 72)) { + memcpy(g_u16_raw_data3_golden_cc_buf, g_u8_data_buf, sizeof(g_u16_raw_data3_golden_cc_buf)); + } else { + DEBUGOUT("read raw data 3 cc ERROR\r\n"); + return ERROR; + } +/* + * DEBUGOUT(" g_u16_raw_data3_golden_cc_buf[0] = %d,g_u16_raw_data3_golden_cc_buf[1 =%d g_u16_raw_data3_golden_cc_buf[2]=%d,\r\n", + * g_u16_raw_data3_golden_cc_buf[0], + * g_u16_raw_data3_golden_cc_buf[1], + * g_u16_raw_data3_golden_cc_buf[2] + * ); + */ + if (read_flash_data(g_u32_ini_uc_cc_addr, 72)) { + memcpy(g_u16_uc_golden_cc_buf, g_u8_data_buf, sizeof(g_u16_uc_golden_cc_buf)); + } else { + DEBUGOUT("read uc cc ERROR\r\n"); + return ERROR; + } + } +/* if (read_flash_data(INI_RAW_DATA2_BL_ADDR, 72)) + * memcpy(g_i16_raw_data2_golden_bl_buf, g_u8_data_buf, sizeof(g_i16_raw_data2_golden_bl_buf)); + */ + return SUCCESS; +} + +STATUS wearable_ic_test_init(void) +{ + if (!wearable_ic_test_info_init()) + return ERROR; + if (!(g_st_test_info.u16_ft_eng_item & IC_TEST_ENG_ITEMS_PANEL_TEST_JIG)) + wearable_ic_test_init_buffer(); + + return SUCCESS; +} + +void wearable_ic_test_init_buffer(void) +{ + g_u32_wearable_test_result = WEARABLE_FT_TEST_RESULT_IC_INIT_STATE; + memset(g_u32_test_result, 0, sizeof(g_u32_test_result)); + memset(g_u8_test_result, 0, sizeof(g_u8_test_result)); + g_u8_is_normal_fw = FALSE; +} + +STATUS handle_burn_log_to_flash(void) +{ + STATUS u8_status = ERROR; + + switch (g_u16_dev_id) { +#if (!SELFTEST_2X && (!SELFTEST)) + case DEVICE_ID_2X: + burn_data_log_to_flash_2x(); + break; +#endif +#if (!SELFTEST_3X && (!SELFTEST)) + case DEVICE_ID_3X: + b_is_auo_jig_testing = false; + burn_header_log_to_flash_3x(TRUE, TRUE); + memset(g_u32_save_config, 0, sizeof(g_u32_save_config)); + if (burn_data_log_to_flash_3x()) + u8_status = SUCCESS; + break; +#endif + } + return u8_status; +} + +void handle_ic_test(void) +{ + + switch (g_u16_dev_id) { +#if (SELFTEST_2X | (!SELFTEST)) + case DEVICE_ID_2X: + do_ic_test_2x(); + break; +#endif +#if (SELFTEST_3X | (!SELFTEST)) + case DEVICE_ID_3X: + do_ic_test_3x(); + break; +#endif + } +} + +void handle_set_display_interface(unsigned char u8_interface) +{ + g_u8_display_interface = u8_interface; + DEBUGOUT("%s: 0x%x \r\n", __func__, u8_interface); +} + diff --git a/raydium/chip_raydium/ic_drv_interface.h b/raydium/chip_raydium/ic_drv_interface.h new file mode 100644 index 0000000000..104ac162f7 --- /dev/null +++ b/raydium/chip_raydium/ic_drv_interface.h @@ -0,0 +1,211 @@ +/* i2c_drv_interface.h + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + +#include "ic_drv_global.h" + +#define DEVICE_ID_2X 0xF302 +#define DEVICE_ID_3X 0xF303 + +#define I2C_PDA2_BYTE_MODE 0x03 +#define I2C_PDA2_WORD_MODE 0x43 +#define I2C_BYTE_MODE (0x80|0x20) +#define I2C_WORD_MODE (0xc0|0x20) +#define I2C_MCU_MODE 0x40 +#define I2C_PDA2_MODE 0x10 +#define I2C_PDA_MODE 0x08 + +#define SPI_BYTE_MODE (0x80|0x03) +#define SPI_WORD_MODE (0xc0|0x03) +#define SPI_MCU_MODE 0x40 + +#define I2C_INTERFACE 0 +#define SPI_INTERFACE 1 + +#define DISPLAY_SPI_MODE 0 +#define DISPLAY_MIPI_MODE 1 +#define DISPLAY_TOUCH_2_DRIVER_MODE 2 + +#define DATA_REMAP_TO_IC_PIN 1 +#define DATA_REMAP_TO_SENSOR_PAD 2 + +#define IC_TEST_RETRY_TIME 3 +#define IC_TEST_TIME_OUT (-1) +#define FT_TEST_STUATUS_PATTERN 0x5A00 + +#define FT_CMD_INIT 0x11 +#define FT_CMD_DO_FT_TEST 0x14 + +#define IC_TEST_ITEMS_SYSTEM 0x0001 +#define IC_TEST_ITEMS_BURN_FW 0x0002 +#define IC_TEST_ITEMS_OPEN 0x0004 +#define IC_TEST_ITEMS_SHORT 0x0008 +#define IC_TEST_ITEMS_UC 0x0010 +#define IC_TEST_ITEMS_UB 0x0020 +#define IC_TEST_ITEMS_BURN_CC 0x0040 +#define IC_TEST_ITEMS_PANEL_TEST_1 0x0080 +#define IC_TEST_ITEMS_PANEL_TEST_2 0x0100 +#define IC_TEST_ITEMS_PANEL_TEST_3 0x0200 +#define IC_TEST_ITEMS_RESV_0 0x0400 +#define IC_TEST_ITEMS_RESV_1 0x0800 +#define IC_TEST_ITEMS_RESV_2 0x1000 +#define IC_TEST_ITEMS_RESV_3 0x2000 +#define IC_TEST_ITEMS_RESV_4 0x4000 +#define IC_TEST_ITEMS_RESV_5 0x8000 + +#define IC_TEST_ENG_ITEMS_RESV_1 0x0001 +#define IC_TEST_ENG_ITEMS_RESV_2 0x0002 +#define IC_TEST_ENG_ITEMS_RESV_3 0x0004 +#define IC_TEST_ENG_ITEMS_TEST_ALL 0x0008 +#define IC_TEST_ENG_ITEMS_RESV_5 0x0010 +#define IC_TEST_ENG_ITEMS_RESV_6 0x0020 +#define IC_TEST_ENG_ITEMS_RESV_7 0x0040 +#define IC_TEST_ENG_ITEMS_RESV_8 0x0080 +#define IC_TEST_ENG_ITEMS_PANEL_TEST_JIG 0x0100 +#define IC_TEST_ENG_ITEMS_RESV_10 0x0200 +#define IC_TEST_ENG_ITEMS_RESV_11 0x0400 +#define IC_TEST_ENG_ITEMS_RESV_12 0x0800 +#define IC_TEST_ENG_ITEMS_RESV_13 0x1000 +#define IC_TEST_ENG_ITEMS_RESV_14 0x2000 +#define IC_TEST_ENG_ITEMS_RESV_15 0x4000 +#define IC_TEST_ENG_ITEMS_RESV_16 0x8000 + +#define WEARABLE_FT_TEST_RESULT_IC_INIT_STATE 0xDFFFE5DF +#define WEARABLE_FT_TEST_RESULT_PANEL_INIT_STATE 0x20001A20 +#define WEARABLE_FT_TEST_RESULT_PASS 0x00000000 +#define WEARABLE_FT_TEST_RESULT_OPEN_NG 0x00000001 +#define WEARABLE_FT_TEST_RESULT_SHORT_NG 0x00000002 +#define WEARABLE_FT_TEST_RESULT_UB_NG 0x00000004 +#define WEARABLE_FT_TEST_RESULT_I2C_NG 0x00000008 +#define WEARABLE_FT_TEST_RESULT_INT_NG 0x00000010 +#define WEARABLE_FT_TEST_RESULT_PANEL_TEST_1_NG 0x00000020 +#define WEARABLE_FT_TEST_RESULT_RESET_NG 0x00000040 +#define WEARABLE_FT_TEST_RESULT_CB_NG 0x00000080 +#define WEARABLE_FT_TEST_RESULT_PRAM_NG 0x00000100 +#define WEARABLE_FT_TEST_RESULT_NORMAL_FW_NG 0x00000200 +#define WEARABLE_FT_TEST_RESULT_BURN_CC_NG 0x00000400 +#define WEARABLE_FT_TEST_RESULT_PANEL_TEST_3_NG 0x00000800 +#define WEARABLE_FT_TEST_RESULT_GET_DATA_NG 0x00001000 +#define WEARABLE_FT_TEST_RESULT_FLASH_ID_NG 0x00002000 +#define WEARABLE_FT_TEST_RESULT_NORMAL_FW_VER_NG 0x00004000 +#define WEARABLE_FT_TEST_RESULT_TEST_FW_VER_NG 0x00008000 +#define WEARABLE_FT_TEST_RESULT_UC_NG 0x00010000 +#define WEARABLE_FT_TEST_RESULT_SINGLE_CC_OPEN_NG 0x00020000 +#define WEARABLE_FT_TEST_RESULT_SINGLE_CC_SHORT_NG 0x00040000 +#define WEARABLE_FT_TEST_RESULT_CC_CALIBRATION_NG 0x00080000 +#define WEARABLE_FT_TEST_RESULT_TEST_INIT_NG 0x00100000 +#define WEARABLE_FT_TEST_RESULT_LOAD_TESTFW_NG 0x00200000 +#define WEARABLE_FT_TEST_RESULT_BURN_FW_NG 0x00400000 +#define WEARABLE_FT_TEST_RESULT_IC_FW_VERIFY_NG 0x00800000 +#define WEARABLE_FT_TEST_RESULT_EXT_FLASH_EMPTY_NG 0x01000000 +#define WEARABLE_FT_TEST_RESULT_MCU_HOLD_NG 0x02000000 +#if SELFTEST +#define WEARABLE_FT_TEST_RESULT_SYSFS_NG 0x04000000 +#else +#define WEARABLE_FT_TEST_RESULT_AUO_JIG_NG 0x04000000 +#endif +#define WEARABLE_FT_TEST_RESULT_NORMAL_FW_RESET_NG 0x08000000 +#define WEARABLE_FT_TEST_RESULT_POWER_ON_NG 0x10000000 +#define WEARABLE_FT_TEST_RESULT_CMD_NG 0x20000000 +#define WEARABLE_FT_TEST_RESULT_IC_SUB_VERSION_NG 0x40000000 +#define WEARABLE_FT_TEST_RESULT_IC_VERSION_NG 0x80000000 + +#define WEARBLE_FT_BURN_CC_SYSTEM_NG_CASE 1 +#define WEARBLE_FT_FW_VER_SYSTEM_NG_CASE 2 +#define WEARBLE_FT_I2C_SYSTEM_NG_CASE 3 +#define WEARBLE_FT_INT_SYSTEM_NG_CASE 4 +#define WEARBLE_FT_LOAD_TEST_FW_SYSTEM_NG_CASE 5 +#define WEARBLE_FT_PRAM_SYSTEM_NG_CASE 6 +#define WEARBLE_FT_RESET_PIN_SYSTEM_NG_CASE 7 + + +// Define ic test state +typedef enum { + IC_TEST_INIT = 0, + IC_TEST_SYSTEM, //1 + IC_TEST_BURN_FW, //2 + IC_TEST_BURN_CC, //3 + IC_TEST_LOAD_TEST_FW, //4 + IC_TEST_ENTER_FW_TEST_MODE, //5 + IC_TEST_OPEN_SHORT, //6 + IC_TEST_READ_TEST_FW_DATA, //7 + IC_ENTER_NORMAL_FW, //8 + IC_TEST_UC_UB, //9 + IC_TEST_PANEL_PATTERN_TEST, //10 + IC_TEST_EXIT, //11 +} IC_TEST_STATE; + +#pragma pack(push) +#pragma pack(1) +typedef struct { + //-------- FT Test Info (Byte 16)-----------------// + unsigned char u8_device_id; + unsigned char u8_company_id; + unsigned char u8_project_id; + unsigned char u8_station_id; + unsigned short u16_ft_test_item; + unsigned short u16_ft_eng_item; + unsigned short u16_ft_test_info_1; + unsigned short u16_ft_test_info_2; + unsigned short u16_ft_test_info_3; + unsigned short u16_ft_test_info_4; +} st_test_info; +extern st_test_info g_st_test_info; + +typedef struct { + //-------- FT Test Threshold (Byte 26)----------------// + unsigned char u8_ft_test_company_id; + unsigned char u8_ft_test_station_id; + signed short i16_ft_test_open_lower_thd; //TEST_THD_01 + signed short i16_ft_test_short_upper_thd; //TEST_THD_02 + signed short i16_ft_test_short_lower_thd; //TEST_THD_03 + signed short i16_ft_test_single_cc_upper_thd; //TEST_THD_04 + signed short i16_ft_test_single_cc_lower_thd; //TEST_THD_05 + signed short i16_ft_test_uniformity_bl_upper_thd; //TEST_THD_06 + signed short i16_ft_test_uniformity_bl_lower_thd; //TEST_THD_07 + signed short i16_ft_test_uniformity_cc_upper_thd; //TEST_THD_08 + signed short i16_ft_test_uniformity_cc_lower_thd; //TEST_THD_09 + signed short i16_ft_test_panel_test_1_thd; //TEST_THD_10 + signed short i16_ft_test_panel_test_3_thd; //TEST_THD_11 + signed short i16_ft_test_panel_test_2_thd; //TEST_THD_12 + //-------- reserve (Byte 10)-----------------// + signed short i16_ft_test_panel_test_2_s2_thd; //TEST_THD_13 + signed short i16_ft_test_thd_14; + signed short i16_ft_test_thd_15; + signed short i16_ft_test_thd_16; + signed short i16_ft_test_thd_resv; +} st_test_threshold; +extern st_test_threshold g_st_test_thd; + +typedef struct { + //-------- FT Test Parameter (Byte 48)-----------------// + unsigned char u8_para_resv_0[40]; + unsigned int u32_normal_fw_version; + unsigned int u32_test_fw_version; +} st_test_para_resv; +extern st_test_para_resv g_st_test_para_resv; + +#pragma pack(pop) + +extern unsigned char g_u8_ic_test_state; +extern STATUS wearable_ic_test_read_info(void); +extern STATUS wearable_ic_test_init(void); +extern void handle_ic_test(void); +extern void wearable_ic_test_init_buffer(void); +extern STATUS handle_burn_log_to_flash(void); +extern void handle_set_display_interface(unsigned char u8_interface); + diff --git a/raydium/drv_interface.c b/raydium/drv_interface.c new file mode 100644 index 0000000000..42af05c7fa --- /dev/null +++ b/raydium/drv_interface.c @@ -0,0 +1,437 @@ +/* drv_interface.c + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include "chip_raydium/ic_drv_global.h" +#include "chip_raydium/ic_drv_interface.h" +#include "drv_interface.h" +#include "raydium_selftest.h" +#include "raydium_driver.h" +#include "chip_raydium/f303_ic_control.h" + +struct timeval timer; +unsigned char g_u8_m_buf[2][128]; +unsigned char g_u8_ini_flash[0x400]; +struct raydium_ts_data *ts; +unsigned char g_u8_mute_i2c_err_log; + + +STATUS i2c_burst_read_pda2(unsigned char u8_addr, unsigned short u16ReadLen, unsigned char *p_u8_output_buf) +{ + return ERROR; +} +STATUS i2c_burst_write_pda2(unsigned char u8_addr, unsigned char bWriteLen, unsigned char *bValue) +{ + return ERROR; +} + +unsigned char spi_write_pda(unsigned int u32_addr, unsigned char u8_write_len, unsigned char *bValue, unsigned char u8_trans_mode) +{ + return ERROR; +} +unsigned char spi_read_pda(unsigned int u32_addr, unsigned char u8_read_len, unsigned char *p_u8_output_buf) +{ + return ERROR; +} +STATUS burn_fw_3x(void) +{ + g_u32_wearable_test_result &= ~(WEARABLE_FT_TEST_RESULT_CMD_NG | WEARABLE_FT_TEST_RESULT_BURN_FW_NG); + return SUCCESS; +} +unsigned char fw_upgrade(unsigned char type) +{ + int i32_ret = ERROR; + + i32_ret = raydium_burn_fw(g_raydium_ts->client); + if (i32_ret < 0) + pr_err("[touch]FW update fail:%d\n", i32_ret); + return i32_ret; + +} +unsigned char read_flash_data(unsigned int u32_addr, unsigned short u16_lenth) +{ + unsigned int u32_data_offset; + + if (g_u16_dev_id == DEVICE_ID_2X) { + u32_data_offset = u32_addr - 0x800; + + if (u32_addr < 0x8000) { + if (u32_data_offset >= 0x8000 && u32_addr < F302_DONGLE_FLASH_INI_ADDR) { + u32_data_offset = u32_data_offset - 0x6200; + memcpy(g_u8_data_buf, g_rad_para_image + u32_data_offset, u16_lenth); + } else if (u32_addr >= F302_DONGLE_FLASH_INI_ADDR) { + u32_data_offset = u32_addr - F302_DONGLE_FLASH_INI_ADDR; + memcpy(g_u8_data_buf, g_u8_ini_flash + u32_data_offset, u16_lenth); + } else + memcpy(g_u8_data_buf, g_rad_fw_image + u32_data_offset, u16_lenth); + } else { + u32_data_offset -= 0x8000; + if (u32_data_offset >= 0x6200 && u32_addr < F302_DONGLE_FLASH_INI_ADDR) { + u32_data_offset = u32_data_offset - 0x6200; + memcpy(g_u8_data_buf, g_rad_testpara_image + u32_data_offset, u16_lenth); + } else if (u32_addr >= F302_DONGLE_FLASH_INI_ADDR) { + u32_data_offset = u32_addr - F302_DONGLE_FLASH_INI_ADDR; + memcpy(g_u8_data_buf, g_u8_ini_flash + u32_data_offset, u16_lenth); + } else { + memcpy(g_u8_data_buf, g_rad_testfw_image + u32_data_offset, u16_lenth); + } + } + return TRUE; + } else if (g_u16_dev_id == DEVICE_ID_3X) { + u32_data_offset = u32_addr - 0x800; + + if (u32_addr < 0x7800) { + if (u32_data_offset >= 0x7300 && u32_addr < F303_DONGLE_FLASH_INI_ADDR) { + u32_data_offset = u32_data_offset - 0x7300; + memcpy(g_u8_data_buf, g_rad_para_image + u32_data_offset, u16_lenth); + } else + memcpy(g_u8_data_buf, g_rad_fw_image + u32_data_offset, u16_lenth); + } else { + u32_data_offset -= 0x7800; + if (u32_data_offset >= 0x7300 && u32_addr < F303_DONGLE_FLASH_INI_ADDR) { + u32_data_offset = u32_data_offset - 0x7300; + memcpy(g_u8_data_buf, g_rad_testpara_image + u32_data_offset, u16_lenth); + } else if (u32_addr >= F303_DONGLE_FLASH_INI_ADDR) { + u32_data_offset = u32_addr - F303_DONGLE_FLASH_INI_ADDR; + pr_info("ini addr 0x%x offset %d\r\n", u32_addr, u32_data_offset); + memcpy(g_u8_data_buf, g_u8_ini_flash + u32_data_offset, u16_lenth); + } else { + memcpy(g_u8_data_buf, g_rad_testfw_image + u32_data_offset, u16_lenth); + } + } + return TRUE; + } + return FALSE; +} + +unsigned int get_system_time(void) +{ +/* unsigned int u32_timer; + * do_gettimeofday(&timer); + * u32_timer = (timer.tv_sec % 1000) * 1000 + (timer.tv_usec / 1000); + * return u32_timer; + */ + #if (KERNEL_VERSION(5, 0, 0) <= LINUX_VERSION_CODE) + struct timespec ts; + + getnstimeofday(&ts); + return (ts.tv_sec*1000 + ts.tv_nsec/1000000); + #else + struct timeval tv; + + do_gettimeofday(&tv); + return (tv.tv_sec*1000 + tv.tv_usec/1000); + #endif +} + +unsigned char gpio_touch_int_pin_state_access(void) +{ + return gpio_get_value(ts->irq_gpio); +} + +unsigned char gpio_touch_int_access(unsigned char u8_is_clear_flag) +{ + unsigned char u8_flag = g_u8_raydium_flag; + + if (u8_is_clear_flag && u8_flag) + g_u8_raydium_flag &= ~INT_FLAG; + + return u8_flag; +} + +unsigned char sysfs_burn_cc_bl(void) +{ + unsigned char ret = ERROR; + + DEBUGOUT("%s\r\n", __func__); + ret = raydium_burn_comp(ts->client); + return ret; +} + +unsigned char raydium_upgrade_test_fw_2x(unsigned long ul_fw_addr) +{ + int ret = ERROR; + unsigned char u8_retry = 2; + unsigned int u32_read; + unsigned int u32_write; + +RETRY: + gpio_touch_reset_pin_control(0); + delay_ms(10); + gpio_touch_reset_pin_control(1); + delay_ms(2); + u32_write = 0x00000030; + handle_ic_write(0x50000918, 4, (unsigned char *)&u32_write, g_u8_drv_interface, I2C_WORD_MODE); + + if (raydium_load_test_fw(ts->client) == SUCCESS) { + ret = SUCCESS; + DEBUGOUT("### Raydium Load test FW SUCCESS ###\n"); + } + + handle_ic_read(0x6A04, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE); + + if (u32_read != g_st_test_para_resv.u32_test_fw_version) { + DEBUGOUT("Read FW version NG=0x%x:0x%x!!\r\n", u32_read, g_st_test_para_resv.u32_test_fw_version); + goto ERROR_EXIT; + } + + return ret; + +ERROR_EXIT: + if (u8_retry) { + u8_retry--; + goto RETRY; + } + + return ERROR; +} + +unsigned char raydium_upgrade_test_fw_3x(unsigned long ul_fw_addr) +{ + int ret = ERROR; + unsigned char u8_retry = 2; + unsigned int u32_read; + +RETRY: + + if (raydium_load_test_fw(ts->client) == SUCCESS) { + ret = SUCCESS; + DEBUGOUT("### Raydium Load test FW SUCCESS ###\n"); + } + + handle_ic_read(0x7B04, 4, (unsigned char *)&u32_read, g_u8_drv_interface, I2C_WORD_MODE); + if (u32_read != g_st_test_para_resv.u32_test_fw_version) { + DEBUGOUT("Read FW version NG=0x%x:0x%x!!\r\n", u32_read, g_st_test_para_resv.u32_test_fw_version); + goto ERROR_EXIT; + } + + return ret; + +ERROR_EXIT: + if (u8_retry) { + u8_retry--; + goto RETRY; + } + + return ERROR; +} + +void gpio_touch_reset_pin_control(unsigned char u8_high) +{ + + if (u8_high) + gpio_set_value(ts->rst_gpio, 1); + else + gpio_set_value(ts->rst_gpio, 0); + + return; + +} + + +void gpio_touch_hw_reset(void) +{ + gpio_touch_reset_pin_control(0); + delay_ms(10); + gpio_touch_reset_pin_control(1); + +} +void set_raydium_ts_data(struct raydium_ts_data *ts_old) +{ + ts = ts_old; +} + +/****************************************************************************** + **Function name:handle_ic_read + ** + **Descriptions:handle read data from ic + ** + **parameters:u32_addr,Address + ** u8_read_len,Read datalength + ** p_u8_output_buf,Data buffer + ** u8_interface,SPI or I2C + ** u8_trans_modePDA2_MODE, PDA_WORD_MODE, PDA_BYTE_MODE, MCU_MODE + ** + **Returned value:ERROR,SUCCESS + ** + ****************************************************************************** + */ +unsigned char handle_ic_read( + unsigned int u32_addr, + unsigned short u8_read_len, + unsigned char *p_u8_output_buf, + unsigned char u8_interface, + unsigned char u8_trans_mode) +{ + if (u8_trans_mode == I2C_PDA2_MODE) { + /*PDA2 MODE */ + if (raydium_i2c_pda2_read(g_raydium_ts->client, (unsigned char)u32_addr, p_u8_output_buf, u8_read_len) == ERROR) { + DEBUGOUT("%s\r\n", __func__); + return ERROR; + } + } else { + /*PDA MODE*/ + if ((u8_trans_mode == I2C_WORD_MODE) && (u32_addr & 0x00000003)) { + DEBUGOUT("[HRW] Handle Read Word ADDR Not Word Align!!\r\n"); + return ERROR; + } + + if (u8_interface == SPI_INTERFACE) { + if (spi_read_pda(u32_addr, u8_read_len, p_u8_output_buf) == ERROR) { + DEBUGOUT("%s\r\n", __func__); + return ERROR; + } + } else { + if (g_u16_dev_id == DEVICE_ID_3X) { + if ((u8_interface & I2C_PDA_MODE) != 0) { + if (raydium_i2c_pda_read(g_raydium_ts->client, u32_addr, p_u8_output_buf, u8_read_len) == ERROR) { + DEBUGOUT("%s\r\n", __func__); + return ERROR; + } + } else { + if (raydium_i2c_read_pda_via_pda2(g_raydium_ts->client, u32_addr, p_u8_output_buf, u8_read_len) == ERROR) { + DEBUGOUT("%s\r\n", __func__); + return ERROR; + } + } + } else { + if (raydium_i2c_pda_read(g_raydium_ts->client, u32_addr, p_u8_output_buf, u8_read_len) == ERROR) { + DEBUGOUT("%s\r\n", __func__); + return ERROR; + } + } + } + } + + return SUCCESS; +} + +/****************************************************************************** + ** Function name:handle_ic_write + ** + ** Descriptions:handle write data to ic + ** + ** parameters:u32_addr,Address + ** u8_write_len,datalength + ** bValue,Data + ** u8_interface,SPI or I2C + ** u8_trans_modePDA2_MODE, PDA_WORD_MODE, PDA_BYTE_MODE, MCU_MODE + ** + ** Returned value:ERROR,SUCCESS + ** + ******************************************************************************/ +unsigned char handle_ic_write( + unsigned int u32_addr, + unsigned char u8_write_len, + unsigned char *bValue, + unsigned char u8_interface, + unsigned char u8_trans_mode) +{ + if (u8_trans_mode == I2C_PDA2_MODE) { + /*PDA2 MODE*/ + if (raydium_i2c_pda2_write(g_raydium_ts->client, (unsigned char)u32_addr, bValue, u8_write_len) == ERROR) { + DEBUGOUT("%s\r\n", __func__); + return ERROR; + } + } else { + /*PDA MODE*/ + if ((u8_trans_mode == I2C_WORD_MODE) && (u32_addr & 0x00000003)) { + DEBUGOUT("[I2CRW] Handle Write Word ADDR Not Word Align!!\r\n"); + return ERROR; + } + + if (u8_interface == SPI_INTERFACE) { + switch (u8_trans_mode) { + case I2C_BYTE_MODE: + u8_trans_mode = SPI_BYTE_MODE; + break; + case I2C_WORD_MODE: + u8_trans_mode = SPI_WORD_MODE; + break; + /* case I2C_MCU_MODE: + * + * u8_trans_mode = SPI_MCU_MODE; + * + * break; + */ + case SPI_BYTE_MODE: + case SPI_WORD_MODE: + break; + default: + DEBUGOUT("%s%d\r\n", __func__, u8_trans_mode); + return ERROR; + } + + if (spi_write_pda(u32_addr, u8_write_len, bValue, u8_trans_mode) == ERROR) { + DEBUGOUT("%s\r\n", __func__); + return ERROR; + } + } else { + if (g_u16_dev_id == DEVICE_ID_3X) { + if ((u8_interface & I2C_PDA_MODE) != 0) { + if (raydium_i2c_pda_write(g_raydium_ts->client, u32_addr, bValue, u8_write_len) == ERROR) { + DEBUGOUT("%s\r\n", __func__); + return ERROR; + } + } else { + if (raydium_i2c_write_pda_via_pda2(g_raydium_ts->client, u32_addr, bValue, u8_write_len) == ERROR) { + DEBUGOUT("%s\r\n", __func__); + return ERROR; + } + } + } else { + if (raydium_i2c_pda_write(g_raydium_ts->client, u32_addr, bValue, u8_write_len) == ERROR) { + DEBUGOUT("%s\r\n", __func__); + return ERROR; + } + } + } + } + + return SUCCESS; +} + +/****************************************************************************** + ** Function name:handle_display_write + ** + ** Descriptions:handle write data to display + ** + ** parameters:p_u8_data,Data + ** u16DataLength,datalength + ** + ** + ** Returned value:ERROR,SUCCESS + ** + ******************************************************************************/ +unsigned char handle_display_write( + unsigned char *p_u8_data, + unsigned short u16DataLength) +{ + + if (WriteDriverByTouchMode(p_u8_data, u16DataLength) == ERROR) { + DEBUGOUT("[HDW] WriteDriverByTouchMode NG!\r\n"); + return ERROR; + } + + return SUCCESS; +} diff --git a/raydium/drv_interface.h b/raydium/drv_interface.h new file mode 100644 index 0000000000..12bacc96d4 --- /dev/null +++ b/raydium/drv_interface.h @@ -0,0 +1,86 @@ +/* drv_interface.h + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + +#include "Config.h" +#include "raydium_driver.h" + +#define DEBUGOUT pr_info +#define delay_ms msleep + +#define I2C_INTERFACE 0 +#define SPI_INTERFACE 1 + +#define M_RX_BUF 1 +#define M_TX_BUF 0 + +#define __I volatile +#define __O volatile +#define __IO volatile + +#define FALSE 0x00 +#define TRUE 0x01 + +#define WORD 4 + +#define RAD_CMD_START_BURN 0x01 +#define RAD_CMD_LOAD_TESTFW 0x04 +#define RAD_CMD_UPDATE_BIN 0x80 +#define RAD_CMD_UPDATE_END 0x81 +#define RAD_CMD_BURN_FINISH 0x82 + +#define WEARABLE_FT_TEST_RESULT_AUO_JIG_NG WEARABLE_FT_TEST_RESULT_SYSFS_NG + + +extern unsigned char g_u8_m_buf[2][128]; +extern struct timeval timer; +extern unsigned char g_rad_fw_flash[32768], g_rad_testfw_flash[32768]; +extern unsigned char g_u8_ini_flash[0x400]; +extern unsigned long g_u32_save_config[64]; +extern unsigned char g_u8_mute_i2c_err_log; +extern short g_i16_raw_data_frame_buffer[101][50]; +extern unsigned char g_u8_test_log_burn_times; + +extern unsigned char read_flash_data(unsigned int u32_addr, unsigned short u16_lenth); +extern unsigned int get_system_time(void); + +extern unsigned char sysfs_mode_control(unsigned char u8_type); +//extern unsigned char sysfs_read_int_flag(unsigned char *p_u8_flag, unsigned char u8_is_clear_flag); +extern unsigned char sysfs_write_int_flag(char *p_cmd); +//extern unsigned char sysfs_i2c_read(char *p_u8_addr, char u8_len, unsigned char *p_u8_data); +//extern unsigned char sysfs_i2c_write(char *p_u8_addr, char u8_len, unsigned char *p_u8_data); +extern unsigned char sysfs_do_cal(void); +extern unsigned char sysfs_burn_cc_bl(void); + +extern void gpio_touch_reset_pin_control(unsigned char u8_high); +extern void gpio_touch_hw_reset(void); +extern unsigned char fw_upgrade(unsigned char type); +extern unsigned char gpio_touch_int_access(unsigned char u8_is_clear_flag); +extern unsigned char gpio_touch_int_pin_state_access(void); + +extern unsigned char handle_read_word(unsigned int addr, unsigned int *p_data); +extern unsigned char handle_write_word(unsigned int u32_addr, unsigned int u32_data); +extern unsigned char handle_read_pda(unsigned int u32_addr, unsigned char u8_read_len, unsigned char *p_u8_output_buf); +extern unsigned char handle_write_pda(unsigned int u32_addr, unsigned char u8_write_len, unsigned char *bValue, unsigned char u8_trans_mode); +extern unsigned char handle_ic_read(unsigned int u32_addr, unsigned short u8_read_len, unsigned char *p_u8_output_buf, unsigned char u8_interface, unsigned char u8_trans_mode); +extern unsigned char handle_ic_write(unsigned int u32_addr, unsigned char u8_write_len, unsigned char *bValue, unsigned char u8_interface, unsigned char u8_trans_mode); + +extern unsigned char raydium_upgrade_test_fw(unsigned long ul_fw_addr); +extern unsigned char raydium_upgrade_test_fw_3x(unsigned long ul_fw_addr); +extern unsigned char raydium_upgrade_test_fw_2x(unsigned long ul_fw_addr); +extern void set_raydium_ts_data(struct raydium_ts_data *ts_old); + diff --git a/raydium/rad_fw_image_30.h b/raydium/rad_fw_image_30.h new file mode 100644 index 0000000000..1f33fe207a --- /dev/null +++ b/raydium/rad_fw_image_30.h @@ -0,0 +1,7757 @@ +/* rad_fw_image_30.h + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + +#define RAD_30 0x3202 +const unsigned char u8_rad_boot_30[] = { +0x18, 0x01, 0x00, 0x20, 0x95, 0x01, 0x00, 0x00, +0x99, 0x01, 0x00, 0x00, 0x9B, 0x01, 0x00, 0x00, +0x9D, 0x01, 0x00, 0x00, 0x9F, 0x01, 0x00, 0x00, +0xA1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xA3, 0x01, 0x00, 0x00, +0xA5, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xA7, 0x01, 0x00, 0x00, 0x81, 0x0F, 0x00, 0x00, +0xE1, 0x0C, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00, +0xA1, 0x0F, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x00, +0xA1, 0x0D, 0x00, 0x00, 0x41, 0x0A, 0x00, 0x00, +0xAB, 0x01, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00, +0xAB, 0x01, 0x00, 0x00, 0x41, 0x0B, 0x00, 0x00, +0xE1, 0x0A, 0x00, 0x00, 0xA1, 0x0A, 0x00, 0x00, +0xC1, 0x0A, 0x00, 0x00, 0x81, 0x0A, 0x00, 0x00, +0xF1, 0x0F, 0x00, 0x00, 0xAB, 0x01, 0x00, 0x00, +0x03, 0xF3, 0x00, 0x0B, 0x03, 0x48, 0x85, 0x46, +0x00, 0xF0, 0x92, 0xF8, 0x00, 0x48, 0x00, 0x47, +0xCB, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x20, +0x80, 0xF3, 0x08, 0x88, 0x70, 0x47, 0x00, 0x00, +0x10, 0xB5, 0x25, 0x48, 0x81, 0x69, 0x01, 0x22, +0x92, 0x03, 0x11, 0x43, 0x81, 0x61, 0x41, 0x69, +0x10, 0x22, 0x11, 0x43, 0x41, 0x61, 0xD4, 0x01, +0x20, 0x68, 0xFF, 0xF7, 0xED, 0xFF, 0x60, 0x68, +0x1E, 0x49, 0x48, 0x60, 0x08, 0x60, 0x80, 0x47, +0x10, 0xBD, 0x01, 0x26, 0xB6, 0x07, 0x30, 0x68, +0x1B, 0x49, 0x08, 0x43, 0x30, 0x60, 0x18, 0x49, +0x48, 0x69, 0x20, 0x22, 0x10, 0x43, 0x48, 0x61, +0x00, 0xF0, 0x34, 0xF8, 0x00, 0xF0, 0xA5, 0xF8, +0x16, 0x4F, 0x17, 0x4C, 0x01, 0x25, 0x60, 0x68, +0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x16, 0xD0, +0x02, 0x28, 0xF8, 0xD1, 0xFF, 0xF7, 0xD0, 0xFF, +0xF5, 0xE7, 0x00, 0xF0, 0x5F, 0xF9, 0x03, 0x28, +0x07, 0xD0, 0x02, 0x28, 0xEF, 0xD1, 0x65, 0x60, +0x7D, 0x20, 0xC0, 0x00, 0x00, 0xF0, 0x84, 0xF8, +0xE9, 0xE7, 0xB8, 0x68, 0x00, 0x28, 0xE6, 0xD1, +0x02, 0x20, 0x60, 0x60, 0xE3, 0xE7, 0x00, 0xF0, +0x91, 0xFA, 0x01, 0x28, 0xDF, 0xD1, 0x00, 0x20, +0x60, 0x60, 0x75, 0x60, 0xDB, 0xE7, 0x00, 0x00, +0x00, 0x09, 0x00, 0x50, 0x0C, 0x00, 0x00, 0x20, +0x40, 0x01, 0x00, 0xC8, 0x10, 0x02, 0x00, 0x20, +0x00, 0x02, 0x00, 0x20, 0x0D, 0x48, 0x0C, 0x49, +0x01, 0x61, 0x0D, 0x4A, 0x03, 0x21, 0xD1, 0x61, +0x01, 0x21, 0x89, 0x07, 0x0A, 0x68, 0x0B, 0x4B, +0x1A, 0x43, 0x0A, 0x60, 0x0A, 0x4A, 0x02, 0x63, +0x02, 0x6A, 0x52, 0x00, 0x52, 0x08, 0x02, 0x62, +0xC2, 0x6A, 0x04, 0x23, 0x1A, 0x43, 0xC2, 0x62, +0x10, 0x20, 0x48, 0x60, 0x70, 0x47, 0x00, 0x00, +0x00, 0x00, 0x06, 0x07, 0x00, 0x06, 0x00, 0x50, +0x00, 0x0E, 0x00, 0x50, 0x10, 0x01, 0x00, 0x80, +0x0A, 0x66, 0x00, 0x00, 0x05, 0x48, 0x00, 0x47, +0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, +0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, +0xFE, 0xE7, 0xFE, 0xE7, 0x85, 0x00, 0x00, 0x00, +0x06, 0x4C, 0x01, 0x25, 0x06, 0x4E, 0x05, 0xE0, +0xE3, 0x68, 0x07, 0xCC, 0x2B, 0x43, 0x0C, 0x3C, +0x98, 0x47, 0x10, 0x34, 0xB4, 0x42, 0xF7, 0xD3, +0xFF, 0xF7, 0x60, 0xFF, 0xDC, 0x06, 0x00, 0x00, +0xFC, 0x06, 0x00, 0x00, 0x30, 0xB4, 0x74, 0x46, +0x64, 0x1E, 0x25, 0x78, 0x64, 0x1C, 0xAB, 0x42, +0x00, 0xD2, 0x1D, 0x46, 0x63, 0x5D, 0x5B, 0x00, +0xE3, 0x18, 0x30, 0xBC, 0x18, 0x47, 0x02, 0xE0, +0x08, 0xC8, 0x12, 0x1F, 0x08, 0xC1, 0x00, 0x2A, +0xFA, 0xD1, 0x70, 0x47, 0x70, 0x47, 0x00, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x49, 0x00, 0x20, +0x88, 0x60, 0xC8, 0x60, 0x48, 0x60, 0x3F, 0x22, +0x0A, 0x60, 0x08, 0x61, 0xFB, 0x49, 0x08, 0x60, +0xFB, 0x49, 0x08, 0x60, 0x48, 0x60, 0x70, 0x47, +0xE1, 0x21, 0x09, 0x01, 0x48, 0x43, 0x00, 0x21, +0x00, 0xE0, 0x49, 0x1C, 0x81, 0x42, 0xFC, 0xD3, +0x70, 0x47, 0x10, 0xB5, 0xF3, 0x49, 0x00, 0x20, +0x48, 0x60, 0x88, 0x60, 0xC8, 0x60, 0xF2, 0x4B, +0x98, 0x60, 0xEF, 0x4C, 0xA0, 0x69, 0x01, 0x21, +0x89, 0x03, 0x88, 0x43, 0xA0, 0x61, 0xFF, 0xF7, +0xD9, 0xFF, 0xA0, 0x69, 0x00, 0x05, 0x05, 0xD5, +0x82, 0x20, 0x99, 0x68, 0x00, 0x29, 0x01, 0xD1, +0x58, 0x60, 0xFA, 0xE7, 0x10, 0xBD, 0x70, 0xB5, +0x05, 0x46, 0x00, 0x20, 0x02, 0x46, 0x01, 0x26, +0x09, 0xE0, 0x34, 0x46, 0x94, 0x40, 0x33, 0x46, +0x2C, 0x42, 0x03, 0xD0, 0x8C, 0x1A, 0x64, 0x1E, +0xA3, 0x40, 0x18, 0x43, 0x52, 0x1C, 0x8A, 0x42, +0xF3, 0xD3, 0x70, 0xBD, 0xF0, 0xB5, 0x00, 0x24, +0xE4, 0x43, 0x0E, 0x46, 0x07, 0x46, 0x00, 0x25, +0x14, 0xE0, 0x78, 0x5D, 0x08, 0x21, 0xFF, 0xF7, +0xE2, 0xFF, 0xC1, 0xB2, 0xD9, 0x4A, 0x00, 0x20, +0x23, 0x46, 0x4B, 0x40, 0xDB, 0x07, 0x02, 0xD0, +0x64, 0x08, 0x54, 0x40, 0x00, 0xE0, 0x64, 0x08, +0x40, 0x1C, 0x49, 0x08, 0xC0, 0xB2, 0x08, 0x28, +0xF2, 0xD3, 0x6D, 0x1C, 0xB5, 0x42, 0xE8, 0xD3, +0x20, 0x21, 0x20, 0x46, 0xFF, 0xF7, 0xCB, 0xFF, +0xF0, 0xBD, 0x1F, 0x20, 0x80, 0x01, 0xCE, 0x49, +0xC0, 0x6B, 0x08, 0x60, 0xC8, 0x49, 0x80, 0x31, +0x0A, 0x68, 0x82, 0x42, 0x15, 0xD1, 0xCB, 0x48, +0xCB, 0x4A, 0x40, 0x6B, 0x10, 0x60, 0x4A, 0x68, +0x82, 0x42, 0x0E, 0xD1, 0xC9, 0x48, 0xCA, 0x4A, +0xC0, 0x6B, 0x10, 0x60, 0x8A, 0x68, 0x82, 0x42, +0x07, 0xD1, 0xC6, 0x48, 0xC7, 0x4A, 0x80, 0x30, +0xC0, 0x6B, 0x10, 0x60, 0xC9, 0x68, 0x81, 0x42, +0x01, 0xD0, 0x00, 0x20, 0x70, 0x47, 0x01, 0x20, +0x70, 0x47, 0x70, 0xB5, 0x0D, 0x46, 0x03, 0x46, +0xC1, 0x4A, 0xB7, 0x4E, 0x02, 0xE0, 0x01, 0x20, +0xFF, 0xF7, 0x7A, 0xFF, 0x74, 0x69, 0x1C, 0x40, +0xAC, 0x42, 0x04, 0xD0, 0x10, 0x46, 0x52, 0x1E, +0x92, 0xB2, 0x00, 0x28, 0xF3, 0xD1, 0x00, 0x2A, +0x01, 0xD0, 0x01, 0x20, 0x70, 0xBD, 0x00, 0x20, +0x70, 0xBD, 0xF0, 0xB5, 0x00, 0x23, 0x01, 0x24, +0xB6, 0x4D, 0xE4, 0x02, 0x19, 0x46, 0x1A, 0x46, +0x01, 0x28, 0x0E, 0xD0, 0x04, 0x28, 0x05, 0xD0, +0x10, 0x28, 0x0E, 0xD1, 0x03, 0x02, 0xB2, 0x4A, +0x21, 0x46, 0x0A, 0xE0, 0x09, 0x23, 0x61, 0x22, +0xA8, 0x49, 0x1B, 0x03, 0xD2, 0x00, 0x38, 0x31, +0x03, 0xE0, 0x80, 0x22, 0xFF, 0x21, 0x23, 0x46, +0xC9, 0x01, 0x9F, 0x4C, 0x20, 0x68, 0x1D, 0x26, +0xB0, 0x43, 0x20, 0x60, 0x23, 0x61, 0x40, 0x27, +0x11, 0xE0, 0x60, 0x69, 0x38, 0x43, 0x60, 0x61, +0x28, 0x46, 0x63, 0x69, 0x5B, 0x06, 0x04, 0xD5, +0x03, 0x46, 0x40, 0x1E, 0x80, 0xB2, 0x00, 0x2B, +0xF7, 0xD1, 0xE0, 0x6B, 0x01, 0xC1, 0x20, 0x69, +0x00, 0x1D, 0x20, 0x61, 0x12, 0x1F, 0x00, 0x2A, +0xEB, 0xD1, 0x20, 0x68, 0x30, 0x43, 0x20, 0x60, +0x01, 0x20, 0xF0, 0xBD, 0xF0, 0xB5, 0x8F, 0x4B, +0x98, 0x68, 0x01, 0x25, 0x8C, 0x4C, 0x00, 0x28, +0x09, 0xD0, 0x80, 0x27, 0x8C, 0x4E, 0x01, 0x28, +0x0F, 0xD0, 0x00, 0x21, 0x02, 0x28, 0x1E, 0xD0, +0x03, 0x28, 0x1F, 0xD1, 0x24, 0xE0, 0xA0, 0x69, +0x80, 0x04, 0x1B, 0xD4, 0xA0, 0x69, 0x00, 0x05, +0x01, 0xD5, 0x9D, 0x60, 0x16, 0xE0, 0x03, 0x20, +0x0F, 0xE0, 0x1D, 0x60, 0xA0, 0x20, 0x30, 0x60, +0x81, 0x20, 0x70, 0x60, 0xFF, 0xF7, 0x65, 0xFF, +0x00, 0x28, 0x00, 0xD1, 0x77, 0x60, 0xA0, 0x69, +0x01, 0x21, 0xC9, 0x02, 0x88, 0x43, 0xA0, 0x61, +0x02, 0x20, 0x98, 0x60, 0x02, 0xE0, 0x18, 0x68, +0x00, 0x28, 0x01, 0xD0, 0x00, 0x20, 0xF0, 0xBD, +0x99, 0x60, 0xFF, 0xF7, 0xEB, 0xFE, 0x27, 0xE0, +0x99, 0x60, 0xA0, 0x69, 0xC0, 0x06, 0x02, 0xD5, +0xA0, 0x69, 0x80, 0x05, 0x15, 0xD5, 0xFF, 0xF7, +0x48, 0xFF, 0x00, 0x28, 0x1C, 0xD1, 0x10, 0x20, +0xFF, 0xF7, 0x7F, 0xFF, 0x04, 0x20, 0xFF, 0xF7, +0x7C, 0xFF, 0x01, 0x20, 0xFF, 0xF7, 0x79, 0xFF, +0xA0, 0x69, 0x80, 0x05, 0x12, 0xD4, 0x21, 0x20, +0x00, 0x01, 0xA0, 0x61, 0x80, 0x06, 0x45, 0x60, +0x0C, 0xE0, 0xA0, 0x69, 0x40, 0x05, 0xA0, 0x69, +0x04, 0xD5, 0x01, 0x21, 0x89, 0x02, 0x88, 0x43, +0xA0, 0x61, 0x01, 0xE0, 0x40, 0x06, 0x01, 0xD5, +0x03, 0x20, 0xF0, 0xBD, 0x77, 0x60, 0x02, 0x20, +0xF0, 0xBD, 0xF0, 0xB5, 0x69, 0x4F, 0x00, 0x26, +0x5B, 0x4C, 0x3E, 0x70, 0x21, 0x68, 0x62, 0x68, +0x91, 0x43, 0x21, 0x60, 0x04, 0x28, 0x2C, 0xD0, +0x10, 0x28, 0x27, 0xD0, 0x20, 0x28, 0x2C, 0xD1, +0x5A, 0x48, 0xC1, 0x21, 0x89, 0x00, 0x38, 0x30, +0xFF, 0xF7, 0xEC, 0xFE, 0x59, 0x4D, 0xE9, 0x6B, +0x88, 0x42, 0x18, 0xD0, 0x20, 0x68, 0x04, 0x21, +0x88, 0x43, 0x20, 0x60, 0x00, 0x20, 0x41, 0x1E, +0x1F, 0x22, 0x92, 0x02, 0x83, 0x00, 0x9B, 0x18, +0x40, 0x1C, 0xC0, 0xB2, 0x99, 0x67, 0xC1, 0x28, +0xF8, 0xD3, 0x4E, 0x48, 0xC1, 0x21, 0x89, 0x00, +0x38, 0x30, 0xAE, 0x63, 0xFF, 0xF7, 0xD2, 0xFE, +0xE8, 0x63, 0x01, 0x20, 0x38, 0x70, 0x20, 0x68, +0x10, 0x21, 0x04, 0xE0, 0x20, 0x68, 0x08, 0x21, +0x01, 0xE0, 0x20, 0x68, 0x02, 0x21, 0x88, 0x43, +0x20, 0x60, 0xF0, 0xBD, 0xF0, 0xB5, 0x3E, 0x4C, +0x0F, 0x46, 0x05, 0x46, 0xC6, 0x19, 0x20, 0x61, +0x0E, 0xE0, 0x60, 0x69, 0x02, 0x21, 0x08, 0x43, +0x60, 0x61, 0x08, 0x21, 0x08, 0x46, 0xFF, 0xF7, +0xFC, 0xFE, 0x00, 0x28, 0x16, 0xD0, 0x21, 0x69, +0x01, 0x20, 0x00, 0x03, 0x08, 0x18, 0x20, 0x61, +0x20, 0x69, 0xB0, 0x42, 0xED, 0xD3, 0x25, 0x61, +0x38, 0x0A, 0x00, 0x02, 0xE0, 0x60, 0x60, 0x69, +0x04, 0x21, 0x08, 0x43, 0x60, 0x61, 0x08, 0x21, +0x08, 0x46, 0xFF, 0xF7, 0xE6, 0xFE, 0x00, 0x28, +0x00, 0xD0, 0x01, 0x20, 0xF0, 0xBD, 0x2A, 0x4A, +0x52, 0x68, 0x04, 0x2A, 0x0D, 0xD0, 0x10, 0x2A, +0x06, 0xD0, 0x20, 0x2A, 0x0F, 0xD1, 0x00, 0x22, +0x02, 0x60, 0x11, 0x20, 0xC0, 0x01, 0x09, 0xE0, +0x01, 0x22, 0x12, 0x03, 0x02, 0x60, 0x2E, 0x48, +0x04, 0xE0, 0x09, 0x22, 0x12, 0x03, 0x02, 0x60, +0x61, 0x20, 0xC0, 0x00, 0x08, 0x60, 0x70, 0x47, +0xF0, 0xB5, 0x2B, 0x4C, 0x00, 0x25, 0x20, 0x68, +0x01, 0x23, 0x1D, 0x4E, 0x1B, 0x4F, 0x00, 0x28, +0x04, 0xD0, 0x01, 0x28, 0x0D, 0xD0, 0x02, 0x28, +0x14, 0xD1, 0x15, 0xE0, 0x24, 0x49, 0x08, 0x31, +0x08, 0x1F, 0xFF, 0xF7, 0xD4, 0xFF, 0x75, 0x60, +0xA8, 0x20, 0x30, 0x60, 0x23, 0x60, 0x3B, 0x60, +0x08, 0xE0, 0x38, 0x68, 0x00, 0x28, 0x05, 0xD1, +0x3B, 0x60, 0x70, 0x68, 0xA8, 0x28, 0x01, 0xD1, +0x02, 0x20, 0x20, 0x60, 0x00, 0x20, 0xF0, 0xBD, +0xA1, 0x68, 0x60, 0x68, 0x49, 0x1E, 0xFF, 0xF7, +0x95, 0xFF, 0x00, 0x28, 0xF7, 0xD0, 0x15, 0x48, +0x07, 0x4E, 0x00, 0x78, 0x01, 0x28, 0x08, 0xD1, +0x0A, 0x48, 0x38, 0x30, 0xB0, 0x60, 0x13, 0x49, +0x30, 0x01, 0xFF, 0xF7, 0x87, 0xFF, 0x00, 0x28, +0xE9, 0xD0, 0x75, 0x63, 0xB5, 0x63, 0x1F, 0xE0, +0x00, 0x09, 0x00, 0x50, 0x00, 0x02, 0x00, 0x20, +0x10, 0x02, 0x00, 0x20, 0x20, 0x83, 0xB8, 0xED, +0x98, 0x01, 0x00, 0x20, 0x40, 0x7C, 0x00, 0x00, +0x9C, 0x01, 0x00, 0x20, 0x40, 0x7F, 0x00, 0x00, +0xA0, 0x01, 0x00, 0x20, 0xA4, 0x01, 0x00, 0x20, +0xB8, 0x0B, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, +0x78, 0x74, 0x00, 0x00, 0x14, 0x00, 0x00, 0x20, +0x00, 0x00, 0x00, 0x20, 0x07, 0x03, 0x00, 0x00, +0x01, 0x20, 0xC0, 0x02, 0xB0, 0x61, 0x01, 0x20, +0x25, 0x60, 0xF0, 0xBD, 0xF0, 0xB5, 0x1C, 0x4C, +0x00, 0x27, 0xE0, 0x68, 0x01, 0x25, 0x1B, 0x4E, +0x03, 0x00, 0xFF, 0xF7, 0xBB, 0xFD, 0x07, 0x05, +0x2E, 0x0F, 0x2E, 0x2E, 0x1B, 0x28, 0x2E, 0x00, +0xB0, 0x68, 0x01, 0x28, 0x25, 0xD1, 0xA1, 0x20, +0x30, 0x60, 0xFF, 0x20, 0x70, 0x60, 0x25, 0x60, +0x02, 0x20, 0x1D, 0xE0, 0x20, 0x68, 0x00, 0x28, +0x1B, 0xD1, 0x25, 0x60, 0x10, 0x48, 0x40, 0x68, +0xFF, 0xF7, 0xFF, 0xFE, 0xA3, 0x20, 0x30, 0x60, +0x05, 0x20, 0x11, 0xE0, 0xFF, 0xF7, 0x78, 0xFF, +0x00, 0x28, 0x0E, 0xD0, 0xB0, 0x68, 0x00, 0x28, +0x01, 0xD0, 0x25, 0x60, 0x01, 0xE0, 0x00, 0x20, +0x20, 0x60, 0x06, 0x20, 0x04, 0xE0, 0x20, 0x68, +0x00, 0x28, 0x02, 0xD1, 0x25, 0x60, 0x01, 0x27, +0xE0, 0x60, 0x38, 0x46, 0xF0, 0xBD, 0x00, 0x00, +0x00, 0x02, 0x00, 0x20, 0x10, 0x02, 0x00, 0x20, +0x00, 0x09, 0x00, 0x50, 0x00, 0x20, 0x01, 0xE0, +0x01, 0xC1, 0x12, 0x1F, 0x00, 0x2A, 0xFB, 0xD1, +0x70, 0x47, 0x00, 0x00, 0xFC, 0x06, 0x00, 0x00, +0x00, 0x00, 0x00, 0x20, 0x18, 0x00, 0x00, 0x00, +0xEE, 0x01, 0x00, 0x00, 0x14, 0x07, 0x00, 0x00, +0x18, 0x00, 0x00, 0x20, 0x04, 0x02, 0x00, 0x00, +0xCC, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xD5, 0x4D, 0xDB, 0x52, +}; +const unsigned char u8_rad_init_30[] = { +0x10, 0x06, 0x00, 0x50, 0x00, 0x00, 0x06, 0x01, +0x1C, 0x0E, 0x00, 0x50, 0x03, 0x00, 0x00, 0x00, +0x28, 0x06, 0x00, 0x50, 0x06, 0x00, 0x00, 0x00, +0x20, 0x06, 0x00, 0x50, 0x00, 0x00, 0x00, 0x40, +0x10, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, +0x14, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, +0x18, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, +0x1C, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, +0x20, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, +0x24, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, +0x28, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, +0x2C, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, +0x30, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, +0x34, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, +0x74, 0x09, 0x00, 0x50, 0xFF, 0x00, 0xAA, 0x55, +0x3C, 0x07, 0x00, 0x20, 0x17, 0x26, 0x28, 0x2A, +}; +const unsigned char u8_rad_fw_30[] = { +0xD0, 0x0C, 0x00, 0x20, 0xB1, 0x08, 0x00, 0x00, +0xB9, 0x08, 0x00, 0x00, 0x95, 0x08, 0x00, 0x00, +0xBD, 0x08, 0x00, 0x00, 0xBF, 0x08, 0x00, 0x00, +0xC1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xC3, 0x08, 0x00, 0x00, +0xC5, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xC7, 0x08, 0x00, 0x00, 0x81, 0x0F, 0x00, 0x00, +0xE1, 0x0C, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00, +0xCB, 0x08, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x00, +0xA1, 0x0D, 0x00, 0x00, 0x41, 0x0A, 0x00, 0x00, +0xCB, 0x08, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00, +0xCB, 0x08, 0x00, 0x00, 0x41, 0x0B, 0x00, 0x00, +0xE1, 0x0A, 0x00, 0x00, 0xA1, 0x0A, 0x00, 0x00, +0xC1, 0x0A, 0x00, 0x00, 0x81, 0x0A, 0x00, 0x00, +0xCB, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, +0x03, 0x48, 0x85, 0x46, 0x00, 0xF0, 0x80, 0xF8, +0x00, 0x48, 0x00, 0x47, 0x19, 0x44, 0x00, 0x00, +0xD0, 0x0C, 0x00, 0x20, 0x04, 0x20, 0x71, 0x46, +0x08, 0x42, 0x02, 0xD0, 0xEF, 0xF3, 0x09, 0x80, +0x01, 0xE0, 0xEF, 0xF3, 0x08, 0x80, 0x71, 0x46, +0x00, 0x4A, 0x10, 0x47, 0xC1, 0x42, 0x00, 0x00, +0x06, 0x48, 0x80, 0x47, 0x06, 0x48, 0x00, 0x47, +0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, +0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, +0xFE, 0xE7, 0xFE, 0xE7, 0x15, 0x0A, 0x00, 0x00, +0x81, 0x08, 0x00, 0x00, 0x30, 0xB5, 0x0B, 0x46, +0x01, 0x46, 0x00, 0x20, 0x20, 0x22, 0x01, 0x24, +0x09, 0xE0, 0x0D, 0x46, 0xD5, 0x40, 0x9D, 0x42, +0x05, 0xD3, 0x1D, 0x46, 0x95, 0x40, 0x49, 0x1B, +0x25, 0x46, 0x95, 0x40, 0x40, 0x19, 0x15, 0x46, +0x52, 0x1E, 0x00, 0x2D, 0xF1, 0xDC, 0x30, 0xBD, +0x70, 0xB5, 0x00, 0x24, 0x25, 0x46, 0x00, 0x28, +0x01, 0xDA, 0x01, 0x24, 0x40, 0x42, 0x00, 0x29, +0x01, 0xDA, 0x01, 0x25, 0x49, 0x42, 0xFF, 0xF7, +0xDD, 0xFF, 0xAC, 0x42, 0x00, 0xD0, 0x40, 0x42, +0x00, 0x2C, 0x00, 0xD0, 0x49, 0x42, 0x70, 0xBD, +0x03, 0x46, 0x0B, 0x43, 0x9B, 0x07, 0x03, 0xD0, +0x09, 0xE0, 0x08, 0xC9, 0x12, 0x1F, 0x08, 0xC0, +0x04, 0x2A, 0xFA, 0xD2, 0x03, 0xE0, 0x0B, 0x78, +0x49, 0x1C, 0x03, 0x70, 0x40, 0x1C, 0x52, 0x1E, +0xF9, 0xD2, 0x70, 0x47, 0xD2, 0xB2, 0x01, 0xE0, +0x02, 0x70, 0x40, 0x1C, 0x49, 0x1E, 0xFB, 0xD2, +0x70, 0x47, 0x00, 0x22, 0xF6, 0xE7, 0x10, 0xB5, +0x04, 0x46, 0x08, 0x46, 0x11, 0x46, 0x02, 0x46, +0x20, 0x46, 0xFF, 0xF7, 0xEF, 0xFF, 0x20, 0x46, +0x10, 0xBD, 0x00, 0x1D, 0x03, 0x21, 0x40, 0x1E, +0x03, 0x78, 0x12, 0x02, 0x1A, 0x43, 0x49, 0x1E, +0xF9, 0xD5, 0x10, 0x46, 0x70, 0x47, 0x00, 0x00, +0x06, 0x4C, 0x01, 0x25, 0x06, 0x4E, 0x05, 0xE0, +0x20, 0x46, 0xE3, 0x68, 0x07, 0xC8, 0x2B, 0x43, +0x98, 0x47, 0x10, 0x34, 0xB4, 0x42, 0xF7, 0xD3, +0xFF, 0xF7, 0x72, 0xFF, 0x58, 0x5C, 0x00, 0x00, +0x78, 0x5C, 0x00, 0x00, 0xC1, 0x06, 0xC9, 0x0E, +0x01, 0x20, 0x88, 0x40, 0x01, 0x49, 0x08, 0x60, +0x70, 0x47, 0x00, 0x00, 0x00, 0xE1, 0x00, 0xE0, +0x0B, 0x49, 0x10, 0xB5, 0x88, 0x42, 0x01, 0xD9, +0x01, 0x20, 0x10, 0xBD, 0x01, 0x02, 0x09, 0x0A, +0x08, 0x48, 0x49, 0x1E, 0x41, 0x61, 0x08, 0x49, +0x07, 0x23, 0xCA, 0x69, 0x12, 0x02, 0x12, 0x0A, +0x04, 0x04, 0x22, 0x43, 0xCA, 0x61, 0x00, 0x21, +0x81, 0x61, 0x03, 0x61, 0x08, 0x46, 0x10, 0xBD, +0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xE0, 0x00, 0xE0, +0x04, 0xED, 0x00, 0xE0, 0x70, 0x47, 0x00, 0x00, +0x03, 0x49, 0x02, 0x20, 0x08, 0x60, 0x02, 0x49, +0x80, 0x39, 0x08, 0x60, 0x70, 0x47, 0x00, 0x00, +0x80, 0xE1, 0x00, 0xE0, 0x62, 0xB6, 0x02, 0x48, +0x00, 0x21, 0x01, 0x60, 0x70, 0x47, 0x00, 0x00, +0x0C, 0x00, 0x00, 0x40, 0x30, 0xB4, 0x74, 0x46, +0x64, 0x1E, 0x25, 0x78, 0x64, 0x1C, 0xAB, 0x42, +0x00, 0xD2, 0x1D, 0x46, 0x63, 0x5D, 0x5B, 0x00, +0xE3, 0x18, 0x30, 0xBC, 0x18, 0x47, 0x00, 0x00, +0x05, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, +0x01, 0x20, 0x80, 0x07, 0x01, 0x6A, 0x03, 0x22, +0xD2, 0x03, 0x11, 0x43, 0x01, 0x62, 0x70, 0x47, +0xD0, 0x00, 0x00, 0x20, 0x02, 0xE0, 0x08, 0xC8, +0x12, 0x1F, 0x08, 0xC1, 0x00, 0x2A, 0xFA, 0xD1, +0x70, 0x47, 0x00, 0x20, 0x01, 0xE0, 0x01, 0xC1, +0x12, 0x1F, 0x00, 0x2A, 0xFB, 0xD1, 0x70, 0x47, +0x01, 0x21, 0x89, 0x07, 0x48, 0x60, 0x70, 0x47, +0x01, 0x20, 0x80, 0x07, 0x41, 0x6A, 0x82, 0x13, +0x11, 0x43, 0x41, 0x62, 0x41, 0x6A, 0xC2, 0x13, +0x11, 0x43, 0x41, 0x62, 0x70, 0x47, 0x10, 0xB5, +0xFF, 0xF7, 0xEE, 0xFF, 0x10, 0xBD, 0x00, 0x00, +0x03, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, +0x02, 0x49, 0x01, 0x20, 0xC8, 0x60, 0x70, 0x47, +0x08, 0x03, 0x00, 0x20, 0x00, 0x03, 0x00, 0x50, +0x10, 0xB5, 0x02, 0xF0, 0x8F, 0xF8, 0x10, 0xBD, +0x70, 0x47, 0x00, 0x00, 0x04, 0x49, 0x06, 0x22, +0x00, 0x28, 0x08, 0x68, 0x01, 0xD0, 0x10, 0x43, +0x00, 0xE0, 0x90, 0x43, 0x08, 0x60, 0x70, 0x47, +0x00, 0x09, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, +0x70, 0x47, 0x00, 0x00, 0x64, 0x20, 0x05, 0x49, +0x02, 0xE0, 0x00, 0xBF, 0x40, 0x1E, 0xC0, 0xB2, +0x4A, 0x68, 0x12, 0x06, 0x01, 0xD5, 0x00, 0x28, +0xF7, 0xD1, 0x70, 0x47, 0x00, 0x06, 0x00, 0x50, +0x00, 0xB5, 0x07, 0x48, 0x01, 0x69, 0x02, 0x29, +0x06, 0xD1, 0x01, 0x61, 0x05, 0x49, 0x8A, 0x78, +0x00, 0x2A, 0x02, 0xD0, 0x02, 0xF0, 0x28, 0xFB, +0x00, 0xBD, 0x01, 0x20, 0xC8, 0x70, 0x00, 0xBD, +0x00, 0x02, 0x00, 0x50, 0x5C, 0x04, 0x00, 0x20, +0x04, 0x49, 0x00, 0x20, 0x08, 0x60, 0x01, 0x20, +0x80, 0x07, 0x41, 0x68, 0x42, 0x14, 0x11, 0x43, +0x41, 0x60, 0x70, 0x47, 0xD0, 0x00, 0x00, 0x20, +0x30, 0xB5, 0x1E, 0x4B, 0x58, 0x68, 0x99, 0x68, +0x00, 0x28, 0x01, 0xDA, 0xDA, 0x04, 0x5A, 0x60, +0x1B, 0x4A, 0x15, 0x68, 0x01, 0x24, 0x05, 0x40, +0x00, 0x2D, 0x02, 0xD1, 0x50, 0x68, 0x08, 0x42, +0x0F, 0xD0, 0x18, 0x48, 0x04, 0x70, 0x00, 0xF0, +0x6F, 0xF9, 0x14, 0x72, 0x16, 0x48, 0x00, 0x78, +0x00, 0x28, 0x06, 0xD0, 0x18, 0x68, 0x01, 0x21, +0x00, 0x09, 0x00, 0x01, 0x89, 0x02, 0x08, 0x43, +0x18, 0x60, 0x58, 0x68, 0x80, 0x02, 0x02, 0xD5, +0x01, 0x20, 0x40, 0x05, 0x58, 0x60, 0x58, 0x68, +0x00, 0x04, 0x03, 0xD5, 0x01, 0x20, 0xC0, 0x03, +0x58, 0x60, 0x94, 0x72, 0x58, 0x68, 0x80, 0x00, +0x03, 0xD5, 0x01, 0x20, 0x40, 0x07, 0x58, 0x60, +0xD4, 0x72, 0x58, 0x68, 0x40, 0x00, 0x03, 0xD5, +0x01, 0x20, 0x80, 0x07, 0x58, 0x60, 0x94, 0x72, +0x30, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, +0x50, 0x02, 0x00, 0x20, 0x99, 0x01, 0x00, 0x20, +0xCF, 0x00, 0x00, 0x20, 0x30, 0xB5, 0x07, 0x49, +0x07, 0x4B, 0x0A, 0x68, 0x00, 0x20, 0x41, 0x00, +0xCD, 0x18, 0x54, 0x5A, 0x2D, 0x88, 0x40, 0x1C, +0x64, 0x1B, 0xC0, 0xB2, 0x54, 0x52, 0x30, 0x28, +0xF5, 0xD3, 0x30, 0xBD, 0xF8, 0x02, 0x00, 0x20, +0x00, 0x00, 0x01, 0x20, 0x10, 0xB5, 0x02, 0xF0, +0x09, 0xF9, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00, +0x70, 0xB5, 0x2B, 0x4B, 0x05, 0x20, 0x19, 0x7A, +0x40, 0x04, 0x02, 0x29, 0x4C, 0xD1, 0x29, 0x4C, +0x21, 0x78, 0x00, 0x29, 0x45, 0xD1, 0x28, 0x49, +0x0A, 0x78, 0x00, 0x2A, 0x44, 0xD0, 0x00, 0x22, +0x0A, 0x70, 0x27, 0x49, 0x25, 0x4A, 0x09, 0x78, +0x26, 0x4D, 0x05, 0xE0, 0x16, 0x78, 0x81, 0x2E, +0x0A, 0xD0, 0x00, 0x29, 0x08, 0xD1, 0x40, 0x1E, +0x2E, 0x78, 0x00, 0x2E, 0x04, 0xD0, 0x00, 0x28, +0x02, 0xD0, 0x1E, 0x7A, 0x02, 0x2E, 0xF1, 0xD0, +0x18, 0x7A, 0x02, 0x28, 0x26, 0xD1, 0x10, 0x78, +0x81, 0x28, 0x23, 0xD0, 0x00, 0x29, 0x21, 0xD1, +0xFF, 0xF7, 0x44, 0xFF, 0x1A, 0x48, 0x40, 0x68, +0x40, 0x01, 0x40, 0x0D, 0xBC, 0x28, 0x01, 0xD9, +0xBC, 0x38, 0x80, 0xB2, 0x17, 0x49, 0x49, 0x68, +0x09, 0x06, 0x0D, 0xD4, 0x16, 0x49, 0x09, 0x88, +0x88, 0x42, 0x03, 0xD3, 0x15, 0x49, 0x09, 0x88, +0x88, 0x42, 0x05, 0xD9, 0x01, 0x20, 0x80, 0x07, +0x81, 0x68, 0x01, 0x22, 0x11, 0x43, 0x81, 0x60, +0x00, 0x20, 0x00, 0xBF, 0x40, 0x1C, 0xC0, 0xB2, +0x14, 0x28, 0xFA, 0xD3, 0x20, 0x78, 0x00, 0x28, +0x02, 0xD0, 0x20, 0x78, 0x40, 0x1E, 0x20, 0x70, +0xFF, 0xF7, 0x3E, 0xFF, 0x70, 0xBD, 0x00, 0x00, +0x50, 0x02, 0x00, 0x20, 0xEC, 0x02, 0x00, 0x20, +0x99, 0x01, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, +0x72, 0x04, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, +0x00, 0x11, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50, +0x60, 0x02, 0x00, 0x20, 0x62, 0x02, 0x00, 0x20, +0x10, 0xB5, 0x02, 0xF0, 0xF1, 0xF9, 0x10, 0xBD, +0x70, 0xB5, 0x01, 0x25, 0xAD, 0x07, 0xE8, 0x68, +0x40, 0x08, 0x40, 0x00, 0xE8, 0x60, 0x1B, 0x48, +0x41, 0x68, 0x01, 0x26, 0x49, 0x07, 0x00, 0x29, +0x2A, 0xDA, 0x42, 0x68, 0x04, 0x21, 0x0A, 0x43, +0x42, 0x60, 0x17, 0x48, 0x00, 0x68, 0x17, 0x4C, +0x40, 0x05, 0x40, 0x0F, 0x20, 0x73, 0x20, 0x7B, +0x06, 0x28, 0x00, 0xD1, 0x21, 0x73, 0x20, 0x7B, +0x02, 0x28, 0x1D, 0xD0, 0x20, 0x7B, 0x06, 0x28, +0x05, 0xD8, 0x20, 0x7B, 0x04, 0x28, 0x02, 0xD3, +0x01, 0x20, 0x01, 0xF0, 0xA1, 0xF9, 0x20, 0x7B, +0x0D, 0x49, 0x09, 0x78, 0x88, 0x42, 0x0B, 0xD0, +0x0C, 0x4C, 0xA0, 0x79, 0x00, 0x28, 0x01, 0xD0, +0x01, 0xF0, 0xF8, 0xF9, 0x0A, 0x48, 0x01, 0x78, +0x31, 0x43, 0x01, 0x70, 0x81, 0x20, 0x20, 0x70, +0xE8, 0x68, 0x30, 0x43, 0xE8, 0x60, 0x70, 0xBD, +0x00, 0x20, 0xE6, 0xE7, 0x40, 0x00, 0x00, 0x50, +0x00, 0x11, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, +0xE5, 0x02, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, +0xCE, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x08, 0x4A, +0x00, 0x21, 0x12, 0x68, 0x60, 0x32, 0x12, 0x78, +0x52, 0x1C, 0xD4, 0xB2, 0x4A, 0x00, 0x83, 0x5E, +0x49, 0x1C, 0x63, 0x43, 0x1B, 0x11, 0xC9, 0xB2, +0x83, 0x52, 0x30, 0x29, 0xF6, 0xD3, 0x10, 0xBD, +0xB8, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, +0x10, 0xB5, 0x14, 0x48, 0x80, 0x69, 0x40, 0x04, +0x23, 0xD5, 0x13, 0x4C, 0xE0, 0x68, 0xA2, 0x68, +0x02, 0x40, 0xE0, 0x68, 0xA1, 0x68, 0x88, 0x43, +0xE0, 0x60, 0x10, 0x07, 0x03, 0xD5, 0x08, 0x20, +0xE0, 0x60, 0x04, 0xF0, 0x3F, 0xFB, 0x50, 0x07, +0x0E, 0xD5, 0x04, 0x20, 0xE0, 0x60, 0x04, 0xF0, +0x39, 0xFB, 0x02, 0xF0, 0xFD, 0xFC, 0x00, 0x28, +0x06, 0xD1, 0x08, 0x48, 0x00, 0x78, 0x00, 0x28, +0x02, 0xD0, 0x01, 0x20, 0xFF, 0xF7, 0x68, 0xFE, +0xFF, 0x20, 0xF3, 0x30, 0xE0, 0x60, 0x01, 0x20, +0xE0, 0x60, 0x10, 0xBD, 0x00, 0x09, 0x00, 0x50, +0x00, 0x05, 0x00, 0x50, 0x90, 0x02, 0x00, 0x20, +0x00, 0xB5, 0x03, 0x46, 0x00, 0x20, 0xFF, 0xF7, +0x5D, 0xFE, 0x04, 0x49, 0x01, 0x20, 0x09, 0x68, +0x0A, 0x88, 0x9A, 0x43, 0x0A, 0x80, 0xFF, 0xF7, +0x55, 0xFE, 0x00, 0xBD, 0x08, 0x00, 0x00, 0x20, +0x01, 0x28, 0x05, 0xD0, 0x02, 0x28, 0x05, 0xD0, +0x04, 0x28, 0x06, 0xD0, 0x00, 0x20, 0x70, 0x47, +0x03, 0x48, 0x70, 0x47, 0x02, 0x48, 0xC0, 0x30, +0x70, 0x47, 0x02, 0x48, 0x70, 0x47, 0x00, 0x00, +0x78, 0x7C, 0x00, 0x00, 0xF8, 0x7D, 0x00, 0x00, +0x03, 0x48, 0x02, 0x49, 0x41, 0x60, 0x03, 0x49, +0x81, 0x60, 0x70, 0x47, 0x1F, 0x1F, 0x5F, 0x1F, +0x00, 0x10, 0x00, 0x50, 0x1F, 0x1F, 0x1F, 0x1F, +0xF8, 0xB5, 0x2B, 0x48, 0x80, 0x69, 0x40, 0x04, +0x51, 0xD5, 0x2A, 0x4D, 0xA8, 0x6A, 0xE9, 0x68, +0xAC, 0x68, 0x0C, 0x40, 0xE9, 0x68, 0xAA, 0x68, +0x91, 0x43, 0xE9, 0x60, 0x26, 0x4E, 0xC0, 0x07, +0x15, 0xD0, 0x68, 0x69, 0x89, 0x27, 0xC0, 0xB2, +0xEF, 0x60, 0xAA, 0x6A, 0x01, 0x21, 0x0A, 0x43, +0xAA, 0x62, 0x0B, 0x28, 0x03, 0xD2, 0x31, 0x70, +0x01, 0xF0, 0x04, 0xF8, 0x03, 0xE0, 0x00, 0x21, +0x31, 0x70, 0x00, 0xF0, 0xDB, 0xFF, 0xBC, 0x43, +0x01, 0x20, 0xFF, 0xF7, 0x05, 0xFE, 0x1B, 0x48, +0xA1, 0x04, 0x04, 0xD5, 0x29, 0x6A, 0x81, 0x43, +0x29, 0x62, 0x41, 0x14, 0xE9, 0x60, 0xE1, 0x04, +0x05, 0xD5, 0x29, 0x6A, 0x81, 0x43, 0x29, 0x62, +0x01, 0x20, 0x00, 0x03, 0xE8, 0x60, 0xA0, 0x05, +0x02, 0xD5, 0x01, 0x20, 0x40, 0x02, 0xE8, 0x60, +0x20, 0x07, 0x09, 0xD5, 0x30, 0x78, 0x01, 0x28, +0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, 0xBA, 0xFF, +0x64, 0x08, 0x64, 0x00, 0x09, 0x20, 0xE8, 0x60, +0x60, 0x07, 0x07, 0xD5, 0x30, 0x78, 0x01, 0x28, +0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, 0xAE, 0xFF, +0x05, 0x20, 0xE8, 0x60, 0xFF, 0x20, 0xF3, 0x30, +0xE8, 0x60, 0x01, 0x20, 0xE8, 0x60, 0xF8, 0xBD, +0x00, 0x09, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50, +0x9B, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, +0x70, 0xB5, 0x41, 0x18, 0x49, 0x1E, 0x64, 0x24, +0x09, 0x04, 0x0B, 0x4D, 0x01, 0x43, 0x69, 0x63, +0xE8, 0x68, 0x81, 0x21, 0x09, 0x06, 0x08, 0x43, +0xE8, 0x60, 0x02, 0xE0, 0x01, 0x20, 0xFF, 0xF7, +0xCB, 0xFE, 0xE8, 0x68, 0xC0, 0x01, 0x04, 0xD5, +0x20, 0x46, 0x64, 0x1E, 0xA4, 0xB2, 0x00, 0x28, +0xF4, 0xD1, 0xA8, 0x6B, 0x70, 0xBD, 0x00, 0x00, +0x40, 0x09, 0x00, 0x50, 0x04, 0x49, 0x29, 0x20, +0xC8, 0x60, 0x04, 0x49, 0x35, 0x20, 0x08, 0x63, +0x04, 0x20, 0x01, 0x07, 0x88, 0x60, 0x70, 0x47, +0x40, 0x00, 0x00, 0x50, 0x80, 0x10, 0x00, 0x50, +0x10, 0xB5, 0x00, 0xF0, 0x09, 0xF8, 0x10, 0xBD, +0x02, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, +0x70, 0x47, 0x00, 0x00, 0x80, 0x04, 0x00, 0x20, +0x01, 0x20, 0x80, 0x07, 0x41, 0x68, 0x82, 0x14, +0x11, 0x43, 0x41, 0x60, 0x70, 0x47, 0x00, 0x00, +0x05, 0x20, 0x00, 0x07, 0x82, 0x69, 0x0C, 0x49, +0x00, 0x2A, 0x07, 0xDA, 0x82, 0x69, 0xC3, 0x00, +0x92, 0x00, 0x92, 0x08, 0xD2, 0x18, 0x82, 0x61, +0x01, 0x22, 0x4A, 0x72, 0x82, 0x69, 0x52, 0x00, +0x08, 0xD5, 0x82, 0x69, 0x01, 0x23, 0x92, 0x00, +0x92, 0x08, 0x9B, 0x07, 0xD2, 0x18, 0x82, 0x61, +0x00, 0x20, 0x48, 0x72, 0x70, 0x47, 0x00, 0x00, +0x84, 0x04, 0x00, 0x20, 0xF0, 0xB5, 0x00, 0x20, +0x84, 0x46, 0x91, 0x48, 0x87, 0xB0, 0x00, 0x68, +0x90, 0x49, 0xB0, 0x30, 0x00, 0x90, 0x00, 0x20, +0x08, 0x5E, 0x8F, 0x49, 0x01, 0x90, 0x00, 0x20, +0x08, 0x5E, 0x02, 0x90, 0x0B, 0xE1, 0x34, 0x21, +0x48, 0x43, 0x81, 0x19, 0x0C, 0x46, 0x20, 0x34, +0x60, 0x7D, 0xFF, 0x28, 0x31, 0xD1, 0x00, 0x22, +0x00, 0x20, 0x08, 0xE0, 0x34, 0x25, 0x45, 0x43, +0xAD, 0x19, 0x20, 0x35, 0x6D, 0x7D, 0x95, 0x42, +0x03, 0xD0, 0x40, 0x1C, 0xC0, 0xB2, 0x83, 0x42, +0xF4, 0xD8, 0x83, 0x42, 0x01, 0xD1, 0x62, 0x75, +0x03, 0xE0, 0x52, 0x1C, 0xD2, 0xB2, 0x02, 0x2A, +0xEA, 0xD3, 0x0A, 0x25, 0x4D, 0x5F, 0x30, 0x20, +0x7C, 0x4E, 0x42, 0x43, 0xAB, 0x01, 0x90, 0x19, +0x83, 0x60, 0xC3, 0x60, 0xB5, 0x50, 0x0C, 0x22, +0x45, 0x60, 0x8A, 0x5E, 0x93, 0x01, 0x83, 0x61, +0x02, 0x61, 0xC3, 0x61, 0x42, 0x61, 0x0E, 0x22, +0x8A, 0x5E, 0x93, 0x01, 0x83, 0x62, 0x02, 0x62, +0xC3, 0x62, 0x42, 0x62, 0x60, 0x7D, 0xFF, 0x28, +0x7E, 0xD0, 0x71, 0x4A, 0xC7, 0xB2, 0x12, 0x78, +0xC8, 0x69, 0x00, 0x2A, 0x11, 0xD1, 0x00, 0x9A, +0xD3, 0x79, 0x94, 0x79, 0x1A, 0x02, 0x22, 0x43, +0x82, 0x42, 0x01, 0xD2, 0x02, 0x22, 0x09, 0xE0, +0x00, 0x9A, 0x53, 0x7A, 0x14, 0x7A, 0x1A, 0x02, +0x22, 0x43, 0x82, 0x42, 0x01, 0xD2, 0x03, 0x22, +0x00, 0xE0, 0x04, 0x22, 0x65, 0x4D, 0x28, 0x78, +0x82, 0x42, 0x28, 0xD0, 0x82, 0x42, 0x02, 0xD3, +0x13, 0x1A, 0x02, 0x2B, 0x23, 0xDA, 0x62, 0x4C, +0x23, 0x78, 0x00, 0x2B, 0x14, 0xD0, 0x61, 0x4E, +0x33, 0x78, 0x9A, 0x42, 0x02, 0xD0, 0x00, 0x23, +0x23, 0x70, 0x32, 0x70, 0x22, 0x78, 0x5D, 0x4B, +0x52, 0x1C, 0xD2, 0xB2, 0x22, 0x70, 0x1B, 0x78, +0x98, 0x42, 0x08, 0xD9, 0x01, 0x2A, 0x06, 0xD9, +0x00, 0x20, 0x20, 0x70, 0x2B, 0x70, 0x0A, 0xE0, +0x56, 0x4B, 0x1A, 0x70, 0xEE, 0xE7, 0x98, 0x42, +0x05, 0xD2, 0x02, 0x2A, 0x03, 0xD9, 0x00, 0x22, +0x22, 0x70, 0x40, 0x1C, 0x28, 0x70, 0x28, 0x78, +0x02, 0x28, 0x6B, 0xD0, 0x03, 0x28, 0x6D, 0xD0, +0x29, 0x22, 0x64, 0x23, 0x05, 0x24, 0x3E, 0x46, +0x30, 0x20, 0x46, 0x43, 0x47, 0x48, 0x30, 0x18, +0xC5, 0x68, 0x03, 0x95, 0x87, 0x68, 0x5D, 0x43, +0x57, 0x43, 0xED, 0x1B, 0x20, 0x35, 0xAD, 0x11, +0xAE, 0x46, 0x0A, 0x25, 0x4D, 0x5F, 0x41, 0x4F, +0x04, 0x95, 0xBF, 0x59, 0xEF, 0x19, 0x45, 0x68, +0x6D, 0x00, 0x7F, 0x19, 0x67, 0x43, 0xBF, 0x1C, +0xBF, 0x10, 0x75, 0x46, 0x7D, 0x19, 0xAE, 0x46, +0xC5, 0x69, 0x87, 0x69, 0x5D, 0x43, 0x57, 0x43, +0xED, 0x1B, 0x20, 0x35, 0xAD, 0x11, 0x06, 0x95, +0x0C, 0x25, 0x4D, 0x5F, 0x05, 0x95, 0x07, 0x69, +0xEF, 0x19, 0x45, 0x69, 0x6D, 0x00, 0x7D, 0x19, +0x65, 0x43, 0xAD, 0x1C, 0xAF, 0x10, 0x00, 0xE0, +0x4D, 0xE0, 0x06, 0x9D, 0x7F, 0x19, 0xC5, 0x6A, +0x5D, 0x43, 0x83, 0x6A, 0x53, 0x43, 0xEA, 0x1A, +0x0E, 0x23, 0x20, 0x32, 0xCB, 0x5E, 0x95, 0x11, +0x06, 0x93, 0x02, 0x6A, 0x9A, 0x18, 0x43, 0x6A, +0x5B, 0x00, 0xD2, 0x18, 0x62, 0x43, 0x92, 0x1C, +0x92, 0x10, 0x52, 0x19, 0x03, 0x9D, 0x85, 0x60, +0x75, 0x46, 0xC5, 0x60, 0xC3, 0x69, 0xC7, 0x61, +0x83, 0x61, 0xC3, 0x6A, 0x83, 0x62, 0xC2, 0x62, +0x20, 0x4C, 0x43, 0x68, 0xA3, 0x51, 0x04, 0x9D, +0x45, 0x60, 0x43, 0x69, 0x03, 0x61, 0x05, 0x9D, +0x45, 0x61, 0x43, 0x6A, 0x03, 0x62, 0x06, 0x9B, +0x43, 0x62, 0x75, 0x46, 0x01, 0x9C, 0xA8, 0x11, +0xBB, 0x11, 0x92, 0x11, 0xA0, 0x42, 0x09, 0xDB, +0x60, 0x1E, 0x0A, 0xE0, 0x1C, 0x22, 0x4D, 0x23, +0x0F, 0x24, 0x94, 0xE7, 0x22, 0x22, 0x58, 0x23, +0x0A, 0x24, 0x90, 0xE7, 0x00, 0x28, 0x00, 0xDA, +0x00, 0x20, 0x02, 0x9C, 0xA3, 0x42, 0x01, 0xDB, +0x63, 0x1E, 0x02, 0xE0, 0x00, 0x2B, 0x00, 0xDA, +0x00, 0x23, 0x00, 0x2A, 0x00, 0xDA, 0x00, 0x22, +0x48, 0x81, 0x8B, 0x81, 0xCA, 0x81, 0x60, 0x46, +0x40, 0x1C, 0xC0, 0xB2, 0x84, 0x46, 0x0C, 0x4E, +0x60, 0x46, 0x33, 0x78, 0x63, 0x45, 0x00, 0xD9, +0xED, 0xE6, 0x07, 0xB0, 0xF0, 0xBD, 0x00, 0x00, +0xB8, 0x02, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20, +0x44, 0x00, 0x00, 0x20, 0x38, 0x01, 0x00, 0x20, +0x2A, 0x00, 0x00, 0x20, 0x4A, 0x00, 0x00, 0x20, +0x49, 0x00, 0x00, 0x20, 0x4B, 0x00, 0x00, 0x20, +0xA0, 0x04, 0x00, 0x20, 0xF0, 0xB5, 0x93, 0xB0, +0x02, 0xF0, 0x62, 0xFD, 0x0F, 0x90, 0x8D, 0x48, +0x00, 0x25, 0x41, 0x5F, 0x0F, 0x98, 0x81, 0x42, +0x7D, 0xDD, 0x8B, 0x48, 0x30, 0x21, 0x00, 0x68, +0x06, 0x7E, 0x40, 0x7E, 0x11, 0x90, 0x02, 0xA8, +0xFF, 0xF7, 0x73, 0xFB, 0x87, 0x48, 0x00, 0x78, +0x06, 0x28, 0x71, 0xD2, 0x01, 0x20, 0x10, 0x90, +0x08, 0xA8, 0x0E, 0x90, 0x34, 0x20, 0x29, 0x46, +0x41, 0x43, 0x83, 0x48, 0x00, 0x27, 0x0C, 0x18, +0x27, 0x76, 0x10, 0x98, 0x00, 0x28, 0x04, 0xD0, +0x7C, 0x48, 0x01, 0x88, 0x7F, 0x48, 0x01, 0x80, +0x09, 0xE0, 0x01, 0xA9, 0x68, 0x46, 0x0F, 0x9A, +0x04, 0xF0, 0x58, 0xF8, 0x00, 0x28, 0x57, 0xD0, +0x10, 0x98, 0x00, 0x28, 0x0E, 0xD0, 0x7A, 0x48, +0x31, 0x46, 0x00, 0x88, 0x10, 0x90, 0xFF, 0xF7, +0x09, 0xFB, 0x69, 0x46, 0x08, 0x70, 0x10, 0x99, +0x70, 0x43, 0x08, 0x1A, 0x69, 0x46, 0x08, 0x71, +0x00, 0x20, 0x10, 0x90, 0x0F, 0x98, 0x02, 0xAA, +0x83, 0xB2, 0x01, 0xA9, 0x68, 0x46, 0x02, 0xF0, +0x65, 0xFD, 0x70, 0x49, 0x88, 0x42, 0x7D, 0xD0, +0x6C, 0x4A, 0x00, 0x21, 0x51, 0x5E, 0x61, 0x86, +0xE0, 0x81, 0x0A, 0x22, 0x50, 0x43, 0xFF, 0xF7, +0x03, 0xFB, 0x34, 0x21, 0x08, 0x55, 0xFF, 0x20, +0x20, 0x71, 0x00, 0x20, 0xE0, 0x61, 0x68, 0x46, +0x01, 0x79, 0x02, 0xAA, 0x01, 0x20, 0x02, 0xF0, +0xC7, 0xFC, 0x65, 0x49, 0x60, 0x81, 0x0A, 0x78, +0x00, 0x2A, 0x0B, 0xD0, 0x63, 0x4A, 0x00, 0x23, +0xD3, 0x5E, 0x63, 0x4A, 0x12, 0x78, 0x9B, 0x1A, +0x98, 0x42, 0x03, 0xDA, 0x90, 0x42, 0x01, 0xDD, +0x00, 0x22, 0x0A, 0x70, 0x6A, 0x46, 0x12, 0x79, +0x00, 0x2A, 0x12, 0xD0, 0x73, 0x1E, 0x9A, 0x42, +0x28, 0xD1, 0x5B, 0x4A, 0x00, 0x27, 0x12, 0x78, +0x53, 0x00, 0xD2, 0x18, 0x93, 0x08, 0x57, 0x4A, +0xD7, 0x5F, 0xFA, 0x1A, 0x90, 0x42, 0x14, 0xDD, +0x01, 0x20, 0x08, 0x70, 0x14, 0xE0, 0x7E, 0xE0, +0x87, 0xE0, 0x53, 0x4A, 0x12, 0x78, 0x53, 0x00, +0xD2, 0x18, 0x92, 0x08, 0x90, 0x42, 0x02, 0xDA, +0x01, 0x20, 0x08, 0x70, 0x02, 0xE0, 0x08, 0x78, +0x00, 0x28, 0x0B, 0xD0, 0x02, 0xAA, 0x01, 0x21, +0x04, 0xE0, 0x08, 0x78, 0x00, 0x28, 0x05, 0xD0, +0x02, 0xAA, 0x00, 0x21, 0x01, 0x20, 0x02, 0xF0, +0xEF, 0xFB, 0x60, 0x81, 0x68, 0x46, 0x01, 0x78, +0x00, 0x20, 0x0E, 0x9A, 0x02, 0xF0, 0x80, 0xFC, +0x44, 0x49, 0xA0, 0x81, 0x0A, 0x78, 0x00, 0x2A, +0x0B, 0xD0, 0x43, 0x4A, 0x00, 0x23, 0xD3, 0x5E, +0x42, 0x4A, 0x12, 0x78, 0x9B, 0x1A, 0x98, 0x42, +0x03, 0xDA, 0x90, 0x42, 0x01, 0xDD, 0x00, 0x22, +0x0A, 0x70, 0x6A, 0x46, 0x12, 0x78, 0x00, 0x2A, +0x13, 0xD0, 0x11, 0x9B, 0x5B, 0x1E, 0x9A, 0x42, +0x28, 0xD1, 0x3A, 0x4A, 0x12, 0x78, 0x53, 0x00, +0xD2, 0x18, 0x93, 0x08, 0x36, 0x4A, 0x00, 0x27, +0xD7, 0x5F, 0x00, 0xE0, 0x37, 0xE0, 0xFA, 0x1A, +0x90, 0x42, 0x12, 0xDD, 0x01, 0x20, 0x08, 0x70, +0x12, 0xE0, 0x32, 0x4A, 0x12, 0x78, 0x53, 0x00, +0xD2, 0x18, 0x92, 0x08, 0x90, 0x42, 0x02, 0xDA, +0x01, 0x20, 0x08, 0x70, 0x02, 0xE0, 0x08, 0x78, +0x00, 0x28, 0x0B, 0xD0, 0x01, 0x21, 0x0E, 0x9A, +0x04, 0xE0, 0x08, 0x78, 0x00, 0x28, 0x05, 0xD0, +0x0E, 0x9A, 0x00, 0x21, 0x00, 0x20, 0x02, 0xF0, +0xA7, 0xFB, 0xA0, 0x81, 0x07, 0x98, 0xA0, 0x82, +0x0D, 0x98, 0xE0, 0x82, 0x68, 0x46, 0x00, 0x79, +0x00, 0x28, 0x0A, 0xD0, 0x71, 0x1E, 0x88, 0x42, +0x07, 0xD0, 0x68, 0x46, 0x00, 0x78, 0x00, 0x28, +0x03, 0xD0, 0x11, 0x99, 0x49, 0x1E, 0x88, 0x42, +0x01, 0xD1, 0x01, 0x20, 0x00, 0xE0, 0x00, 0x20, +0x6D, 0x1C, 0x20, 0x76, 0xED, 0xB2, 0x03, 0x2D, +0x00, 0xD2, 0x13, 0xE7, 0x09, 0xE0, 0x0E, 0x49, +0x00, 0x20, 0x08, 0x70, 0x16, 0x48, 0x40, 0x88, +0x0A, 0x28, 0x02, 0xD9, 0x00, 0x20, 0x13, 0xB0, +0xF0, 0xBD, 0x08, 0x48, 0x00, 0x78, 0x03, 0x28, +0x03, 0xD3, 0x07, 0x49, 0x00, 0x20, 0x08, 0x70, +0x01, 0xE0, 0x05, 0x48, 0x05, 0x70, 0x01, 0x20, +0xF1, 0xE7, 0x00, 0x00, 0x24, 0x00, 0x00, 0x20, +0xB8, 0x02, 0x00, 0x20, 0x73, 0x04, 0x00, 0x20, +0xA0, 0x04, 0x00, 0x20, 0x28, 0x00, 0x00, 0x20, +0x32, 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0xFF, +0xCC, 0x00, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20, +0x2E, 0x00, 0x00, 0x20, 0xCD, 0x00, 0x00, 0x20, +0x44, 0x00, 0x00, 0x20, 0x2F, 0x00, 0x00, 0x20, +0x84, 0x04, 0x00, 0x20, 0xF0, 0xB5, 0x8E, 0x46, +0x02, 0x21, 0x4B, 0x00, 0xC3, 0x5E, 0x00, 0x2B, +0x03, 0xDD, 0x49, 0x1E, 0x09, 0xB2, 0x00, 0x29, +0xF7, 0xDA, 0x49, 0x1C, 0x00, 0x27, 0x0B, 0xB2, +0x9C, 0x46, 0x3C, 0x46, 0x01, 0x25, 0x0E, 0xE0, +0x5E, 0x00, 0x86, 0x5F, 0x00, 0x2E, 0x02, 0xDC, +0x5B, 0x1E, 0x1B, 0xB2, 0x09, 0xE0, 0x31, 0x46, +0x69, 0x43, 0x6D, 0x1C, 0xCF, 0x19, 0x34, 0x19, +0x5B, 0x1C, 0x2D, 0xB2, 0x1B, 0xB2, 0x05, 0x2B, +0xEE, 0xDB, 0x61, 0x46, 0x59, 0x1A, 0x49, 0x1C, +0x66, 0x08, 0x63, 0x46, 0x00, 0x25, 0x51, 0x61, +0x06, 0xE0, 0x59, 0x00, 0x41, 0x5E, 0x4D, 0x19, +0xB5, 0x42, 0x03, 0xD8, 0x5B, 0x1C, 0x1B, 0xB2, +0x05, 0x2B, 0xF6, 0xDB, 0x5E, 0x00, 0x81, 0x5F, +0x69, 0x1A, 0x11, 0x60, 0x80, 0x5F, 0x50, 0x60, +0x60, 0x1B, 0xD4, 0x60, 0x90, 0x60, 0x60, 0x46, +0x18, 0x1A, 0x60, 0x43, 0x38, 0x1A, 0x10, 0x61, +0x70, 0x46, 0x00, 0x78, 0x9B, 0x1E, 0xC0, 0x18, +0x71, 0x46, 0x08, 0x70, 0x20, 0xB2, 0xF0, 0xBD, +0xF8, 0xB5, 0x8D, 0x4E, 0x00, 0x27, 0x30, 0x78, +0x3C, 0x46, 0x00, 0x28, 0x01, 0xD0, 0x03, 0xF0, +0xE7, 0xF9, 0x00, 0x20, 0x05, 0x46, 0x89, 0x4A, +0x0D, 0xE0, 0x34, 0x21, 0x41, 0x43, 0x89, 0x18, +0x0B, 0x79, 0x1B, 0x06, 0x01, 0xD5, 0xCD, 0x71, +0x03, 0xE0, 0xCB, 0x79, 0x5B, 0x1C, 0xCB, 0x71, +0x01, 0x27, 0x40, 0x1C, 0xC0, 0xB2, 0x11, 0x78, +0x81, 0x42, 0xEE, 0xD8, 0x7F, 0x49, 0x30, 0x78, +0x09, 0x78, 0x88, 0x42, 0x01, 0xD1, 0x00, 0x2F, +0x02, 0xD0, 0x03, 0xF0, 0x63, 0xFD, 0x0E, 0xE0, +0xB0, 0x70, 0x00, 0x20, 0x34, 0x21, 0x41, 0x43, +0x89, 0x19, 0x8D, 0x71, 0x40, 0x1C, 0xCD, 0x71, +0xC0, 0xB2, 0x0D, 0x72, 0x03, 0x28, 0xF5, 0xD3, +0x74, 0x48, 0x75, 0x70, 0x45, 0x70, 0x30, 0x78, +0xB1, 0x78, 0x88, 0x42, 0x01, 0xD0, 0x03, 0xF0, +0x61, 0xFB, 0x02, 0xF0, 0x89, 0xFE, 0x70, 0x49, +0x70, 0x4A, 0x08, 0x88, 0x10, 0x80, 0x84, 0x46, +0x00, 0x20, 0x08, 0x80, 0x6A, 0x49, 0x6E, 0x4D, +0x0A, 0x78, 0x2B, 0xE0, 0x34, 0x23, 0x43, 0x43, +0x5E, 0x18, 0x69, 0x4B, 0xF1, 0x69, 0x1F, 0x88, +0xB9, 0x42, 0x00, 0xD9, 0x19, 0x80, 0x61, 0x46, +0x00, 0x29, 0x0B, 0xD1, 0x29, 0x68, 0xB0, 0x31, +0x4F, 0x7A, 0x09, 0x7A, 0x3F, 0x02, 0x0F, 0x43, +0x19, 0x88, 0x8F, 0x42, 0x02, 0xD2, 0x63, 0x4B, +0x01, 0x21, 0x19, 0x70, 0x29, 0x68, 0x20, 0x36, +0xA0, 0x31, 0x33, 0x7D, 0x49, 0x78, 0x8B, 0x42, +0x17, 0xD9, 0x5F, 0x4B, 0x01, 0x24, 0x19, 0x78, +0x49, 0x1C, 0xC9, 0xB2, 0x19, 0x70, 0x02, 0x29, +0x02, 0xD9, 0x5C, 0x4B, 0x01, 0x21, 0x19, 0x70, +0x40, 0x1C, 0xC0, 0xB2, 0x52, 0x49, 0x82, 0x42, +0xD0, 0xD8, 0x59, 0x49, 0x00, 0x2C, 0x08, 0x78, +0x06, 0xD0, 0x64, 0x28, 0x08, 0xD2, 0x40, 0x1C, +0x05, 0xE0, 0x00, 0x21, 0x52, 0x4B, 0xEE, 0xE7, +0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, 0x08, 0x70, +0x03, 0x20, 0x81, 0x1A, 0x34, 0x20, 0x48, 0x4E, +0x41, 0x43, 0x42, 0x43, 0x90, 0x19, 0x00, 0x1D, +0xFF, 0xF7, 0x6F, 0xF9, 0x00, 0x24, 0x45, 0x4F, +0x0F, 0xE0, 0x38, 0x78, 0x34, 0x21, 0x00, 0x19, +0x48, 0x43, 0xC1, 0x19, 0x30, 0x78, 0x34, 0x22, +0x00, 0x19, 0x50, 0x43, 0x80, 0x19, 0x09, 0x1D, +0x00, 0x1D, 0xFF, 0xF7, 0x45, 0xF9, 0x64, 0x1C, +0xE4, 0xB2, 0x71, 0x78, 0xA1, 0x42, 0xEC, 0xD8, +0x32, 0x78, 0x00, 0x20, 0x53, 0x18, 0x0E, 0xE0, +0x34, 0x22, 0x42, 0x43, 0x91, 0x19, 0x06, 0x22, +0x42, 0x43, 0xD2, 0x19, 0xA0, 0x32, 0x4C, 0x89, +0x14, 0x80, 0x8C, 0x89, 0x54, 0x80, 0xC9, 0x89, +0x40, 0x1C, 0x91, 0x80, 0xC0, 0xB2, 0x31, 0x46, +0x83, 0x42, 0xED, 0xD8, 0x00, 0x20, 0x37, 0x4C, +0x01, 0x22, 0x20, 0x80, 0x0C, 0xE0, 0x34, 0x26, +0x46, 0x43, 0x76, 0x18, 0x36, 0x79, 0x77, 0x06, +0x7F, 0x0E, 0x16, 0x46, 0xBE, 0x40, 0x27, 0x88, +0x3E, 0x43, 0x40, 0x1C, 0x26, 0x80, 0xC0, 0xB2, +0x83, 0x42, 0xF0, 0xD8, 0x23, 0x88, 0x00, 0x20, +0x16, 0x46, 0x32, 0x46, 0x82, 0x40, 0x1A, 0x42, +0x05, 0xD0, 0x40, 0x1C, 0xC0, 0xB2, 0x02, 0x28, +0xF7, 0xD3, 0x00, 0x26, 0x37, 0xE0, 0x28, 0x4A, +0x10, 0x70, 0xFA, 0xE7, 0x31, 0x46, 0x34, 0x22, +0x51, 0x43, 0x0C, 0x18, 0x27, 0x46, 0x20, 0x37, +0x38, 0x7C, 0x02, 0x28, 0x29, 0xD2, 0x0C, 0x23, +0x0A, 0x22, 0x2E, 0x21, 0x2C, 0x20, 0xE3, 0x5E, +0xA2, 0x5E, 0x61, 0x5E, 0x20, 0x5E, 0x04, 0xF0, +0x95, 0xF9, 0x29, 0x68, 0xB0, 0x31, 0xCA, 0x7B, +0x8B, 0x7B, 0x11, 0x02, 0x19, 0x43, 0x81, 0x42, +0x14, 0xD2, 0x38, 0x7C, 0x2C, 0x22, 0x0A, 0x23, +0xA2, 0x5E, 0x02, 0x21, 0xE3, 0x5E, 0x09, 0x1A, +0x4A, 0x43, 0x43, 0x43, 0xD2, 0x18, 0x52, 0x10, +0x62, 0x81, 0x2E, 0x22, 0xA2, 0x5E, 0x4A, 0x43, +0x0C, 0x21, 0x61, 0x5E, 0x41, 0x43, 0x50, 0x18, +0x40, 0x10, 0xA0, 0x81, 0x38, 0x7C, 0x40, 0x1C, +0x38, 0x74, 0x76, 0x1C, 0xF6, 0xB2, 0x02, 0x48, +0x01, 0x78, 0xB1, 0x42, 0xC6, 0xD8, 0xF8, 0xBD, +0xA0, 0x04, 0x00, 0x20, 0x40, 0x05, 0x00, 0x20, +0x34, 0x00, 0x00, 0x20, 0x36, 0x00, 0x00, 0x20, +0xB8, 0x02, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, +0x46, 0x00, 0x00, 0x20, 0x47, 0x00, 0x00, 0x20, +0x2A, 0x00, 0x00, 0x20, 0x3E, 0x00, 0x00, 0x20, +0x31, 0x00, 0x00, 0x20, 0xF1, 0xB5, 0x59, 0x48, +0x8A, 0xB0, 0x00, 0x68, 0x08, 0x90, 0x1B, 0x30, +0x57, 0x4B, 0x09, 0x90, 0x00, 0x20, 0x18, 0x5E, +0xC1, 0x0F, 0x08, 0x18, 0xC0, 0x03, 0x01, 0x0C, +0x08, 0x98, 0x00, 0x91, 0x90, 0x30, 0x41, 0x7B, +0x02, 0x7B, 0x08, 0x02, 0x10, 0x43, 0x00, 0x99, +0x07, 0x90, 0x88, 0x42, 0x00, 0xD9, 0x00, 0x90, +0x4F, 0x48, 0x4E, 0x4A, 0x02, 0x80, 0x4F, 0x49, +0xD0, 0x43, 0x08, 0x80, 0x4E, 0x49, 0x00, 0x23, +0x0A, 0x80, 0x49, 0x49, 0x1E, 0x46, 0x08, 0x80, +0x4C, 0x49, 0x0B, 0x70, 0x01, 0x21, 0xC9, 0x03, +0x74, 0x00, 0x01, 0xA8, 0x01, 0x53, 0x48, 0x1E, +0x03, 0xA9, 0x08, 0x53, 0x00, 0x20, 0x01, 0x46, +0x35, 0x46, 0x1C, 0xE0, 0x09, 0x9A, 0x52, 0x5D, +0x41, 0x2A, 0x16, 0xD0, 0x0A, 0x9A, 0x6B, 0x00, +0xD2, 0x5E, 0x01, 0xAB, 0x49, 0x1C, 0x1F, 0x5F, +0x80, 0x18, 0xC9, 0xB2, 0x97, 0x42, 0x00, 0xDA, +0x1A, 0x53, 0x03, 0xAB, 0x1F, 0x5F, 0x97, 0x42, +0x00, 0xDD, 0x1A, 0x53, 0x00, 0x9B, 0x9A, 0x42, +0x03, 0xDD, 0x3A, 0x4B, 0x1A, 0x78, 0x52, 0x1C, +0x1A, 0x70, 0x2D, 0x1D, 0xED, 0xB2, 0x30, 0x2D, +0xE0, 0xD3, 0x00, 0x29, 0x02, 0xD0, 0xFF, 0xF7, +0x4B, 0xF8, 0x00, 0xE0, 0x00, 0x20, 0x05, 0xA9, +0x08, 0x53, 0x2D, 0x49, 0x01, 0xAD, 0x00, 0x22, +0x28, 0x5F, 0x8A, 0x5E, 0x90, 0x42, 0x00, 0xDD, +0x08, 0x80, 0x2D, 0x49, 0x03, 0xAF, 0x00, 0x22, +0x38, 0x5F, 0x8A, 0x5E, 0x90, 0x42, 0x00, 0xDA, +0x08, 0x80, 0x76, 0x1C, 0xF6, 0xB2, 0x04, 0x2E, +0xB8, 0xD3, 0x07, 0x98, 0x26, 0x49, 0x43, 0x08, +0x00, 0x20, 0x08, 0x5E, 0x26, 0x4A, 0x83, 0x42, +0x06, 0xDA, 0x1F, 0x49, 0x00, 0x23, 0xCB, 0x5E, +0x19, 0x1A, 0x07, 0x98, 0x81, 0x42, 0x06, 0xDB, +0x08, 0x98, 0x80, 0x30, 0x81, 0x7B, 0x1F, 0x48, +0x00, 0x78, 0x81, 0x42, 0x01, 0xD9, 0x01, 0x20, +0x00, 0xE0, 0x00, 0x20, 0x10, 0x70, 0x00, 0x24, +0x05, 0xAE, 0x60, 0x00, 0x31, 0x5E, 0x00, 0x91, +0x3B, 0x5E, 0x2A, 0x5E, 0x21, 0x46, 0x0A, 0x98, +0x00, 0xF0, 0x5C, 0xF9, 0x64, 0x1C, 0xE4, 0xB2, +0x04, 0x2C, 0xF2, 0xD3, 0x0D, 0x48, 0x00, 0x68, +0x90, 0x30, 0x41, 0x7B, 0x02, 0x7B, 0x08, 0x02, +0x10, 0x43, 0x0E, 0x49, 0x00, 0x22, 0x8A, 0x5E, +0x82, 0x42, 0x0D, 0xDA, 0x0F, 0x48, 0x41, 0x88, +0x0A, 0x29, 0x09, 0xD9, 0x40, 0x88, 0x0B, 0x28, +0x03, 0xD1, 0x80, 0x21, 0x0C, 0x48, 0xFF, 0xF7, +0x20, 0xF8, 0x00, 0x20, 0x0B, 0xB0, 0xF0, 0xBD, +0x01, 0x20, 0xFB, 0xE7, 0xB8, 0x02, 0x00, 0x20, +0x7C, 0x04, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, +0x76, 0x04, 0x00, 0x20, 0x78, 0x04, 0x00, 0x20, +0x7A, 0x04, 0x00, 0x20, 0x8F, 0x02, 0x00, 0x20, +0x9A, 0x01, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, +0xF4, 0x05, 0x00, 0x20, 0xF0, 0xB5, 0x00, 0x20, +0x41, 0x1F, 0x8C, 0x46, 0x1E, 0x49, 0x05, 0x24, +0x09, 0x78, 0xC9, 0x07, 0x36, 0xD1, 0x1D, 0x49, +0x09, 0x68, 0x90, 0x31, 0x4A, 0x7B, 0x0B, 0x7B, +0x11, 0x02, 0x1B, 0x4A, 0x19, 0x43, 0x13, 0x5E, +0x99, 0x42, 0x06, 0xDA, 0x19, 0x49, 0x09, 0x88, +0x00, 0x29, 0x01, 0xD0, 0x0A, 0x24, 0x00, 0xE0, +0x5A, 0x24, 0x17, 0x4F, 0x17, 0x4D, 0x00, 0x26, +0x17, 0x4A, 0x41, 0x00, 0x52, 0x5E, 0xC9, 0x19, +0x00, 0x23, 0xCB, 0x5E, 0x9A, 0x42, 0x02, 0xDD, +0x2A, 0x5C, 0x52, 0x1C, 0x03, 0xE0, 0x9A, 0x42, +0x03, 0xDA, 0x2A, 0x5C, 0x52, 0x1E, 0x2A, 0x54, +0x00, 0xE0, 0x2E, 0x54, 0x2A, 0x56, 0xA2, 0x42, +0x02, 0xDB, 0x0A, 0x88, 0x52, 0x1C, 0x03, 0xE0, +0x62, 0x45, 0x03, 0xDC, 0x0A, 0x88, 0x52, 0x1E, +0x0A, 0x80, 0x2E, 0x54, 0x40, 0x1C, 0xC0, 0xB2, +0x30, 0x28, 0xDD, 0xD3, 0xF0, 0xBD, 0x00, 0x00, +0xEB, 0x02, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, +0x78, 0x04, 0x00, 0x20, 0xF0, 0x02, 0x00, 0x20, +0x00, 0x00, 0x01, 0x20, 0x0C, 0x03, 0x00, 0x20, +0xD4, 0x00, 0x00, 0x20, 0xFE, 0xB5, 0x04, 0x46, +0x37, 0x48, 0x00, 0x78, 0x00, 0x28, 0x46, 0xD0, +0x1E, 0x20, 0x00, 0x90, 0x40, 0x42, 0x01, 0x90, +0x00, 0x29, 0x12, 0xD0, 0x33, 0x48, 0x00, 0x78, +0x15, 0x28, 0x40, 0xD0, 0x32, 0x4B, 0x00, 0x20, +0x41, 0x00, 0x5A, 0x5A, 0x40, 0x1C, 0xD5, 0x00, +0xAA, 0x1A, 0x65, 0x5A, 0xC0, 0xB2, 0x52, 0x19, +0x12, 0xB2, 0xD2, 0x10, 0x5A, 0x52, 0x30, 0x28, +0xF2, 0xD3, 0x00, 0x25, 0x00, 0x21, 0x0A, 0x46, +0x08, 0x46, 0x2A, 0x4E, 0x2B, 0x18, 0x36, 0x68, +0x9E, 0x19, 0xF6, 0x7E, 0x41, 0x2E, 0x19, 0xD0, +0x5F, 0x00, 0x25, 0x4B, 0xE6, 0x5F, 0xDB, 0x5F, +0xB4, 0x46, 0xF3, 0x1A, 0xDE, 0x0F, 0xF3, 0x18, +0x5B, 0x10, 0x00, 0x9E, 0x1B, 0xB2, 0xB3, 0x42, +0x02, 0xDA, 0x66, 0x46, 0xF6, 0x1A, 0xE6, 0x53, +0x00, 0x9E, 0xB3, 0x42, 0x04, 0xDA, 0x01, 0x9E, +0xB3, 0x42, 0x01, 0xDD, 0xD2, 0x18, 0x12, 0xB2, +0x49, 0x1C, 0xC9, 0xB2, 0x40, 0x1C, 0xC0, 0xB2, +0x03, 0x28, 0xDA, 0xD3, 0x00, 0x29, 0x21, 0xD0, +0x01, 0x29, 0x0A, 0xD0, 0x0A, 0xE0, 0x0F, 0x20, +0x00, 0x90, 0x40, 0x42, 0xB7, 0xE7, 0x60, 0x22, +0x21, 0x46, 0x11, 0x48, 0xFE, 0xF7, 0x48, 0xFF, +0xC7, 0xE7, 0x02, 0x21, 0x10, 0x46, 0xFE, 0xF7, +0x2F, 0xFF, 0x02, 0xB2, 0x0D, 0x4B, 0x00, 0x20, +0x1E, 0x68, 0x29, 0x18, 0x8E, 0x19, 0xF6, 0x7E, +0x41, 0x2E, 0x03, 0xD0, 0x49, 0x00, 0x66, 0x5A, +0xB6, 0x1A, 0x66, 0x52, 0x40, 0x1C, 0xC0, 0xB2, +0x03, 0x28, 0xF1, 0xD3, 0x2D, 0x1D, 0xED, 0xB2, +0x30, 0x2D, 0xAF, 0xD3, 0xFE, 0xBD, 0x00, 0x00, +0x85, 0x02, 0x00, 0x20, 0x70, 0x04, 0x00, 0x20, +0x3C, 0x03, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, +0x10, 0xB5, 0x21, 0x49, 0x1F, 0x48, 0xC8, 0x61, +0x20, 0x48, 0x81, 0x68, 0x01, 0x22, 0x52, 0x02, +0x11, 0x43, 0x81, 0x60, 0x1E, 0x4C, 0x20, 0x68, +0x30, 0x21, 0x88, 0x43, 0x20, 0x60, 0x1D, 0x48, +0x00, 0x78, 0x00, 0x28, 0x11, 0xD0, 0x1C, 0xA0, +0x03, 0xF0, 0xA4, 0xFA, 0x1C, 0x48, 0x01, 0x68, +0x4A, 0x06, 0x90, 0x21, 0x00, 0x2A, 0x02, 0x68, +0x02, 0xDA, 0x0A, 0x43, 0x02, 0x60, 0x10, 0xBD, +0x8A, 0x43, 0x02, 0x60, 0x17, 0x49, 0x41, 0x61, +0x10, 0xBD, 0x17, 0xA0, 0x03, 0xF0, 0x92, 0xFA, +0x05, 0x21, 0x17, 0x48, 0x09, 0x07, 0x48, 0x62, +0x80, 0x07, 0x41, 0x6A, 0x82, 0x13, 0x11, 0x43, +0x41, 0x62, 0x14, 0x49, 0x0A, 0x68, 0x03, 0x12, +0x1A, 0x43, 0x0A, 0x60, 0xE2, 0x68, 0x09, 0x68, +0x0A, 0x43, 0xE2, 0x60, 0x01, 0x68, 0x02, 0x14, +0x11, 0x43, 0x01, 0x60, 0x0E, 0x49, 0x40, 0x14, +0x08, 0x60, 0x10, 0xBD, 0x0B, 0x0B, 0x0B, 0x0B, +0x40, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50, +0x00, 0x10, 0x00, 0x50, 0xCF, 0x00, 0x00, 0x20, +0x41, 0x53, 0x49, 0x0D, 0x0A, 0x00, 0x00, 0x00, +0x00, 0x11, 0x00, 0x50, 0x16, 0x00, 0x03, 0x00, +0x41, 0x53, 0x54, 0x0D, 0x0A, 0x00, 0x00, 0x00, +0xF1, 0x2A, 0x02, 0x00, 0x50, 0x02, 0x00, 0x20, +0x00, 0xE1, 0x00, 0xE0, 0xFF, 0xB5, 0x04, 0x46, +0x55, 0x48, 0x1D, 0x46, 0x02, 0x68, 0x00, 0x21, +0x17, 0x46, 0x1B, 0x32, 0x94, 0x46, 0x3A, 0x46, +0x80, 0x32, 0x53, 0x7A, 0x16, 0x7A, 0x1A, 0x06, +0x12, 0x14, 0x03, 0x23, 0x32, 0x43, 0xDB, 0x03, +0x5A, 0x43, 0x12, 0x14, 0x96, 0x46, 0x4D, 0x4A, +0x83, 0xB0, 0x16, 0x78, 0x3A, 0x46, 0x90, 0x32, +0x57, 0x7B, 0x12, 0x7B, 0x3F, 0x02, 0x3A, 0x43, +0x08, 0x46, 0x00, 0x2E, 0x03, 0xD1, 0x48, 0x4B, +0x1B, 0x78, 0x14, 0x2B, 0x01, 0xD2, 0x53, 0x08, +0x00, 0xE0, 0x93, 0x08, 0x00, 0x93, 0x45, 0x4B, +0x76, 0x46, 0x1E, 0x80, 0x0C, 0x9B, 0x00, 0x2B, +0x07, 0xDA, 0x43, 0x4E, 0x00, 0x27, 0xF7, 0x5F, +0x00, 0x9E, 0xB7, 0x42, 0x01, 0xDA, 0x53, 0x42, +0x01, 0xE0, 0x53, 0x08, 0x5B, 0x42, 0x0C, 0x9F, +0x1B, 0xB2, 0x05, 0x9E, 0x01, 0x93, 0xF6, 0x1B, +0x3B, 0x46, 0xEF, 0x1B, 0x00, 0x2E, 0x00, 0xDA, +0x76, 0x42, 0x00, 0x2F, 0x00, 0xDA, 0x7F, 0x42, +0xBE, 0x42, 0x01, 0xDA, 0x00, 0x2B, 0x15, 0xDD, +0x00, 0x9B, 0x0C, 0x9E, 0xDF, 0x00, 0xF3, 0x1B, +0xAB, 0x42, 0x06, 0xDD, 0x00, 0x2D, 0x04, 0xDA, +0x05, 0x9B, 0x5B, 0x19, 0xD2, 0x18, 0x52, 0x10, +0x01, 0xE0, 0x00, 0x9A, 0xAA, 0x18, 0x13, 0xB2, +0x72, 0x46, 0x73, 0x45, 0x00, 0xDD, 0x13, 0x46, +0x04, 0x9A, 0x19, 0xE0, 0x05, 0x9B, 0x9A, 0x1A, +0x13, 0xB2, 0x72, 0x46, 0x52, 0x42, 0x93, 0x42, +0x00, 0xDA, 0x13, 0xB2, 0x04, 0x9A, 0x1F, 0xE0, +0x66, 0x46, 0xB6, 0x5C, 0x41, 0x2E, 0x09, 0xD0, +0x56, 0x00, 0xA6, 0x5F, 0x9E, 0x42, 0x05, 0xDA, +0x01, 0x9F, 0xBE, 0x42, 0x02, 0xDD, 0x30, 0x18, +0x49, 0x1C, 0xC9, 0xB2, 0x12, 0x1D, 0xD2, 0xB2, +0x30, 0x2A, 0xED, 0xD3, 0x0E, 0xE0, 0x66, 0x46, +0xB6, 0x5C, 0x41, 0x2E, 0x06, 0xD0, 0x56, 0x00, +0xA6, 0x5F, 0x9E, 0x42, 0x02, 0xDD, 0x30, 0x18, +0x49, 0x1C, 0xC9, 0xB2, 0x12, 0x1D, 0xD2, 0xB2, +0x30, 0x2A, 0xF0, 0xD3, 0x00, 0x29, 0x02, 0xD0, +0xFE, 0xF7, 0x22, 0xFE, 0x01, 0x46, 0x04, 0x98, +0x05, 0xE0, 0x42, 0x00, 0xA3, 0x5A, 0x5B, 0x1A, +0x00, 0x1D, 0xA3, 0x52, 0xC0, 0xB2, 0x30, 0x28, +0xF7, 0xD3, 0x0E, 0x4A, 0x00, 0x23, 0x68, 0x1A, +0xD3, 0x5E, 0x00, 0xB2, 0x98, 0x42, 0x00, 0xDA, +0x10, 0x80, 0x05, 0x98, 0x00, 0x22, 0x40, 0x1A, +0x09, 0x49, 0x00, 0xB2, 0x8A, 0x5E, 0x90, 0x42, +0x00, 0xDD, 0x08, 0x80, 0x07, 0xB0, 0xF0, 0xBD, +0xB8, 0x02, 0x00, 0x20, 0x9A, 0x01, 0x00, 0x20, +0xEA, 0x02, 0x00, 0x20, 0xEE, 0x02, 0x00, 0x20, +0x7C, 0x04, 0x00, 0x20, 0x76, 0x04, 0x00, 0x20, +0x78, 0x04, 0x00, 0x20, 0xF8, 0xB5, 0x06, 0x46, +0x0D, 0x46, 0x30, 0x23, 0xFF, 0xF7, 0x80, 0xF8, +0x04, 0x00, 0x28, 0xD0, 0x19, 0x4F, 0x00, 0x2A, +0x21, 0xD0, 0x00, 0x20, 0xFE, 0xF7, 0xCA, 0xFE, +0x02, 0xE0, 0x98, 0x00, 0x29, 0x58, 0x21, 0x50, +0x18, 0x46, 0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, +0xF7, 0xD1, 0x38, 0x68, 0x01, 0x88, 0x31, 0x43, +0x01, 0x80, 0x11, 0x49, 0x09, 0x68, 0xCA, 0x79, +0x8B, 0x79, 0x11, 0x02, 0x19, 0x43, 0x41, 0x80, +0xC1, 0x21, 0x89, 0x00, 0x0D, 0x48, 0xFF, 0xF7, +0xDF, 0xF8, 0x0D, 0x49, 0xC8, 0x63, 0x01, 0x20, +0xFE, 0xF7, 0xAC, 0xFE, 0x0C, 0xE0, 0x38, 0x68, +0x00, 0x88, 0x30, 0x40, 0x03, 0xD1, 0xF8, 0xBD, +0x98, 0x00, 0x21, 0x58, 0x29, 0x50, 0x18, 0x46, +0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, 0xF7, 0xD1, +0x01, 0x20, 0xF8, 0xBD, 0x08, 0x00, 0x00, 0x20, +0xB8, 0x02, 0x00, 0x20, 0x78, 0x7C, 0x00, 0x00, +0x40, 0x7F, 0x00, 0x00, 0x70, 0xB5, 0x17, 0x48, +0x5A, 0x25, 0x01, 0x78, 0x16, 0x48, 0x00, 0x29, +0x25, 0xD0, 0x09, 0x21, 0x01, 0x70, 0x15, 0x4C, +0x01, 0x21, 0xE0, 0x89, 0x89, 0x02, 0x88, 0x42, +0x01, 0xD0, 0x03, 0x20, 0x60, 0x71, 0x11, 0x48, +0x30, 0x21, 0x28, 0x30, 0xFE, 0xF7, 0xC9, 0xFD, +0x00, 0x20, 0x60, 0x72, 0xA0, 0x71, 0x60, 0x62, +0x16, 0x21, 0x21, 0x72, 0x0C, 0x49, 0x20, 0x71, +0x08, 0x70, 0xA5, 0x81, 0x60, 0x81, 0x60, 0x8A, +0x09, 0x21, 0x09, 0x03, 0x08, 0x43, 0x60, 0x82, +0x03, 0x20, 0x40, 0x02, 0xE0, 0x61, 0x0D, 0x20, +0xC0, 0x01, 0x20, 0x62, 0x70, 0xBD, 0x05, 0x70, +0xD9, 0xE7, 0x00, 0x00, 0x85, 0x02, 0x00, 0x20, +0xB5, 0x02, 0x00, 0x20, 0xE4, 0x02, 0x00, 0x20, +0x87, 0x02, 0x00, 0x20, 0x0E, 0x48, 0x03, 0x21, +0x41, 0x71, 0x0E, 0x49, 0x41, 0x61, 0x0D, 0x49, +0x60, 0x31, 0x81, 0x61, 0x01, 0x21, 0x01, 0x70, +0x07, 0x22, 0x42, 0x70, 0x0A, 0x4B, 0x05, 0x22, +0x1A, 0x70, 0x0A, 0x4B, 0x1A, 0x70, 0x0A, 0x4B, +0x55, 0x22, 0xDA, 0x70, 0x04, 0x22, 0x02, 0x82, +0x00, 0x22, 0xC2, 0x70, 0x09, 0x22, 0x12, 0x03, +0x42, 0x82, 0x81, 0x70, 0x70, 0x47, 0x00, 0x00, +0xE4, 0x02, 0x00, 0x20, 0x00, 0x00, 0x04, 0x20, +0x85, 0x02, 0x00, 0x20, 0x71, 0x04, 0x00, 0x20, +0x90, 0x02, 0x00, 0x20, 0x04, 0x22, 0x0F, 0x49, +0x0C, 0x28, 0x10, 0xD0, 0x8B, 0x05, 0x0D, 0x28, +0x08, 0x6A, 0x10, 0xD0, 0x18, 0x43, 0x08, 0x62, +0x88, 0x6A, 0x10, 0x43, 0x88, 0x62, 0x0A, 0x4A, +0x01, 0x20, 0x10, 0x70, 0xC8, 0x68, 0xC8, 0x60, +0x88, 0x6A, 0x88, 0x62, 0x70, 0x47, 0x08, 0x6A, +0x40, 0x00, 0x40, 0x08, 0x00, 0xE0, 0x18, 0x43, +0x08, 0x62, 0x88, 0x6A, 0x90, 0x43, 0x88, 0x62, +0xF0, 0xE7, 0x00, 0x00, 0x00, 0x06, 0x00, 0x50, +0x9B, 0x01, 0x00, 0x20, 0x00, 0xB5, 0x08, 0x49, +0x0A, 0x28, 0x05, 0xD0, 0x07, 0x48, 0x00, 0x0C, +0x48, 0x63, 0x07, 0x48, 0x08, 0x63, 0x00, 0xBD, +0x06, 0x48, 0x00, 0x68, 0x08, 0x62, 0x0D, 0x20, +0xFF, 0xF7, 0xCC, 0xFF, 0x00, 0xBD, 0x00, 0x00, +0x00, 0x06, 0x00, 0x50, 0xBC, 0x02, 0x00, 0x20, +0xCC, 0x02, 0x00, 0x20, 0xC0, 0x02, 0x00, 0x20, +0x10, 0xB5, 0x1B, 0x49, 0x00, 0x20, 0x03, 0x00, +0xFE, 0xF7, 0xA0, 0xFD, 0x0C, 0x07, 0x0A, 0x0E, +0x26, 0x26, 0x11, 0x14, 0x17, 0x1A, 0x1D, 0x20, +0x23, 0x26, 0x16, 0x4A, 0x0A, 0x80, 0x1E, 0xE0, +0x14, 0x4A, 0x12, 0x1D, 0x4A, 0x80, 0x1A, 0xE0, +0x13, 0x4A, 0x8A, 0x80, 0x17, 0xE0, 0x13, 0x4A, +0x4A, 0x81, 0x14, 0xE0, 0x12, 0x4A, 0x8A, 0x81, +0x11, 0xE0, 0x12, 0x4A, 0xCA, 0x81, 0x0E, 0xE0, +0x11, 0x4A, 0x0A, 0x82, 0x0B, 0xE0, 0x11, 0x4A, +0x4A, 0x82, 0x08, 0xE0, 0x10, 0x4A, 0x8A, 0x82, +0x05, 0xE0, 0x10, 0x4A, 0xCA, 0x82, 0x02, 0xE0, +0x0F, 0x4A, 0x43, 0x00, 0xCA, 0x52, 0x40, 0x1C, +0x80, 0xB2, 0x0C, 0x28, 0xCF, 0xD3, 0x0D, 0xA0, +0x03, 0xF0, 0x88, 0xF8, 0x10, 0xBD, 0x00, 0x00, +0xCC, 0x02, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, +0x88, 0x02, 0x00, 0x20, 0x34, 0x01, 0x00, 0x20, +0xE0, 0x06, 0x00, 0x20, 0xE4, 0x06, 0x00, 0x20, +0xD8, 0x06, 0x00, 0x20, 0xC0, 0x02, 0x00, 0x20, +0xBC, 0x02, 0x00, 0x20, 0x9C, 0x01, 0x00, 0x20, +0xC4, 0x02, 0x00, 0x20, 0x49, 0x32, 0x43, 0x20, +0x4F, 0x4B, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0x70, 0xB5, 0x01, 0x25, 0xCA, 0x07, 0x0A, 0xD0, +0x00, 0x20, 0x70, 0xBD, 0x93, 0x00, 0xC4, 0x58, +0x66, 0x1C, 0x02, 0xD0, 0x1B, 0x18, 0x5B, 0x68, +0x23, 0x60, 0x92, 0x1C, 0x92, 0xB2, 0x8A, 0x42, +0xF4, 0xD3, 0x28, 0x46, 0x70, 0xBD, 0x00, 0x00, +0x10, 0xB5, 0x1D, 0x49, 0x0A, 0x68, 0x1C, 0x48, +0x40, 0x30, 0x02, 0x61, 0x4A, 0x68, 0x42, 0x61, +0x8A, 0x68, 0x82, 0x61, 0xC9, 0x68, 0xC1, 0x61, +0x82, 0x69, 0xF0, 0x21, 0x8A, 0x43, 0x82, 0x61, +0x82, 0x69, 0x0A, 0x43, 0x82, 0x61, 0x41, 0x69, +0x49, 0x04, 0x04, 0xD5, 0x41, 0x69, 0x01, 0x22, +0x52, 0x03, 0x89, 0x1A, 0x41, 0x61, 0x10, 0x48, +0x40, 0x38, 0x01, 0x68, 0x01, 0x22, 0x12, 0x06, +0x11, 0x43, 0x01, 0x60, 0x30, 0x21, 0x0D, 0x48, +0xFF, 0xF7, 0xC6, 0xFF, 0x0B, 0x48, 0x40, 0x21, +0xC0, 0x30, 0xFF, 0xF7, 0xC1, 0xFF, 0x0B, 0x20, +0xFE, 0xF7, 0xD4, 0xFC, 0x03, 0x20, 0xFE, 0xF7, +0xD1, 0xFC, 0x00, 0x20, 0xFE, 0xF7, 0xCE, 0xFC, +0x05, 0x20, 0xFE, 0xF7, 0xCB, 0xFC, 0x09, 0x20, +0xFE, 0xF7, 0xC8, 0xFC, 0x01, 0x20, 0x10, 0xBD, +0x40, 0x14, 0x00, 0x50, 0x98, 0x5A, 0x00, 0x00, +0x0E, 0x4A, 0x00, 0x21, 0x11, 0x60, 0x0E, 0x4A, +0x20, 0x21, 0x11, 0x60, 0x5A, 0x28, 0x13, 0xD0, +0x0C, 0x48, 0x01, 0x23, 0x00, 0x05, 0x00, 0x0D, +0x5B, 0x03, 0xC0, 0x18, 0x05, 0x22, 0x12, 0x07, +0x10, 0x62, 0x90, 0x00, 0xC2, 0x68, 0x0A, 0x43, +0xC2, 0x60, 0x05, 0x4A, 0x80, 0x3A, 0x11, 0x60, +0x41, 0x68, 0x19, 0x43, 0x41, 0x60, 0x70, 0x47, +0xFF, 0x20, 0xEA, 0xE7, 0xD0, 0x00, 0x00, 0x20, +0x80, 0xE1, 0x00, 0xE0, 0x24, 0x09, 0x00, 0x00, +0x10, 0xB5, 0x17, 0x4A, 0x11, 0x78, 0x81, 0x42, +0x29, 0xD0, 0x16, 0x49, 0x0B, 0x68, 0x01, 0x24, +0xA4, 0x02, 0x23, 0x43, 0x0B, 0x60, 0x10, 0x70, +0x05, 0x22, 0x40, 0x24, 0x12, 0x49, 0x12, 0x07, +0x80, 0x23, 0x00, 0x28, 0x09, 0xD0, 0x08, 0x68, +0x40, 0x06, 0x0C, 0xD4, 0x08, 0x68, 0x20, 0x43, +0x08, 0x60, 0xD0, 0x68, 0x98, 0x43, 0xD0, 0x60, +0x05, 0xE0, 0xD0, 0x68, 0x18, 0x43, 0xD0, 0x60, +0x08, 0x68, 0xA0, 0x43, 0x08, 0x60, 0x08, 0x68, +0x18, 0x43, 0x08, 0x60, 0xD0, 0x68, 0x01, 0x21, +0xC9, 0x03, 0x08, 0x43, 0xD0, 0x60, 0xE1, 0x20, +0x00, 0x02, 0x01, 0xF0, 0x61, 0xF8, 0x10, 0xBD, +0x04, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, +0x00, 0x11, 0x00, 0x50, 0x10, 0xB5, 0x10, 0x4C, +0xA0, 0x79, 0x00, 0x28, 0x18, 0xD1, 0x00, 0xF0, +0xA5, 0xFF, 0x00, 0xF0, 0xA7, 0xFE, 0xFF, 0xF7, +0xF7, 0xFC, 0x0C, 0x48, 0x01, 0x78, 0x0C, 0x48, +0x00, 0x29, 0x02, 0xD0, 0x09, 0x21, 0x09, 0x02, +0x00, 0xE0, 0x0A, 0x49, 0x01, 0x60, 0x01, 0x20, +0xA0, 0x71, 0xE0, 0x71, 0x08, 0x48, 0x00, 0x78, +0xC0, 0x07, 0x02, 0xD0, 0x00, 0xF0, 0x0E, 0xF8, +0x10, 0xBD, 0x00, 0xF0, 0x77, 0xFE, 0x10, 0xBD, +0x84, 0x04, 0x00, 0x20, 0xCF, 0x00, 0x00, 0x20, +0x00, 0x10, 0x00, 0x50, 0x04, 0x08, 0x00, 0x00, +0xCE, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x1D, 0x4C, +0xA0, 0x79, 0x00, 0x28, 0x35, 0xD0, 0x1C, 0xA0, +0x02, 0xF0, 0x88, 0xFF, 0x00, 0x20, 0xA0, 0x71, +0xE0, 0x71, 0x1E, 0x48, 0x81, 0x68, 0x01, 0x22, +0x52, 0x02, 0x91, 0x43, 0x81, 0x60, 0x1C, 0x48, +0x01, 0x68, 0x92, 0x00, 0x91, 0x43, 0x01, 0x60, +0x1A, 0x48, 0x01, 0x68, 0x10, 0x22, 0x91, 0x43, +0x01, 0x60, 0x01, 0x68, 0x80, 0x22, 0x11, 0x43, +0x01, 0x60, 0x17, 0x49, 0x41, 0x61, 0x17, 0x48, +0x00, 0x78, 0x00, 0x28, 0x15, 0xD1, 0x05, 0x20, +0x00, 0x07, 0x41, 0x6A, 0x92, 0x02, 0x91, 0x43, +0x41, 0x62, 0x41, 0x6A, 0x09, 0x22, 0x12, 0x05, +0x11, 0x43, 0x41, 0x62, 0x80, 0x00, 0xC2, 0x68, +0x41, 0x14, 0x8A, 0x43, 0xC2, 0x60, 0x02, 0x68, +0x03, 0x14, 0x9A, 0x43, 0x02, 0x60, 0x0C, 0x48, +0x01, 0x60, 0x10, 0xBD, 0x84, 0x04, 0x00, 0x20, +0x4C, 0x65, 0x61, 0x76, 0x65, 0x20, 0x41, 0x75, +0x74, 0x6F, 0x53, 0x63, 0x61, 0x6E, 0x0D, 0x0A, +0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x50, +0x00, 0x10, 0x00, 0x50, 0x00, 0x11, 0x00, 0x50, +0xC8, 0x03, 0x60, 0x00, 0xCF, 0x00, 0x00, 0x20, +0x80, 0xE1, 0x00, 0xE0, 0x00, 0xB5, 0x08, 0x49, +0x83, 0x20, 0x08, 0x70, 0x07, 0x49, 0x00, 0x20, +0x08, 0x70, 0x07, 0x48, 0x00, 0x68, 0x07, 0x49, +0x40, 0x05, 0x40, 0x0F, 0x08, 0x73, 0x5A, 0x20, +0xFF, 0xF7, 0x16, 0xFF, 0x00, 0xBD, 0x00, 0x00, +0x84, 0x04, 0x00, 0x20, 0xCE, 0x00, 0x00, 0x20, +0x00, 0x11, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, +0xF0, 0xB5, 0x04, 0x46, 0x8F, 0xB0, 0xA6, 0x48, +0x0A, 0x90, 0xA5, 0x48, 0xA6, 0x4D, 0x60, 0x30, +0x0B, 0x90, 0xA4, 0x48, 0x03, 0x90, 0xA5, 0x48, +0x05, 0x95, 0x01, 0x68, 0x22, 0x46, 0xA4, 0xA0, +0x02, 0xF0, 0x10, 0xFF, 0x03, 0x98, 0x08, 0x90, +0xA5, 0x48, 0xA6, 0x4A, 0x00, 0x21, 0x09, 0x95, +0x8B, 0x00, 0x08, 0x9D, 0x49, 0x1C, 0xE8, 0x50, +0x09, 0x9D, 0x89, 0xB2, 0xEA, 0x50, 0x18, 0x29, +0xF6, 0xD3, 0x01, 0x20, 0x80, 0x02, 0x06, 0x90, +0x20, 0x46, 0x07, 0x94, 0x50, 0x30, 0x00, 0xB2, +0x50, 0x3C, 0x0C, 0x90, 0x20, 0xB2, 0x0D, 0x90, +0x00, 0xF0, 0x6E, 0xFC, 0x00, 0xF0, 0xE2, 0xFE, +0x00, 0xF0, 0x6E, 0xF9, 0x98, 0x48, 0x00, 0x78, +0xC0, 0x07, 0x18, 0xD1, 0x01, 0x90, 0x00, 0x20, +0x08, 0x9A, 0x81, 0x00, 0x53, 0x58, 0x0A, 0x9A, +0x40, 0x1C, 0x53, 0x50, 0x09, 0x9A, 0x80, 0xB2, +0x53, 0x58, 0x0B, 0x9A, 0x18, 0x28, 0x53, 0x50, +0xF2, 0xD3, 0x00, 0xF0, 0xB7, 0xFD, 0x00, 0xF0, +0xC9, 0xFE, 0x00, 0xF0, 0x55, 0xF9, 0x8C, 0x48, +0x00, 0x78, 0xC0, 0x07, 0x02, 0xD0, 0x00, 0x20, +0x0F, 0xB0, 0xF0, 0xBD, 0x3B, 0x46, 0x89, 0xA0, +0x00, 0x9A, 0x01, 0x99, 0x02, 0xF0, 0xCA, 0xFE, +0x00, 0x25, 0x2D, 0xE0, 0x00, 0x2D, 0x2B, 0xD0, +0x01, 0x98, 0x05, 0x28, 0x73, 0xD9, 0x30, 0x2F, +0x71, 0xD2, 0x7A, 0x4C, 0x05, 0x98, 0x40, 0x3C, +0x04, 0x90, 0x60, 0x6B, 0x03, 0x26, 0x36, 0x05, +0x01, 0x21, 0xB0, 0x43, 0x49, 0x05, 0x40, 0x18, +0x60, 0x63, 0x00, 0xF0, 0x8F, 0xFD, 0x00, 0xF0, +0xA1, 0xFE, 0x00, 0xF0, 0x2D, 0xF9, 0x60, 0x6B, +0x01, 0x21, 0xB0, 0x43, 0x09, 0x05, 0x40, 0x18, +0x60, 0x63, 0x00, 0x27, 0x00, 0x26, 0x79, 0x48, +0x00, 0x68, 0x80, 0x19, 0xC0, 0x7E, 0x41, 0x28, +0x0E, 0xD0, 0x70, 0x00, 0x04, 0x99, 0x02, 0x90, +0x0C, 0x5A, 0x16, 0x2E, 0x12, 0xD0, 0x1A, 0xE0, +0x00, 0x98, 0x30, 0x28, 0x77, 0xD0, 0x03, 0x98, +0x04, 0x90, 0x00, 0x20, 0x00, 0x90, 0xE9, 0xE7, +0x00, 0x2D, 0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, +0x68, 0xE0, 0x00, 0x98, 0x40, 0x1C, 0x80, 0xB2, +0x00, 0x90, 0x63, 0xE0, 0x6A, 0x48, 0x2C, 0x23, +0x00, 0x68, 0x22, 0x46, 0xC3, 0x5E, 0x31, 0x46, +0x68, 0xA0, 0x02, 0xF0, 0x7F, 0xFE, 0x01, 0x98, +0x0A, 0x28, 0x0C, 0xD2, 0x64, 0x49, 0x02, 0x98, +0x09, 0x68, 0x09, 0x5E, 0x07, 0x98, 0x81, 0x42, +0x01, 0xDA, 0x06, 0x98, 0x84, 0x43, 0x06, 0x98, +0x40, 0x08, 0x04, 0x43, 0x32, 0xE0, 0x5E, 0x48, +0x02, 0x99, 0x00, 0x68, 0x41, 0x5E, 0x00, 0x29, +0x01, 0xDB, 0x08, 0x46, 0x00, 0xE0, 0x48, 0x42, +0x00, 0xB2, 0x40, 0x30, 0xC2, 0x17, 0x52, 0x0E, +0x10, 0x18, 0x0D, 0x9A, 0xC0, 0x11, 0x91, 0x42, +0x10, 0xDA, 0x21, 0x05, 0x06, 0xD5, 0x20, 0x1A, +0x84, 0xB2, 0x01, 0x20, 0xC0, 0x02, 0x84, 0x42, +0x11, 0xD3, 0x17, 0xE0, 0x84, 0x42, 0x03, 0xDD, +0x20, 0x1A, 0x84, 0xB2, 0x12, 0xE0, 0x2E, 0xE0, +0x00, 0x24, 0x0F, 0xE0, 0x0C, 0x9A, 0x91, 0x42, +0x15, 0xDD, 0x21, 0x05, 0x05, 0xD5, 0x20, 0x18, +0x84, 0xB2, 0x4D, 0x48, 0x04, 0xE0, 0x04, 0x46, +0x04, 0xE0, 0x20, 0x18, 0x84, 0xB2, 0x4B, 0x48, +0x84, 0x42, 0xF8, 0xD8, 0x00, 0x2D, 0x0F, 0xD0, +0x01, 0x20, 0xC0, 0x02, 0x04, 0x43, 0x05, 0x99, +0x02, 0x98, 0x0C, 0x52, 0x0E, 0xE0, 0x00, 0x2D, +0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, 0xF3, 0xE7, +0x00, 0x98, 0x40, 0x1C, 0x80, 0xB2, 0x00, 0x90, +0x03, 0x99, 0x02, 0x98, 0x0C, 0x52, 0x01, 0x98, +0x05, 0x28, 0xE9, 0xD9, 0x76, 0x1C, 0xB6, 0xB2, +0x30, 0x2E, 0x00, 0xD2, 0x77, 0xE7, 0x6D, 0x1C, +0xED, 0xB2, 0x02, 0x2D, 0x00, 0xD2, 0x51, 0xE7, +0x06, 0x98, 0x40, 0x08, 0x06, 0x90, 0x38, 0xA0, +0x02, 0xF0, 0x14, 0xFE, 0x00, 0x98, 0x30, 0x28, +0x03, 0xD1, 0x30, 0x2F, 0x01, 0xD1, 0x40, 0x20, +0x01, 0x90, 0x01, 0x98, 0x40, 0x1C, 0x80, 0xB2, +0x01, 0x90, 0x40, 0x28, 0x00, 0xD8, 0x1A, 0xE7, +0x1C, 0x4C, 0x40, 0x3C, 0x60, 0x6B, 0x03, 0x25, +0x2D, 0x05, 0x01, 0x21, 0xA8, 0x43, 0x09, 0x05, +0x40, 0x18, 0x60, 0x63, 0x00, 0xF0, 0xD6, 0xFC, +0x00, 0xF0, 0xE8, 0xFD, 0x00, 0xF0, 0x74, 0xF8, +0x21, 0x4E, 0x01, 0x21, 0x30, 0x68, 0x00, 0xF0, +0x31, 0xFE, 0x60, 0x6B, 0x01, 0x21, 0xA8, 0x43, +0x49, 0x05, 0x40, 0x18, 0x60, 0x63, 0x00, 0xF0, +0xC5, 0xFC, 0x00, 0xF0, 0xD7, 0xFD, 0x00, 0xF0, +0x63, 0xF8, 0x01, 0x21, 0x30, 0x68, 0x00, 0xF0, +0x21, 0xFE, 0x60, 0x6B, 0x28, 0x43, 0x60, 0x63, +0x00, 0xF0, 0xB8, 0xFC, 0x00, 0xF0, 0xCA, 0xFD, +0x00, 0xF0, 0x56, 0xF8, 0x01, 0x21, 0x30, 0x68, +0x00, 0xF0, 0x14, 0xFE, 0x01, 0x20, 0xFF, 0xE6, +0x00, 0x20, 0x00, 0x50, 0xF4, 0x05, 0x00, 0x20, +0x00, 0x00, 0x01, 0x20, 0xC0, 0x10, 0x00, 0x50, +0x5B, 0x43, 0x46, 0x42, 0x3A, 0x25, 0x78, 0x3A, +0x25, 0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, +0x00, 0x04, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x0C, +0xCE, 0x00, 0x00, 0x20, 0x5B, 0x25, 0x64, 0x3A, +0x25, 0x64, 0x3A, 0x25, 0x64, 0x5D, 0x20, 0x0D, +0x0A, 0x00, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20, +0xF8, 0x02, 0x00, 0x20, 0x25, 0x64, 0x2C, 0x30, +0x78, 0x25, 0x78, 0x2C, 0x25, 0x64, 0x2C, 0x00, +0xFF, 0x0F, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, +0x0D, 0x0A, 0x00, 0x00, 0x10, 0xB5, 0x0B, 0x49, +0x30, 0x24, 0x00, 0x28, 0x0A, 0x4A, 0x0B, 0x4B, +0x08, 0x68, 0x06, 0xD0, 0x20, 0x43, 0x08, 0x60, +0x09, 0x48, 0x10, 0x60, 0x08, 0x48, 0x60, 0x30, +0x05, 0xE0, 0xA0, 0x43, 0x08, 0x60, 0x07, 0x48, +0x10, 0x60, 0x06, 0x48, 0x60, 0x30, 0x18, 0x60, +0x10, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, +0xF8, 0x02, 0x00, 0x20, 0xFC, 0x02, 0x00, 0x20, +0x00, 0x00, 0x04, 0x20, 0x00, 0x10, 0x04, 0x20, +0xF0, 0xB5, 0x11, 0x48, 0x01, 0x68, 0x11, 0x4D, +0x89, 0x06, 0x30, 0x22, 0x60, 0x35, 0x00, 0x29, +0x0E, 0x4B, 0x0F, 0x4C, 0x0F, 0x4E, 0x10, 0x4F, +0x01, 0x68, 0x0A, 0xDA, 0x91, 0x43, 0x01, 0x60, +0x23, 0x60, 0x0E, 0x48, 0x35, 0x60, 0x07, 0x60, +0x0B, 0x48, 0x0D, 0x49, 0x60, 0x30, 0x08, 0x60, +0xF0, 0xBD, 0x11, 0x43, 0x01, 0x60, 0x08, 0x48, +0x27, 0x60, 0x60, 0x30, 0x30, 0x60, 0x07, 0x48, +0x03, 0x60, 0x07, 0x48, 0x05, 0x60, 0xF0, 0xBD, +0x00, 0x10, 0x00, 0x50, 0x00, 0x00, 0x04, 0x20, +0x64, 0x02, 0x00, 0x20, 0x68, 0x02, 0x00, 0x20, +0x00, 0x10, 0x04, 0x20, 0xF8, 0x02, 0x00, 0x20, +0xFC, 0x02, 0x00, 0x20, 0x00, 0xB5, 0x09, 0x48, +0x00, 0x78, 0x00, 0x28, 0x08, 0xD0, 0x01, 0x28, +0x09, 0xD0, 0x02, 0x28, 0x03, 0xD1, 0x06, 0x49, +0x04, 0x20, 0x00, 0xF0, 0x65, 0xF8, 0x00, 0xBD, +0x04, 0x49, 0x01, 0x20, 0xF9, 0xE7, 0x04, 0x49, +0x02, 0x20, 0xF6, 0xE7, 0x71, 0x04, 0x00, 0x20, +0xFC, 0x03, 0x00, 0x20, 0x9C, 0x03, 0x00, 0x20, +0xEC, 0x06, 0x00, 0x20, 0xF8, 0xB5, 0x07, 0x46, +0x00, 0xF0, 0xAA, 0xFA, 0x21, 0x4D, 0x60, 0x21, +0x28, 0x46, 0xFE, 0xF7, 0xAE, 0xF9, 0x00, 0x24, +0x00, 0xF0, 0x18, 0xFD, 0xFF, 0xF7, 0xA4, 0xFF, +0xFE, 0xF7, 0xF4, 0xFA, 0x00, 0x2C, 0x0C, 0xD0, +0x1B, 0x49, 0x00, 0x20, 0x0E, 0x68, 0x42, 0x00, +0x51, 0x19, 0x0B, 0x88, 0xB2, 0x5A, 0x40, 0x1C, +0x9A, 0x18, 0xC0, 0xB2, 0x0A, 0x80, 0x30, 0x28, +0xF5, 0xD3, 0x64, 0x1C, 0xE4, 0xB2, 0x05, 0x2C, +0xE6, 0xD3, 0x00, 0x20, 0x41, 0x00, 0x49, 0x19, +0x00, 0x22, 0x8A, 0x5E, 0x40, 0x1C, 0x92, 0x10, +0xC0, 0xB2, 0x0A, 0x80, 0x30, 0x28, 0xF5, 0xD3, +0x39, 0x46, 0x0E, 0xA0, 0x02, 0xF0, 0x02, 0xFD, +0x01, 0x21, 0x28, 0x46, 0x00, 0xF0, 0x3E, 0xFD, +0x01, 0x20, 0xFE, 0xF7, 0x3D, 0xFB, 0x0C, 0x4C, +0x20, 0x78, 0x38, 0x42, 0x08, 0xD1, 0x0B, 0x48, +0x60, 0x22, 0x29, 0x46, 0x00, 0x68, 0xFE, 0xF7, +0x5B, 0xF9, 0x20, 0x78, 0x38, 0x43, 0x20, 0x70, +0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, 0x01, 0x20, +0xF8, 0x02, 0x00, 0x20, 0x42, 0x43, 0x5F, 0x4F, +0x4B, 0x5F, 0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, +0x98, 0x01, 0x00, 0x20, 0x6C, 0x02, 0x00, 0x20, +0xF0, 0xB5, 0x21, 0x48, 0x00, 0x22, 0x00, 0x68, +0x20, 0x4C, 0xA0, 0x42, 0x28, 0xD8, 0x20, 0x48, +0x00, 0x78, 0x04, 0x28, 0x27, 0xD1, 0x1F, 0x4D, +0x1F, 0x4C, 0x2D, 0x68, 0x00, 0x20, 0x2E, 0x18, +0xF6, 0x7E, 0x41, 0x2E, 0x08, 0xD0, 0x43, 0x00, +0x1F, 0x19, 0x00, 0x26, 0xBE, 0x5F, 0xCB, 0x5E, +0xF3, 0x1A, 0x00, 0xD5, 0x5B, 0x42, 0x1B, 0xB2, +0x9A, 0x42, 0x00, 0xDA, 0x1A, 0x46, 0x40, 0x1C, +0xC0, 0xB2, 0x30, 0x28, 0xEB, 0xD3, 0x60, 0x20, +0x40, 0x5D, 0x90, 0x35, 0x40, 0x1C, 0x50, 0x43, +0x6A, 0x7B, 0x2B, 0x7B, 0x00, 0x11, 0x12, 0x02, +0x00, 0xB2, 0x1A, 0x43, 0x82, 0x42, 0x03, 0xDA, +0x0E, 0x49, 0x00, 0x20, 0x08, 0x70, 0xF0, 0xBD, +0x00, 0x20, 0x42, 0x00, 0x8B, 0x5E, 0x00, 0x26, +0xDD, 0x00, 0xEB, 0x1A, 0x15, 0x19, 0xAE, 0x5F, +0x40, 0x1C, 0x9B, 0x19, 0xDB, 0x10, 0xC0, 0xB2, +0x8B, 0x52, 0x30, 0x28, 0xF1, 0xD3, 0xF0, 0xBD, +0x08, 0x03, 0x00, 0x20, 0x40, 0x77, 0x1B, 0x00, +0x87, 0x02, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, +0x00, 0x00, 0x01, 0x20, 0x98, 0x01, 0x00, 0x20, +0x70, 0xB5, 0x21, 0x48, 0x21, 0x49, 0x00, 0x78, +0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x08, 0xD0, +0x02, 0x28, 0x16, 0xD0, 0x05, 0x28, 0x2F, 0xD1, +0x34, 0xE0, 0x01, 0x25, 0x2C, 0x46, 0x1C, 0x48, +0x02, 0xE0, 0x02, 0x25, 0x1B, 0x48, 0x2C, 0x46, +0x08, 0x60, 0x00, 0x22, 0x1A, 0x49, 0x28, 0x46, +0xFF, 0xF7, 0xD4, 0xFA, 0x00, 0x28, 0x18, 0xD0, +0x18, 0x48, 0x00, 0x78, 0x00, 0x28, 0x04, 0xD0, +0x13, 0xE0, 0x04, 0x25, 0x2C, 0x46, 0x16, 0x48, +0xEE, 0xE7, 0x16, 0x48, 0x41, 0x6B, 0x03, 0x22, +0x12, 0x05, 0x11, 0x43, 0x41, 0x63, 0x14, 0x48, +0x00, 0xF0, 0x30, 0xFA, 0x00, 0x28, 0x0B, 0xD1, +0x20, 0x46, 0xFF, 0xF7, 0x27, 0xFF, 0x00, 0x28, +0x06, 0xD1, 0x01, 0x22, 0x21, 0x46, 0x28, 0x46, +0x00, 0xF0, 0x1E, 0xF8, 0x00, 0x28, 0x04, 0xD0, +0x0C, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, +0x00, 0x20, 0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD, +0x85, 0x02, 0x00, 0x20, 0x6C, 0x02, 0x00, 0x20, +0x9C, 0x03, 0x00, 0x20, 0xEC, 0x06, 0x00, 0x20, +0x00, 0x20, 0x00, 0x50, 0xE7, 0x02, 0x00, 0x20, +0xFC, 0x03, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, +0x00, 0x00, 0x01, 0x20, 0xCE, 0x00, 0x00, 0x20, +0xF7, 0xB5, 0x07, 0x46, 0xFE, 0xF7, 0xD0, 0xFB, +0x14, 0x48, 0x00, 0x25, 0x05, 0x70, 0x78, 0x07, +0x1D, 0xD0, 0x01, 0x21, 0x02, 0x20, 0x00, 0xF0, +0x33, 0xFB, 0x11, 0x4C, 0x60, 0x6B, 0x03, 0x26, +0x36, 0x05, 0x01, 0x21, 0xB0, 0x43, 0x09, 0x05, +0x40, 0x18, 0x60, 0x63, 0x00, 0x20, 0xFF, 0xF7, +0xFB, 0xFC, 0x00, 0x28, 0x0D, 0xD0, 0x01, 0x98, +0xFF, 0xF7, 0xE4, 0xFE, 0x01, 0x22, 0x09, 0x49, +0x38, 0x46, 0xFF, 0xF7, 0x73, 0xFA, 0x08, 0x48, +0x05, 0x70, 0x08, 0x48, 0x05, 0x70, 0x01, 0x20, +0xFE, 0xBD, 0x60, 0x6B, 0x30, 0x43, 0x60, 0x63, +0x00, 0x20, 0xFE, 0xBD, 0x82, 0x02, 0x00, 0x20, +0x80, 0x10, 0x00, 0x50, 0x00, 0x20, 0x00, 0x50, +0xE7, 0x02, 0x00, 0x20, 0xE8, 0x02, 0x00, 0x20, +0xF0, 0xB5, 0xB4, 0x48, 0x01, 0x22, 0x23, 0x23, +0x13, 0x24, 0x92, 0x02, 0x5B, 0x01, 0xA4, 0x01, +0x05, 0x46, 0x1C, 0xC5, 0x29, 0x21, 0x49, 0x01, +0xC1, 0x60, 0xAF, 0x48, 0x07, 0x68, 0x38, 0x46, +0x40, 0x30, 0x45, 0x7C, 0x06, 0x7C, 0x2D, 0x04, +0x36, 0x02, 0x35, 0x43, 0x0C, 0x35, 0x15, 0x60, +0x45, 0x7C, 0x06, 0x7C, 0x2D, 0x04, 0x36, 0x02, +0x35, 0x43, 0x0C, 0x35, 0x55, 0x60, 0x01, 0x25, +0x95, 0x60, 0xD5, 0x60, 0x05, 0x7D, 0xBC, 0x46, +0x6E, 0x1E, 0x6D, 0x08, 0x6D, 0x1E, 0xF6, 0x05, +0xED, 0x05, 0xF6, 0x09, 0xED, 0x0D, 0x2E, 0x43, +0x16, 0x61, 0xA0, 0x4D, 0x55, 0x61, 0x03, 0x25, +0x95, 0x61, 0x03, 0x26, 0x36, 0x02, 0x00, 0x25, +0x16, 0x62, 0xD5, 0x61, 0x06, 0x7E, 0xD6, 0x62, +0x06, 0x7E, 0x87, 0x7E, 0xF6, 0x19, 0x16, 0x63, +0x87, 0x7E, 0x06, 0x7E, 0x7F, 0x00, 0xF6, 0x19, +0x56, 0x63, 0x95, 0x63, 0x06, 0x7E, 0xBC, 0x36, +0xD6, 0x63, 0x06, 0x7E, 0x87, 0x7E, 0xF6, 0x19, +0xBC, 0x36, 0x16, 0x64, 0x07, 0x7E, 0x86, 0x7E, +0x95, 0x64, 0x76, 0x00, 0xBC, 0x36, 0xBE, 0x19, +0x56, 0x64, 0x67, 0x46, 0x55, 0x62, 0x8E, 0x4E, +0x50, 0x37, 0x96, 0x62, 0xBC, 0x46, 0xFE, 0x7B, +0xBF, 0x7B, 0x36, 0x02, 0x3E, 0x43, 0xF6, 0x1C, +0xB7, 0x05, 0x8A, 0x4E, 0xBF, 0x0D, 0xBE, 0x19, +0xD6, 0x64, 0x89, 0x4E, 0x16, 0x65, 0x46, 0x7C, +0x0C, 0x3E, 0xF7, 0xB2, 0x05, 0x26, 0x76, 0x02, +0xBE, 0x19, 0x56, 0x65, 0x06, 0x7D, 0x36, 0x02, +0x21, 0x36, 0x96, 0x65, 0xD5, 0x65, 0x42, 0x7C, +0x06, 0x7C, 0x12, 0x04, 0x36, 0x02, 0x32, 0x43, +0x0C, 0x32, 0x1A, 0x60, 0x42, 0x7C, 0x06, 0x7C, +0x12, 0x04, 0x36, 0x02, 0x32, 0x43, 0x0C, 0x32, +0x5A, 0x60, 0x01, 0x22, 0x9A, 0x60, 0xDA, 0x60, +0x42, 0x7D, 0x56, 0x1E, 0x52, 0x08, 0x52, 0x1E, +0xF6, 0x05, 0xD2, 0x05, 0xF7, 0x09, 0xD2, 0x0D, +0x17, 0x43, 0x72, 0x4A, 0x1F, 0x61, 0x5A, 0x61, +0x03, 0x22, 0xDD, 0x61, 0x9A, 0x61, 0x12, 0x02, +0x1A, 0x62, 0x02, 0x7E, 0xDA, 0x62, 0x02, 0x7E, +0xC6, 0x7E, 0x92, 0x19, 0x1A, 0x63, 0xC6, 0x7E, +0x02, 0x7E, 0x76, 0x00, 0x92, 0x19, 0x9D, 0x63, +0x5A, 0x63, 0x02, 0x7E, 0x67, 0x46, 0xBC, 0x32, +0xDA, 0x63, 0x02, 0x7E, 0xC6, 0x7E, 0x92, 0x19, +0xBC, 0x32, 0x1A, 0x64, 0xC6, 0x7E, 0x02, 0x7E, +0x76, 0x00, 0xBC, 0x36, 0x9D, 0x64, 0x92, 0x19, +0x5D, 0x62, 0x5A, 0x64, 0x60, 0x4A, 0x9A, 0x62, +0xFA, 0x7B, 0xBE, 0x7B, 0x12, 0x02, 0x32, 0x43, +0xD2, 0x1C, 0x96, 0x05, 0x5D, 0x4A, 0xB6, 0x0D, +0xB2, 0x18, 0xDA, 0x64, 0x5C, 0x4A, 0x1A, 0x65, +0x42, 0x7C, 0x0C, 0x3A, 0xD6, 0xB2, 0x05, 0x22, +0x52, 0x02, 0xB2, 0x18, 0x5A, 0x65, 0x42, 0x7D, +0xDD, 0x65, 0x12, 0x02, 0x21, 0x32, 0x9A, 0x65, +0xC2, 0x7C, 0x83, 0x7C, 0x12, 0x04, 0x1B, 0x02, +0x1A, 0x43, 0x0C, 0x32, 0x23, 0x46, 0x1A, 0x60, +0xC2, 0x7C, 0x83, 0x7C, 0x12, 0x04, 0x1B, 0x02, +0x1A, 0x43, 0x0C, 0x32, 0x23, 0x46, 0x5A, 0x60, +0x82, 0x7D, 0x52, 0x1E, 0xD3, 0x05, 0xDB, 0x0D, +0x22, 0x46, 0x93, 0x60, 0x82, 0x7D, 0x52, 0x08, +0x52, 0x1E, 0xD2, 0x05, 0x23, 0x46, 0xD2, 0x0D, +0x1D, 0x61, 0xDA, 0x60, 0x47, 0x4A, 0x5A, 0x61, +0x26, 0x46, 0x01, 0x23, 0xF5, 0x61, 0xB3, 0x61, +0x13, 0x12, 0x33, 0x62, 0x43, 0x7E, 0xF3, 0x62, +0x43, 0x7E, 0x06, 0x7F, 0x9B, 0x19, 0x26, 0x46, +0x33, 0x63, 0x06, 0x7F, 0x43, 0x7E, 0x76, 0x00, +0x9B, 0x19, 0x26, 0x46, 0xB5, 0x63, 0xF5, 0x63, +0x35, 0x64, 0x73, 0x63, 0x65, 0x64, 0x38, 0x4B, +0xA5, 0x64, 0x3F, 0x33, 0x65, 0x62, 0xA3, 0x62, +0x36, 0x4B, 0xDB, 0x1C, 0xE3, 0x64, 0x36, 0x4B, +0x23, 0x65, 0xC6, 0x7C, 0x0C, 0x3E, 0xF7, 0xB2, +0x05, 0x26, 0x76, 0x02, 0xBF, 0x19, 0x67, 0x65, +0x87, 0x7D, 0x7F, 0x08, 0x3F, 0x02, 0x21, 0x37, +0xA7, 0x65, 0xE5, 0x65, 0xC4, 0x7C, 0x80, 0x7C, +0x24, 0x04, 0x00, 0x02, 0x04, 0x43, 0x0C, 0x34, +0x27, 0x48, 0x0C, 0x60, 0x00, 0x68, 0x40, 0x30, +0xC4, 0x7C, 0x87, 0x7C, 0x24, 0x04, 0x3F, 0x02, +0x3C, 0x43, 0x0C, 0x34, 0x4C, 0x60, 0xC4, 0x7D, +0x64, 0x1E, 0xE4, 0x05, 0xE4, 0x0D, 0x8C, 0x60, +0xC4, 0x7D, 0x0D, 0x61, 0x64, 0x08, 0x64, 0x1E, +0xE4, 0x05, 0xE4, 0x0D, 0xCC, 0x60, 0x4A, 0x61, +0x01, 0x22, 0xCD, 0x61, 0x8A, 0x61, 0x12, 0x02, +0x0A, 0x62, 0x42, 0x7E, 0xCA, 0x62, 0x42, 0x7E, +0x44, 0x7F, 0x12, 0x19, 0x0A, 0x63, 0x44, 0x7F, +0x42, 0x7E, 0x64, 0x00, 0x12, 0x19, 0x8D, 0x63, +0x4A, 0x63, 0x42, 0x7E, 0xBC, 0x32, 0xCA, 0x63, +0x42, 0x7E, 0x44, 0x7F, 0x12, 0x19, 0xBC, 0x32, +0x0A, 0x64, 0x42, 0x7F, 0x44, 0x7E, 0x52, 0x00, +0xBC, 0x32, 0xA2, 0x18, 0x8D, 0x64, 0x4A, 0x64, +0x01, 0x22, 0x92, 0x04, 0x4A, 0x62, 0x0C, 0x4A, +0x3F, 0x32, 0x8A, 0x62, 0x0B, 0x4A, 0x0B, 0x65, +0xD2, 0x1C, 0xCA, 0x64, 0xC2, 0x7C, 0x0C, 0x3A, +0xD2, 0xB2, 0x92, 0x19, 0x4A, 0x65, 0xC0, 0x7D, +0xCD, 0x65, 0x40, 0x08, 0x00, 0x02, 0x21, 0x30, +0x88, 0x65, 0xF0, 0xBD, 0x70, 0x02, 0x00, 0x20, +0xB8, 0x02, 0x00, 0x20, 0x03, 0x00, 0x03, 0x00, +0x00, 0x3F, 0x3F, 0x3F, 0x00, 0x30, 0xBC, 0x00, +0x00, 0x26, 0x31, 0x00, 0x01, 0x00, 0x01, 0x00, +0x10, 0xB5, 0x00, 0xF0, 0x73, 0xFA, 0x00, 0xF0, +0x75, 0xF9, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, +0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x00, 0x20, +0xFF, 0xF7, 0xD4, 0xFC, 0x09, 0x48, 0x01, 0x78, +0x09, 0x48, 0x00, 0x29, 0x04, 0xD0, 0x01, 0x68, +0xFF, 0x22, 0x01, 0x32, 0x11, 0x43, 0x01, 0x60, +0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x91, 0x43, +0x01, 0x60, 0x04, 0x49, 0x02, 0x20, 0x08, 0x72, +0x10, 0xBD, 0x00, 0x00, 0xCF, 0x00, 0x00, 0x20, +0x00, 0x10, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, +0x10, 0xB5, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, +0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x09, 0x48, +0x07, 0x49, 0x41, 0x60, 0x08, 0x49, 0x81, 0x60, +0xFF, 0xF7, 0x52, 0xFE, 0x80, 0x21, 0x07, 0x48, +0xFD, 0xF7, 0xCF, 0xFE, 0x80, 0x21, 0x06, 0x48, +0xFD, 0xF7, 0xCB, 0xFE, 0x10, 0xBD, 0x00, 0x00, +0x1F, 0x1F, 0x5F, 0x1F, 0x00, 0x10, 0x00, 0x50, +0x1F, 0x1F, 0x1F, 0x1F, 0x4C, 0x07, 0x00, 0x20, +0x9C, 0x01, 0x00, 0x20, 0x01, 0x21, 0x89, 0x07, +0x0A, 0x15, 0x00, 0x28, 0x48, 0x69, 0x02, 0xD0, +0x10, 0x43, 0x48, 0x61, 0x70, 0x47, 0x90, 0x43, +0xFB, 0xE7, 0x00, 0x00, 0x10, 0xB5, 0x0F, 0x49, +0x0A, 0x78, 0x0F, 0x49, 0x00, 0x2A, 0x09, 0x78, +0x04, 0xD0, 0x01, 0x2A, 0x07, 0xD0, 0x02, 0x2A, +0x12, 0xD1, 0x09, 0xE0, 0xC9, 0x07, 0x0F, 0xD0, +0x60, 0x22, 0x0A, 0x49, 0x08, 0xE0, 0x89, 0x07, +0x0A, 0xD5, 0x60, 0x22, 0x08, 0x49, 0x03, 0xE0, +0x49, 0x07, 0x05, 0xD5, 0x60, 0x22, 0x07, 0x49, +0xFD, 0xF7, 0x7E, 0xFE, 0x01, 0x20, 0x10, 0xBD, +0x00, 0x20, 0x10, 0xBD, 0x85, 0x02, 0x00, 0x20, +0x98, 0x01, 0x00, 0x20, 0x9C, 0x03, 0x00, 0x20, +0xEC, 0x06, 0x00, 0x20, 0xFC, 0x03, 0x00, 0x20, +0x10, 0xB5, 0x13, 0x49, 0x13, 0x4B, 0x09, 0x68, +0x13, 0x4A, 0x40, 0x31, 0x00, 0x28, 0x0F, 0xD0, +0x01, 0x28, 0x12, 0xD0, 0x02, 0x28, 0x15, 0xD0, +0x03, 0x28, 0x08, 0xD1, 0x48, 0x7E, 0x84, 0x1E, +0x1C, 0x80, 0x49, 0x7F, 0x4B, 0x00, 0xC9, 0x18, +0x89, 0x1C, 0x40, 0x18, 0x10, 0x80, 0x10, 0xBD, +0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, 0x89, 0x7E, +0xF4, 0xE7, 0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, +0xC9, 0x7E, 0xEF, 0xE7, 0x48, 0x7E, 0x84, 0x1E, +0x1C, 0x80, 0x09, 0x7F, 0xEA, 0xE7, 0x00, 0x00, +0xB8, 0x02, 0x00, 0x20, 0x60, 0x02, 0x00, 0x20, +0x62, 0x02, 0x00, 0x20, 0xF8, 0xB5, 0x05, 0x46, +0x23, 0x48, 0x00, 0x21, 0x01, 0x60, 0x41, 0x60, +0x22, 0x49, 0x01, 0x20, 0x08, 0x70, 0x22, 0x48, +0x22, 0x4C, 0x00, 0x78, 0x00, 0x28, 0x06, 0xD0, +0x2D, 0x26, 0x09, 0x27, 0x01, 0x28, 0x10, 0xD0, +0x02, 0x28, 0x1D, 0xD1, 0x21, 0xE0, 0x1E, 0xA0, +0x02, 0xF0, 0xC0, 0xF9, 0x5A, 0x20, 0x00, 0x2D, +0x20, 0x70, 0x01, 0xD0, 0x00, 0x20, 0x00, 0xE0, +0x01, 0x20, 0x00, 0xF0, 0x23, 0xF9, 0x5A, 0x20, +0x0C, 0xE0, 0x1A, 0xA0, 0x02, 0xF0, 0xB2, 0xF9, +0x00, 0x2D, 0x02, 0xD0, 0x26, 0x70, 0x02, 0x20, +0x01, 0xE0, 0x27, 0x70, 0x03, 0x20, 0x00, 0xF0, +0x15, 0xF9, 0x01, 0x20, 0xFF, 0xF7, 0x90, 0xF9, +0x05, 0x20, 0x14, 0x49, 0x00, 0x02, 0x08, 0x60, +0xF8, 0xBD, 0x13, 0xA0, 0x02, 0xF0, 0x9E, 0xF9, +0x00, 0x2D, 0x02, 0xD0, 0x26, 0x70, 0x02, 0x20, +0x01, 0xE0, 0x27, 0x70, 0x03, 0x20, 0x00, 0xF0, +0x01, 0xF9, 0x10, 0x48, 0x81, 0x6A, 0x10, 0x4A, +0x11, 0x40, 0x81, 0x62, 0xE5, 0xE7, 0x00, 0x00, +0x50, 0x02, 0x00, 0x20, 0xCF, 0x00, 0x00, 0x20, +0x85, 0x02, 0x00, 0x20, 0xB5, 0x02, 0x00, 0x20, +0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x0D, 0x0A, +0x00, 0x00, 0x00, 0x00, 0x49, 0x64, 0x6C, 0x65, +0x0D, 0x0A, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, +0x47, 0x65, 0x73, 0x74, 0x75, 0x72, 0x65, 0x0D, +0x0A, 0x00, 0x00, 0x00, 0xC0, 0x11, 0x00, 0x50, +0xFF, 0xFF, 0x00, 0xF8, 0x70, 0xB5, 0x1C, 0x4D, +0x1C, 0x4C, 0x28, 0x70, 0x02, 0x46, 0x21, 0x78, +0x1B, 0xA0, 0x02, 0xF0, 0x67, 0xF9, 0x1E, 0x49, +0x01, 0x20, 0x08, 0x70, 0x00, 0xF0, 0x7E, 0xF9, +0x00, 0xF0, 0x50, 0xF9, 0x00, 0xF0, 0x52, 0xF8, +0x01, 0x20, 0xFD, 0xF7, 0x8D, 0xFE, 0xFF, 0xF7, +0x09, 0xFC, 0x01, 0x20, 0xFF, 0xF7, 0x7E, 0xFF, +0x00, 0xF0, 0xC6, 0xFF, 0xFE, 0xF7, 0xF6, 0xFF, +0x28, 0x78, 0x20, 0x70, 0x05, 0x28, 0x12, 0xD0, +0xFF, 0xF7, 0xBE, 0xFC, 0x00, 0x28, 0x0F, 0xD0, +0x10, 0x48, 0x00, 0x7A, 0x00, 0x28, 0x03, 0xD1, +0x00, 0x21, 0x02, 0x20, 0x00, 0xF0, 0x4C, 0xF8, +0x00, 0x20, 0x00, 0xF0, 0x5B, 0xF9, 0x0C, 0x48, +0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, 0x00, 0x20, +0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD, 0x00, 0x00, +0x85, 0x02, 0x00, 0x20, 0x71, 0x04, 0x00, 0x20, +0x53, 0x4D, 0x3D, 0x5B, 0x25, 0x64, 0x3A, 0x25, +0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0x83, 0x02, 0x00, 0x20, 0x50, 0x02, 0x00, 0x20, +0xCE, 0x00, 0x00, 0x20, 0x08, 0x49, 0x02, 0x20, +0x08, 0x72, 0x08, 0x48, 0x01, 0x78, 0x08, 0x48, +0x00, 0x29, 0x01, 0x68, 0x04, 0xD0, 0x01, 0x22, +0x92, 0x02, 0x91, 0x43, 0x01, 0x60, 0x70, 0x47, +0x01, 0x22, 0x11, 0x43, 0xFA, 0xE7, 0x00, 0x00, +0x50, 0x02, 0x00, 0x20, 0xCF, 0x00, 0x00, 0x20, +0x00, 0x10, 0x00, 0x50, 0x10, 0xB5, 0x08, 0x48, +0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x11, 0x43, +0x01, 0x60, 0x00, 0x68, 0x05, 0x4C, 0xC0, 0x07, +0x03, 0xD0, 0x02, 0x20, 0x20, 0x72, 0x00, 0xF0, +0xED, 0xF8, 0x00, 0x20, 0x20, 0x72, 0x10, 0xBD, +0x00, 0x10, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, +0x70, 0xB5, 0x05, 0x00, 0x0E, 0x46, 0x16, 0xD0, +0xFF, 0xF7, 0x6A, 0xFE, 0x00, 0x24, 0x6D, 0x1E, +0x07, 0xE0, 0x00, 0xF0, 0xDB, 0xF8, 0xFF, 0xF7, +0x67, 0xFB, 0xFF, 0xF7, 0xC3, 0xFF, 0x64, 0x1C, +0xE4, 0xB2, 0xAC, 0x42, 0xF5, 0xDB, 0x00, 0xF0, +0xD1, 0xF8, 0xFF, 0xF7, 0x5D, 0xFB, 0x00, 0x2E, +0x02, 0xD0, 0xFF, 0xF7, 0xCF, 0xFF, 0x70, 0xBD, +0xFF, 0xF7, 0xB4, 0xFF, 0x70, 0xBD, 0x00, 0x00, +0x30, 0xB5, 0x01, 0x24, 0x1C, 0x4A, 0xA4, 0x07, +0x23, 0x13, 0x1B, 0x49, 0x40, 0x32, 0x00, 0x28, +0x21, 0xD0, 0x01, 0x28, 0x1F, 0xD0, 0x02, 0x28, +0x01, 0xD0, 0x03, 0x28, 0x1A, 0xD1, 0x25, 0x68, +0x1D, 0x43, 0x25, 0x60, 0x62, 0x23, 0x93, 0x61, +0xD3, 0x61, 0x14, 0x4B, 0xCB, 0x63, 0x14, 0x49, +0x09, 0x68, 0x40, 0x31, 0x02, 0x28, 0x48, 0x7E, +0x17, 0xD0, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D, +0x10, 0x60, 0x4B, 0x7E, 0x48, 0x7F, 0x41, 0x00, +0x40, 0x18, 0x80, 0x1C, 0x18, 0x18, 0x40, 0x05, +0x40, 0x0D, 0x90, 0x60, 0x30, 0xBD, 0x20, 0x68, +0x98, 0x43, 0x20, 0x60, 0x01, 0x20, 0x90, 0x61, +0xD0, 0x61, 0x00, 0x20, 0xC8, 0x63, 0x10, 0x60, +0xF3, 0xE7, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D, +0x10, 0x60, 0x4B, 0x7E, 0x08, 0x7F, 0xE6, 0xE7, +0x80, 0x11, 0x00, 0x50, 0x01, 0x00, 0x01, 0x00, +0xB8, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x04, 0x46, +0x81, 0x00, 0x25, 0x48, 0x41, 0x58, 0x25, 0x48, +0x0A, 0x68, 0x42, 0x60, 0x4A, 0x68, 0x82, 0x60, +0x8A, 0x68, 0x02, 0x61, 0xCA, 0x68, 0x42, 0x61, +0x21, 0x4D, 0x08, 0x69, 0x28, 0x62, 0x1F, 0x4A, +0x48, 0x69, 0xC0, 0x32, 0x90, 0x61, 0x88, 0x69, +0xD0, 0x61, 0x48, 0x6A, 0x10, 0x62, 0x88, 0x6A, +0x50, 0x62, 0x1A, 0x48, 0xCB, 0x69, 0x40, 0x38, +0xC3, 0x60, 0x0B, 0x6A, 0x03, 0x61, 0xCB, 0x6A, +0x93, 0x62, 0x0B, 0x6B, 0xD3, 0x63, 0x16, 0x4B, +0x4E, 0x6B, 0x80, 0x3B, 0x1E, 0x61, 0x8E, 0x6B, +0x5E, 0x62, 0xCE, 0x6B, 0xD6, 0x62, 0x0A, 0x6C, +0x1A, 0x60, 0x4A, 0x6C, 0x5A, 0x61, 0x8A, 0x6C, +0x9A, 0x62, 0xCA, 0x6C, 0xAA, 0x62, 0x0D, 0x4B, +0x0A, 0x6D, 0x40, 0x33, 0x5A, 0x63, 0x0B, 0x4B, +0x4A, 0x6D, 0x80, 0x33, 0x9A, 0x60, 0x0B, 0x4A, +0x8B, 0x6D, 0x13, 0x60, 0xC9, 0x6D, 0x51, 0x60, +0xC2, 0x68, 0x09, 0x49, 0x0A, 0x60, 0x00, 0x69, +0x48, 0x60, 0x20, 0x46, 0xFF, 0xF7, 0x44, 0xFE, +0x20, 0x46, 0xFF, 0xF7, 0x71, 0xFF, 0x70, 0xBD, +0x70, 0x02, 0x00, 0x20, 0x40, 0x10, 0x00, 0x50, +0xC0, 0x11, 0x00, 0x50, 0x00, 0x19, 0x00, 0x50, +0x50, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x0E, 0x4C, +0x21, 0x78, 0x81, 0x42, 0x17, 0xD0, 0x20, 0x70, +0x01, 0x46, 0x0C, 0xA0, 0x02, 0xF0, 0x32, 0xF8, +0xFF, 0xF7, 0x24, 0xFF, 0x20, 0x78, 0xFF, 0xF7, +0x55, 0xFE, 0xFF, 0xF7, 0xA9, 0xFA, 0x0B, 0x48, +0x80, 0x79, 0x01, 0x28, 0x05, 0xD1, 0x0A, 0x48, +0x01, 0x68, 0x01, 0x22, 0xD2, 0x02, 0x11, 0x43, +0x01, 0x60, 0xFF, 0xF7, 0xFB, 0xFE, 0x10, 0xBD, +0x74, 0x04, 0x00, 0x20, 0x54, 0x50, 0x20, 0x53, +0x70, 0x65, 0x65, 0x64, 0x20, 0x25, 0x64, 0x0D, +0x0A, 0x00, 0x00, 0x00, 0x84, 0x04, 0x00, 0x20, +0x00, 0x10, 0x00, 0x50, 0x70, 0xB5, 0x05, 0x24, +0x64, 0x04, 0xFD, 0xF7, 0xA9, 0xFF, 0x00, 0x22, +0x0C, 0x49, 0x0D, 0x48, 0x0D, 0x4B, 0x05, 0xE0, +0x05, 0x78, 0xED, 0x07, 0x01, 0xD0, 0x0A, 0x72, +0x09, 0xE0, 0x64, 0x1E, 0x0D, 0x7A, 0x02, 0x2D, +0x02, 0xD0, 0x1D, 0x68, 0xED, 0x07, 0x02, 0xD0, +0x00, 0x2C, 0xF1, 0xD1, 0x01, 0xE0, 0x00, 0x2C, +0x03, 0xD1, 0x0A, 0x72, 0x04, 0xA0, 0x01, 0xF0, +0xF1, 0xFF, 0x70, 0xBD, 0x50, 0x02, 0x00, 0x20, +0xCE, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, +0x57, 0x53, 0x46, 0x20, 0x54, 0x4F, 0x0D, 0x0A, +0x00, 0x00, 0x00, 0x00, 0x00, 0xB5, 0x03, 0x46, +0x00, 0x20, 0x00, 0xF0, 0xF1, 0xF8, 0x05, 0x21, +0x09, 0x07, 0xC8, 0x69, 0x00, 0x2B, 0x03, 0xD0, +0xC0, 0x0B, 0xC0, 0x03, 0x05, 0x4A, 0x03, 0xE0, +0xC0, 0x0B, 0x04, 0x4A, 0xC0, 0x03, 0xFC, 0x3A, +0x80, 0x18, 0xC8, 0x61, 0x01, 0x20, 0x00, 0xF0, +0xDF, 0xF8, 0x00, 0xBD, 0x50, 0x71, 0x00, 0x00, +0x03, 0x49, 0x0A, 0x68, 0x10, 0x18, 0x0A, 0x68, +0x90, 0x42, 0xFC, 0xD1, 0x70, 0x47, 0x00, 0x00, +0x08, 0x03, 0x00, 0x20, 0xF8, 0xB5, 0x1B, 0x4E, +0x05, 0x46, 0x0C, 0x46, 0x80, 0x21, 0x30, 0x46, +0xFD, 0xF7, 0x3B, 0xFC, 0x00, 0x2C, 0x04, 0xD0, +0x31, 0x46, 0x28, 0x46, 0x00, 0xF0, 0x5C, 0xFB, +0x04, 0xE0, 0x80, 0x22, 0x29, 0x46, 0x30, 0x46, +0xFD, 0xF7, 0x16, 0xFC, 0x29, 0x46, 0x12, 0xA0, +0x01, 0xF0, 0xA8, 0xFF, 0x00, 0x25, 0x14, 0x4F, +0x13, 0xE0, 0x00, 0x24, 0x08, 0xE0, 0x68, 0x43, +0x00, 0x19, 0x40, 0x00, 0x31, 0x5E, 0x11, 0xA0, +0x01, 0xF0, 0x9C, 0xFF, 0x64, 0x1C, 0xE4, 0xB2, +0x38, 0x68, 0x00, 0x7E, 0xA0, 0x42, 0xF2, 0xD8, +0x0E, 0xA0, 0x01, 0xF0, 0x93, 0xFF, 0x6D, 0x1C, +0xED, 0xB2, 0x38, 0x68, 0x40, 0x7E, 0xA8, 0x42, +0xE7, 0xD8, 0x0A, 0xA0, 0x01, 0xF0, 0x8A, 0xFF, +0xF8, 0xBD, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x20, +0x49, 0x6D, 0x61, 0x67, 0x65, 0x3A, 0x30, 0x78, +0x25, 0x78, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0xB8, 0x02, 0x00, 0x20, 0x25, 0x36, 0x64, 0x2C, +0x00, 0x00, 0x00, 0x00, 0x0D, 0x0A, 0x00, 0x00, +0x08, 0x49, 0x8A, 0x78, 0x52, 0x1E, 0x8A, 0x70, +0x4B, 0x78, 0x0A, 0x1D, 0xD2, 0x5C, 0x02, 0x70, +0x48, 0x78, 0x40, 0x1C, 0x48, 0x70, 0x48, 0x78, +0x10, 0x28, 0x01, 0xD1, 0x00, 0x20, 0x48, 0x70, +0x70, 0x47, 0x00, 0x00, 0x5C, 0x04, 0x00, 0x20, +0xF8, 0xB5, 0x01, 0x27, 0x05, 0x46, 0xBF, 0x07, +0x38, 0x68, 0x08, 0x21, 0x08, 0x43, 0x38, 0x60, +0x01, 0x23, 0x15, 0x48, 0x80, 0x22, 0x02, 0x60, +0x14, 0x48, 0x00, 0x21, 0x81, 0x70, 0x01, 0x70, +0x41, 0x70, 0xC3, 0x70, 0x12, 0x4B, 0x98, 0x68, +0x02, 0x21, 0x88, 0x43, 0x98, 0x60, 0xF8, 0x68, +0x10, 0x43, 0xF8, 0x60, 0x0F, 0x4C, 0x61, 0x61, +0x0F, 0x48, 0x00, 0x68, 0xE9, 0x00, 0x46, 0x06, +0x0E, 0x48, 0xFD, 0xF7, 0x7F, 0xFB, 0xE0, 0x60, +0x30, 0x20, 0xA0, 0x60, 0x06, 0x49, 0x80, 0x20, +0x80, 0x39, 0x08, 0x60, 0x08, 0x20, 0x78, 0x60, +0xE0, 0x68, 0x68, 0x43, 0xC1, 0x00, 0x08, 0xA0, +0x01, 0xF0, 0x30, 0xFF, 0xF8, 0xBD, 0x00, 0x00, +0x80, 0xE1, 0x00, 0xE0, 0x5C, 0x04, 0x00, 0x20, +0x40, 0x09, 0x00, 0x50, 0x00, 0x02, 0x00, 0x50, +0x00, 0x11, 0x00, 0x50, 0x00, 0x36, 0x6E, 0x01, +0x55, 0x41, 0x52, 0x54, 0x28, 0x25, 0x64, 0x29, +0x21, 0x0D, 0x0A, 0x00, 0x70, 0xB5, 0x14, 0x4A, +0x91, 0x78, 0x14, 0x4C, 0x0E, 0x29, 0x02, 0xD3, +0x61, 0x68, 0x49, 0x07, 0xFC, 0xD5, 0x61, 0x69, +0x02, 0x25, 0xA9, 0x43, 0x61, 0x61, 0x91, 0x78, +0x00, 0x26, 0x10, 0x29, 0x0D, 0xD2, 0x0C, 0x49, +0x13, 0x78, 0x09, 0x1D, 0xC8, 0x54, 0x10, 0x78, +0x40, 0x1C, 0x10, 0x70, 0x10, 0x78, 0x10, 0x28, +0x00, 0xD1, 0x16, 0x70, 0x90, 0x78, 0x40, 0x1C, +0x90, 0x70, 0xD0, 0x78, 0x00, 0x28, 0x03, 0xD0, +0xD6, 0x70, 0x20, 0x46, 0xFF, 0xF7, 0x80, 0xFF, +0x60, 0x69, 0x28, 0x43, 0x60, 0x61, 0x70, 0xBD, +0x5C, 0x04, 0x00, 0x20, 0x00, 0x02, 0x00, 0x50, +0x01, 0x21, 0x89, 0x07, 0x00, 0xB5, 0x8A, 0x14, +0x00, 0x28, 0x08, 0x68, 0x04, 0xD0, 0x10, 0x43, +0x08, 0x60, 0xFD, 0xF7, 0x81, 0xFE, 0x00, 0xBD, +0x90, 0x43, 0x08, 0x60, 0x00, 0xBD, 0x00, 0x00, +0xFE, 0xB5, 0x63, 0x49, 0x61, 0x48, 0x09, 0x68, +0x00, 0x78, 0x90, 0x31, 0x4A, 0x7B, 0x0B, 0x7B, +0x11, 0x02, 0x19, 0x43, 0x5F, 0x4B, 0x00, 0x24, +0x04, 0x25, 0x0F, 0x26, 0x5A, 0x22, 0x1C, 0x5F, +0x00, 0x28, 0x01, 0xD0, 0x01, 0x28, 0x15, 0xD1, +0x58, 0x4B, 0x02, 0x27, 0x38, 0x43, 0x18, 0x70, +0x59, 0x48, 0x00, 0x78, 0x15, 0x28, 0x14, 0xD1, +0x58, 0x48, 0x00, 0x23, 0xC3, 0x5E, 0x99, 0x42, +0x0F, 0xDC, 0x13, 0x20, 0xC0, 0x43, 0x84, 0x42, +0x01, 0xDA, 0x55, 0x48, 0x02, 0x70, 0x17, 0x20, +0x54, 0x4B, 0x18, 0x70, 0x54, 0x48, 0x00, 0x27, +0xC7, 0x5F, 0xB9, 0x42, 0x03, 0xDA, 0x04, 0x20, +0x02, 0xE0, 0x00, 0x20, 0xF4, 0xE7, 0x02, 0x20, +0x00, 0x90, 0x50, 0x48, 0x40, 0x88, 0x00, 0x28, +0x67, 0xD0, 0x46, 0x48, 0x00, 0x78, 0x01, 0x90, +0x83, 0x07, 0x4D, 0x48, 0x01, 0x78, 0x48, 0x1C, +0xC0, 0xB2, 0x00, 0x2B, 0x06, 0xDA, 0x10, 0x2F, +0x04, 0xDA, 0x02, 0x21, 0x00, 0x91, 0x48, 0x49, +0x08, 0x70, 0x19, 0xE0, 0x47, 0x4B, 0x1B, 0x78, +0xDB, 0x07, 0x11, 0xD0, 0x44, 0x49, 0x00, 0x20, +0x08, 0x70, 0x13, 0x20, 0xC0, 0x43, 0x84, 0x42, +0x05, 0xDA, 0x40, 0x48, 0x80, 0x88, 0x05, 0x28, +0x01, 0xD2, 0x41, 0x48, 0x02, 0x80, 0x30, 0x21, +0x40, 0x48, 0xFD, 0xF7, 0x02, 0xFB, 0x03, 0xE0, +0x3B, 0x4A, 0x05, 0x29, 0x00, 0xD2, 0x10, 0x70, +0x39, 0x48, 0x01, 0x78, 0x00, 0x98, 0x81, 0x42, +0x37, 0xD3, 0x3B, 0x49, 0x10, 0x2F, 0x19, 0xDA, +0x1D, 0x20, 0xC0, 0x43, 0x84, 0x42, 0x15, 0xDD, +0x08, 0x78, 0xF0, 0x28, 0x01, 0xD2, 0x40, 0x1C, +0x08, 0x70, 0xC0, 0xB2, 0x02, 0x28, 0x0F, 0xD9, +0x2C, 0x49, 0x08, 0x78, 0x16, 0x28, 0x03, 0xD2, +0x02, 0x28, 0x01, 0xD9, 0x17, 0x20, 0x08, 0x70, +0x27, 0x49, 0x00, 0x20, 0x08, 0x70, 0x2C, 0x49, +0x08, 0x80, 0x01, 0xE0, 0x00, 0x20, 0x08, 0x70, +0x01, 0x98, 0x1E, 0x4A, 0x04, 0x28, 0x12, 0xD0, +0x22, 0x49, 0x08, 0x78, 0x18, 0x28, 0x32, 0xD2, +0x40, 0x1C, 0xC0, 0xB2, 0x08, 0x70, 0x10, 0x2F, +0x02, 0xDB, 0x62, 0x42, 0xBA, 0x42, 0x13, 0xDD, +0x1E, 0x4A, 0x12, 0x7A, 0x01, 0x2A, 0x05, 0xD9, +0x03, 0x26, 0x02, 0x25, 0x04, 0xE0, 0xFE, 0xF7, +0xB9, 0xFA, 0xFE, 0xBD, 0x01, 0x26, 0x35, 0x46, +0x09, 0x22, 0xD2, 0x43, 0x94, 0x42, 0x03, 0xDD, +0x02, 0x28, 0x01, 0xD9, 0x18, 0x20, 0x08, 0x70, +0xC1, 0xB2, 0x2B, 0x46, 0x32, 0x46, 0x19, 0xA0, +0x01, 0xF0, 0x2C, 0xFE, 0x1C, 0x4C, 0x1D, 0x4F, +0x00, 0x20, 0x42, 0x00, 0x11, 0x19, 0x00, 0x23, +0xCB, 0x5E, 0xBA, 0x5E, 0x73, 0x43, 0x9A, 0x18, +0x2A, 0x41, 0x40, 0x1C, 0xC0, 0xB2, 0x0A, 0x80, +0x30, 0x28, 0xF2, 0xD3, 0xFE, 0xBD, 0x04, 0x20, +0x10, 0x70, 0xFE, 0xBD, 0x87, 0x02, 0x00, 0x20, +0xB8, 0x02, 0x00, 0x20, 0x7A, 0x04, 0x00, 0x20, +0x70, 0x04, 0x00, 0x20, 0x78, 0x04, 0x00, 0x20, +0x75, 0x04, 0x00, 0x20, 0x8E, 0x02, 0x00, 0x20, +0x7C, 0x04, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, +0xED, 0x02, 0x00, 0x20, 0xEB, 0x02, 0x00, 0x20, +0xF0, 0x02, 0x00, 0x20, 0x0C, 0x03, 0x00, 0x20, +0xEA, 0x02, 0x00, 0x20, 0x46, 0x61, 0x73, 0x74, +0x4B, 0x25, 0x64, 0x28, 0x25, 0x64, 0x3A, 0x25, +0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x01, 0x20, 0xD4, 0x00, 0x00, 0x20, +0xF8, 0xB5, 0x3F, 0x4F, 0x01, 0x24, 0x38, 0x7B, +0x3E, 0x49, 0x0A, 0x78, 0x3E, 0x4E, 0x3F, 0x4D, +0x90, 0x42, 0x05, 0xD1, 0x28, 0x78, 0x81, 0x28, +0x02, 0xD0, 0x30, 0x78, 0xC0, 0x07, 0x66, 0xD0, +0x00, 0x20, 0x30, 0x70, 0x37, 0x48, 0x00, 0x78, +0x07, 0x28, 0x0C, 0xD3, 0x38, 0x48, 0x00, 0x68, +0x40, 0x05, 0x40, 0x0F, 0x38, 0x73, 0x01, 0x20, +0xFF, 0xF7, 0x06, 0xFE, 0x00, 0x20, 0x30, 0x70, +0x38, 0x7B, 0x02, 0x28, 0x18, 0xD0, 0xA8, 0x79, +0x00, 0x28, 0x01, 0xD0, 0xFE, 0xF7, 0x36, 0xFE, +0x38, 0x7B, 0x2C, 0x4F, 0x38, 0x70, 0x81, 0x20, +0x28, 0x70, 0x2E, 0x4A, 0x01, 0x21, 0x10, 0x88, +0x09, 0x03, 0x08, 0x43, 0x10, 0x80, 0x38, 0x78, +0x03, 0x00, 0xFD, 0xF7, 0x9F, 0xFA, 0x07, 0x25, +0x35, 0x35, 0x3B, 0x0B, 0x18, 0x0B, 0x3B, 0x00, +0x83, 0x20, 0x28, 0x70, 0x05, 0x20, 0xFF, 0xF7, +0x3D, 0xFC, 0xF8, 0xBD, 0x24, 0xA0, 0x01, 0xF0, +0xA9, 0xFD, 0x01, 0x20, 0xFE, 0xF7, 0xB4, 0xFD, +0x00, 0x20, 0xFF, 0xF7, 0x33, 0xFC, 0x00, 0x28, +0x1C, 0xD0, 0x01, 0x20, 0x18, 0xE0, 0x20, 0xA0, +0x01, 0xF0, 0x9C, 0xFD, 0x01, 0x20, 0xFE, 0xF7, +0xA7, 0xFD, 0x01, 0x20, 0xFF, 0xF7, 0x26, 0xFC, +0x00, 0x28, 0x0F, 0xD0, 0x04, 0x20, 0x0B, 0xE0, +0x1B, 0xA0, 0x01, 0xF0, 0x8F, 0xFD, 0x00, 0x20, +0xFE, 0xF7, 0x9A, 0xFD, 0x02, 0x20, 0xFF, 0xF7, +0x19, 0xFC, 0x00, 0x28, 0x02, 0xD0, 0x02, 0x20, +0x28, 0x70, 0x07, 0xE0, 0x00, 0x24, 0x05, 0xE0, +0x83, 0x20, 0x28, 0x70, 0x05, 0x20, 0xFF, 0xF7, +0x0D, 0xFC, 0x04, 0x46, 0x2A, 0x78, 0x39, 0x78, +0x11, 0xA0, 0x01, 0xF0, 0x77, 0xFD, 0x30, 0x78, +0xC0, 0x07, 0x02, 0xD0, 0x81, 0x20, 0x28, 0x70, +0x00, 0x24, 0x20, 0x46, 0xF8, 0xBD, 0x00, 0x00, +0x50, 0x02, 0x00, 0x20, 0xE5, 0x02, 0x00, 0x20, +0xCE, 0x00, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, +0x00, 0x11, 0x00, 0x50, 0xF6, 0x02, 0x00, 0x20, +0x44, 0x41, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0x44, 0x49, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0x44, 0x53, 0x54, 0x42, 0x0D, 0x0A, 0x00, 0x00, +0x44, 0x53, 0x50, 0x3D, 0x25, 0x64, 0x2C, 0x50, +0x57, 0x52, 0x3D, 0x25, 0x64, 0x20, 0x0D, 0x0A, +0x00, 0x00, 0x00, 0x00, 0x70, 0xB5, 0x0E, 0x4D, +0x28, 0x68, 0xFE, 0xF7, 0xFB, 0xF8, 0x04, 0x00, +0x07, 0xD0, 0x80, 0x21, 0x0B, 0x48, 0xFD, 0xF7, +0xC4, 0xF9, 0x0A, 0x49, 0x28, 0x68, 0x00, 0xF0, +0xE7, 0xF8, 0x09, 0x48, 0x00, 0x88, 0x81, 0x07, +0x06, 0xD0, 0x00, 0x07, 0x04, 0xD5, 0x80, 0x21, +0x04, 0x48, 0xFD, 0xF7, 0xB6, 0xF9, 0x00, 0x24, +0xFF, 0xF7, 0x52, 0xFE, 0x20, 0x46, 0x70, 0xBD, +0xF8, 0x02, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, +0xF6, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x46, +0x01, 0x24, 0xFE, 0xF7, 0xFF, 0xFB, 0x00, 0x2D, +0x09, 0xD0, 0xF0, 0x20, 0xFD, 0xF7, 0xD4, 0xF9, +0xFE, 0xF7, 0xC6, 0xFC, 0xFE, 0xF7, 0x5C, 0xFC, +0x00, 0x20, 0xFE, 0xF7, 0x25, 0xFD, 0xFE, 0xF7, +0xD9, 0xFD, 0xFF, 0xF7, 0xB5, 0xFA, 0x20, 0x46, +0x70, 0xBD, 0x00, 0x00, 0x70, 0xB5, 0x35, 0x49, +0x08, 0x78, 0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, +0x08, 0x70, 0x33, 0x4B, 0x33, 0x49, 0x18, 0x78, +0x09, 0x78, 0x00, 0x25, 0x32, 0x4A, 0x33, 0x4C, +0x00, 0x28, 0x07, 0xD1, 0x5E, 0x78, 0x00, 0x2E, +0x04, 0xD1, 0x9B, 0x78, 0x00, 0x2B, 0x01, 0xD1, +0x00, 0x29, 0x12, 0xD0, 0x65, 0x80, 0xA3, 0x88, +0xE1, 0x26, 0xB6, 0x00, 0xB3, 0x42, 0x08, 0xD2, +0x01, 0x43, 0x06, 0xD0, 0x2A, 0x49, 0x09, 0x78, +0x04, 0x29, 0xA1, 0x88, 0x00, 0xD1, 0x49, 0x1C, +0xA1, 0x80, 0x00, 0x28, 0x19, 0xD0, 0x15, 0x70, +0x1C, 0xE0, 0xA0, 0x88, 0x00, 0x28, 0x05, 0xD0, +0x20, 0x7A, 0xFF, 0x28, 0x02, 0xD2, 0x20, 0x7A, +0x40, 0x1C, 0x20, 0x72, 0xA5, 0x80, 0x60, 0x88, +0x01, 0x21, 0x09, 0x03, 0x88, 0x42, 0x02, 0xD2, +0x60, 0x88, 0x40, 0x1C, 0x60, 0x80, 0x1D, 0x49, +0x08, 0x78, 0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, +0x08, 0x70, 0x10, 0x78, 0x00, 0x28, 0x01, 0xD0, +0x40, 0x1E, 0x10, 0x70, 0x20, 0x78, 0x01, 0x28, +0x16, 0xD0, 0x02, 0x28, 0x14, 0xD0, 0x04, 0x28, +0x12, 0xD0, 0x82, 0x28, 0x12, 0xD1, 0x14, 0x4E, +0x30, 0x78, 0x30, 0x28, 0x0E, 0xD1, 0x13, 0xA0, +0x01, 0xF0, 0xB8, 0xFC, 0x05, 0x20, 0xFF, 0xF7, +0xEB, 0xFC, 0xFD, 0xF7, 0x37, 0xFC, 0x13, 0xA0, +0x01, 0xF0, 0xB0, 0xFC, 0x35, 0x70, 0x01, 0xE0, +0x00, 0xF0, 0xDE, 0xF8, 0x14, 0x48, 0x00, 0x78, +0xC0, 0x07, 0x01, 0xD0, 0x81, 0x20, 0x20, 0x70, +0x70, 0xBD, 0x00, 0x00, 0x70, 0x04, 0x00, 0x20, +0xA0, 0x04, 0x00, 0x20, 0x73, 0x04, 0x00, 0x20, +0x72, 0x04, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, +0xE5, 0x02, 0x00, 0x20, 0x75, 0x04, 0x00, 0x20, +0x88, 0x02, 0x00, 0x20, 0x73, 0x6C, 0x65, 0x65, +0x70, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x0D, 0x0A, +0x00, 0x00, 0x00, 0x00, 0x6C, 0x65, 0x61, 0x76, +0x65, 0x20, 0x73, 0x6C, 0x65, 0x65, 0x70, 0x20, +0x6D, 0x6F, 0x64, 0x65, 0x0D, 0x0A, 0x00, 0x00, +0xCE, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x10, 0x4D, +0x68, 0x78, 0x00, 0x07, 0x01, 0xD5, 0x00, 0xF0, +0x45, 0xF9, 0x0E, 0x4C, 0x60, 0x22, 0x0E, 0x48, +0x21, 0x68, 0xFD, 0xF7, 0xDD, 0xF8, 0xFD, 0xF7, +0x2D, 0xFA, 0x68, 0x78, 0x96, 0x21, 0x08, 0x42, +0x01, 0xD0, 0x00, 0xF0, 0x37, 0xF9, 0x09, 0x48, +0x00, 0x78, 0x00, 0x06, 0x06, 0xD4, 0x01, 0x21, +0x20, 0x68, 0xFE, 0xF7, 0x2F, 0xF9, 0x20, 0x68, +0xFD, 0xF7, 0xF0, 0xFA, 0x70, 0xBD, 0x00, 0x00, +0x88, 0x02, 0x00, 0x20, 0xF8, 0x02, 0x00, 0x20, +0xD4, 0x00, 0x00, 0x20, 0x86, 0x02, 0x00, 0x20, +0x30, 0xB5, 0x08, 0x4A, 0x14, 0x68, 0x1B, 0x34, +0x00, 0x22, 0xA3, 0x5C, 0x41, 0x2B, 0x03, 0xD0, +0x55, 0x00, 0x45, 0x5B, 0x5B, 0x00, 0xCD, 0x52, +0x52, 0x1C, 0xD2, 0xB2, 0x30, 0x2A, 0xF4, 0xD3, +0x30, 0xBD, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20, +0x01, 0x20, 0x80, 0x07, 0x40, 0x69, 0x40, 0x05, +0x01, 0xD5, 0x01, 0x20, 0x70, 0x47, 0x00, 0x20, +0x70, 0x47, 0x00, 0x00, 0x10, 0xB5, 0xFF, 0xF7, +0x25, 0xFC, 0x04, 0x48, 0x00, 0x78, 0x00, 0x28, +0x02, 0xD1, 0x01, 0x20, 0xFF, 0xF7, 0xF2, 0xF9, +0x10, 0xBD, 0x00, 0x00, 0x90, 0x02, 0x00, 0x20, +0x36, 0x03, 0x35, 0x03, 0x2C, 0x03, 0x9C, 0x03, +0xE9, 0x02, 0x28, 0x03, 0x29, 0x03, 0x1C, 0x03, +0x9D, 0x03, 0x2F, 0x03, 0x4A, 0x03, 0x4D, 0x03, +0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, +0x3D, 0x03, 0x3C, 0x03, 0x2C, 0x03, 0x9C, 0x03, +0xD3, 0x02, 0x28, 0x03, 0x29, 0x03, 0xD6, 0x02, +0x9E, 0x03, 0x30, 0x03, 0x37, 0x03, 0x3A, 0x03, +0xFF, 0x03, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x10, 0xB5, 0x00, 0x20, 0xFD, 0xF7, 0xCC, 0xFA, +0x05, 0x49, 0x08, 0x20, 0x08, 0x70, 0x05, 0x49, +0x06, 0x20, 0x08, 0x70, 0x01, 0x20, 0x04, 0x49, +0x80, 0x02, 0x08, 0x80, 0x10, 0xBD, 0x00, 0x00, +0xE5, 0x02, 0x00, 0x20, 0x71, 0x04, 0x00, 0x20, +0xF2, 0x02, 0x00, 0x20, 0x06, 0x49, 0x01, 0x20, +0x08, 0x70, 0x06, 0x49, 0x08, 0x20, 0x08, 0x70, +0x05, 0x49, 0x06, 0x20, 0x08, 0x70, 0x01, 0x20, +0x04, 0x49, 0x80, 0x02, 0x08, 0x80, 0x70, 0x47, +0xE7, 0x02, 0x00, 0x20, 0xE5, 0x02, 0x00, 0x20, +0x71, 0x04, 0x00, 0x20, 0xF2, 0x02, 0x00, 0x20, +0xF8, 0xB5, 0x3E, 0x4C, 0x3E, 0x4A, 0x20, 0x88, +0x3E, 0x4D, 0xC1, 0x04, 0x0C, 0xD5, 0x51, 0x10, +0x88, 0x43, 0x20, 0x80, 0x29, 0x78, 0x00, 0x29, +0x03, 0xD0, 0x01, 0x29, 0x01, 0xD0, 0x02, 0x29, +0x65, 0xD1, 0x10, 0x43, 0x20, 0x80, 0x62, 0xE0, +0x81, 0x04, 0x03, 0xD5, 0x90, 0x43, 0x20, 0x80, +0xFE, 0xF7, 0xF8, 0xFB, 0x20, 0x88, 0x81, 0x07, +0x0E, 0xD0, 0x01, 0x04, 0x57, 0xD5, 0x40, 0x04, +0x40, 0x0C, 0x20, 0x80, 0x81, 0x07, 0x01, 0xD5, +0x00, 0x20, 0x02, 0xE0, 0xC0, 0x07, 0x4E, 0xD0, +0x01, 0x20, 0xFF, 0xF7, 0x67, 0xFB, 0x4A, 0xE0, +0x00, 0x04, 0x5A, 0x21, 0x2A, 0x4F, 0x2B, 0x4E, +0x00, 0x28, 0x05, 0xDA, 0x38, 0x78, 0x00, 0x28, +0x00, 0xD0, 0x31, 0x80, 0x00, 0x20, 0x20, 0x80, +0x27, 0x48, 0x40, 0x88, 0x00, 0x28, 0x19, 0xD0, +0x26, 0x48, 0x00, 0x78, 0x04, 0x28, 0x15, 0xD3, +0x25, 0x4A, 0x00, 0x20, 0x10, 0x5E, 0x27, 0x22, +0xD2, 0x43, 0x90, 0x42, 0x0E, 0xDB, 0x23, 0x48, +0x00, 0x68, 0x90, 0x30, 0x42, 0x7B, 0x03, 0x7B, +0x10, 0x02, 0x18, 0x43, 0x42, 0x00, 0x80, 0x18, +0x1F, 0x4A, 0x00, 0x23, 0xD3, 0x5E, 0x80, 0x08, +0x98, 0x42, 0x01, 0xDA, 0x31, 0x80, 0x04, 0xE0, +0x30, 0x88, 0x00, 0x28, 0x1B, 0xD0, 0x5A, 0x28, +0x08, 0xD1, 0x38, 0x78, 0x00, 0x28, 0x05, 0xD1, +0x18, 0xA0, 0x01, 0xF0, 0x6B, 0xFB, 0x01, 0x20, +0xFF, 0xF7, 0x2C, 0xFB, 0x28, 0x78, 0x02, 0x28, +0x04, 0xD1, 0x30, 0x88, 0x5A, 0x28, 0x01, 0xD1, +0x2D, 0x20, 0x30, 0x80, 0x30, 0x88, 0x40, 0x1E, +0x80, 0xB2, 0x30, 0x80, 0x39, 0x78, 0x00, 0x29, +0x01, 0xD0, 0x00, 0x28, 0x02, 0xD0, 0xFD, 0xF7, +0x2B, 0xF9, 0xF8, 0xBD, 0x0D, 0xA0, 0x01, 0xF0, +0x51, 0xFB, 0xA5, 0xE7, 0xF6, 0x02, 0x00, 0x20, +0x00, 0x20, 0x00, 0x00, 0x85, 0x02, 0x00, 0x20, +0x74, 0x04, 0x00, 0x20, 0x7E, 0x04, 0x00, 0x20, +0x84, 0x04, 0x00, 0x20, 0x87, 0x02, 0x00, 0x20, +0x76, 0x04, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, +0x78, 0x04, 0x00, 0x20, 0x53, 0x50, 0x55, 0x0D, +0x0A, 0x00, 0x00, 0x00, 0x53, 0x50, 0x44, 0x0D, +0x0A, 0x00, 0x00, 0x00, 0xF8, 0xB5, 0x55, 0x4E, +0x30, 0x78, 0x00, 0x28, 0x7E, 0xD1, 0x70, 0x78, +0x44, 0x08, 0x70, 0x88, 0x64, 0x00, 0x00, 0x0A, +0xC0, 0x07, 0x03, 0xD0, 0x70, 0x88, 0x00, 0x0A, +0x00, 0x02, 0x04, 0x43, 0x30, 0x78, 0x00, 0x28, +0x01, 0xD1, 0x70, 0x88, 0x04, 0x43, 0x70, 0x78, +0xC0, 0x07, 0x6B, 0xD1, 0x4A, 0x48, 0x42, 0x2C, +0x41, 0x69, 0x2F, 0xD0, 0x0F, 0xDC, 0x49, 0x4F, +0x08, 0x2C, 0x2F, 0xD0, 0x05, 0xDC, 0x02, 0x2C, +0x1A, 0xD0, 0x04, 0x2C, 0x7C, 0xD1, 0x46, 0x4F, +0x16, 0xE0, 0x10, 0x2C, 0x26, 0xD0, 0x20, 0x2C, +0x76, 0xD1, 0x80, 0x22, 0x5B, 0xE0, 0x50, 0x2C, +0x04, 0xD0, 0x05, 0xDC, 0x44, 0x2C, 0x1B, 0xD0, +0x48, 0x2C, 0x6D, 0xD1, 0x87, 0x69, 0x05, 0xE0, +0xFF, 0x3C, 0x09, 0x3C, 0x01, 0xD0, 0x08, 0x2C, +0x66, 0xD1, 0x0F, 0x46, 0x00, 0x2F, 0x63, 0xD0, +0x80, 0x21, 0x3A, 0x48, 0xFC, 0xF7, 0x75, 0xFF, +0x39, 0x48, 0x00, 0x78, 0x00, 0x06, 0x5C, 0xD5, +0x60, 0x22, 0x39, 0x46, 0x35, 0x48, 0xFC, 0xF7, +0x53, 0xFF, 0x5A, 0xE0, 0x35, 0x4F, 0xEF, 0xE7, +0x35, 0x4F, 0xED, 0xE7, 0x60, 0x22, 0x35, 0x48, +0xFC, 0xF7, 0x4A, 0xFF, 0x33, 0x4D, 0x20, 0x07, +0x0A, 0xD5, 0x2F, 0x20, 0x41, 0x00, 0xCB, 0x19, +0x6A, 0x5A, 0x1B, 0x88, 0x40, 0x1E, 0xD2, 0x1A, +0x40, 0xB2, 0x6A, 0x52, 0x00, 0x28, 0xF5, 0xDA, +0x00, 0x21, 0x2C, 0x48, 0xFD, 0xF7, 0x9A, 0xFF, +0x2A, 0x48, 0xFD, 0xF7, 0x5B, 0xF9, 0x26, 0x48, +0x00, 0x78, 0x00, 0x06, 0x04, 0xD5, 0x60, 0x22, +0x26, 0x49, 0x27, 0x48, 0xFC, 0xF7, 0x2C, 0xFF, +0x24, 0x48, 0xFD, 0xF7, 0x73, 0xFE, 0x20, 0x07, +0x0A, 0xD5, 0x2F, 0x20, 0x41, 0x00, 0xCB, 0x19, +0x6A, 0x5A, 0x1B, 0x88, 0x40, 0x1E, 0xD2, 0x18, +0x40, 0xB2, 0x6A, 0x52, 0x00, 0x28, 0xF5, 0xDA, +0x19, 0x48, 0x00, 0xE0, 0x1C, 0xE0, 0x00, 0x78, +0x00, 0x06, 0x05, 0xD5, 0x60, 0x22, 0x19, 0x49, +0x14, 0x48, 0xFC, 0xF7, 0x11, 0xFF, 0x0F, 0xE0, +0x18, 0x49, 0x12, 0x4B, 0x0A, 0x68, 0x00, 0x20, +0x11, 0x18, 0xC9, 0x7E, 0x41, 0x29, 0x03, 0xD0, +0x44, 0x00, 0x2C, 0x5B, 0x49, 0x00, 0x5C, 0x52, +0x40, 0x1C, 0x40, 0xB2, 0x30, 0x28, 0xF3, 0xDB, +0x70, 0x78, 0x01, 0x21, 0x08, 0x43, 0x70, 0x70, +0xF8, 0xBD, 0x08, 0x49, 0x38, 0x46, 0xFF, 0xF7, +0x37, 0xFE, 0x71, 0x78, 0x01, 0x20, 0x01, 0x43, +0x71, 0x70, 0xF8, 0xBD, 0x88, 0x02, 0x00, 0x20, +0xE4, 0x02, 0x00, 0x20, 0x00, 0x00, 0x01, 0x20, +0x00, 0x20, 0x00, 0x50, 0x9C, 0x01, 0x00, 0x20, +0x86, 0x02, 0x00, 0x20, 0x00, 0x00, 0x02, 0x20, +0x00, 0x30, 0x00, 0x50, 0x4C, 0x07, 0x00, 0x20, +0x4C, 0x00, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, +0xF7, 0xB5, 0x45, 0x49, 0x84, 0xB0, 0x00, 0x28, +0x13, 0xD0, 0x44, 0x48, 0x00, 0x88, 0x84, 0xB2, +0x05, 0x98, 0x00, 0x28, 0x03, 0xD0, 0x90, 0x68, +0x01, 0x26, 0x10, 0x60, 0x00, 0xE0, 0x26, 0x46, +0x20, 0x20, 0x03, 0x90, 0x08, 0x68, 0x28, 0x27, +0x80, 0x30, 0x00, 0x7C, 0x02, 0x90, 0x3C, 0x48, +0x13, 0xE0, 0x3C, 0x48, 0x00, 0x88, 0x84, 0xB2, +0x05, 0x98, 0x00, 0x28, 0x03, 0xD0, 0x90, 0x68, +0x01, 0x26, 0x10, 0x60, 0x00, 0xE0, 0x26, 0x46, +0x20, 0x20, 0x03, 0x90, 0x08, 0x68, 0x28, 0x27, +0x80, 0x30, 0x40, 0x7C, 0x40, 0x08, 0x02, 0x90, +0x33, 0x48, 0x05, 0x78, 0x10, 0x68, 0x00, 0x28, +0x01, 0xDC, 0x01, 0x20, 0x10, 0x60, 0x53, 0x68, +0x01, 0x46, 0xC0, 0x18, 0x00, 0x04, 0x40, 0x0C, +0x83, 0x42, 0x01, 0xDB, 0x18, 0x46, 0x00, 0xE0, +0x50, 0x60, 0x41, 0x18, 0x68, 0x43, 0xFC, 0xF7, +0x87, 0xFE, 0x00, 0xB2, 0x00, 0x90, 0x29, 0x48, +0x00, 0x88, 0xC1, 0x00, 0x40, 0x18, 0xC0, 0x08, +0x28, 0x1A, 0x81, 0xB2, 0x00, 0x98, 0x01, 0x91, +0x88, 0x42, 0x12, 0xDD, 0x09, 0x21, 0xE8, 0x08, +0xFC, 0xF7, 0x76, 0xFE, 0x01, 0x06, 0x09, 0x0E, +0x00, 0xD1, 0x01, 0x21, 0x01, 0x9A, 0x00, 0x98, +0x80, 0x1A, 0xFC, 0xF7, 0x6D, 0xFE, 0x20, 0x30, +0xC0, 0xB2, 0x28, 0x28, 0x02, 0xD2, 0x07, 0x46, +0x00, 0xE0, 0x03, 0x9F, 0x00, 0x98, 0x69, 0x00, +0x47, 0x43, 0x08, 0x37, 0x38, 0x11, 0x08, 0x1A, +0x00, 0xB2, 0x00, 0x28, 0x01, 0xDA, 0x00, 0x20, +0x02, 0xE0, 0xA8, 0x42, 0x00, 0xDD, 0x28, 0x46, +0x05, 0x99, 0x00, 0x29, 0x01, 0xD0, 0x30, 0x18, +0x00, 0xE0, 0x30, 0x1A, 0x02, 0x99, 0x65, 0x08, +0x49, 0x00, 0x40, 0x1B, 0x61, 0x1A, 0x60, 0x43, +0x4A, 0x10, 0x80, 0x18, 0xFC, 0xF7, 0x48, 0xFE, +0x28, 0x18, 0x01, 0x28, 0x01, 0xDA, 0x01, 0x20, +0x02, 0xE0, 0xA0, 0x42, 0x00, 0xDD, 0x20, 0x46, +0x40, 0x1E, 0x00, 0xB2, 0x07, 0xB0, 0xF0, 0xBD, +0xB8, 0x02, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20, +0x2E, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, +0x2F, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x20, +0xF7, 0xB5, 0x1A, 0x49, 0x09, 0x68, 0x80, 0x31, +0x00, 0x28, 0x06, 0xD0, 0x18, 0x48, 0x0E, 0x7C, +0x00, 0x88, 0xCD, 0x7C, 0x84, 0xB2, 0x4F, 0x7D, +0x05, 0xE0, 0x16, 0x48, 0x4E, 0x7C, 0x00, 0x88, +0x0D, 0x7D, 0x8F, 0x7D, 0x84, 0xB2, 0x10, 0x69, +0xD1, 0x68, 0x68, 0x43, 0x4A, 0x10, 0x80, 0x18, +0xFC, 0xF7, 0x16, 0xFE, 0xC1, 0x19, 0x01, 0x98, +0x40, 0x1E, 0x68, 0x43, 0x08, 0x18, 0x69, 0x08, +0x40, 0x1A, 0x65, 0x08, 0x71, 0x00, 0x40, 0x1B, +0x61, 0x1A, 0x60, 0x43, 0x4A, 0x10, 0x80, 0x18, +0xFC, 0xF7, 0x06, 0xFE, 0x28, 0x18, 0x01, 0x28, +0x01, 0xDA, 0x01, 0x20, 0x02, 0xE0, 0xA0, 0x42, +0x00, 0xDD, 0x20, 0x46, 0x40, 0x1E, 0x00, 0xB2, +0xFE, 0xBD, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20, +0x42, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, +0x05, 0x48, 0x00, 0x68, 0x90, 0x30, 0x41, 0x7B, +0x02, 0x7B, 0x08, 0x02, 0x10, 0x43, 0x03, 0x4A, +0x02, 0x21, 0x11, 0x80, 0x00, 0xB2, 0x70, 0x47, +0xB8, 0x02, 0x00, 0x20, 0x26, 0x00, 0x00, 0x20, +0x70, 0xB5, 0x1A, 0x4D, 0x16, 0x20, 0x28, 0x70, +0x68, 0x78, 0x29, 0x46, 0x00, 0x24, 0x14, 0x31, +0x02, 0x28, 0x0C, 0xD8, 0x00, 0x28, 0x05, 0xD1, +0xA8, 0x78, 0x00, 0x28, 0x02, 0xD0, 0x08, 0x46, +0x1C, 0x30, 0x04, 0x70, 0x48, 0x88, 0x00, 0x28, +0x01, 0xD1, 0x03, 0x20, 0x68, 0x71, 0xAC, 0x70, +0x2C, 0x81, 0xEC, 0x80, 0xAC, 0x81, 0x6C, 0x81, +0x4C, 0x80, 0x8C, 0x80, 0x0C, 0x72, 0x0C, 0x48, +0x44, 0x70, 0x84, 0x70, 0x04, 0x70, 0x09, 0x48, +0xB4, 0x21, 0xD0, 0x30, 0xFC, 0xF7, 0xE9, 0xFD, +0x06, 0x48, 0xA0, 0x21, 0x30, 0x30, 0xFC, 0xF7, +0xE4, 0xFD, 0xEC, 0x70, 0x5A, 0x20, 0xE8, 0x81, +0x2C, 0x71, 0x2C, 0x61, 0x01, 0xF0, 0x66, 0xFC, +0x70, 0xBD, 0x00, 0x00, 0x70, 0x04, 0x00, 0x20, +0x90, 0x02, 0x00, 0x20, 0xFF, 0xB5, 0xA5, 0xB0, +0x18, 0xB2, 0x17, 0x90, 0x00, 0x20, 0x0D, 0x90, +0x25, 0x98, 0x01, 0x78, 0x03, 0x91, 0x26, 0x98, +0x03, 0x78, 0xFE, 0x4A, 0x04, 0x93, 0x10, 0x68, +0x01, 0x46, 0x90, 0x31, 0xCB, 0x7A, 0x89, 0x7A, +0x1B, 0x02, 0x0B, 0x43, 0xA0, 0x21, 0x1C, 0x93, +0x0B, 0x56, 0x00, 0x21, 0x1F, 0x93, 0x18, 0x91, +0x03, 0x7E, 0x07, 0x93, 0x41, 0x7E, 0x1A, 0x91, +0x03, 0x99, 0x59, 0x43, 0x04, 0x9B, 0xC9, 0x18, +0x89, 0xB2, 0x16, 0x91, 0x55, 0x21, 0x0A, 0x91, +0x38, 0x21, 0x13, 0x91, 0x16, 0x99, 0xF0, 0x4B, +0x49, 0x00, 0x22, 0x91, 0x59, 0x5E, 0x1C, 0x9B, +0x99, 0x42, 0x04, 0xDA, 0x0A, 0x23, 0x59, 0x43, +0x49, 0x11, 0x11, 0x91, 0x07, 0xE0, 0xCB, 0x00, +0xC9, 0x18, 0x49, 0x11, 0x11, 0x91, 0x40, 0x21, +0x0A, 0x91, 0x2A, 0x21, 0x13, 0x91, 0xB0, 0x30, +0x41, 0x7A, 0x02, 0x7A, 0x08, 0x02, 0xE5, 0x49, +0x10, 0x43, 0x0A, 0x88, 0xE4, 0x49, 0x90, 0x42, +0x02, 0xD2, 0x08, 0x78, 0x01, 0x28, 0x03, 0xD0, +0xE2, 0x48, 0x00, 0x78, 0x00, 0x28, 0x0B, 0xD0, +0x0A, 0x98, 0x42, 0x00, 0x80, 0x18, 0xC0, 0x05, +0x00, 0x0E, 0x0A, 0x90, 0x13, 0x98, 0x42, 0x00, +0x80, 0x18, 0xC0, 0x05, 0x00, 0x0E, 0x13, 0x90, +0x07, 0x9A, 0x04, 0x98, 0x10, 0x1A, 0x40, 0x1E, +0x23, 0x90, 0x16, 0x98, 0x02, 0x90, 0x00, 0x20, +0x08, 0x90, 0x0C, 0x90, 0x32, 0x21, 0xD6, 0x48, +0xFC, 0xF7, 0x6F, 0xFD, 0x0A, 0x21, 0xD5, 0x48, +0xFC, 0xF7, 0x6B, 0xFD, 0x0A, 0x21, 0xD4, 0x48, +0xFC, 0xF7, 0x67, 0xFD, 0xD2, 0x48, 0x69, 0x46, +0x00, 0x1D, 0x12, 0x90, 0xCF, 0x48, 0x00, 0x1D, +0x09, 0x90, 0x00, 0x20, 0x48, 0x70, 0xCA, 0x48, +0x00, 0x78, 0x01, 0x28, 0x01, 0xD9, 0x02, 0x20, +0x00, 0xE0, 0x0E, 0x20, 0x08, 0x70, 0x88, 0x70, +0x01, 0x20, 0x00, 0x24, 0x0E, 0x90, 0x2C, 0xE0, +0x00, 0x2C, 0x2A, 0xD0, 0x0E, 0x98, 0x00, 0x28, +0x03, 0x98, 0x13, 0xD0, 0x84, 0x42, 0x06, 0xD9, +0x16, 0x98, 0x00, 0x24, 0x02, 0x90, 0x0E, 0x94, +0x68, 0x46, 0x01, 0x70, 0x6D, 0xE1, 0x07, 0x99, +0x02, 0x98, 0x40, 0x1A, 0x80, 0xB2, 0x02, 0x90, +0x02, 0x20, 0x00, 0x1B, 0x41, 0x00, 0xBB, 0x48, +0x08, 0x18, 0x0C, 0xE0, 0x01, 0x19, 0x1A, 0x98, +0x81, 0x42, 0x77, 0xD0, 0x07, 0x99, 0x02, 0x98, +0x40, 0x18, 0x80, 0xB2, 0x02, 0x90, 0xB5, 0x48, +0x61, 0x00, 0x08, 0x18, 0x00, 0x1D, 0x69, 0x46, +0x09, 0x90, 0x08, 0x78, 0x48, 0x70, 0x00, 0x20, +0x08, 0x70, 0x00, 0x20, 0x19, 0x90, 0x15, 0x90, +0x12, 0xE0, 0x00, 0x28, 0x10, 0xD0, 0x04, 0x98, +0x14, 0x90, 0x00, 0x20, 0x0F, 0x90, 0x68, 0x46, +0x40, 0x78, 0x01, 0x25, 0x08, 0x27, 0x18, 0x21, +0x08, 0x40, 0x0B, 0x90, 0x14, 0x98, 0x02, 0x28, +0x01, 0xD9, 0x02, 0x20, 0x14, 0x90, 0x17, 0xE1, +0x23, 0x98, 0x00, 0x25, 0xC0, 0xB2, 0x14, 0x90, +0x01, 0x20, 0x0F, 0x90, 0x68, 0x46, 0x40, 0x78, +0x04, 0x27, 0x40, 0x07, 0x40, 0x0F, 0xEC, 0xE7, +0x0F, 0x98, 0x00, 0x28, 0x01, 0xD0, 0x10, 0x95, +0x01, 0xE0, 0x68, 0x42, 0x10, 0x90, 0x0B, 0x98, +0xB8, 0x43, 0x0B, 0x90, 0x10, 0x99, 0x02, 0x98, +0x40, 0x18, 0x1B, 0x90, 0x40, 0x00, 0x92, 0x49, +0x1D, 0x90, 0x0E, 0x5E, 0x68, 0x46, 0x40, 0x78, +0x1E, 0x90, 0x38, 0x42, 0x01, 0xD1, 0x00, 0x2C, +0x7E, 0xD1, 0x00, 0x2E, 0x7C, 0xDD, 0xF0, 0x07, +0x0B, 0xD1, 0x09, 0x98, 0x09, 0x99, 0x00, 0x88, +0x80, 0x19, 0x08, 0x80, 0x10, 0x98, 0x12, 0x99, +0x40, 0x00, 0x09, 0x5A, 0x12, 0x9A, 0x89, 0x19, +0x11, 0x52, 0x11, 0x98, 0x86, 0x42, 0x6B, 0xDB, +0xF0, 0x07, 0x69, 0xD1, 0x0A, 0x98, 0x70, 0x43, +0x40, 0x11, 0x00, 0xB2, 0x20, 0x90, 0x13, 0x98, +0x70, 0x43, 0x40, 0x11, 0x00, 0xB2, 0x21, 0x90, +0x00, 0x20, 0x01, 0x90, 0x06, 0x90, 0x0E, 0x98, +0x00, 0x28, 0x08, 0xD0, 0x00, 0x2C, 0x10, 0xD0, +0x03, 0x98, 0x00, 0x1B, 0x00, 0x28, 0x0C, 0xDD, +0x01, 0x20, 0x09, 0xE0, 0xFC, 0xE0, 0x00, 0x2C, +0x07, 0xD0, 0x03, 0x98, 0x01, 0x19, 0x1A, 0x98, +0x40, 0x1E, 0x81, 0x42, 0x01, 0xDA, 0x02, 0x20, +0x01, 0x90, 0x0F, 0x98, 0x00, 0x28, 0x0A, 0xD0, +0x00, 0x2D, 0x12, 0xD0, 0x04, 0x98, 0x41, 0x19, +0x07, 0x98, 0x40, 0x1E, 0x81, 0x42, 0x0C, 0xDA, +0x08, 0x21, 0x01, 0x98, 0x07, 0xE0, 0x00, 0x2D, +0x07, 0xD0, 0x04, 0x98, 0x40, 0x1B, 0x00, 0x28, +0x03, 0xDD, 0x01, 0x98, 0x04, 0x21, 0x08, 0x43, +0x01, 0x90, 0x03, 0x98, 0x00, 0x1B, 0x00, 0x28, +0x01, 0xDC, 0x01, 0x20, 0x06, 0xE0, 0x03, 0x98, +0x01, 0x19, 0x1A, 0x98, 0x40, 0x1E, 0x81, 0x42, +0x01, 0xDB, 0x02, 0x20, 0x06, 0x90, 0x04, 0x98, +0x41, 0x19, 0x07, 0x98, 0x40, 0x1E, 0x81, 0x42, +0x02, 0xDB, 0x08, 0x21, 0x06, 0x98, 0x05, 0xE0, +0x04, 0x98, 0x40, 0x1B, 0x00, 0x28, 0x03, 0xDC, +0x06, 0x98, 0x04, 0x21, 0x08, 0x43, 0x06, 0x90, +0x20, 0x46, 0x28, 0x43, 0x4F, 0xD0, 0x00, 0x20, +0x05, 0x90, 0x00, 0x2C, 0x14, 0xD0, 0x00, 0x2D, +0x18, 0xD0, 0x41, 0x00, 0x3C, 0x20, 0xC8, 0x40, +0x01, 0x99, 0x08, 0x40, 0x01, 0x07, 0x00, 0xE0, +0x39, 0xE0, 0x09, 0x0F, 0x06, 0x98, 0x81, 0x43, +0x1F, 0xD0, 0x1B, 0x98, 0x80, 0xB2, 0x00, 0xF0, +0xFF, 0xFB, 0xC1, 0x07, 0x19, 0xD1, 0x0C, 0xE0, +0x01, 0x46, 0x04, 0x20, 0xC8, 0x40, 0x81, 0x07, +0x89, 0x0F, 0x03, 0xE0, 0x02, 0x21, 0x81, 0x40, +0x0C, 0x20, 0x01, 0x40, 0x01, 0x98, 0x01, 0x43, +0xE8, 0xE7, 0x18, 0x9A, 0x0D, 0x99, 0x11, 0x43, +0x04, 0xD1, 0x21, 0x99, 0x88, 0x42, 0x01, 0xDD, +0x01, 0x21, 0x18, 0x91, 0x20, 0x99, 0x88, 0x42, +0x05, 0xDC, 0x05, 0x98, 0x40, 0x1C, 0xC0, 0xB2, +0x05, 0x90, 0x03, 0x28, 0xC9, 0xD3, 0x05, 0x98, +0x03, 0x28, 0x14, 0xD0, 0x09, 0x98, 0x09, 0x9A, +0x01, 0x88, 0x70, 0x10, 0x09, 0x1A, 0x11, 0x80, +0x10, 0x99, 0x12, 0x9A, 0x49, 0x00, 0x52, 0x5A, +0x10, 0x1A, 0x12, 0x9A, 0x50, 0x52, 0x0B, 0x98, +0x00, 0x28, 0x39, 0xD0, 0x0F, 0x98, 0x00, 0x28, +0x2E, 0xD0, 0x7F, 0x08, 0x2E, 0xE0, 0x08, 0x99, +0x1B, 0x98, 0x4A, 0x00, 0x2E, 0x49, 0x88, 0x52, +0x08, 0x98, 0x40, 0x1C, 0x80, 0xB2, 0x08, 0x90, +0x0C, 0x98, 0x86, 0x42, 0x00, 0xDD, 0x0C, 0x96, +0x25, 0x48, 0x1D, 0x99, 0x42, 0x5A, 0x01, 0x21, +0x0A, 0x43, 0x1D, 0x99, 0x42, 0x52, 0x69, 0x46, +0x08, 0x78, 0x38, 0x43, 0x08, 0x70, 0x00, 0x2C, +0x0D, 0xD0, 0x0F, 0x98, 0x00, 0x28, 0x1E, 0x98, +0x05, 0xD0, 0x79, 0x08, 0x08, 0x43, 0x69, 0x46, +0x48, 0x70, 0x00, 0x2D, 0x03, 0xD1, 0x79, 0x00, +0x08, 0x43, 0x69, 0x46, 0x48, 0x70, 0x19, 0x98, +0x40, 0x1C, 0xC0, 0xB2, 0x19, 0x90, 0xCD, 0xE7, +0x78, 0x06, 0x07, 0x0E, 0x6D, 0x1C, 0xED, 0xB2, +0x14, 0x98, 0x85, 0x42, 0x00, 0xD8, 0xEF, 0xE6, +0x15, 0x98, 0x40, 0x1C, 0xC0, 0xB2, 0x15, 0x90, +0x02, 0x28, 0x00, 0xD2, 0xC9, 0xE6, 0x00, 0x2C, +0x02, 0xD1, 0x69, 0x46, 0x08, 0x78, 0x88, 0x70, +0x19, 0x98, 0x00, 0x28, 0x01, 0xD0, 0x02, 0x2C, +0x09, 0xD1, 0x0E, 0x98, 0x00, 0x28, 0x1F, 0xD0, +0x16, 0x98, 0x00, 0x24, 0x69, 0x46, 0x0E, 0x94, +0x02, 0x90, 0x88, 0x78, 0x08, 0x70, 0x68, 0x46, +0x81, 0x78, 0x64, 0x1C, 0xE4, 0xB2, 0x02, 0x2C, +0x10, 0xE0, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20, +0x4C, 0x07, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20, +0x40, 0x05, 0x00, 0x20, 0x2A, 0x00, 0x00, 0x20, +0x1C, 0x02, 0x00, 0x20, 0x1A, 0x00, 0x00, 0x20, +0x10, 0x00, 0x00, 0x20, 0x00, 0xD8, 0x6B, 0xE6, +0x08, 0x98, 0x01, 0x28, 0x1B, 0xD0, 0x1F, 0x99, +0x88, 0x42, 0x18, 0xDA, 0x2C, 0x48, 0x00, 0x68, +0xB0, 0x30, 0x41, 0x7A, 0x02, 0x7A, 0x08, 0x02, +0x2A, 0x49, 0x10, 0x43, 0x09, 0x88, 0x88, 0x42, +0x03, 0xD2, 0x29, 0x48, 0x00, 0x78, 0x01, 0x28, +0x09, 0xD0, 0x0D, 0x98, 0x01, 0x28, 0x06, 0xD0, +0x18, 0x98, 0x00, 0x28, 0x03, 0xD0, 0x0D, 0x98, +0x00, 0x28, 0x05, 0xD0, 0x2E, 0xE0, 0x17, 0x99, +0x0C, 0x98, 0x88, 0x42, 0x2A, 0xDC, 0x38, 0xE0, +0x01, 0x20, 0x0D, 0x90, 0x38, 0x20, 0x0A, 0x90, +0x1E, 0x4A, 0x22, 0x98, 0x1C, 0x99, 0x10, 0x5E, +0x88, 0x42, 0x05, 0xDA, 0x09, 0x21, 0x09, 0x03, +0x48, 0x43, 0x00, 0x14, 0x11, 0x90, 0x06, 0xE0, +0x05, 0x21, 0x49, 0x03, 0x48, 0x43, 0x00, 0x14, +0x11, 0x90, 0x2A, 0x20, 0x0A, 0x90, 0x00, 0x20, +0x15, 0x4B, 0x08, 0xE0, 0x41, 0x00, 0x59, 0x5A, +0x49, 0x00, 0x54, 0x5A, 0x64, 0x08, 0x64, 0x00, +0x40, 0x1C, 0x54, 0x52, 0x80, 0xB2, 0x08, 0x99, +0x88, 0x42, 0xF3, 0xD3, 0x0D, 0x98, 0x02, 0x28, +0x00, 0xD2, 0xF6, 0xE5, 0x27, 0x9A, 0x0D, 0x48, +0x18, 0x32, 0x25, 0x99, 0xFD, 0xF7, 0x1E, 0xF9, +0x0B, 0x48, 0x27, 0x9A, 0x26, 0x99, 0xFD, 0xF7, +0x19, 0xF9, 0x17, 0x9A, 0x0C, 0x99, 0x91, 0x42, +0x00, 0xDC, 0x08, 0x48, 0x29, 0xB0, 0xF0, 0xBD, +0xB8, 0x02, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20, +0x40, 0x05, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, +0x1C, 0x02, 0x00, 0x20, 0x1A, 0x00, 0x00, 0x20, +0x10, 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0xFF, +0x06, 0x4A, 0x07, 0x4B, 0x00, 0x21, 0x00, 0x20, +0x40, 0x1C, 0x90, 0x42, 0xFC, 0xDB, 0x49, 0x1C, +0x99, 0x42, 0xF8, 0xDB, 0x03, 0x48, 0x01, 0x21, +0x01, 0x60, 0x70, 0x47, 0x10, 0x27, 0x00, 0x00, +0xB8, 0x0B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, +0xF0, 0xB5, 0x1A, 0x48, 0x8F, 0xB0, 0x01, 0x78, +0x02, 0x29, 0x01, 0xD9, 0x02, 0x21, 0x01, 0x70, +0x00, 0x25, 0x24, 0xE0, 0x68, 0x1C, 0xC4, 0xB2, +0x0D, 0x90, 0x1A, 0xE0, 0x34, 0x21, 0x69, 0x43, +0x34, 0x22, 0x0E, 0x18, 0x62, 0x43, 0x17, 0x18, +0x31, 0x79, 0x38, 0x79, 0x81, 0x42, 0x0E, 0xD9, +0x31, 0x1D, 0x34, 0x22, 0x68, 0x46, 0xFC, 0xF7, +0x03, 0xFB, 0x39, 0x1D, 0x30, 0x1D, 0x34, 0x22, +0xFC, 0xF7, 0xFE, 0xFA, 0x38, 0x1D, 0x34, 0x22, +0x69, 0x46, 0xFC, 0xF7, 0xF9, 0xFA, 0x64, 0x1C, +0xE4, 0xB2, 0x06, 0x48, 0x01, 0x78, 0xA1, 0x42, +0xE0, 0xD8, 0x0D, 0x98, 0xC5, 0xB2, 0x03, 0x49, +0x08, 0x78, 0xA8, 0x42, 0xD6, 0xD8, 0x0F, 0xB0, +0xF0, 0xBD, 0x00, 0x00, 0xA0, 0x04, 0x00, 0x20, +0x70, 0xB5, 0x7B, 0x24, 0x21, 0x48, 0x24, 0x02, +0x04, 0x60, 0xF7, 0x20, 0xC0, 0x01, 0x20, 0x4A, +0xC1, 0x7C, 0x11, 0x70, 0x1F, 0x4A, 0x01, 0x7D, +0x11, 0x70, 0x1F, 0x4A, 0x41, 0x7D, 0x11, 0x70, +0x1E, 0x49, 0x80, 0x7D, 0x08, 0x70, 0x60, 0x7C, +0x21, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, +0x1B, 0x49, 0x05, 0x46, 0x08, 0x80, 0xE0, 0x7C, +0xA1, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, +0x18, 0x49, 0x08, 0x80, 0x20, 0x7E, 0x61, 0x7E, +0x48, 0x43, 0x17, 0x49, 0x08, 0x80, 0x17, 0x48, +0xFC, 0xF7, 0xE3, 0xFA, 0x10, 0x21, 0xC8, 0x41, +0x15, 0x49, 0x08, 0x60, 0x04, 0x22, 0x21, 0x1D, +0x14, 0x48, 0xFC, 0xF7, 0xB5, 0xFA, 0x11, 0x49, +0x06, 0x22, 0x89, 0x1F, 0x12, 0x48, 0xFC, 0xF7, +0xAF, 0xFA, 0x60, 0x7D, 0x22, 0x7D, 0x01, 0x02, +0x11, 0x43, 0x28, 0x46, 0x0A, 0x22, 0x50, 0x43, +0xFC, 0xF7, 0x92, 0xFA, 0x0D, 0x49, 0x08, 0x80, +0x70, 0xBD, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20, +0x2C, 0x00, 0x00, 0x20, 0x2D, 0x00, 0x00, 0x20, +0x2E, 0x00, 0x00, 0x20, 0x2F, 0x00, 0x00, 0x20, +0x42, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, +0x3C, 0x00, 0x00, 0x20, 0x10, 0x7B, 0x00, 0x00, +0xD8, 0x06, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20, +0xE4, 0x06, 0x00, 0x20, 0x40, 0x00, 0x00, 0x20, +0x64, 0x24, 0x00, 0x20, 0x56, 0x49, 0x08, 0x70, +0xA9, 0xE0, 0x55, 0x48, 0x00, 0x78, 0x03, 0x00, +0xFC, 0xF7, 0xFC, 0xFA, 0x07, 0x05, 0x10, 0x1E, +0x18, 0x37, 0x48, 0x50, 0xA3, 0x00, 0x00, 0xF0, +0xCB, 0xF9, 0x01, 0x28, 0x02, 0xD1, 0x4E, 0x49, +0x08, 0x70, 0x02, 0xE0, 0x03, 0x20, 0x4C, 0x49, +0x08, 0x70, 0x93, 0xE0, 0x00, 0xF0, 0xD6, 0xF8, +0x01, 0x28, 0x02, 0xD1, 0x02, 0x20, 0x48, 0x49, +0x08, 0x70, 0x8B, 0xE0, 0x00, 0xF0, 0xB2, 0xF9, +0x00, 0x20, 0x45, 0x49, 0x08, 0x70, 0x85, 0xE0, +0xFF, 0xF7, 0x06, 0xF8, 0x01, 0x28, 0x10, 0xD1, +0xFF, 0xF7, 0xE0, 0xF8, 0x41, 0x48, 0x00, 0x78, +0x80, 0x21, 0x08, 0x42, 0x03, 0xD0, 0x01, 0x20, +0x3D, 0x49, 0x08, 0x70, 0x02, 0xE0, 0x04, 0x20, +0x3B, 0x49, 0x08, 0x70, 0xFC, 0xF7, 0x74, 0xFD, +0x02, 0xE0, 0x01, 0x20, 0x38, 0x49, 0x08, 0x70, +0x6C, 0xE0, 0x00, 0xF0, 0xBB, 0xF9, 0x01, 0x28, +0x0B, 0xD1, 0x36, 0x48, 0x00, 0x78, 0x80, 0x21, +0x08, 0x42, 0x03, 0xD0, 0x01, 0x20, 0x32, 0x49, +0x08, 0x70, 0x02, 0xE0, 0x05, 0x20, 0x30, 0x49, +0x08, 0x70, 0x5B, 0xE0, 0x00, 0xF0, 0x6A, 0xF8, +0x01, 0x28, 0x02, 0xD1, 0x06, 0x20, 0x2C, 0x49, +0x08, 0x70, 0x53, 0xE0, 0x2C, 0x48, 0x00, 0x78, +0x00, 0x28, 0x09, 0xD1, 0x01, 0x20, 0xFC, 0xF7, +0xEF, 0xFA, 0x00, 0x20, 0x29, 0x49, 0x08, 0x70, +0x01, 0x20, 0x25, 0x49, 0x08, 0x70, 0x45, 0xE0, +0xFF, 0xF7, 0x72, 0xF9, 0x01, 0x28, 0x2E, 0xD1, +0x24, 0x48, 0x00, 0x78, 0x00, 0x28, 0x2A, 0xD1, +0x01, 0xF0, 0xAA, 0xFA, 0x22, 0x48, 0xC0, 0x78, +0x01, 0x28, 0x04, 0xD0, 0x21, 0x48, 0x40, 0x78, +0xC0, 0x07, 0xC0, 0x0F, 0x2D, 0xD0, 0x01, 0x25, +0xAD, 0x07, 0xA9, 0x14, 0x28, 0x68, 0x08, 0x40, +0x00, 0x0A, 0x0A, 0x21, 0x08, 0x43, 0x19, 0x49, +0xC8, 0x70, 0xB4, 0x24, 0x1A, 0x48, 0x00, 0x78, +0xFF, 0x28, 0x05, 0xDA, 0x18, 0x48, 0x00, 0x78, +0x40, 0x1C, 0x17, 0x49, 0x08, 0x70, 0x02, 0xE0, +0x01, 0x20, 0x15, 0x49, 0x08, 0x70, 0x14, 0x48, +0x00, 0x78, 0x10, 0x49, 0x08, 0x70, 0x00, 0x20, +0xFC, 0xF7, 0xB6, 0xFA, 0x0D, 0xE0, 0x00, 0x2C, +0x05, 0xD0, 0x60, 0x1E, 0xC4, 0xB2, 0x01, 0x20, +0xFC, 0xF7, 0xBE, 0xFB, 0x05, 0xE0, 0x00, 0x20, +0x08, 0x49, 0x08, 0x70, 0x01, 0x20, 0xFC, 0xF7, +0xA7, 0xFA, 0x01, 0x20, 0x02, 0x49, 0x08, 0x70, +0x00, 0xE0, 0x00, 0xBF, 0x00, 0xBF, 0x54, 0xE7, +0x80, 0x02, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, +0xE4, 0x02, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, +0xA0, 0x04, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20, +0x84, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x18, 0x48, +0x00, 0x78, 0x03, 0x00, 0xFC, 0xF7, 0x42, 0xFA, +0x06, 0x28, 0x04, 0x0A, 0x16, 0x1C, 0x22, 0x28, +0x01, 0xF0, 0xD2, 0xF8, 0x02, 0x20, 0x12, 0x49, +0x08, 0x70, 0x1D, 0xE0, 0x01, 0xF0, 0xC0, 0xF8, +0x00, 0x28, 0x03, 0xD0, 0x03, 0x20, 0x0E, 0x49, +0x08, 0x70, 0x02, 0xE0, 0x05, 0x20, 0x0C, 0x49, +0x08, 0x70, 0x11, 0xE0, 0x01, 0xF0, 0xB0, 0xF8, +0x04, 0x20, 0x09, 0x49, 0x08, 0x70, 0x0B, 0xE0, +0x01, 0xF0, 0x14, 0xF9, 0x05, 0x20, 0x06, 0x49, +0x08, 0x70, 0x05, 0xE0, 0x01, 0xF0, 0x3E, 0xFA, +0x01, 0x20, 0x03, 0x49, 0x08, 0x70, 0x10, 0xBD, +0x00, 0xBF, 0x00, 0x20, 0xFB, 0xE7, 0x00, 0x00, +0x0C, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x63, 0x4C, +0x20, 0x78, 0x00, 0x28, 0x41, 0xD0, 0x02, 0x20, +0xFC, 0xF7, 0x66, 0xFB, 0x62, 0x88, 0x21, 0x78, +0x5F, 0xA0, 0x00, 0xF0, 0x1F, 0xFD, 0x60, 0x88, +0x61, 0x4A, 0x00, 0x25, 0x90, 0x42, 0x02, 0xD1, +0xA5, 0x70, 0x28, 0x0A, 0xE0, 0x70, 0x20, 0x78, +0x5E, 0x4E, 0x5F, 0x49, 0x44, 0x28, 0x75, 0xD0, +0x13, 0xDC, 0x01, 0x22, 0x33, 0x28, 0x34, 0xD0, +0x08, 0xDC, 0x20, 0x28, 0x74, 0xD0, 0x30, 0x28, +0x28, 0xD0, 0x31, 0x28, 0x3D, 0xD0, 0x32, 0x28, +0x3B, 0xD1, 0x27, 0xE0, 0x34, 0x28, 0x39, 0xD0, +0x42, 0x28, 0x6F, 0xD0, 0x43, 0x28, 0x34, 0xD1, +0x90, 0xE0, 0x60, 0x28, 0x78, 0xD0, 0x07, 0xDC, +0x5D, 0x26, 0x5A, 0x28, 0x0B, 0xD0, 0x5C, 0x28, +0x0C, 0xD0, 0x5D, 0x28, 0x29, 0xD1, 0x0D, 0xE0, +0x62, 0x28, 0x65, 0xD0, 0x70, 0x28, 0x53, 0xD0, +0x71, 0x28, 0x7A, 0xD1, 0x71, 0xE0, 0xFF, 0xF7, +0xEF, 0xF8, 0x01, 0xE0, 0xFF, 0xF7, 0x02, 0xF9, +0x26, 0x70, 0x73, 0xE0, 0xC8, 0x78, 0x00, 0x28, +0x6F, 0xD0, 0x6F, 0xE0, 0x45, 0x49, 0x82, 0x20, +0x08, 0x70, 0x6B, 0xE0, 0x60, 0x88, 0x44, 0x49, +0x54, 0xE0, 0x60, 0x88, 0x08, 0x82, 0xA5, 0x70, +0x28, 0x0A, 0xE0, 0x70, 0x08, 0x8A, 0x04, 0x28, +0x5F, 0xD1, 0xFF, 0xF7, 0x8D, 0xF8, 0x00, 0x28, +0x02, 0xD1, 0x01, 0x20, 0xFC, 0xF7, 0xFC, 0xF9, +0x35, 0x70, 0x56, 0xE0, 0x60, 0x88, 0xC0, 0x07, +0x04, 0xD0, 0x48, 0x8A, 0x39, 0x4A, 0x80, 0x08, +0x80, 0x00, 0x15, 0xE0, 0x60, 0x88, 0x80, 0x07, +0x05, 0xD5, 0x48, 0x8A, 0x35, 0x4A, 0x80, 0x08, +0x80, 0x00, 0x52, 0x1C, 0x0C, 0xE0, 0x60, 0x88, +0xC0, 0x04, 0x48, 0x8A, 0x04, 0xD5, 0x80, 0x09, +0x30, 0x4A, 0x80, 0x01, 0xD2, 0x1C, 0x03, 0xE0, +0x80, 0x09, 0x80, 0x01, 0x01, 0x22, 0xD2, 0x03, +0x10, 0x43, 0x48, 0x82, 0x60, 0x88, 0xC0, 0x05, +0x03, 0xD5, 0x48, 0x8A, 0x08, 0x22, 0x10, 0x43, +0x48, 0x82, 0x0D, 0x72, 0xA5, 0x70, 0x28, 0x0A, +0xE0, 0x70, 0x2A, 0xE0, 0x30, 0xE0, 0x03, 0xE0, +0x25, 0x70, 0x25, 0x48, 0x05, 0x70, 0x25, 0xE0, +0x60, 0x88, 0x00, 0x28, 0xC8, 0x79, 0x03, 0xD0, +0x10, 0x43, 0x03, 0xE0, 0x20, 0xE0, 0x0B, 0xE0, +0x40, 0x08, 0x40, 0x00, 0xC8, 0x71, 0x18, 0xE0, +0xC8, 0x69, 0x02, 0x80, 0x60, 0x30, 0xC2, 0x83, +0x60, 0x88, 0x1C, 0x49, 0x08, 0x70, 0xE1, 0xE7, +0x48, 0x89, 0xA0, 0x70, 0x00, 0x0A, 0xE0, 0x70, +0x19, 0x48, 0x00, 0x68, 0x60, 0x30, 0x00, 0x78, +0x04, 0xE0, 0x71, 0x20, 0xA0, 0x70, 0x00, 0x20, +0xE0, 0x70, 0x0B, 0x20, 0x20, 0x71, 0x00, 0x0A, +0x60, 0x71, 0x25, 0x70, 0x01, 0x20, 0x70, 0xBD, +0x0A, 0x70, 0xFB, 0xE7, 0x0D, 0x70, 0xF9, 0xE7, +0x01, 0x20, 0xFC, 0xF7, 0x99, 0xF9, 0x35, 0x70, +0xF4, 0xE7, 0x00, 0x00, 0x88, 0x02, 0x00, 0x20, +0x43, 0x4D, 0x44, 0x3A, 0x30, 0x78, 0x25, 0x78, +0x2C, 0x30, 0x78, 0x25, 0x78, 0x0D, 0x0A, 0x00, +0xAA, 0x55, 0x00, 0x00, 0x90, 0x02, 0x00, 0x20, +0xE4, 0x02, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, +0x2B, 0x00, 0x00, 0x20, 0x01, 0x80, 0x00, 0x00, +0x80, 0x02, 0x00, 0x20, 0x86, 0x02, 0x00, 0x20, +0xB8, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x01, 0x20, +0x00, 0x06, 0xFC, 0xF7, 0x64, 0xF9, 0x10, 0xBD, +0x10, 0xB5, 0x00, 0x24, 0x00, 0xF0, 0x46, 0xFE, +0x01, 0xF0, 0x0E, 0xF8, 0x01, 0x20, 0xFE, 0xF7, +0x11, 0xFF, 0x04, 0x46, 0x00, 0x2C, 0x04, 0xD1, +0x05, 0xA0, 0x00, 0xF0, 0x33, 0xFC, 0x00, 0x20, +0x10, 0xBD, 0x06, 0x49, 0x06, 0x48, 0x81, 0x70, +0x09, 0x0A, 0xC1, 0x70, 0x01, 0x20, 0xF7, 0xE7, +0x49, 0x4E, 0x49, 0x54, 0x20, 0x4E, 0x47, 0x0D, +0x0A, 0x00, 0x00, 0x00, 0xAA, 0x55, 0x00, 0x00, +0x88, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x1C, 0x48, +0x00, 0x78, 0x01, 0x28, 0x02, 0xD0, 0x02, 0x28, +0x2E, 0xD1, 0x1E, 0xE0, 0xFE, 0xF7, 0xDE, 0xFF, +0xFD, 0xF7, 0x92, 0xFE, 0x17, 0x48, 0x00, 0x7A, +0x00, 0x28, 0x04, 0xD1, 0x01, 0x20, 0x16, 0x49, +0x08, 0x70, 0x00, 0x20, 0x10, 0xBD, 0xFC, 0xF7, +0xD9, 0xF9, 0xFE, 0xF7, 0x87, 0xFF, 0x13, 0x48, +0x00, 0x78, 0x80, 0x21, 0x08, 0x42, 0x04, 0xD0, +0x06, 0x20, 0x0F, 0x49, 0x08, 0x70, 0x00, 0x20, +0xF0, 0xE7, 0x02, 0x20, 0x0A, 0x49, 0x08, 0x70, +0x0E, 0xE0, 0xFE, 0xF7, 0xAB, 0xFE, 0x00, 0x28, +0x03, 0xD0, 0x01, 0x20, 0x06, 0x49, 0x08, 0x70, +0xE4, 0xE7, 0x01, 0x20, 0x04, 0x49, 0x08, 0x70, +0x06, 0x20, 0x05, 0x49, 0x08, 0x70, 0x00, 0xBF, +0x00, 0xBF, 0x00, 0x20, 0xDA, 0xE7, 0x00, 0x00, +0x81, 0x02, 0x00, 0x20, 0x50, 0x02, 0x00, 0x20, +0x80, 0x02, 0x00, 0x20, 0x86, 0x02, 0x00, 0x20, +0x0A, 0x07, 0x01, 0xD5, 0x40, 0x1C, 0x02, 0xE0, +0x4A, 0x07, 0x01, 0xD5, 0x40, 0x1E, 0x80, 0xB2, +0x07, 0x4A, 0xCB, 0x07, 0x12, 0x68, 0x02, 0xD0, +0x11, 0x7E, 0x40, 0x1A, 0x03, 0xE0, 0x89, 0x07, +0x02, 0xD5, 0x11, 0x7E, 0x08, 0x18, 0x80, 0xB2, +0x02, 0x49, 0x40, 0x00, 0x08, 0x5E, 0x70, 0x47, +0xB8, 0x02, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, +0xF0, 0xB5, 0x41, 0x18, 0x15, 0x4C, 0x16, 0x4D, +0x8C, 0x46, 0x0E, 0xE0, 0x29, 0x78, 0x01, 0x23, +0x22, 0x88, 0x05, 0xE0, 0x1E, 0x46, 0x8E, 0x40, +0x16, 0x42, 0x09, 0xD0, 0x49, 0x1C, 0xC9, 0xB2, +0x02, 0x29, 0xF7, 0xD3, 0x40, 0x1C, 0x29, 0x70, +0xC0, 0xB2, 0x84, 0x45, 0xEE, 0xD8, 0xF0, 0xBD, +0x34, 0x27, 0x06, 0x46, 0x7E, 0x43, 0x0B, 0x4F, +0xF6, 0x19, 0x1F, 0x46, 0x31, 0x71, 0x8F, 0x40, +0x73, 0x72, 0x17, 0x43, 0x27, 0x80, 0x73, 0x71, +0x73, 0x89, 0x33, 0x82, 0xB2, 0x89, 0x72, 0x82, +0xB3, 0x85, 0xF2, 0x85, 0xFF, 0x22, 0x20, 0x36, +0x72, 0x75, 0xE3, 0xE7, 0x3E, 0x00, 0x00, 0x20, +0x31, 0x00, 0x00, 0x20, 0xA0, 0x04, 0x00, 0x20, +0xF0, 0xB5, 0x95, 0xB0, 0x00, 0x20, 0x0C, 0x90, +0x12, 0x90, 0x30, 0x21, 0x68, 0x46, 0xFC, 0xF7, +0x08, 0xF8, 0xC9, 0x48, 0x01, 0x78, 0x40, 0x78, +0x08, 0x18, 0x01, 0x06, 0x09, 0x0E, 0xC7, 0x48, +0x0E, 0x91, 0x01, 0xD0, 0x00, 0x27, 0x99, 0xE0, +0x01, 0x78, 0x00, 0x20, 0xFF, 0xF7, 0xB4, 0xFF, +0x15, 0xB0, 0xF0, 0xBD, 0x34, 0x20, 0x39, 0x46, +0x41, 0x43, 0xC0, 0x48, 0x00, 0x25, 0x08, 0x18, +0x0F, 0x90, 0x00, 0x79, 0xFF, 0x28, 0x7D, 0xD1, +0x38, 0x01, 0x12, 0x9E, 0x10, 0x90, 0x75, 0xE0, +0x10, 0x9A, 0x68, 0x46, 0x14, 0x18, 0x60, 0x19, +0xFF, 0x21, 0x13, 0x90, 0x41, 0x70, 0xA8, 0x00, +0x20, 0x18, 0xB7, 0x49, 0x11, 0x90, 0x41, 0x60, +0x0F, 0x98, 0x0C, 0x21, 0x41, 0x5E, 0x0A, 0x22, +0x8C, 0x46, 0x82, 0x5E, 0x06, 0x20, 0x31, 0x46, +0x41, 0x43, 0xAF, 0x48, 0x00, 0x23, 0x08, 0x18, +0xA0, 0x30, 0x02, 0x21, 0xC3, 0x5E, 0x41, 0x5E, +0x18, 0x46, 0x63, 0x46, 0x01, 0xF0, 0x52, 0xF8, +0x0D, 0x90, 0xAC, 0x48, 0x01, 0x88, 0x00, 0x29, +0x07, 0xD0, 0xAB, 0x48, 0x00, 0x88, 0x88, 0x42, +0x03, 0xD9, 0xFB, 0xF7, 0x7B, 0xFF, 0xC0, 0xB2, +0x00, 0xE0, 0x00, 0x20, 0x34, 0x21, 0xA2, 0x4A, +0x71, 0x43, 0x89, 0x18, 0xC9, 0x69, 0xA6, 0x4B, +0x4A, 0x00, 0xA4, 0x49, 0x1B, 0x78, 0x09, 0x78, +0x40, 0x1C, 0x59, 0x43, 0x41, 0x43, 0x50, 0x18, +0x9D, 0x49, 0x88, 0x42, 0x01, 0xD9, 0x08, 0x46, +0x03, 0xE0, 0xA0, 0x49, 0x88, 0x42, 0x00, 0xD2, +0x08, 0x46, 0x9F, 0x49, 0x09, 0x68, 0xB0, 0x31, +0x4A, 0x7A, 0x0B, 0x7A, 0x11, 0x02, 0x98, 0x4A, +0x19, 0x43, 0x12, 0x88, 0x91, 0x42, 0x04, 0xD2, +0x91, 0x49, 0x09, 0x78, 0x01, 0x29, 0x00, 0xD1, +0x91, 0x48, 0x0D, 0x99, 0x81, 0x42, 0x1B, 0xD8, +0x08, 0x46, 0x11, 0x99, 0x6D, 0x1C, 0x48, 0x60, +0x13, 0x98, 0xED, 0xB2, 0x46, 0x70, 0x01, 0x2D, +0x12, 0xD9, 0x68, 0x1E, 0x0D, 0xE0, 0x81, 0x00, +0x62, 0x18, 0x94, 0x46, 0x52, 0x68, 0x63, 0x58, +0x9A, 0x42, 0x05, 0xD2, 0x22, 0x18, 0x52, 0x78, +0x22, 0x54, 0x62, 0x46, 0x52, 0x68, 0x62, 0x50, +0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, 0xEE, 0xD1, +0x76, 0x1C, 0xF6, 0xB2, 0x0E, 0x98, 0x86, 0x42, +0x86, 0xD3, 0x00, 0xE0, 0x08, 0xE0, 0x10, 0x98, +0x69, 0x46, 0x0D, 0x54, 0x00, 0x2D, 0x03, 0xD0, +0x0C, 0x98, 0x40, 0x1C, 0xC0, 0xB2, 0x0C, 0x90, +0x7F, 0x1C, 0xFF, 0xB2, 0x77, 0x48, 0x00, 0x78, +0x84, 0x46, 0xB8, 0x42, 0x00, 0xD9, 0x65, 0xE7, +0x0C, 0x98, 0x00, 0x28, 0x7D, 0xD0, 0x01, 0x28, +0x59, 0xD9, 0x00, 0x24, 0x55, 0xE0, 0x60, 0x1C, +0xC3, 0xB2, 0x0C, 0x90, 0x4D, 0xE0, 0x26, 0x01, +0x69, 0x46, 0x72, 0x18, 0x1D, 0x01, 0x69, 0x18, +0x50, 0x78, 0x4F, 0x78, 0xB8, 0x42, 0x42, 0xD1, +0x50, 0x68, 0x4F, 0x68, 0xB8, 0x42, 0x1F, 0xD2, +0x00, 0x20, 0x0C, 0xE0, 0x0A, 0x18, 0x56, 0x78, +0x97, 0x78, 0x57, 0x70, 0x96, 0x70, 0x82, 0x00, +0x8A, 0x18, 0x97, 0x68, 0x56, 0x68, 0x40, 0x1C, +0x57, 0x60, 0xC0, 0xB2, 0x96, 0x60, 0x6A, 0x46, +0x52, 0x5D, 0x52, 0x1E, 0x82, 0x42, 0xED, 0xDC, +0x6A, 0x46, 0x50, 0x5D, 0x00, 0x28, 0x04, 0xD0, +0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, 0x50, 0x55, +0x21, 0xD1, 0xFF, 0x20, 0x48, 0x70, 0x1E, 0xE0, +0x00, 0x20, 0x0C, 0xE0, 0x11, 0x18, 0x4D, 0x78, +0x8F, 0x78, 0x4F, 0x70, 0x8D, 0x70, 0x81, 0x00, +0x51, 0x18, 0x8F, 0x68, 0x4D, 0x68, 0x40, 0x1C, +0x4F, 0x60, 0xC0, 0xB2, 0x8D, 0x60, 0x69, 0x46, +0x89, 0x5D, 0x49, 0x1E, 0x81, 0x42, 0xED, 0xDC, +0x69, 0x46, 0x88, 0x5D, 0x00, 0x28, 0x04, 0xD0, +0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, 0x88, 0x55, +0x01, 0xD1, 0xFF, 0x20, 0x50, 0x70, 0x5B, 0x1C, +0xDB, 0xB2, 0x9C, 0x45, 0xAF, 0xD8, 0x0C, 0x98, +0xC4, 0xB2, 0xA4, 0x45, 0xA7, 0xD8, 0x00, 0x26, +0x6E, 0xE0, 0x32, 0x01, 0x68, 0x46, 0x10, 0x18, +0x0D, 0x90, 0x40, 0x78, 0xFF, 0x28, 0x29, 0xD0, +0x34, 0x22, 0x50, 0x43, 0x3E, 0x4A, 0x12, 0x23, +0x85, 0x18, 0x34, 0x20, 0x70, 0x43, 0x44, 0x18, +0x10, 0x22, 0xAA, 0x5E, 0x22, 0x82, 0xEB, 0x5E, +0x63, 0x82, 0x0C, 0x21, 0x0A, 0x20, 0x61, 0x5E, +0x20, 0x5E, 0x00, 0xF0, 0x6B, 0xFF, 0x0C, 0x90, +0x3D, 0x48, 0x07, 0x68, 0x38, 0x46, 0xA7, 0x30, +0x00, 0xE0, 0x52, 0xE0, 0xFB, 0xF7, 0xE5, 0xFE, +0x01, 0x46, 0x0C, 0x98, 0x81, 0x42, 0x0E, 0xD2, +0x38, 0x46, 0xB0, 0x30, 0xC1, 0x79, 0x82, 0x79, +0x08, 0x02, 0x31, 0x49, 0x10, 0x43, 0x09, 0x88, +0x88, 0x42, 0x04, 0xD9, 0x01, 0x21, 0x30, 0x46, +0xFF, 0xF7, 0x82, 0xFE, 0x36, 0xE0, 0x0D, 0x98, +0x41, 0x68, 0x68, 0x7A, 0xC8, 0x28, 0x01, 0xD2, +0x40, 0x1C, 0x60, 0x72, 0xE8, 0x69, 0x40, 0x18, +0x40, 0x08, 0xE0, 0x61, 0x28, 0x79, 0x20, 0x71, +0x03, 0x21, 0x61, 0x71, 0x80, 0x21, 0x08, 0x43, +0x28, 0x71, 0xA8, 0x79, 0xA0, 0x71, 0xE8, 0x79, +0xE0, 0x71, 0x28, 0x7A, 0x20, 0x72, 0x38, 0x46, +0xA3, 0x30, 0xFB, 0xF7, 0xB6, 0xFE, 0x01, 0x46, +0x0C, 0x98, 0x81, 0x42, 0x03, 0xD2, 0x60, 0x89, +0x20, 0x82, 0xA0, 0x89, 0x60, 0x82, 0x60, 0x7A, +0x02, 0x28, 0x07, 0xD1, 0x30, 0x20, 0x00, 0x5D, +0x00, 0x28, 0x03, 0xD1, 0x60, 0x89, 0xA8, 0x85, +0xA0, 0x89, 0xE8, 0x85, 0xA8, 0x8D, 0xA0, 0x85, +0xE8, 0x8D, 0xE0, 0x85, 0x20, 0x35, 0x68, 0x7D, +0x20, 0x34, 0x60, 0x75, 0x76, 0x1C, 0xF6, 0xB2, +0x0C, 0x49, 0x08, 0x78, 0xB0, 0x42, 0x8C, 0xD8, +0x8E, 0xE6, 0x00, 0x24, 0x09, 0x4D, 0x0B, 0xE0, +0x34, 0x20, 0x60, 0x43, 0x40, 0x19, 0x00, 0x79, +0xFF, 0x28, 0x03, 0xD1, 0x01, 0x21, 0x20, 0x46, +0xFF, 0xF7, 0x36, 0xFE, 0x64, 0x1C, 0xE4, 0xB2, +0x28, 0x78, 0xA0, 0x42, 0xF0, 0xD8, 0x7B, 0xE6, +0x40, 0x05, 0x00, 0x20, 0xA0, 0x04, 0x00, 0x20, +0xFF, 0xFF, 0x07, 0x00, 0x36, 0x00, 0x00, 0x20, +0x34, 0x00, 0x00, 0x20, 0x2C, 0x00, 0x00, 0x20, +0x2D, 0x00, 0x00, 0x20, 0xFF, 0xFF, 0x00, 0x00, +0xB8, 0x02, 0x00, 0x20, 0xF0, 0xB5, 0x00, 0x24, +0x63, 0x49, 0xA9, 0xB0, 0x02, 0x20, 0x08, 0x70, +0x62, 0x48, 0x25, 0x46, 0x86, 0x78, 0x97, 0xE0, +0x34, 0x21, 0x60, 0x48, 0x71, 0x43, 0x09, 0x18, +0x08, 0x7A, 0x00, 0x28, 0x19, 0xD1, 0x5E, 0x4A, +0x0A, 0x20, 0x08, 0x5E, 0x12, 0x78, 0x90, 0x42, +0x11, 0xDB, 0x5C, 0x4F, 0x00, 0x23, 0xFB, 0x5E, +0x9A, 0x1A, 0x90, 0x42, 0x0B, 0xDC, 0x5A, 0x4A, +0x0C, 0x20, 0x08, 0x5E, 0x12, 0x78, 0x90, 0x42, +0x05, 0xDB, 0x58, 0x4F, 0x00, 0x23, 0xFB, 0x5E, +0x9A, 0x1A, 0x90, 0x42, 0x01, 0xDD, 0x02, 0x20, +0x08, 0x72, 0x88, 0x79, 0x4F, 0x4A, 0x00, 0x90, +0x00, 0x28, 0x06, 0xD0, 0x52, 0x4B, 0x52, 0x78, +0x1B, 0x78, 0x00, 0x20, 0x9C, 0x46, 0x96, 0x46, +0x66, 0xE0, 0x4F, 0x4F, 0x38, 0x78, 0x00, 0x19, +0x03, 0x28, 0x63, 0xD2, 0x01, 0x22, 0x8A, 0x71, +0x34, 0x22, 0x50, 0x43, 0xC0, 0x19, 0x09, 0x1D, +0x00, 0x1D, 0xFB, 0xF7, 0x05, 0xFE, 0x39, 0x78, +0x34, 0x22, 0x09, 0x19, 0x51, 0x43, 0x00, 0x20, +0xC9, 0x19, 0x48, 0x71, 0x45, 0x49, 0x64, 0x1C, +0x08, 0x70, 0x45, 0x49, 0xE4, 0xB2, 0x08, 0x70, +0x4C, 0xE0, 0x0A, 0x79, 0x80, 0x23, 0x1A, 0x43, +0x63, 0x46, 0x1B, 0x18, 0x34, 0x27, 0x7B, 0x43, +0x3D, 0x4F, 0xDB, 0x19, 0x1B, 0x79, 0x9A, 0x42, +0x3C, 0xD1, 0x3E, 0x48, 0x00, 0x78, 0x00, 0x28, +0x04, 0xD1, 0x0F, 0x46, 0x20, 0x37, 0x38, 0x7D, +0x46, 0x28, 0x02, 0xD9, 0x00, 0x20, 0x88, 0x71, +0x34, 0xE0, 0x00, 0x98, 0x40, 0x1C, 0xC0, 0xB2, +0x88, 0x71, 0x0A, 0x7E, 0x00, 0x2A, 0x05, 0xD0, +0x02, 0x28, 0x03, 0xD1, 0x4A, 0x89, 0x4A, 0x84, +0x8A, 0x89, 0x8A, 0x84, 0x28, 0x4A, 0x0B, 0x7A, +0x12, 0x78, 0x9A, 0x18, 0x90, 0x42, 0x0F, 0xD3, +0x00, 0x20, 0x88, 0x71, 0x83, 0x20, 0x38, 0x70, +0x34, 0x20, 0x68, 0x43, 0x01, 0xAA, 0x80, 0x18, +0x09, 0x1D, 0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7, +0xBF, 0xFD, 0x6D, 0x1C, 0xED, 0xB2, 0x11, 0xE0, +0x60, 0x46, 0x23, 0x4A, 0x00, 0x19, 0x34, 0x23, +0x58, 0x43, 0x80, 0x18, 0x09, 0x1D, 0x00, 0x1D, +0x1A, 0x46, 0xFB, 0xF7, 0xB1, 0xFD, 0x64, 0x1C, +0xE4, 0xB2, 0x03, 0xE0, 0x40, 0x1C, 0xC0, 0xB2, +0x86, 0x45, 0xB2, 0xD8, 0x76, 0x1C, 0xF6, 0xB2, +0x14, 0x48, 0x01, 0x78, 0xB1, 0x42, 0x00, 0xD9, +0x62, 0xE7, 0x06, 0x46, 0xB0, 0x78, 0x2A, 0x46, +0x41, 0x19, 0x31, 0x70, 0x34, 0x21, 0x48, 0x43, +0x4A, 0x43, 0x80, 0x19, 0x00, 0x1D, 0x02, 0xA9, +0xFB, 0xF7, 0x96, 0xFD, 0x10, 0x4D, 0x00, 0x20, +0x07, 0x46, 0x2B, 0x78, 0x0A, 0xE0, 0x19, 0x18, +0x34, 0x22, 0x51, 0x43, 0x4A, 0x19, 0x12, 0x79, +0x12, 0x06, 0x01, 0xD4, 0x89, 0x19, 0x8F, 0x71, +0x40, 0x1C, 0xC0, 0xB2, 0x71, 0x78, 0x81, 0x42, +0xF1, 0xD8, 0x74, 0x70, 0x29, 0xB0, 0xF0, 0xBD, +0x30, 0x00, 0x00, 0x20, 0xA0, 0x04, 0x00, 0x20, +0x2C, 0x00, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20, +0x2D, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, +0x40, 0x05, 0x00, 0x20, 0xCC, 0x00, 0x00, 0x20, +0xCD, 0x00, 0x00, 0x20, 0x73, 0x04, 0x00, 0x20, +0xF7, 0xB5, 0x00, 0x25, 0x84, 0xB0, 0x0C, 0x46, +0x16, 0x46, 0x61, 0x27, 0x5C, 0xE0, 0x25, 0x28, +0x54, 0xD1, 0x64, 0x1C, 0x00, 0x22, 0x20, 0x78, +0x13, 0x46, 0x00, 0x28, 0x57, 0xD0, 0x25, 0x28, +0x4C, 0xD0, 0x2D, 0x28, 0x01, 0xD1, 0x64, 0x1C, +0x01, 0x23, 0x02, 0x20, 0x21, 0x78, 0x30, 0x29, +0x07, 0xD1, 0x64, 0x1C, 0x03, 0x43, 0xF9, 0xE7, +0x0A, 0x21, 0x4A, 0x43, 0x30, 0x3A, 0x82, 0x18, +0x64, 0x1C, 0x20, 0x78, 0x01, 0x46, 0x30, 0x39, +0x09, 0x29, 0xF5, 0xD9, 0xC1, 0xB2, 0x73, 0x29, +0x0A, 0xD0, 0x64, 0x28, 0x10, 0xD0, 0x78, 0x28, +0x13, 0xD0, 0x58, 0x28, 0x19, 0xD0, 0x75, 0x28, +0x1F, 0xD0, 0x63, 0x28, 0x23, 0xD0, 0x2E, 0xE0, +0x02, 0xCE, 0x00, 0x29, 0x00, 0xD1, 0x1D, 0xA1, +0x04, 0x98, 0x00, 0xF0, 0x90, 0xF8, 0x0A, 0xE0, +0x68, 0x46, 0x8C, 0xC0, 0x02, 0xCE, 0x01, 0x23, +0x13, 0xE0, 0x68, 0x46, 0x8C, 0xC0, 0x08, 0xE0, +0x04, 0x98, 0x00, 0xF0, 0x3C, 0xF8, 0x45, 0x19, +0x19, 0xE0, 0x41, 0x20, 0x01, 0x93, 0x00, 0x92, +0x02, 0x90, 0x02, 0xCE, 0x00, 0x23, 0x10, 0x22, +0xF2, 0xE7, 0x68, 0x46, 0x8C, 0xC0, 0x02, 0xCE, +0x00, 0x23, 0x0A, 0x22, 0xEC, 0xE7, 0x02, 0xCE, +0x68, 0x46, 0x01, 0x73, 0x00, 0x21, 0x41, 0x73, +0x03, 0xA9, 0xD9, 0xE7, 0xC1, 0xB2, 0x04, 0x98, +0x00, 0xF0, 0x14, 0xF8, 0x6D, 0x1C, 0x64, 0x1C, +0x20, 0x78, 0x00, 0x28, 0x9F, 0xD1, 0x04, 0x98, +0x00, 0x28, 0x03, 0xD0, 0x04, 0x99, 0x00, 0x20, +0x09, 0x68, 0x08, 0x70, 0x28, 0x46, 0x07, 0xB0, +0xF0, 0xBD, 0x00, 0x00, 0x28, 0x6E, 0x75, 0x6C, +0x6C, 0x29, 0x00, 0x00, 0x10, 0xB5, 0x00, 0x28, +0x05, 0xD0, 0x02, 0x68, 0x11, 0x70, 0x01, 0x68, +0x49, 0x1C, 0x01, 0x60, 0x10, 0xBD, 0xC8, 0xB2, +0xFE, 0xF7, 0x64, 0xF9, 0x10, 0xBD, 0xFF, 0xB5, +0x00, 0x27, 0x83, 0xB0, 0x0C, 0x9D, 0x3E, 0x46, +0x08, 0x00, 0x3A, 0x46, 0x05, 0xD0, 0x00, 0x2B, +0x12, 0xD0, 0x05, 0x9B, 0x0A, 0x2B, 0x0B, 0xD0, +0x0E, 0xE0, 0x30, 0x20, 0x69, 0x46, 0x08, 0x70, +0x4A, 0x70, 0x2A, 0x46, 0x0D, 0x9B, 0x03, 0x98, +0x00, 0xF0, 0x31, 0xF8, 0x07, 0xB0, 0xF0, 0xBD, +0x00, 0x29, 0x01, 0xDA, 0x01, 0x27, 0x40, 0x42, +0x02, 0xAC, 0x69, 0x46, 0x03, 0x34, 0xCA, 0x72, +0x0A, 0xE0, 0x05, 0x99, 0xFB, 0xF7, 0x9A, 0xFC, +0x0A, 0x29, 0x02, 0xDB, 0x0E, 0x9A, 0x89, 0x18, +0x3A, 0x39, 0x30, 0x31, 0x64, 0x1E, 0x21, 0x70, +0x00, 0x28, 0xF2, 0xD1, 0x00, 0x2F, 0x0E, 0xD0, +0x00, 0x2D, 0x09, 0xD0, 0x0D, 0x98, 0x80, 0x07, +0x06, 0xD5, 0x2D, 0x21, 0x03, 0x98, 0xFF, 0xF7, +0xB9, 0xFF, 0x76, 0x1C, 0x6D, 0x1E, 0x02, 0xE0, +0x2D, 0x20, 0x64, 0x1E, 0x20, 0x70, 0x2A, 0x46, +0x21, 0x46, 0x0D, 0x9B, 0x03, 0x98, 0x00, 0xF0, +0x02, 0xF8, 0x80, 0x19, 0xCE, 0xE7, 0xFF, 0xB5, +0x00, 0x25, 0x20, 0x27, 0x81, 0xB0, 0x0E, 0x46, +0x14, 0x46, 0x00, 0x2A, 0x0E, 0xDD, 0x00, 0x20, +0x01, 0xE0, 0x40, 0x1C, 0x49, 0x1C, 0x0A, 0x78, +0x00, 0x2A, 0xFA, 0xD1, 0xA0, 0x42, 0x01, 0xDB, +0x00, 0x24, 0x00, 0xE0, 0x24, 0x1A, 0x98, 0x07, +0x00, 0xD5, 0x30, 0x27, 0xD8, 0x07, 0x06, 0xD0, +0x0D, 0xE0, 0x39, 0x46, 0x01, 0x98, 0xFF, 0xF7, +0x8D, 0xFF, 0x6D, 0x1C, 0x64, 0x1E, 0x00, 0x2C, +0xF7, 0xDC, 0x04, 0xE0, 0x01, 0x98, 0xFF, 0xF7, +0x85, 0xFF, 0x6D, 0x1C, 0x76, 0x1C, 0x31, 0x78, +0x00, 0x29, 0xF7, 0xD1, 0x05, 0xE0, 0x39, 0x46, +0x01, 0x98, 0xFF, 0xF7, 0x7B, 0xFF, 0x6D, 0x1C, +0x64, 0x1E, 0x00, 0x2C, 0xF7, 0xDC, 0x28, 0x46, +0x05, 0xB0, 0xF0, 0xBD, 0x0F, 0xB4, 0x10, 0xB5, +0x03, 0xAA, 0x00, 0x20, 0x02, 0x99, 0xFF, 0xF7, +0xF7, 0xFE, 0x10, 0xBC, 0x08, 0xBC, 0x04, 0xB0, +0x18, 0x47, 0x00, 0x00, 0xF0, 0xB5, 0xA9, 0xB0, +0x50, 0x49, 0x00, 0x24, 0x8C, 0x70, 0x9C, 0x21, +0x02, 0xA8, 0xFB, 0xF7, 0x6E, 0xFC, 0x00, 0x26, +0x66, 0xE0, 0x34, 0x21, 0x71, 0x43, 0x0D, 0x18, +0x29, 0x79, 0xFF, 0x29, 0x5E, 0xD0, 0x0A, 0x06, +0x36, 0xD5, 0x48, 0x4F, 0x00, 0x20, 0x3A, 0x78, +0x94, 0x46, 0x2E, 0xE0, 0x34, 0x22, 0x42, 0x43, +0xD2, 0x19, 0x12, 0x79, 0x80, 0x23, 0x1A, 0x43, +0x8A, 0x42, 0x24, 0xD1, 0x34, 0x21, 0x48, 0x43, +0x40, 0x49, 0x22, 0x46, 0x47, 0x18, 0x34, 0x20, +0x42, 0x43, 0x01, 0xA8, 0x10, 0x18, 0x00, 0x90, +0x39, 0x1D, 0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7, +0x2F, 0xFC, 0x28, 0x79, 0x64, 0x1C, 0x41, 0x06, +0x00, 0x98, 0x49, 0x0E, 0x01, 0x71, 0x00, 0x98, +0x69, 0x79, 0x41, 0x71, 0x78, 0x79, 0x80, 0x21, +0x08, 0x43, 0x78, 0x71, 0xA8, 0x79, 0xE4, 0xB2, +0x00, 0x28, 0x2F, 0xD1, 0x31, 0x48, 0x81, 0x78, +0x49, 0x1C, 0x81, 0x70, 0x2A, 0xE0, 0x40, 0x1C, +0xC0, 0xB2, 0x84, 0x45, 0xCE, 0xD8, 0x25, 0xE0, +0xA9, 0x79, 0x00, 0x29, 0x22, 0xD1, 0xE9, 0x79, +0x03, 0x29, 0x02, 0xD9, 0x00, 0x20, 0xE8, 0x71, +0x1C, 0xE0, 0x34, 0x20, 0x22, 0x46, 0x42, 0x43, +0x01, 0xA8, 0x17, 0x18, 0x29, 0x1D, 0x38, 0x1D, +0x34, 0x22, 0xFB, 0xF7, 0x01, 0xFC, 0x06, 0x20, +0x23, 0x49, 0x70, 0x43, 0x40, 0x18, 0xA0, 0x30, +0x01, 0x88, 0x79, 0x81, 0x40, 0x88, 0xB8, 0x81, +0x1E, 0x48, 0x64, 0x1C, 0x81, 0x78, 0xE4, 0xB2, +0x49, 0x1C, 0x81, 0x70, 0x28, 0x79, 0x80, 0x21, +0x08, 0x43, 0x28, 0x71, 0x76, 0x1C, 0xF6, 0xB2, +0x19, 0x48, 0x01, 0x78, 0x42, 0x78, 0x89, 0x18, +0xB1, 0x42, 0x92, 0xD8, 0x00, 0x25, 0x15, 0x4E, +0x1A, 0xE0, 0x34, 0x20, 0x68, 0x43, 0x81, 0x19, +0x48, 0x79, 0x02, 0x06, 0x03, 0xD5, 0x40, 0x06, +0x40, 0x0E, 0x48, 0x71, 0x0E, 0xE0, 0x02, 0x2C, +0x11, 0xD0, 0x00, 0x20, 0x88, 0x71, 0x34, 0x20, +0x60, 0x43, 0x01, 0xAA, 0x80, 0x18, 0x09, 0x1D, +0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7, 0xCC, 0xFB, +0x64, 0x1C, 0xE4, 0xB2, 0x6D, 0x1C, 0xED, 0xB2, +0x30, 0x78, 0xA8, 0x42, 0xE1, 0xD8, 0x34, 0x70, +0x34, 0x20, 0x44, 0x43, 0x22, 0x46, 0x02, 0xA9, +0x30, 0x1D, 0xFB, 0xF7, 0xBD, 0xFB, 0x29, 0xB0, +0xF0, 0xBD, 0x00, 0x00, 0xA0, 0x04, 0x00, 0x20, +0x40, 0x05, 0x00, 0x20, 0xF8, 0xB5, 0x59, 0x4C, +0x59, 0x4D, 0x00, 0x26, 0xE6, 0x70, 0x16, 0x21, +0x28, 0x1D, 0xFB, 0xF7, 0xC6, 0xFB, 0x57, 0x48, +0x57, 0x49, 0x00, 0x78, 0x09, 0x78, 0xC0, 0x01, +0x08, 0x43, 0xC1, 0xB2, 0x55, 0x48, 0x01, 0x70, +0x55, 0x48, 0x02, 0x78, 0x01, 0x20, 0x00, 0x2A, +0x04, 0xD0, 0xAA, 0x7E, 0x8A, 0x42, 0x01, 0xD0, +0xA9, 0x76, 0xE0, 0x70, 0x51, 0x49, 0x0A, 0x78, +0x00, 0x2A, 0x10, 0xD1, 0x50, 0x4A, 0x12, 0x78, +0x18, 0x2A, 0x0C, 0xD1, 0x4F, 0x4A, 0x12, 0x78, +0x03, 0x2A, 0x08, 0xD3, 0xA9, 0x78, 0xC9, 0x07, +0x04, 0xD1, 0xE0, 0x70, 0xA8, 0x70, 0x6E, 0x70, +0x4B, 0x48, 0x06, 0x70, 0xF8, 0xBD, 0xAA, 0x78, +0xD3, 0x07, 0x4A, 0x4A, 0x07, 0xD0, 0xE0, 0x70, +0xAE, 0x70, 0x6E, 0x70, 0x2D, 0x20, 0x08, 0x70, +0x03, 0x20, 0x10, 0x70, 0xF8, 0xBD, 0x11, 0x78, +0x00, 0x29, 0xFB, 0xD1, 0x21, 0x78, 0x00, 0x29, +0x07, 0xD0, 0xE0, 0x70, 0x69, 0x70, 0x38, 0x48, +0x00, 0x24, 0x40, 0x1C, 0x40, 0x4F, 0x00, 0x90, +0x63, 0xE0, 0x69, 0x78, 0x00, 0x29, 0x00, 0xD0, +0xE0, 0x70, 0x6E, 0x70, 0xF8, 0xBD, 0x34, 0x20, +0x30, 0x49, 0x60, 0x43, 0x45, 0x18, 0x20, 0x46, +0x0B, 0x26, 0x70, 0x43, 0x2A, 0x79, 0xC6, 0x18, +0x32, 0x71, 0x68, 0x89, 0x76, 0x1C, 0x30, 0x71, +0x00, 0x0A, 0x70, 0x71, 0xA8, 0x89, 0xB0, 0x71, +0x00, 0x0A, 0xF0, 0x71, 0xE8, 0x89, 0x30, 0x72, +0x00, 0x0A, 0x70, 0x72, 0x20, 0x20, 0x40, 0x5D, +0xC0, 0x07, 0x07, 0xD0, 0xE8, 0x8C, 0x30, 0x71, +0x00, 0x0A, 0x70, 0x71, 0x28, 0x8D, 0xB0, 0x71, +0x00, 0x0A, 0xF0, 0x71, 0x08, 0x78, 0x01, 0x28, +0x2D, 0xD1, 0xC8, 0x79, 0x00, 0x28, 0x2A, 0xD0, +0x00, 0x98, 0xC1, 0x79, 0x80, 0x79, 0x09, 0x06, +0x0B, 0x14, 0x03, 0x43, 0x00, 0x98, 0x41, 0x79, +0x00, 0x79, 0x09, 0x06, 0x0A, 0x14, 0x02, 0x43, +0x22, 0x48, 0x00, 0x21, 0x41, 0x5E, 0x00, 0x20, +0x38, 0x5E, 0x00, 0xF0, 0xCB, 0xFB, 0x20, 0x49, +0x0A, 0x68, 0xB0, 0x32, 0x51, 0x7B, 0x12, 0x7B, +0x09, 0x02, 0x11, 0x43, 0x81, 0x42, 0x0E, 0xD2, +0x00, 0x98, 0x41, 0x79, 0x02, 0x79, 0x08, 0x06, +0x00, 0x14, 0x10, 0x43, 0x38, 0x80, 0x00, 0x98, +0xC2, 0x79, 0x81, 0x79, 0x10, 0x06, 0x00, 0x14, +0x08, 0x43, 0x14, 0x49, 0x08, 0x80, 0xA8, 0x8A, +0xB0, 0x72, 0x00, 0x0A, 0xF0, 0x72, 0xE8, 0x8A, +0x30, 0x73, 0x00, 0x0A, 0x70, 0x73, 0x64, 0x1C, +0xE4, 0xB2, 0x03, 0x4B, 0x58, 0x78, 0xA0, 0x42, +0x9D, 0xD8, 0xF8, 0xBD, 0xA0, 0x04, 0x00, 0x20, +0x90, 0x02, 0x00, 0x20, 0x74, 0x04, 0x00, 0x20, +0x85, 0x02, 0x00, 0x20, 0x34, 0x01, 0x00, 0x20, +0x2B, 0x00, 0x00, 0x20, 0x72, 0x04, 0x00, 0x20, +0x8E, 0x02, 0x00, 0x20, 0x73, 0x04, 0x00, 0x20, +0x83, 0x02, 0x00, 0x20, 0x75, 0x04, 0x00, 0x20, +0x38, 0x00, 0x00, 0x20, 0x3A, 0x00, 0x00, 0x20, +0xB8, 0x02, 0x00, 0x20, 0xF7, 0xB5, 0x00, 0x20, +0x84, 0xB0, 0x03, 0x90, 0x32, 0x48, 0x00, 0x27, +0x01, 0x90, 0x38, 0x46, 0x56, 0xE0, 0x00, 0x24, +0x00, 0x29, 0x05, 0xD0, 0x40, 0x1E, 0x88, 0x42, +0x00, 0xD1, 0x02, 0x24, 0x00, 0x26, 0x45, 0xE0, +0x01, 0x24, 0xFB, 0xE7, 0x78, 0x00, 0x2B, 0x4A, +0x02, 0x90, 0x10, 0x5E, 0x06, 0x9B, 0x98, 0x42, +0x38, 0xDD, 0xC3, 0x07, 0x36, 0xD1, 0x01, 0x9B, +0x98, 0x42, 0x33, 0xDD, 0xA4, 0x07, 0xA4, 0x0F, +0x00, 0x2E, 0x0B, 0xD0, 0x49, 0x1E, 0xB1, 0x42, +0x01, 0xD1, 0x08, 0x20, 0x04, 0x43, 0x00, 0x25, +0x21, 0x48, 0x41, 0x5D, 0x20, 0x46, 0x08, 0x42, +0x0F, 0xD1, 0x01, 0xE0, 0x04, 0x20, 0xF5, 0xE7, +0x38, 0x46, 0xFF, 0xF7, 0x69, 0xFA, 0x1B, 0x4A, +0x02, 0x99, 0x04, 0x2D, 0x51, 0x5E, 0x02, 0xD2, +0x81, 0x42, 0x17, 0xDB, 0x01, 0xE0, 0x81, 0x42, +0x04, 0xDD, 0x6D, 0x1C, 0xED, 0xB2, 0x08, 0x2D, +0xE6, 0xD3, 0x01, 0xE0, 0x08, 0x2D, 0x0D, 0xD3, +0x12, 0x49, 0x02, 0x98, 0x08, 0x5E, 0x01, 0x90, +0x04, 0x99, 0x00, 0x98, 0x08, 0x70, 0x05, 0x98, +0x10, 0x49, 0x06, 0x70, 0x01, 0x20, 0x03, 0x90, +0x01, 0x98, 0x08, 0x80, 0x7F, 0x1C, 0x76, 0x1C, +0xBF, 0xB2, 0xF6, 0xB2, 0x0C, 0x48, 0x00, 0x68, +0x01, 0x7E, 0xB1, 0x42, 0xB6, 0xD8, 0x00, 0x98, +0x40, 0x1C, 0xC0, 0xB2, 0x08, 0x4A, 0x00, 0x90, +0x10, 0x68, 0x00, 0x99, 0x40, 0x7E, 0x88, 0x42, +0xA1, 0xD8, 0x03, 0x98, 0x07, 0xB0, 0xF0, 0xBD, +0x00, 0x80, 0xFF, 0xFF, 0x4C, 0x07, 0x00, 0x20, +0x8E, 0x5A, 0x00, 0x00, 0x28, 0x00, 0x00, 0x20, +0xB8, 0x02, 0x00, 0x20, 0x05, 0x48, 0x01, 0x78, +0x82, 0x29, 0x05, 0xD1, 0xC0, 0x79, 0x01, 0x28, +0x02, 0xD1, 0x03, 0x49, 0xFF, 0x20, 0x08, 0x70, +0x70, 0x47, 0x00, 0x00, 0x84, 0x04, 0x00, 0x20, +0xEC, 0x02, 0x00, 0x20, 0x01, 0x20, 0x02, 0x49, +0x08, 0x70, 0x02, 0x49, 0x08, 0x70, 0x70, 0x47, +0x0C, 0x00, 0x00, 0x20, 0x81, 0x02, 0x00, 0x20, +0x70, 0xB5, 0x24, 0x4D, 0x00, 0x21, 0x28, 0x78, +0x00, 0x28, 0x2C, 0xD1, 0x22, 0x4A, 0x23, 0x4B, +0x10, 0x80, 0x04, 0x22, 0x1A, 0x70, 0x22, 0x4B, +0x1A, 0x70, 0x22, 0x4A, 0x10, 0x70, 0x22, 0x4A, +0x10, 0x70, 0x22, 0x4A, 0x10, 0x70, 0x22, 0x4A, +0x10, 0x70, 0x22, 0x4A, 0x10, 0x70, 0x1A, 0xE0, +0x34, 0x20, 0x48, 0x43, 0x40, 0x19, 0x02, 0x46, +0x20, 0x32, 0x13, 0x78, 0x1C, 0x06, 0x06, 0xD5, +0x5B, 0x06, 0x5B, 0x0E, 0x13, 0x70, 0x43, 0x89, +0xC3, 0x84, 0x83, 0x89, 0x03, 0x85, 0x13, 0x78, +0x9C, 0x07, 0x06, 0xD5, 0xFD, 0x24, 0x23, 0x40, +0x13, 0x70, 0x42, 0x89, 0x42, 0x84, 0x82, 0x89, +0x82, 0x84, 0x49, 0x1C, 0xC9, 0xB2, 0x28, 0x78, +0x88, 0x42, 0xE1, 0xD8, 0x12, 0x4E, 0x00, 0x24, +0x30, 0x70, 0x68, 0x78, 0x70, 0x70, 0x20, 0x46, +0x34, 0x21, 0x48, 0x43, 0x41, 0x19, 0x80, 0x19, +0x09, 0x1D, 0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7, +0x13, 0xFA, 0x64, 0x1C, 0xE4, 0xB2, 0x03, 0x2C, +0xF1, 0xD3, 0x70, 0xBD, 0xA0, 0x04, 0x00, 0x20, +0x34, 0x00, 0x00, 0x20, 0x4A, 0x00, 0x00, 0x20, +0x4B, 0x00, 0x00, 0x20, 0x49, 0x00, 0x00, 0x20, +0x2A, 0x00, 0x00, 0x20, 0x47, 0x00, 0x00, 0x20, +0x46, 0x00, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, +0x40, 0x05, 0x00, 0x20, 0x02, 0x49, 0x00, 0x20, +0x08, 0x70, 0x02, 0x49, 0x08, 0x80, 0x70, 0x47, +0x31, 0x00, 0x00, 0x20, 0x3E, 0x00, 0x00, 0x20, +0xF0, 0xB5, 0x3B, 0x4F, 0x00, 0x20, 0x38, 0x70, +0x01, 0x21, 0x3A, 0x4C, 0xC9, 0x03, 0x21, 0x80, +0x39, 0x4B, 0x3B, 0x49, 0x18, 0x80, 0x39, 0x4E, +0x0A, 0x88, 0x0D, 0xE0, 0x43, 0x00, 0x00, 0x25, +0xF1, 0x5E, 0x65, 0x5F, 0xA9, 0x42, 0x02, 0xDD, +0x33, 0x4D, 0x21, 0x80, 0x28, 0x80, 0x49, 0x08, +0x49, 0x00, 0x40, 0x1C, 0xF1, 0x52, 0x80, 0xB2, +0x90, 0x42, 0xEF, 0xD3, 0x00, 0x20, 0x20, 0x5E, +0x41, 0x00, 0x41, 0x18, 0xCB, 0x17, 0x5B, 0x0F, +0x59, 0x18, 0xCB, 0x10, 0x2D, 0x49, 0x09, 0x68, +0x90, 0x31, 0x4C, 0x7B, 0x0D, 0x7B, 0x21, 0x02, +0x29, 0x43, 0x81, 0x42, 0x0C, 0xDA, 0x00, 0x20, +0x08, 0xE0, 0x41, 0x00, 0x71, 0x5E, 0x99, 0x42, +0x02, 0xDD, 0x39, 0x78, 0x49, 0x1C, 0x39, 0x70, +0x40, 0x1C, 0x80, 0xB2, 0x90, 0x42, 0xF4, 0xD3, +0x23, 0x48, 0x24, 0x4A, 0x01, 0x78, 0x21, 0x48, +0x00, 0x68, 0x80, 0x30, 0x00, 0x29, 0x81, 0x7B, +0x02, 0xD0, 0xCB, 0x00, 0xC9, 0x18, 0xC9, 0x08, +0x11, 0x70, 0xC3, 0x7B, 0x1E, 0x49, 0x00, 0x20, +0x08, 0x5E, 0x83, 0x42, 0x0C, 0xDA, 0x1D, 0x49, +0x09, 0x78, 0x02, 0x29, 0x08, 0xD2, 0x1C, 0x49, +0x0B, 0x78, 0x11, 0x78, 0x49, 0x1E, 0x8B, 0x42, +0x02, 0xDD, 0x11, 0x78, 0x49, 0x1C, 0x39, 0x70, +0x18, 0x49, 0x00, 0x23, 0xCB, 0x5E, 0x59, 0x42, +0x81, 0x42, 0x01, 0xDD, 0x00, 0x20, 0x38, 0x70, +0x3B, 0x78, 0x12, 0x78, 0x14, 0x49, 0x93, 0x42, +0x08, 0x78, 0x06, 0xD9, 0x03, 0x28, 0x01, 0xD2, +0x40, 0x1C, 0x00, 0xE0, 0x0C, 0x20, 0x08, 0x70, +0xF0, 0xBD, 0x00, 0x28, 0xFC, 0xD0, 0x3A, 0x78, +0x03, 0x2A, 0xF9, 0xD2, 0x40, 0x1E, 0xF6, 0xE7, +0x0E, 0x00, 0x00, 0x20, 0x24, 0x00, 0x00, 0x20, +0x32, 0x00, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, +0x3C, 0x00, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, +0x70, 0x04, 0x00, 0x20, 0x0D, 0x00, 0x00, 0x20, +0x7C, 0x04, 0x00, 0x20, 0xA0, 0x04, 0x00, 0x20, +0x8F, 0x02, 0x00, 0x20, 0x7A, 0x04, 0x00, 0x20, +0x73, 0x04, 0x00, 0x20, 0x10, 0xB5, 0x06, 0x4C, +0x00, 0x20, 0x20, 0x80, 0x60, 0x80, 0xFF, 0xF7, +0x5D, 0xFF, 0x04, 0x48, 0xA0, 0x80, 0xE0, 0x80, +0x03, 0x49, 0x03, 0x20, 0x08, 0x70, 0x10, 0xBD, +0x34, 0x00, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, +0x82, 0x02, 0x00, 0x20, 0x10, 0xB5, 0xFF, 0xF7, +0x4D, 0xFF, 0x08, 0x49, 0x5A, 0x20, 0x08, 0x70, +0x07, 0x49, 0x00, 0x20, 0x08, 0x70, 0x08, 0x48, +0x06, 0x49, 0x81, 0x80, 0xC1, 0x80, 0x07, 0x49, +0x03, 0x20, 0x08, 0x70, 0x06, 0x49, 0x01, 0x20, +0x08, 0x70, 0x10, 0xBD, 0xB5, 0x02, 0x00, 0x20, +0x73, 0x04, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, +0x34, 0x00, 0x00, 0x20, 0x82, 0x02, 0x00, 0x20, +0x83, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x12, 0x4D, +0x06, 0x46, 0x2C, 0x78, 0xE0, 0x07, 0x07, 0xD0, +0x80, 0x22, 0x10, 0x49, 0x10, 0x48, 0xFB, 0xF7, +0x1F, 0xF9, 0x60, 0x08, 0x40, 0x00, 0x28, 0x70, +0x28, 0x78, 0x80, 0x07, 0x12, 0xD5, 0x00, 0x2E, +0x10, 0xD1, 0x0C, 0x49, 0x00, 0x20, 0x09, 0x4D, +0x09, 0x4C, 0x0E, 0x88, 0x08, 0xE0, 0x41, 0x00, +0x62, 0x5E, 0x6B, 0x5E, 0xD2, 0x18, 0x52, 0x10, +0x62, 0x52, 0x40, 0x1C, 0x6A, 0x52, 0x80, 0xB2, +0xB0, 0x42, 0xF4, 0xD3, 0x70, 0xBD, 0x00, 0x00, +0x82, 0x02, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, +0xF4, 0x05, 0x00, 0x20, 0x3C, 0x00, 0x00, 0x20, +0x10, 0xB5, 0xFB, 0xF7, 0x11, 0xFF, 0x10, 0xBD, +0x10, 0xB5, 0x04, 0x48, 0x40, 0x78, 0x80, 0x06, +0x01, 0xD5, 0xFE, 0xF7, 0x53, 0xF9, 0xFB, 0xF7, +0x81, 0xFD, 0x10, 0xBD, 0x88, 0x02, 0x00, 0x20, +0xF8, 0xB5, 0x22, 0x49, 0x00, 0x20, 0x03, 0x46, +0x1F, 0x4A, 0x0C, 0x88, 0x06, 0xE0, 0x41, 0x00, +0x55, 0x5E, 0x00, 0x2D, 0x00, 0xDA, 0x53, 0x52, +0x40, 0x1C, 0xC0, 0xB2, 0xA0, 0x42, 0xF6, 0xD3, +0x1C, 0x48, 0x1B, 0x4D, 0x01, 0x78, 0x1D, 0x48, +0x1B, 0x4A, 0x04, 0x68, 0x2E, 0x78, 0x20, 0x46, +0xB0, 0x30, 0x12, 0x88, 0x02, 0x2E, 0x0F, 0xD0, +0xC3, 0x79, 0x84, 0x79, 0x18, 0x02, 0x20, 0x43, +0x90, 0x42, 0x01, 0xD8, 0x01, 0x29, 0x01, 0xD9, +0x03, 0x20, 0x28, 0x70, 0x00, 0x20, 0xFF, 0xF7, +0x99, 0xFF, 0xFF, 0xF7, 0xD1, 0xFE, 0xF8, 0xBD, +0x90, 0x34, 0x66, 0x7B, 0x27, 0x7B, 0x34, 0x02, +0x3C, 0x43, 0x0F, 0x4E, 0x00, 0x27, 0xF7, 0x5F, +0xBC, 0x42, 0x05, 0xDA, 0x00, 0x29, 0x03, 0xD1, +0x0C, 0x49, 0x09, 0x78, 0x01, 0x29, 0xE7, 0xD0, +0xC1, 0x79, 0x84, 0x79, 0x08, 0x02, 0x20, 0x43, +0x90, 0x42, 0xE3, 0xD2, 0x2B, 0x70, 0xE1, 0xE7, +0x4C, 0x07, 0x00, 0x20, 0x3C, 0x00, 0x00, 0x20, +0x82, 0x02, 0x00, 0x20, 0x40, 0x05, 0x00, 0x20, +0x34, 0x00, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, +0x78, 0x04, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, +0x10, 0xB5, 0xFE, 0xF7, 0xAD, 0xFD, 0xFF, 0xF7, +0x49, 0xFF, 0x10, 0xBD, 0xF8, 0xB5, 0x00, 0x26, +0xFB, 0xF7, 0xE8, 0xFB, 0x83, 0x4C, 0x20, 0x78, +0x01, 0x28, 0x2A, 0xD1, 0x60, 0x7A, 0x0A, 0x28, +0x27, 0xD2, 0x81, 0x4F, 0x81, 0x49, 0xB8, 0x5F, +0x88, 0x42, 0x22, 0xD0, 0x80, 0x4D, 0x0C, 0x23, +0x0A, 0x22, 0xE3, 0x5E, 0xA2, 0x5E, 0xA9, 0x5F, +0x00, 0xF0, 0x1C, 0xF9, 0x7D, 0x49, 0x0A, 0x68, +0xB0, 0x32, 0x51, 0x7B, 0x12, 0x7B, 0x09, 0x02, +0x11, 0x43, 0x81, 0x42, 0x11, 0xD3, 0xB8, 0x5F, +0x41, 0x00, 0x40, 0x18, 0x0A, 0x21, 0x61, 0x5E, +0x40, 0x18, 0x80, 0x10, 0x60, 0x81, 0xA9, 0x5F, +0x4A, 0x00, 0x89, 0x18, 0x0C, 0x22, 0xA2, 0x5E, +0x89, 0x18, 0x89, 0x10, 0xA1, 0x81, 0x60, 0x84, +0xA1, 0x84, 0x00, 0x25, 0xCC, 0xE0, 0x70, 0x4F, +0x34, 0x23, 0x00, 0x20, 0x6B, 0x43, 0x39, 0x78, +0x9C, 0x18, 0x0A, 0xE0, 0x22, 0x79, 0x80, 0x23, +0x1A, 0x43, 0x34, 0x23, 0x43, 0x43, 0xDB, 0x19, +0x1B, 0x79, 0x9A, 0x42, 0x03, 0xD0, 0x40, 0x1C, +0xC0, 0xB2, 0x81, 0x42, 0xF2, 0xD8, 0x81, 0x42, +0x7E, 0xD9, 0x34, 0x21, 0x48, 0x43, 0xC1, 0x19, +0x14, 0x20, 0x08, 0x5E, 0x14, 0x22, 0x43, 0x00, +0xC0, 0x18, 0xA2, 0x5E, 0x01, 0x23, 0x10, 0x18, +0x80, 0x03, 0xDB, 0x03, 0xC0, 0x18, 0x00, 0x14, +0xA0, 0x82, 0x16, 0x20, 0x08, 0x5E, 0x16, 0x22, +0x47, 0x00, 0xC0, 0x19, 0xA2, 0x5E, 0x27, 0x46, +0x10, 0x18, 0x80, 0x03, 0xC0, 0x18, 0x00, 0x14, +0xE0, 0x82, 0x20, 0x37, 0x20, 0x31, 0x0A, 0x22, +0x38, 0x46, 0xFB, 0xF7, 0x25, 0xF8, 0x38, 0x78, +0x40, 0x07, 0x07, 0xD5, 0x00, 0x20, 0x53, 0x49, +0x38, 0x70, 0x08, 0x70, 0x52, 0x49, 0x08, 0x70, +0x52, 0x49, 0x08, 0x70, 0x24, 0x23, 0x22, 0x22, +0x0C, 0x21, 0x0A, 0x20, 0xE3, 0x5E, 0xA2, 0x5E, +0x61, 0x5E, 0x20, 0x5E, 0x00, 0xF0, 0xB2, 0xF8, +0x39, 0x78, 0xCA, 0x07, 0x67, 0xD0, 0x62, 0x7A, +0x2D, 0x2A, 0x09, 0xD2, 0x48, 0x4A, 0x12, 0x78, +0x00, 0x2A, 0x05, 0xD0, 0x22, 0x7E, 0x00, 0x2A, +0x02, 0xD1, 0x02, 0x22, 0x11, 0x43, 0x39, 0x70, +0x40, 0x49, 0x0A, 0x68, 0x92, 0x21, 0x89, 0x5C, +0x81, 0x42, 0x01, 0xD2, 0x01, 0x26, 0x16, 0xE0, +0x10, 0x46, 0xB0, 0x30, 0x41, 0x7A, 0x03, 0x7A, +0x08, 0x02, 0x18, 0x43, 0x81, 0x08, 0x3E, 0x48, +0x00, 0x88, 0x81, 0x42, 0x07, 0xD2, 0x39, 0x48, +0x00, 0x78, 0x00, 0x28, 0x03, 0xD1, 0x3B, 0x48, +0x00, 0x78, 0x00, 0x28, 0x36, 0xD0, 0x00, 0x20, +0xB8, 0x72, 0x00, 0x2E, 0x4E, 0xD0, 0x38, 0x78, +0x04, 0x21, 0x08, 0x43, 0x38, 0x70, 0xB0, 0x32, +0x50, 0x7A, 0x12, 0x7A, 0x00, 0x02, 0x10, 0x43, +0x31, 0x4A, 0x79, 0x7D, 0x12, 0x88, 0x90, 0x42, +0x40, 0xD9, 0x26, 0x23, 0xE3, 0x5E, 0x30, 0x20, +0x2F, 0x4A, 0x9F, 0x01, 0x41, 0x43, 0x88, 0x18, +0x87, 0x60, 0xC7, 0x60, 0x2D, 0x4F, 0x00, 0xE0, +0x34, 0xE0, 0xBF, 0x88, 0x0A, 0x2F, 0x01, 0xD8, +0x0A, 0x23, 0xE3, 0x5E, 0x53, 0x50, 0x0A, 0x21, +0x61, 0x5E, 0x41, 0x60, 0x28, 0x21, 0x61, 0x5E, +0x8A, 0x01, 0x82, 0x61, 0xC2, 0x61, 0x25, 0x4A, +0x92, 0x88, 0x0A, 0x2A, 0x01, 0xD8, 0x0C, 0x21, +0x61, 0x5E, 0x01, 0x61, 0x0C, 0x21, 0x61, 0x5E, +0x41, 0x61, 0x1B, 0xE0, 0xB8, 0x7A, 0x40, 0x1C, +0xC0, 0xB2, 0xB8, 0x72, 0x02, 0x28, 0xC4, 0xD9, +0x00, 0x20, 0xB8, 0x72, 0xAA, 0xE7, 0x00, 0x29, +0x10, 0xD1, 0x12, 0x49, 0x09, 0x68, 0x80, 0x31, +0x89, 0x7C, 0x49, 0x08, 0x81, 0x42, 0x11, 0xD9, +0x78, 0x78, 0x40, 0x1C, 0xC0, 0xB2, 0x78, 0x70, +0x0A, 0x28, 0x03, 0xD9, 0x00, 0x20, 0x78, 0x70, +0x83, 0x20, 0x38, 0x70, 0x6D, 0x1C, 0xED, 0xB2, +0x04, 0x4A, 0x10, 0x78, 0xA8, 0x42, 0x00, 0xD9, +0x2D, 0xE7, 0xF8, 0xBD, 0x00, 0x20, 0x78, 0x70, +0x02, 0x20, 0xF2, 0xE7, 0xA0, 0x04, 0x00, 0x20, +0x38, 0x00, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, +0x3A, 0x00, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, +0x40, 0x05, 0x00, 0x20, 0x75, 0x04, 0x00, 0x20, +0x47, 0x00, 0x00, 0x20, 0x46, 0x00, 0x00, 0x20, +0x34, 0x00, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, +0x38, 0x01, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, +0x10, 0xB5, 0x03, 0x48, 0x00, 0x78, 0x00, 0x06, +0x01, 0xD4, 0xFF, 0xF7, 0xAF, 0xFB, 0x10, 0xBD, +0x86, 0x02, 0x00, 0x20, 0x10, 0xB5, 0xFF, 0xF7, +0x07, 0xFD, 0x10, 0xBD, 0x10, 0x1A, 0x59, 0x1A, +0x40, 0x43, 0x49, 0x43, 0xC2, 0x17, 0xCB, 0x17, +0x08, 0x18, 0x53, 0x41, 0x00, 0x22, 0xD2, 0x43, +0x00, 0x21, 0x12, 0x1A, 0x99, 0x41, 0x01, 0xD2, +0x00, 0x20, 0xC0, 0x43, 0x70, 0x47, 0x05, 0x01, +0x09, 0x04, 0x08, 0x02, 0x06, 0x0A, 0x00, 0x00, +0x00, 0x00, 0x00, 0x40, 0x10, 0x03, 0x42, 0x88, +0x0C, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, +0x00, 0x09, 0x00, 0x50, 0x1F, 0x00, 0x00, 0x00, +0x04, 0x03, 0x00, 0x50, 0x64, 0x00, 0x00, 0x00, +0x00, 0x03, 0x00, 0x50, 0x77, 0x00, 0x01, 0x48, +0x44, 0x09, 0x00, 0x50, 0x39, 0x5A, 0x5B, 0x00, +0x10, 0x06, 0x00, 0x50, 0x00, 0x00, 0x06, 0x07, +0x00, 0x06, 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, +0x08, 0x06, 0x00, 0x50, 0x0C, 0x30, 0x00, 0x00, +0x28, 0x06, 0x00, 0x50, 0x06, 0x00, 0x00, 0x00, +0x2C, 0x06, 0x00, 0x50, 0x0A, 0x66, 0x00, 0x00, +0x30, 0x06, 0x00, 0x50, 0xCC, 0x02, 0x00, 0x20, +0x34, 0x06, 0x00, 0x50, 0x00, 0x20, 0x00, 0x00, +0x44, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00, +0x1C, 0x00, 0x00, 0x50, 0x50, 0x71, 0x02, 0x00, +0x20, 0x00, 0x00, 0x50, 0x24, 0x29, 0x00, 0x00, +0x14, 0x00, 0x00, 0x40, 0x04, 0x04, 0x00, 0x00, +0x00, 0x00, 0x00, 0x40, 0x14, 0x33, 0x43, 0xC8, +0x0C, 0x00, 0x00, 0x40, 0x29, 0x0A, 0x00, 0x00, +0x04, 0x00, 0x00, 0x40, 0x10, 0x32, 0x00, 0x00, +0x1C, 0x0E, 0x00, 0x50, 0x03, 0x00, 0x00, 0x00, +0x54, 0x00, 0x00, 0x50, 0x14, 0x07, 0x00, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, +0x2C, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, +0x58, 0x10, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00, +0x04, 0x11, 0x00, 0x50, 0x78, 0x11, 0x00, 0x00, +0x0C, 0x11, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00, +0x10, 0x11, 0x00, 0x50, 0x78, 0x01, 0x00, 0x00, +0x14, 0x11, 0x00, 0x50, 0xC8, 0x03, 0x60, 0x00, +0x4C, 0x00, 0x00, 0x50, 0x31, 0x00, 0x00, 0x00, +0x3C, 0x10, 0x00, 0x50, 0x00, 0x00, 0x10, 0x00, +0xB4, 0x10, 0x00, 0x50, 0x00, 0x26, 0x31, 0x00, +0xC0, 0x10, 0x00, 0x50, 0x33, 0x03, 0x33, 0x03, +0xC4, 0x10, 0x00, 0x50, 0x33, 0x03, 0x33, 0x03, +0xC8, 0x10, 0x00, 0x50, 0x0C, 0x0A, 0x00, 0x00, +0xCC, 0x10, 0x00, 0x50, 0x1A, 0x00, 0x00, 0x00, +0xD0, 0x10, 0x00, 0x50, 0x03, 0x19, 0x19, 0x00, +0xF0, 0x11, 0x00, 0x50, 0x12, 0x00, 0x00, 0x00, +0xEC, 0x11, 0x00, 0x50, 0x5C, 0x00, 0x00, 0x00, +0xF4, 0x11, 0x00, 0x50, 0x01, 0x00, 0x01, 0x00, +0x2C, 0x10, 0x00, 0x50, 0x10, 0x00, 0x90, 0x00, +0x30, 0x10, 0x00, 0x50, 0x20, 0x0C, 0x90, 0x00, +0x34, 0x10, 0x00, 0x50, 0x30, 0x0C, 0x30, 0x0C, +0x38, 0x10, 0x00, 0x50, 0xFF, 0x0F, 0x00, 0x00, +0x7C, 0x10, 0x00, 0x50, 0x88, 0x88, 0xFE, 0x88, +0x80, 0x10, 0x00, 0x50, 0x88, 0xFF, 0x00, 0x00, +0x84, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55, +0x88, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55, +0x8C, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55, +0xE8, 0x10, 0x00, 0x50, 0x3F, 0x16, 0x3F, 0x15, +0x04, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, +0x78, 0x5C, 0x00, 0x00, 0x04, 0x00, 0x00, 0x20, +0x08, 0x00, 0x00, 0x00, 0x5C, 0x0A, 0x00, 0x00, +0x80, 0x5C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x20, +0xC4, 0x0C, 0x00, 0x00, 0x6A, 0x0A, 0x00, 0x00, +0xFF, 0x00, 0x00, 0x00, 0x78, 0x7F, 0x00, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0x6E, 0x53, 0x58, 0x91, +}; +const unsigned char u8_rad_para_30[] = { +0xA1, 0x00, 0x03, 0xF3, 0x02, 0x02, 0x00, 0x01, +0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, +0x40, 0x01, 0x68, 0x01, 0x44, 0x01, 0x44, 0x01, +0x05, 0x05, 0x00, 0x00, 0x01, 0x05, 0x41, 0x06, +0x0A, 0x0B, 0x41, 0x0F, 0x10, 0x14, 0x41, 0x15, +0x02, 0x07, 0x41, 0x16, 0x41, 0x41, 0x41, 0x41, +0x41, 0x41, 0x41, 0x04, 0x03, 0x09, 0x41, 0x08, +0x0E, 0x0D, 0x41, 0x13, 0x12, 0x18, 0x41, 0x17, +0x0C, 0x11, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, +0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, +0x0C, 0x3E, 0x0C, 0x3E, 0x08, 0x08, 0x02, 0x02, +0x14, 0x03, 0x08, 0x08, 0x01, 0x01, 0xC8, 0x00, +0x1A, 0x01, 0x08, 0x14, 0x82, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x01, +0xA0, 0x00, 0x64, 0x00, 0x4A, 0x00, 0x0A, 0x78, +0x0F, 0x08, 0x1A, 0x4A, 0x4A, 0x3C, 0x3C, 0x00, +0x00, 0x00, 0x6A, 0x00, 0x38, 0x00, 0x1C, 0x00, +0x06, 0x32, 0x69, 0xE3, 0x0D, 0x00, 0x00, 0x72, +0x45, 0x00, 0x00, 0x06, 0x00, 0xA0, 0x00, 0x78, +0x00, 0x64, 0xDB, 0x13, 0xB5, 0x0C, 0x2D, 0x03, +0xCB, 0x00, 0x14, 0x0A, 0xB1, 0x01, 0xF1, 0x05, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x55, 0xAA, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xD6, 0x9D, 0x10, 0x3D, 0xCB, 0x25, 0xBF, 0xD8, +}; +const unsigned char u8_rad_testfw_30[] = { +0x60, 0x0E, 0x00, 0x20, 0xB1, 0x08, 0x00, 0x00, +0xB9, 0x08, 0x00, 0x00, 0x95, 0x08, 0x00, 0x00, +0xBD, 0x08, 0x00, 0x00, 0xBF, 0x08, 0x00, 0x00, +0xC1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xC3, 0x08, 0x00, 0x00, +0xC5, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xC7, 0x08, 0x00, 0x00, 0x81, 0x0F, 0x00, 0x00, +0xE1, 0x0C, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00, +0xCB, 0x08, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x00, +0xA1, 0x0D, 0x00, 0x00, 0x41, 0x0A, 0x00, 0x00, +0xCB, 0x08, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00, +0xCB, 0x08, 0x00, 0x00, 0x41, 0x0B, 0x00, 0x00, +0xE1, 0x0A, 0x00, 0x00, 0xA1, 0x0A, 0x00, 0x00, +0xC1, 0x0A, 0x00, 0x00, 0x81, 0x0A, 0x00, 0x00, +0xCB, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, +0x03, 0x48, 0x85, 0x46, 0x00, 0xF0, 0x80, 0xF8, +0x00, 0x48, 0x00, 0x47, 0xD1, 0x35, 0x00, 0x00, +0x60, 0x0E, 0x00, 0x20, 0x04, 0x20, 0x71, 0x46, +0x08, 0x42, 0x02, 0xD0, 0xEF, 0xF3, 0x09, 0x80, +0x01, 0xE0, 0xEF, 0xF3, 0x08, 0x80, 0x71, 0x46, +0x00, 0x4A, 0x10, 0x47, 0xE9, 0x34, 0x00, 0x00, +0x06, 0x48, 0x80, 0x47, 0x06, 0x48, 0x00, 0x47, +0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, +0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, +0xFE, 0xE7, 0xFE, 0xE7, 0x15, 0x0A, 0x00, 0x00, +0x81, 0x08, 0x00, 0x00, 0x30, 0xB5, 0x0B, 0x46, +0x01, 0x46, 0x00, 0x20, 0x20, 0x22, 0x01, 0x24, +0x09, 0xE0, 0x0D, 0x46, 0xD5, 0x40, 0x9D, 0x42, +0x05, 0xD3, 0x1D, 0x46, 0x95, 0x40, 0x49, 0x1B, +0x25, 0x46, 0x95, 0x40, 0x40, 0x19, 0x15, 0x46, +0x52, 0x1E, 0x00, 0x2D, 0xF1, 0xDC, 0x30, 0xBD, +0x70, 0xB5, 0x00, 0x24, 0x25, 0x46, 0x00, 0x28, +0x01, 0xDA, 0x01, 0x24, 0x40, 0x42, 0x00, 0x29, +0x01, 0xDA, 0x01, 0x25, 0x49, 0x42, 0xFF, 0xF7, +0xDD, 0xFF, 0xAC, 0x42, 0x00, 0xD0, 0x40, 0x42, +0x00, 0x2C, 0x00, 0xD0, 0x49, 0x42, 0x70, 0xBD, +0x03, 0x46, 0x0B, 0x43, 0x9B, 0x07, 0x03, 0xD0, +0x09, 0xE0, 0x08, 0xC9, 0x12, 0x1F, 0x08, 0xC0, +0x04, 0x2A, 0xFA, 0xD2, 0x03, 0xE0, 0x0B, 0x78, +0x49, 0x1C, 0x03, 0x70, 0x40, 0x1C, 0x52, 0x1E, +0xF9, 0xD2, 0x70, 0x47, 0xD2, 0xB2, 0x01, 0xE0, +0x02, 0x70, 0x40, 0x1C, 0x49, 0x1E, 0xFB, 0xD2, +0x70, 0x47, 0x00, 0x22, 0xF6, 0xE7, 0x10, 0xB5, +0x04, 0x46, 0x08, 0x46, 0x11, 0x46, 0x02, 0x46, +0x20, 0x46, 0xFF, 0xF7, 0xEF, 0xFF, 0x20, 0x46, +0x10, 0xBD, 0x00, 0x1D, 0x03, 0x21, 0x40, 0x1E, +0x03, 0x78, 0x12, 0x02, 0x1A, 0x43, 0x49, 0x1E, +0xF9, 0xD5, 0x10, 0x46, 0x70, 0x47, 0x00, 0x00, +0x06, 0x4C, 0x01, 0x25, 0x06, 0x4E, 0x05, 0xE0, +0x20, 0x46, 0xE3, 0x68, 0x07, 0xC8, 0x2B, 0x43, +0x98, 0x47, 0x10, 0x34, 0xB4, 0x42, 0xF7, 0xD3, +0xFF, 0xF7, 0x72, 0xFF, 0x1C, 0x3B, 0x00, 0x00, +0x3C, 0x3B, 0x00, 0x00, 0xC1, 0x06, 0xC9, 0x0E, +0x01, 0x20, 0x88, 0x40, 0x01, 0x49, 0x08, 0x60, +0x70, 0x47, 0x00, 0x00, 0x00, 0xE1, 0x00, 0xE0, +0x0B, 0x49, 0x10, 0xB5, 0x88, 0x42, 0x01, 0xD9, +0x01, 0x20, 0x10, 0xBD, 0x01, 0x02, 0x09, 0x0A, +0x08, 0x48, 0x49, 0x1E, 0x41, 0x61, 0x08, 0x49, +0x07, 0x23, 0xCA, 0x69, 0x12, 0x02, 0x12, 0x0A, +0x04, 0x04, 0x22, 0x43, 0xCA, 0x61, 0x00, 0x21, +0x81, 0x61, 0x03, 0x61, 0x08, 0x46, 0x10, 0xBD, +0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xE0, 0x00, 0xE0, +0x04, 0xED, 0x00, 0xE0, 0x70, 0x47, 0x00, 0x00, +0x03, 0x49, 0x02, 0x20, 0x08, 0x60, 0x02, 0x49, +0x80, 0x39, 0x08, 0x60, 0x70, 0x47, 0x00, 0x00, +0x80, 0xE1, 0x00, 0xE0, 0x62, 0xB6, 0x02, 0x48, +0x00, 0x21, 0x01, 0x60, 0x70, 0x47, 0x00, 0x00, +0x0C, 0x00, 0x00, 0x40, 0x30, 0xB4, 0x74, 0x46, +0x64, 0x1E, 0x25, 0x78, 0x64, 0x1C, 0xAB, 0x42, +0x00, 0xD2, 0x1D, 0x46, 0x63, 0x5D, 0x5B, 0x00, +0xE3, 0x18, 0x30, 0xBC, 0x18, 0x47, 0x00, 0x00, +0x05, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, +0x01, 0x20, 0x80, 0x07, 0x01, 0x6A, 0x03, 0x22, +0xD2, 0x03, 0x11, 0x43, 0x01, 0x62, 0x70, 0x47, +0x34, 0x00, 0x00, 0x20, 0x02, 0xE0, 0x08, 0xC8, +0x12, 0x1F, 0x08, 0xC1, 0x00, 0x2A, 0xFA, 0xD1, +0x70, 0x47, 0x00, 0x20, 0x01, 0xE0, 0x01, 0xC1, +0x12, 0x1F, 0x00, 0x2A, 0xFB, 0xD1, 0x70, 0x47, +0x10, 0xB5, 0x00, 0xF0, 0x55, 0xF8, 0x10, 0xBD, +0x01, 0x20, 0x80, 0x07, 0x41, 0x6A, 0x82, 0x13, +0x11, 0x43, 0x41, 0x62, 0x41, 0x6A, 0xC2, 0x13, +0x11, 0x43, 0x41, 0x62, 0x70, 0x47, 0x10, 0xB5, +0x02, 0xF0, 0xDC, 0xF8, 0x10, 0xBD, 0x00, 0x00, +0x03, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, +0x02, 0x49, 0x01, 0x20, 0xC8, 0x60, 0x70, 0x47, +0x5C, 0x01, 0x00, 0x20, 0x00, 0x03, 0x00, 0x50, +0x10, 0xB5, 0x00, 0xF0, 0xE7, 0xFA, 0x10, 0xBD, +0x70, 0x47, 0x00, 0x00, 0x04, 0x49, 0x06, 0x22, +0x00, 0x28, 0x08, 0x68, 0x01, 0xD0, 0x10, 0x43, +0x00, 0xE0, 0x90, 0x43, 0x08, 0x60, 0x70, 0x47, +0x00, 0x09, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, +0x70, 0x47, 0x00, 0x00, 0x03, 0x48, 0x02, 0x49, +0x41, 0x60, 0x03, 0x49, 0x81, 0x60, 0x70, 0x47, +0x1F, 0x1F, 0x5F, 0x1F, 0x00, 0x10, 0x00, 0x50, +0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, 0x00, 0x00, +0x00, 0xB5, 0x07, 0x48, 0x01, 0x69, 0x02, 0x29, +0x06, 0xD1, 0x01, 0x61, 0x05, 0x49, 0x8A, 0x78, +0x00, 0x2A, 0x02, 0xD0, 0x02, 0xF0, 0x9A, 0xFA, +0x00, 0xBD, 0x01, 0x20, 0xC8, 0x70, 0x00, 0xBD, +0x00, 0x02, 0x00, 0x50, 0xE0, 0x05, 0x00, 0x20, +0x01, 0x21, 0x89, 0x07, 0x0A, 0x15, 0x00, 0x28, +0x48, 0x69, 0x02, 0xD0, 0x10, 0x43, 0x48, 0x61, +0x70, 0x47, 0x90, 0x43, 0xFB, 0xE7, 0x00, 0x00, +0x30, 0xB5, 0x1E, 0x4B, 0x58, 0x68, 0x99, 0x68, +0x00, 0x28, 0x01, 0xDA, 0xDA, 0x04, 0x5A, 0x60, +0x1B, 0x4A, 0x15, 0x68, 0x01, 0x24, 0x05, 0x40, +0x00, 0x2D, 0x02, 0xD1, 0x50, 0x68, 0x08, 0x42, +0x0F, 0xD0, 0x18, 0x48, 0x04, 0x70, 0xFF, 0xF7, +0xBD, 0xFF, 0x14, 0x72, 0x16, 0x48, 0x00, 0x78, +0x00, 0x28, 0x06, 0xD0, 0x18, 0x68, 0x01, 0x21, +0x00, 0x09, 0x00, 0x01, 0x89, 0x02, 0x08, 0x43, +0x18, 0x60, 0x58, 0x68, 0x80, 0x02, 0x02, 0xD5, +0x01, 0x20, 0x40, 0x05, 0x58, 0x60, 0x58, 0x68, +0x00, 0x04, 0x03, 0xD5, 0x01, 0x20, 0xC0, 0x03, +0x58, 0x60, 0x94, 0x72, 0x58, 0x68, 0x80, 0x00, +0x03, 0xD5, 0x01, 0x20, 0x40, 0x07, 0x58, 0x60, +0xD4, 0x72, 0x58, 0x68, 0x40, 0x00, 0x03, 0xD5, +0x01, 0x20, 0x80, 0x07, 0x58, 0x60, 0x94, 0x72, +0x30, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, +0x20, 0x00, 0x00, 0x20, 0x39, 0x00, 0x00, 0x20, +0x31, 0x00, 0x00, 0x20, 0x01, 0x28, 0x05, 0xD0, +0x02, 0x28, 0x05, 0xD0, 0x04, 0x28, 0x06, 0xD0, +0x00, 0x20, 0x70, 0x47, 0x03, 0x48, 0x70, 0x47, +0x02, 0x48, 0xC0, 0x30, 0x70, 0x47, 0x02, 0x48, +0x70, 0x47, 0x00, 0x00, 0x78, 0x7C, 0x00, 0x00, +0xF8, 0x7D, 0x00, 0x00, 0x10, 0xB5, 0x00, 0xF0, +0x67, 0xF8, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00, +0x70, 0x47, 0x00, 0x00, 0xF8, 0xB5, 0x06, 0x46, +0x0D, 0x46, 0x30, 0x23, 0xFF, 0xF7, 0xDE, 0xFF, +0x04, 0x00, 0x28, 0xD0, 0x19, 0x4F, 0x00, 0x2A, +0x21, 0xD0, 0x00, 0x20, 0xFF, 0xF7, 0x52, 0xFF, +0x02, 0xE0, 0x98, 0x00, 0x29, 0x58, 0x21, 0x50, +0x18, 0x46, 0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, +0xF7, 0xD1, 0x38, 0x68, 0x01, 0x88, 0x31, 0x43, +0x01, 0x80, 0x11, 0x49, 0x09, 0x68, 0xCA, 0x79, +0x8B, 0x79, 0x11, 0x02, 0x19, 0x43, 0x41, 0x80, +0xC1, 0x21, 0x89, 0x00, 0x0D, 0x48, 0x00, 0xF0, +0x1D, 0xF8, 0x0D, 0x49, 0xC8, 0x63, 0x01, 0x20, +0xFF, 0xF7, 0x34, 0xFF, 0x0C, 0xE0, 0x38, 0x68, +0x00, 0x88, 0x30, 0x40, 0x03, 0xD1, 0xF8, 0xBD, +0x98, 0x00, 0x21, 0x58, 0x29, 0x50, 0x18, 0x46, +0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, 0xF7, 0xD1, +0x01, 0x20, 0xF8, 0xBD, 0x08, 0x00, 0x00, 0x20, +0x98, 0x01, 0x00, 0x20, 0x78, 0x7C, 0x00, 0x00, +0x40, 0x7F, 0x00, 0x00, 0x70, 0xB5, 0x41, 0x18, +0x49, 0x1E, 0x64, 0x24, 0x09, 0x04, 0x0B, 0x4D, +0x01, 0x43, 0x69, 0x63, 0xE8, 0x68, 0x81, 0x21, +0x09, 0x06, 0x08, 0x43, 0xE8, 0x60, 0x02, 0xE0, +0x01, 0x20, 0xFF, 0xF7, 0xA3, 0xFF, 0xE8, 0x68, +0xC0, 0x01, 0x04, 0xD5, 0x20, 0x46, 0x64, 0x1E, +0xA4, 0xB2, 0x00, 0x28, 0xF4, 0xD1, 0xA8, 0x6B, +0x70, 0xBD, 0x00, 0x00, 0x40, 0x09, 0x00, 0x50, +0x03, 0x49, 0x0A, 0x68, 0x10, 0x18, 0x0A, 0x68, +0x90, 0x42, 0xFC, 0xD1, 0x70, 0x47, 0x00, 0x00, +0x5C, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, +0x70, 0xB5, 0x01, 0x25, 0xAD, 0x07, 0xE8, 0x68, +0x40, 0x08, 0x40, 0x00, 0xE8, 0x60, 0x19, 0x48, +0x41, 0x68, 0x01, 0x26, 0x49, 0x07, 0x00, 0x29, +0x25, 0xDA, 0x42, 0x68, 0x04, 0x21, 0x0A, 0x43, +0x42, 0x60, 0x15, 0x48, 0x00, 0x68, 0x15, 0x4C, +0x40, 0x05, 0x40, 0x0F, 0x20, 0x73, 0x20, 0x7B, +0x06, 0x28, 0x00, 0xD1, 0x21, 0x73, 0x20, 0x7B, +0x02, 0x28, 0x18, 0xD0, 0x20, 0x7B, 0x06, 0x28, +0x05, 0xD8, 0x20, 0x7B, 0x04, 0x28, 0x02, 0xD3, +0x01, 0x20, 0x01, 0xF0, 0x39, 0xF9, 0x20, 0x7B, +0x0B, 0x49, 0x09, 0x78, 0x88, 0x42, 0x06, 0xD0, +0x0A, 0x48, 0x01, 0x78, 0x31, 0x43, 0x01, 0x70, +0x09, 0x49, 0x81, 0x20, 0x08, 0x70, 0xE8, 0x68, +0x30, 0x43, 0xE8, 0x60, 0x70, 0xBD, 0x00, 0x20, +0xEB, 0xE7, 0x00, 0x00, 0x40, 0x00, 0x00, 0x50, +0x00, 0x11, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20, +0x39, 0x01, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20, +0x20, 0x08, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x46, +0x06, 0x4C, 0x01, 0xF0, 0x83, 0xFC, 0x00, 0x21, +0x8A, 0x00, 0x49, 0x1C, 0xA0, 0x58, 0xC9, 0xB2, +0xA8, 0x50, 0x18, 0x29, 0xF8, 0xD3, 0x01, 0x20, +0x70, 0xBD, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, +0x01, 0x20, 0x80, 0x07, 0x41, 0x68, 0x82, 0x14, +0x11, 0x43, 0x41, 0x60, 0x70, 0x47, 0x00, 0x00, +0x10, 0xB5, 0x14, 0x48, 0x80, 0x69, 0x40, 0x04, +0x23, 0xD5, 0x13, 0x4C, 0xE0, 0x68, 0xA2, 0x68, +0x02, 0x40, 0xE0, 0x68, 0xA1, 0x68, 0x88, 0x43, +0xE0, 0x60, 0x10, 0x07, 0x03, 0xD5, 0x08, 0x20, +0xE0, 0x60, 0x02, 0xF0, 0x85, 0xFD, 0x50, 0x07, +0x0E, 0xD5, 0x04, 0x20, 0xE0, 0x60, 0x02, 0xF0, +0x7F, 0xFD, 0x00, 0xF0, 0xCB, 0xF8, 0x00, 0x28, +0x06, 0xD1, 0x08, 0x48, 0x00, 0x78, 0x00, 0x28, +0x02, 0xD0, 0x01, 0x20, 0xFF, 0xF7, 0x48, 0xFE, +0xFF, 0x20, 0xF3, 0x30, 0xE0, 0x60, 0x01, 0x20, +0xE0, 0x60, 0x10, 0xBD, 0x00, 0x09, 0x00, 0x50, +0x00, 0x05, 0x00, 0x50, 0x90, 0x02, 0x00, 0x20, +0x70, 0xB5, 0x0C, 0x49, 0x00, 0x20, 0x0C, 0x4B, +0x89, 0x25, 0x0C, 0x68, 0xAD, 0x00, 0x21, 0x18, +0xC9, 0x7E, 0x41, 0x29, 0x08, 0xD0, 0x42, 0x00, +0x99, 0x5A, 0x49, 0x05, 0x49, 0x0D, 0x99, 0x52, +0x4E, 0x05, 0x01, 0xD5, 0x49, 0x1B, 0x99, 0x52, +0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, 0xEE, 0xD3, +0x01, 0x20, 0x70, 0xBD, 0x98, 0x01, 0x00, 0x20, +0xAC, 0x03, 0x00, 0x20, 0x01, 0x21, 0x89, 0x07, +0x00, 0xB5, 0x8A, 0x14, 0x00, 0x28, 0x08, 0x68, +0x04, 0xD0, 0x10, 0x43, 0x08, 0x60, 0xFF, 0xF7, +0x9F, 0xFF, 0x00, 0xBD, 0x90, 0x43, 0x08, 0x60, +0x00, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xF8, 0xB5, 0x33, 0x48, 0x80, 0x69, 0x40, 0x04, +0x61, 0xD5, 0x32, 0x4D, 0xA8, 0x6A, 0xE9, 0x68, +0xAC, 0x68, 0x0C, 0x40, 0xE9, 0x68, 0xAA, 0x68, +0x91, 0x43, 0xE9, 0x60, 0x2E, 0x4E, 0xC0, 0x07, +0x15, 0xD0, 0x68, 0x69, 0x89, 0x27, 0xC0, 0xB2, +0xEF, 0x60, 0xAA, 0x6A, 0x01, 0x21, 0x0A, 0x43, +0xAA, 0x62, 0x0B, 0x28, 0x03, 0xD2, 0x31, 0x70, +0x00, 0xF0, 0x92, 0xFD, 0x03, 0xE0, 0x00, 0x21, +0x31, 0x70, 0x00, 0xF0, 0x69, 0xFD, 0xBC, 0x43, +0x01, 0x20, 0xFF, 0xF7, 0xE5, 0xFD, 0x23, 0x48, +0xA1, 0x04, 0x04, 0xD5, 0x29, 0x6A, 0x81, 0x43, +0x29, 0x62, 0x41, 0x14, 0xE9, 0x60, 0xE1, 0x04, +0x05, 0xD5, 0x29, 0x6A, 0x81, 0x43, 0x29, 0x62, +0x01, 0x20, 0x00, 0x03, 0xE8, 0x60, 0xA0, 0x05, +0x02, 0xD5, 0x01, 0x20, 0x40, 0x02, 0xE8, 0x60, +0x20, 0x07, 0x09, 0xD5, 0x30, 0x78, 0x01, 0x28, +0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, 0x48, 0xFD, +0x64, 0x08, 0x64, 0x00, 0x09, 0x20, 0xE8, 0x60, +0x60, 0x07, 0x17, 0xD5, 0x30, 0x78, 0x01, 0x28, +0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, 0x3C, 0xFD, +0x05, 0x20, 0xE8, 0x60, 0xA8, 0x69, 0x0E, 0x49, +0x88, 0x42, 0x08, 0xD0, 0xA9, 0x69, 0x0C, 0x48, +0x40, 0x1C, 0x81, 0x42, 0x03, 0xD0, 0xA9, 0x69, +0xC0, 0x1C, 0x81, 0x42, 0x02, 0xD1, 0x01, 0x20, +0xFF, 0xF7, 0xAA, 0xFD, 0xFF, 0x20, 0xF3, 0x30, +0xE8, 0x60, 0x01, 0x20, 0xE8, 0x60, 0xF8, 0xBD, +0x00, 0x09, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50, +0xCC, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, +0x88, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x02, 0x46, +0x07, 0x49, 0x60, 0x32, 0x11, 0x80, 0x00, 0x21, +0x51, 0x80, 0x4C, 0x00, 0x53, 0x88, 0x04, 0x5B, +0x49, 0x1C, 0x1B, 0x19, 0xC9, 0xB2, 0x53, 0x80, +0x31, 0x29, 0xF6, 0xD3, 0x10, 0xBD, 0x00, 0x00, +0xAA, 0x55, 0x00, 0x00, 0x01, 0x20, 0x80, 0x07, +0x40, 0x69, 0x40, 0x05, 0x01, 0xD5, 0x01, 0x20, +0x70, 0x47, 0x00, 0x20, 0x70, 0x47, 0x00, 0x00, +0x02, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, +0x70, 0x47, 0x00, 0x00, 0x1C, 0x08, 0x00, 0x20, +0x01, 0x20, 0x01, 0x49, 0x08, 0x70, 0x70, 0x47, +0xD3, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, +0x05, 0x20, 0x00, 0x07, 0x82, 0x69, 0x0C, 0x49, +0x00, 0x2A, 0x07, 0xDA, 0x82, 0x69, 0xC3, 0x00, +0x92, 0x00, 0x92, 0x08, 0xD2, 0x18, 0x82, 0x61, +0x01, 0x22, 0x4A, 0x72, 0x82, 0x69, 0x52, 0x00, +0x08, 0xD5, 0x82, 0x69, 0x01, 0x23, 0x92, 0x00, +0x92, 0x08, 0x9B, 0x07, 0xD2, 0x18, 0x82, 0x61, +0x00, 0x20, 0x48, 0x72, 0x70, 0x47, 0x00, 0x00, +0x20, 0x08, 0x00, 0x20, 0x70, 0xB5, 0x1D, 0x4D, +0x68, 0x78, 0x00, 0x06, 0x2A, 0xD5, 0x05, 0x20, +0xFF, 0xF7, 0x04, 0xFE, 0x01, 0x20, 0xFF, 0xF7, +0x43, 0xFD, 0x68, 0x78, 0x40, 0x06, 0x40, 0x0E, +0x68, 0x70, 0x68, 0x78, 0x16, 0x4C, 0x10, 0x38, +0x03, 0x00, 0xFF, 0xF7, 0x0F, 0xFD, 0x05, 0x04, +0x06, 0x0C, 0x1C, 0x1E, 0x0E, 0x00, 0x06, 0x20, +0x06, 0xE0, 0x01, 0x20, 0x20, 0x70, 0x11, 0xA0, +0x02, 0xF0, 0xE7, 0xFB, 0x01, 0xE0, 0x02, 0x20, +0x20, 0x70, 0x68, 0x78, 0x80, 0x21, 0x08, 0x43, +0x28, 0x71, 0x00, 0x0A, 0x68, 0x71, 0x00, 0x20, +0xFF, 0xF7, 0x22, 0xFD, 0xA9, 0x88, 0x0C, 0xA0, +0x02, 0xF0, 0xD7, 0xFB, 0x70, 0xBD, 0x03, 0x20, +0xEE, 0xE7, 0x0D, 0x48, 0x69, 0x78, 0x82, 0x88, +0x0C, 0xA0, 0x02, 0xF0, 0xCE, 0xFB, 0x05, 0x20, +0xE6, 0xE7, 0x00, 0x00, 0x88, 0x02, 0x00, 0x20, +0xCE, 0x00, 0x00, 0x20, 0x43, 0x4D, 0x44, 0x5F, +0x49, 0x4E, 0x49, 0x54, 0x0D, 0x0A, 0x00, 0x00, +0x43, 0x4D, 0x44, 0x20, 0x45, 0x78, 0x69, 0x74, +0x5B, 0x25, 0x78, 0x5D, 0x3D, 0x0D, 0x0A, 0x00, +0x74, 0x06, 0x00, 0x20, 0x43, 0x4D, 0x44, 0x3D, +0x30, 0x78, 0x25, 0x78, 0x3A, 0x25, 0x78, 0x0D, +0x0A, 0x00, 0x00, 0x00, 0xF8, 0xB5, 0x1A, 0x4C, +0x01, 0x20, 0x20, 0x70, 0x19, 0x4E, 0x00, 0x20, +0x30, 0x71, 0x70, 0x71, 0x18, 0x48, 0xB0, 0x70, +0x00, 0x0A, 0xF0, 0x70, 0x00, 0x20, 0xFF, 0xF7, +0xC9, 0xFE, 0x16, 0x4F, 0x04, 0x25, 0x20, 0x78, +0x03, 0x00, 0xFF, 0xF7, 0xB7, 0xFC, 0x07, 0x14, +0x05, 0x12, 0x12, 0x16, 0x19, 0x1F, 0x14, 0x00, +0x37, 0x71, 0x38, 0x0A, 0x70, 0x71, 0x25, 0x70, +0x70, 0x20, 0x30, 0x70, 0x0E, 0x48, 0x30, 0x71, +0x00, 0x0A, 0x70, 0x71, 0x00, 0xF0, 0x92, 0xFB, +0xE9, 0xE7, 0x00, 0xF0, 0x8F, 0xFB, 0x25, 0x70, +0xE5, 0xE7, 0xFF, 0xF7, 0x7B, 0xFF, 0xE2, 0xE7, +0x08, 0xA0, 0x02, 0xF0, 0x7E, 0xFB, 0x00, 0xF0, +0x11, 0xF8, 0xF2, 0xE7, 0xF8, 0xBD, 0x00, 0x00, +0xCE, 0x00, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20, +0xCC, 0x55, 0x00, 0x00, 0x55, 0xAA, 0x00, 0x00, +0x11, 0x5A, 0x00, 0x00, 0x5B, 0x53, 0x54, 0x5D, +0x0D, 0x0A, 0x00, 0x00, 0x70, 0xB5, 0x1F, 0x48, +0x00, 0x25, 0x84, 0x88, 0x01, 0x21, 0x00, 0x2C, +0x1A, 0xD0, 0x0B, 0x00, 0xFF, 0xF7, 0x7E, 0xFC, +0x07, 0x16, 0x05, 0x13, 0x1A, 0x16, 0x0F, 0x1F, +0x16, 0x00, 0x00, 0xF0, 0x55, 0xF8, 0x00, 0xF0, +0xC1, 0xF9, 0x01, 0x28, 0x01, 0xD1, 0x05, 0x21, +0x08, 0xE0, 0x06, 0x21, 0x06, 0xE0, 0x02, 0xF0, +0xA1, 0xF8, 0x02, 0x21, 0x02, 0xE0, 0x00, 0xF0, +0xA7, 0xFE, 0x03, 0x21, 0x00, 0x2C, 0xE4, 0xD1, +0x28, 0x46, 0x70, 0xBD, 0x01, 0xF0, 0xFE, 0xFE, +0x00, 0xF0, 0x66, 0xFE, 0xED, 0xE7, 0x0C, 0xA0, +0x02, 0xF0, 0x3F, 0xFB, 0x0E, 0x48, 0xFF, 0xF7, +0xE9, 0xFE, 0x0E, 0x48, 0xFF, 0xF7, 0xE6, 0xFE, +0x0D, 0x48, 0xFF, 0xF7, 0xE3, 0xFE, 0x0D, 0x48, +0xFF, 0xF7, 0xE0, 0xFE, 0x00, 0xF0, 0x8A, 0xFA, +0x0B, 0x48, 0x01, 0x68, 0x0B, 0xA0, 0x02, 0xF0, +0x2C, 0xFB, 0xE1, 0xE7, 0x74, 0x06, 0x00, 0x20, +0x54, 0x45, 0x53, 0x54, 0x5F, 0x43, 0x4D, 0x44, +0x5F, 0x45, 0x58, 0x49, 0x54, 0x0D, 0x0A, 0x00, +0xE4, 0x02, 0x00, 0x20, 0x48, 0x03, 0x00, 0x20, +0xAC, 0x03, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20, +0x0C, 0x05, 0x00, 0x20, 0x67, 0x5F, 0x75, 0x33, +0x32, 0x5F, 0x77, 0x65, 0x61, 0x72, 0x61, 0x62, +0x6C, 0x65, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x5F, +0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x3A, 0x20, +0x30, 0x78, 0x25, 0x78, 0x0D, 0x0A, 0x00, 0x00, +0x10, 0xB5, 0xC8, 0x21, 0x0E, 0x48, 0xFF, 0xF7, +0xB8, 0xFB, 0xC8, 0x21, 0x0D, 0x48, 0xFF, 0xF7, +0xB4, 0xFB, 0xC8, 0x21, 0x0C, 0x48, 0xFF, 0xF7, +0xB0, 0xFB, 0xC8, 0x21, 0x0B, 0x48, 0xFF, 0xF7, +0xAC, 0xFB, 0x64, 0x21, 0x0A, 0x48, 0xFF, 0xF7, +0xA8, 0xFB, 0x32, 0x21, 0x09, 0x48, 0xFF, 0xF7, +0xA4, 0xFB, 0x09, 0x49, 0x00, 0x20, 0x08, 0x60, +0x08, 0xA0, 0x02, 0xF0, 0xE6, 0xFA, 0x10, 0xBD, +0xE4, 0x02, 0x00, 0x20, 0x48, 0x03, 0x00, 0x20, +0xAC, 0x03, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20, +0x74, 0x04, 0x00, 0x20, 0xD8, 0x04, 0x00, 0x20, +0x0C, 0x05, 0x00, 0x20, 0x46, 0x54, 0x20, 0x52, +0x65, 0x73, 0x75, 0x6C, 0x74, 0x20, 0x49, 0x6E, +0x69, 0x74, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0xF3, 0xB5, 0xC1, 0xB0, 0x42, 0x98, 0x04, 0x28, +0x01, 0xD2, 0x04, 0x20, 0x42, 0x90, 0xC0, 0x21, +0x68, 0x46, 0xFF, 0xF7, 0x7A, 0xFB, 0x01, 0xF0, +0xAB, 0xFB, 0x01, 0xF0, 0xDB, 0xFD, 0x01, 0xF0, +0x83, 0xF8, 0x00, 0x24, 0x6E, 0x46, 0x1A, 0xE0, +0x01, 0xF0, 0xEC, 0xFC, 0x01, 0xF0, 0xD2, 0xFD, +0x01, 0xF0, 0x7A, 0xF8, 0x18, 0x49, 0x00, 0x20, +0x0A, 0x68, 0x18, 0x49, 0x0F, 0x68, 0x39, 0x18, +0xC9, 0x7E, 0x41, 0x29, 0x05, 0xD0, 0x81, 0x00, +0x45, 0x00, 0x73, 0x58, 0x55, 0x5F, 0x5B, 0x19, +0x73, 0x50, 0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, +0xF1, 0xD3, 0x64, 0x1C, 0xE4, 0xB2, 0x42, 0x98, +0x84, 0x42, 0xE1, 0xD3, 0x01, 0xF0, 0xB6, 0xFD, +0x01, 0xF0, 0xE4, 0xFC, 0x0B, 0x4D, 0x00, 0x24, +0x28, 0x68, 0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, +0x07, 0xD0, 0xA0, 0x00, 0x30, 0x58, 0x42, 0x99, +0xFF, 0xF7, 0x16, 0xFB, 0x41, 0x99, 0x62, 0x00, +0x88, 0x52, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C, +0xEE, 0xD3, 0x01, 0x20, 0x43, 0xB0, 0xF0, 0xBD, +0x4C, 0x01, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, +0x70, 0xB5, 0x24, 0x48, 0xFF, 0x24, 0x41, 0x6B, +0x2D, 0x34, 0x03, 0x22, 0x12, 0x05, 0x91, 0x43, +0x01, 0x22, 0x12, 0x05, 0x89, 0x18, 0x41, 0x63, +0x1F, 0x4D, 0x20, 0x48, 0xA9, 0x7E, 0xFF, 0xF7, +0x9F, 0xFF, 0xE8, 0x7E, 0x29, 0x7E, 0x41, 0x43, +0x89, 0x04, 0x28, 0x7F, 0x09, 0x0E, 0x02, 0x04, +0x09, 0x02, 0x0A, 0x43, 0x17, 0x49, 0x0C, 0x32, +0x40, 0x39, 0x4A, 0x60, 0x0C, 0x38, 0xC0, 0xB2, +0xC9, 0x14, 0x40, 0x18, 0x13, 0x49, 0x40, 0x31, +0x88, 0x60, 0xA9, 0x7E, 0x14, 0x48, 0xFF, 0xF7, +0x87, 0xFF, 0x14, 0x49, 0x12, 0x4B, 0x0A, 0x68, +0x00, 0x20, 0x11, 0x18, 0xC9, 0x7E, 0x41, 0x29, +0x03, 0xD0, 0x41, 0x00, 0x5D, 0x5A, 0x2D, 0x1B, +0x5D, 0x52, 0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, +0xF3, 0xD3, 0x0D, 0xA0, 0x02, 0xF0, 0x41, 0xFA, +0x01, 0x21, 0x08, 0x48, 0x01, 0xF0, 0x24, 0xFE, +0x0C, 0xA0, 0x02, 0xF0, 0x3A, 0xFA, 0x01, 0x21, +0x05, 0x48, 0x01, 0xF0, 0x1D, 0xFE, 0x01, 0x20, +0x70, 0xBD, 0x00, 0x00, 0x80, 0x10, 0x00, 0x50, +0xA8, 0x06, 0x00, 0x20, 0x74, 0x04, 0x00, 0x20, +0x48, 0x03, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, +0x42, 0x61, 0x73, 0x65, 0x6C, 0x69, 0x6E, 0x65, +0x0D, 0x0A, 0x00, 0x00, 0x51, 0x75, 0x69, 0x63, +0x6B, 0x28, 0x30, 0x78, 0x31, 0x32, 0x43, 0x29, +0x0D, 0x0A, 0x00, 0x00, 0xF8, 0xB5, 0x3C, 0x48, +0x00, 0x24, 0x60, 0x21, 0x00, 0x68, 0xFF, 0xF7, +0xCC, 0xFA, 0x3A, 0x4E, 0x3A, 0x48, 0x00, 0x68, +0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x5A, 0xD0, +0xA5, 0x07, 0xAD, 0x0F, 0xA7, 0x08, 0x0C, 0x21, +0x20, 0x46, 0xFF, 0xF7, 0x7B, 0xFA, 0x88, 0x08, +0x00, 0x21, 0xC9, 0x43, 0x71, 0x62, 0x09, 0x0C, +0xB1, 0x62, 0x20, 0x2C, 0x05, 0xD2, 0x72, 0x6A, +0x01, 0x21, 0xA1, 0x40, 0x8A, 0x43, 0x72, 0x62, +0x06, 0xE0, 0xB3, 0x6A, 0x21, 0x46, 0x20, 0x39, +0x01, 0x22, 0x8A, 0x40, 0x93, 0x43, 0xB3, 0x62, +0x03, 0x2D, 0x02, 0xD2, 0x69, 0x1C, 0xC9, 0xB2, +0x00, 0xE0, 0x08, 0x21, 0xF2, 0x6A, 0x27, 0x4B, +0x1A, 0x40, 0x01, 0x23, 0xBB, 0x40, 0x1B, 0x05, +0x1B, 0x09, 0x1A, 0x43, 0xF2, 0x62, 0x72, 0x6B, +0x0E, 0x23, 0x9A, 0x43, 0x01, 0x23, 0x83, 0x40, +0x5B, 0x07, 0x1B, 0x0F, 0x1A, 0x43, 0x72, 0x63, +0x40, 0x1C, 0x82, 0x07, 0x92, 0x0F, 0x90, 0x05, +0x13, 0x05, 0x18, 0x43, 0x93, 0x04, 0x18, 0x43, +0x12, 0x04, 0x18, 0x4F, 0x10, 0x43, 0x40, 0x37, +0xFA, 0x6A, 0xFF, 0x23, 0x1B, 0x04, 0x9A, 0x43, +0x10, 0x43, 0xF8, 0x62, 0xF0, 0x6B, 0x09, 0x07, +0xF4, 0x22, 0x09, 0x0E, 0x90, 0x43, 0x09, 0x1D, +0x08, 0x43, 0xF0, 0x63, 0xB0, 0x6B, 0x74, 0x21, +0x88, 0x43, 0x6D, 0x1C, 0x69, 0x07, 0x49, 0x0E, +0x09, 0x1D, 0x08, 0x43, 0xB0, 0x63, 0x01, 0xF0, +0xE9, 0xFB, 0x01, 0xF0, 0xCF, 0xFC, 0x64, 0x1C, +0xE4, 0xB2, 0x30, 0x2C, 0x9A, 0xD3, 0x06, 0x48, +0x60, 0x22, 0x01, 0x68, 0x08, 0x48, 0xFF, 0xF7, +0x47, 0xFA, 0x01, 0x21, 0x06, 0x48, 0x01, 0xF0, +0x8B, 0xFD, 0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, +0x4C, 0x01, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, +0x98, 0x01, 0x00, 0x20, 0xFF, 0xFF, 0x00, 0xF0, +0xE4, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x00, 0xF0, +0xC1, 0xF8, 0x0E, 0x48, 0x00, 0x68, 0x0E, 0x4C, +0x40, 0x05, 0x40, 0x0F, 0x20, 0x70, 0x00, 0x20, +0x01, 0xF0, 0xAA, 0xFF, 0x0B, 0x49, 0x01, 0x20, +0x08, 0x70, 0x08, 0x20, 0x20, 0x70, 0x0A, 0x49, +0x06, 0x20, 0x08, 0x70, 0x01, 0x20, 0x09, 0x49, +0x80, 0x02, 0x08, 0x80, 0x01, 0xF0, 0xFA, 0xFE, +0x01, 0xF0, 0x98, 0xFC, 0x01, 0xF0, 0xC6, 0xFB, +0x01, 0x20, 0x10, 0xBD, 0x00, 0x11, 0x00, 0x50, +0x39, 0x01, 0x00, 0x20, 0x3B, 0x01, 0x00, 0x20, +0x0D, 0x08, 0x00, 0x20, 0x46, 0x01, 0x00, 0x20, +0xF8, 0xB5, 0x36, 0x48, 0x00, 0x24, 0x36, 0x4D, +0x44, 0x80, 0x2C, 0x60, 0x6C, 0x60, 0x35, 0xA0, +0x02, 0xF0, 0x63, 0xF9, 0x32, 0x48, 0x61, 0x1E, +0x80, 0x38, 0xC1, 0x61, 0x09, 0x0C, 0x01, 0x62, +0x35, 0x49, 0x41, 0x62, 0x81, 0x62, 0xC1, 0x62, +0x01, 0x06, 0x0A, 0x68, 0x0B, 0x13, 0x9A, 0x43, +0x0A, 0x60, 0x32, 0x49, 0x01, 0x26, 0x8E, 0x61, +0xCE, 0x61, 0x30, 0x4A, 0x40, 0x3A, 0xD4, 0x63, +0x0C, 0x60, 0x8C, 0x60, 0x2E, 0x4B, 0x1F, 0x7F, +0xDB, 0x7E, 0x3A, 0x04, 0x1B, 0x02, 0x1A, 0x43, +0x0C, 0x32, 0xBC, 0x46, 0x42, 0x60, 0x82, 0x60, +0x1F, 0x22, 0x02, 0x61, 0x44, 0x61, 0x0C, 0x62, +0x1F, 0x48, 0x28, 0x4A, 0x40, 0x30, 0x82, 0x61, +0x03, 0x22, 0xC2, 0x61, 0x1C, 0x4A, 0xC0, 0x3A, +0xD4, 0x60, 0x03, 0x23, 0x1B, 0x02, 0x13, 0x61, +0x14, 0x23, 0x83, 0x62, 0x34, 0x23, 0xC3, 0x63, +0x17, 0x4B, 0x54, 0x27, 0x80, 0x33, 0x1F, 0x61, +0x5C, 0x62, 0xD0, 0x27, 0xC7, 0x62, 0xF0, 0x27, +0x1F, 0x60, 0xFF, 0x27, 0x11, 0x37, 0x5F, 0x61, +0x9C, 0x62, 0x04, 0x62, 0x1A, 0x4B, 0x43, 0x62, +0x1A, 0x48, 0x88, 0x62, 0x0E, 0x49, 0x1A, 0x48, +0x40, 0x39, 0x48, 0x63, 0x67, 0x46, 0x0C, 0x3F, +0xF8, 0xB2, 0xC9, 0x14, 0x40, 0x18, 0xA8, 0x60, +0x17, 0x48, 0x16, 0x49, 0x01, 0x60, 0x44, 0x60, +0xD1, 0x68, 0x16, 0x48, 0x01, 0x60, 0x11, 0x69, +0x41, 0x60, 0x15, 0x48, 0x06, 0x70, 0x10, 0x15, +0x10, 0x60, 0x14, 0xA0, 0x02, 0xF0, 0x01, 0xF9, +0xF8, 0xBD, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x20, +0xC0, 0x10, 0x00, 0x50, 0x4F, 0x70, 0x65, 0x6E, +0x20, 0x49, 0x6E, 0x69, 0x74, 0x20, 0x53, 0x74, +0x61, 0x72, 0x74, 0x0D, 0x0A, 0x00, 0x00, 0x00, +0x55, 0x55, 0x55, 0x55, 0xC0, 0x11, 0x00, 0x50, +0xA8, 0x06, 0x00, 0x20, 0x03, 0x00, 0x03, 0x00, +0x00, 0x3F, 0x3F, 0x3F, 0x03, 0x30, 0xBC, 0x00, +0x00, 0x26, 0x32, 0x00, 0x21, 0x20, 0x00, 0x00, +0x00, 0x19, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20, +0x31, 0x00, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, +0x20, 0x49, 0x6E, 0x69, 0x74, 0x20, 0x45, 0x6E, +0x64, 0x0D, 0x0A, 0x00, 0x10, 0xB5, 0x90, 0xB0, +0x40, 0x21, 0x68, 0x46, 0xFF, 0xF7, 0x85, 0xF9, +0x41, 0x22, 0x40, 0x21, 0x68, 0x46, 0xFF, 0xF7, +0x79, 0xF9, 0x0E, 0x49, 0x00, 0x20, 0x0B, 0x68, +0x6A, 0x46, 0x19, 0x18, 0xC9, 0x7E, 0x41, 0x29, +0x00, 0xD0, 0x50, 0x54, 0x40, 0x1C, 0xC0, 0xB2, +0x30, 0x28, 0xF6, 0xD3, 0x08, 0x49, 0x00, 0x20, +0x13, 0x5C, 0x44, 0x00, 0x40, 0x1C, 0xC0, 0xB2, +0x0B, 0x53, 0x40, 0x28, 0xF8, 0xD3, 0x00, 0x21, +0x03, 0x48, 0x01, 0xF0, 0x95, 0xFC, 0x01, 0x20, +0x10, 0xB0, 0x10, 0xBD, 0x98, 0x01, 0x00, 0x20, +0x9C, 0x01, 0x00, 0x20, 0x0E, 0x49, 0x10, 0xB5, +0x5A, 0x20, 0x08, 0x74, 0x00, 0x20, 0x0A, 0x46, +0x48, 0x74, 0x20, 0x3A, 0x14, 0x5C, 0x4B, 0x7C, +0x40, 0x1C, 0x1B, 0x19, 0xC0, 0xB2, 0x4B, 0x74, +0x31, 0x28, 0xF7, 0xD3, 0x07, 0x49, 0x00, 0x20, +0x13, 0x5C, 0x44, 0x00, 0x40, 0x1C, 0xC0, 0xB2, +0x0B, 0x53, 0x40, 0x28, 0xF8, 0xD3, 0x01, 0x21, +0x02, 0x48, 0x01, 0xF0, 0x71, 0xFC, 0x10, 0xBD, +0xF8, 0x04, 0x00, 0x20, 0x9C, 0x01, 0x00, 0x20, +0x70, 0xB5, 0x2E, 0xA0, 0x02, 0xF0, 0x81, 0xF8, +0x32, 0x49, 0x31, 0x48, 0x08, 0x60, 0x33, 0x4B, +0x31, 0x48, 0x58, 0x60, 0x31, 0x4C, 0x32, 0x48, +0x80, 0x34, 0xA0, 0x60, 0x31, 0x48, 0x40, 0x78, +0x41, 0x1E, 0xC9, 0x05, 0xC9, 0x0D, 0x19, 0x61, +0x2F, 0x4A, 0x00, 0x21, 0x11, 0x62, 0x2B, 0x4A, +0x2E, 0x4D, 0x40, 0x32, 0x55, 0x63, 0x05, 0x02, +0x2D, 0x48, 0x6D, 0x1C, 0x05, 0x60, 0x41, 0x60, +0x09, 0x25, 0x26, 0x48, 0x2D, 0x05, 0x40, 0x38, +0x05, 0x63, 0x41, 0x63, 0x81, 0x63, 0xD9, 0x63, +0x11, 0x60, 0xA1, 0x62, 0x27, 0x4B, 0x19, 0x70, +0x04, 0x23, 0x03, 0x60, 0x83, 0x02, 0xC3, 0x60, +0x01, 0x61, 0xC3, 0x68, 0x24, 0x49, 0x0B, 0x60, +0x03, 0x69, 0x4B, 0x60, 0x03, 0x21, 0x23, 0x4B, +0x09, 0x06, 0x19, 0x60, 0x01, 0x21, 0x49, 0x02, +0x11, 0x62, 0x81, 0x04, 0xD1, 0x62, 0x81, 0x69, +0x03, 0x23, 0x9B, 0x03, 0x19, 0x43, 0x81, 0x61, +0xC1, 0x69, 0x1D, 0x4B, 0x19, 0x43, 0xC1, 0x61, +0xD0, 0x21, 0x11, 0x63, 0xC1, 0x6A, 0x10, 0x22, +0x11, 0x43, 0xC1, 0x62, 0x55, 0x20, 0xE0, 0x62, +0x60, 0x21, 0x18, 0x48, 0xFF, 0xF7, 0xE9, 0xF8, +0x16, 0x48, 0x60, 0x21, 0x60, 0x30, 0xFF, 0xF7, +0xE4, 0xF8, 0x15, 0xA0, 0x02, 0xF0, 0x29, 0xF8, +0x70, 0xBD, 0x00, 0x00, 0x53, 0x68, 0x6F, 0x72, +0x74, 0x20, 0x49, 0x6E, 0x69, 0x74, 0x20, 0x53, +0x74, 0x61, 0x72, 0x74, 0x0D, 0x0A, 0x00, 0x00, +0x00, 0x00, 0x04, 0x20, 0x4C, 0x01, 0x00, 0x20, +0x0C, 0x0D, 0xF2, 0x00, 0x40, 0x10, 0x00, 0x50, +0xE6, 0x0A, 0x00, 0x00, 0xC8, 0x06, 0x00, 0x20, +0xC0, 0x11, 0x00, 0x50, 0x00, 0x36, 0x30, 0x04, +0x00, 0x19, 0x00, 0x50, 0x31, 0x00, 0x00, 0x20, +0x20, 0x00, 0x00, 0x20, 0x00, 0x07, 0x00, 0x50, +0x00, 0xC0, 0x00, 0xC0, 0x00, 0x20, 0x00, 0x50, +0x53, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x49, 0x6E, +0x69, 0x74, 0x20, 0x45, 0x6E, 0x64, 0x0D, 0x0A, +0x00, 0x00, 0x00, 0x00, 0x10, 0xB5, 0x1C, 0x48, +0x64, 0x24, 0x41, 0x79, 0x02, 0x79, 0x09, 0x02, +0x11, 0x43, 0x80, 0x22, 0x91, 0x43, 0x01, 0x71, +0x09, 0x0A, 0x41, 0x71, 0x41, 0x79, 0x02, 0x79, +0x09, 0x02, 0x11, 0x43, 0x2D, 0x22, 0x52, 0x02, +0x11, 0x43, 0x01, 0x71, 0x09, 0x0A, 0x41, 0x71, +0x81, 0x88, 0x12, 0xA0, 0x01, 0xF0, 0xDD, 0xFF, +0x06, 0xE0, 0x21, 0x46, 0x16, 0xA0, 0x01, 0xF0, +0xD8, 0xFF, 0x01, 0x20, 0xFF, 0xF7, 0xDA, 0xF9, +0xFF, 0xF7, 0x94, 0xFB, 0x00, 0x28, 0x04, 0xD1, +0x20, 0x46, 0x64, 0x1E, 0x24, 0xB2, 0x00, 0x28, +0xEF, 0xD1, 0x64, 0x1C, 0x08, 0xD1, 0x01, 0x20, +0xFF, 0xF7, 0x0E, 0xF9, 0x10, 0xA0, 0x01, 0xF0, +0xC4, 0xFF, 0x0A, 0x20, 0xFF, 0xF7, 0xC6, 0xF9, +0x00, 0x20, 0xFF, 0xF7, 0x05, 0xF9, 0x10, 0xBD, +0x88, 0x02, 0x00, 0x20, 0x46, 0x54, 0x20, 0x55, +0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x53, 0x74, +0x61, 0x74, 0x75, 0x73, 0x20, 0x28, 0x30, 0x78, +0x25, 0x34, 0x78, 0x29, 0x0D, 0x0A, 0x00, 0x00, +0x49, 0x4E, 0x54, 0x20, 0x4C, 0x6F, 0x77, 0x21, +0x21, 0x28, 0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, +0x49, 0x4E, 0x54, 0x20, 0x70, 0x61, 0x64, 0x20, +0x69, 0x73, 0x20, 0x6C, 0x6F, 0x77, 0x20, 0x6C, +0x65, 0x76, 0x65, 0x6C, 0x20, 0x21, 0x21, 0x00, +0x70, 0xB5, 0x17, 0x48, 0x5A, 0x25, 0x01, 0x78, +0x16, 0x48, 0x00, 0x29, 0x25, 0xD0, 0x09, 0x21, +0x01, 0x70, 0x15, 0x4C, 0x01, 0x21, 0xE0, 0x89, +0x89, 0x02, 0x88, 0x42, 0x01, 0xD0, 0x03, 0x20, +0x60, 0x71, 0x11, 0x48, 0x30, 0x21, 0x28, 0x30, +0xFF, 0xF7, 0x3B, 0xF8, 0x00, 0x20, 0x60, 0x72, +0xA0, 0x71, 0x60, 0x62, 0x16, 0x21, 0x21, 0x72, +0x0C, 0x49, 0x20, 0x71, 0x08, 0x70, 0xA5, 0x81, +0x60, 0x81, 0x60, 0x8A, 0x09, 0x21, 0x09, 0x03, +0x08, 0x43, 0x60, 0x82, 0x03, 0x20, 0x40, 0x02, +0xE0, 0x61, 0x0D, 0x20, 0xC0, 0x01, 0x20, 0x62, +0x70, 0xBD, 0x05, 0x70, 0xD9, 0xE7, 0x00, 0x00, +0x92, 0x01, 0x00, 0x20, 0x94, 0x01, 0x00, 0x20, +0x38, 0x01, 0x00, 0x20, 0x93, 0x01, 0x00, 0x20, +0x0E, 0x48, 0x03, 0x21, 0x41, 0x71, 0x0E, 0x49, +0x41, 0x61, 0x0D, 0x49, 0x60, 0x31, 0x81, 0x61, +0x01, 0x21, 0x01, 0x70, 0x07, 0x22, 0x42, 0x70, +0x0A, 0x4B, 0x05, 0x22, 0x1A, 0x70, 0x0A, 0x4B, +0x1A, 0x70, 0x0A, 0x4B, 0x55, 0x22, 0xDA, 0x70, +0x04, 0x22, 0x02, 0x82, 0x00, 0x22, 0xC2, 0x70, +0x09, 0x22, 0x12, 0x03, 0x42, 0x82, 0x81, 0x70, +0x70, 0x47, 0x00, 0x00, 0x38, 0x01, 0x00, 0x20, +0x00, 0x00, 0x04, 0x20, 0x92, 0x01, 0x00, 0x20, +0x0D, 0x08, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, +0x04, 0x22, 0x0F, 0x49, 0x0C, 0x28, 0x10, 0xD0, +0x8B, 0x05, 0x0D, 0x28, 0x08, 0x6A, 0x10, 0xD0, +0x18, 0x43, 0x08, 0x62, 0x88, 0x6A, 0x10, 0x43, +0x88, 0x62, 0x0A, 0x4A, 0x01, 0x20, 0x10, 0x70, +0xC8, 0x68, 0xC8, 0x60, 0x88, 0x6A, 0x88, 0x62, +0x70, 0x47, 0x08, 0x6A, 0x40, 0x00, 0x40, 0x08, +0x00, 0xE0, 0x18, 0x43, 0x08, 0x62, 0x88, 0x6A, +0x90, 0x43, 0x88, 0x62, 0xF0, 0xE7, 0x00, 0x00, +0x00, 0x06, 0x00, 0x50, 0xCC, 0x00, 0x00, 0x20, +0x00, 0xB5, 0x08, 0x49, 0x0A, 0x28, 0x05, 0xD0, +0x07, 0x48, 0x00, 0x0C, 0x48, 0x63, 0x07, 0x48, +0x08, 0x63, 0x00, 0xBD, 0x06, 0x48, 0x00, 0x68, +0x08, 0x62, 0x0D, 0x20, 0xFF, 0xF7, 0xCC, 0xFF, +0x00, 0xBD, 0x00, 0x00, 0x00, 0x06, 0x00, 0x50, +0xBC, 0x02, 0x00, 0x20, 0xCC, 0x02, 0x00, 0x20, +0xC0, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x1B, 0x49, +0x00, 0x20, 0x03, 0x00, 0xFF, 0xF7, 0x12, 0xF8, +0x0C, 0x07, 0x0A, 0x0E, 0x26, 0x26, 0x11, 0x14, +0x17, 0x1A, 0x1D, 0x20, 0x23, 0x26, 0x16, 0x4A, +0x0A, 0x80, 0x1E, 0xE0, 0x14, 0x4A, 0x12, 0x1D, +0x4A, 0x80, 0x1A, 0xE0, 0x13, 0x4A, 0x8A, 0x80, +0x17, 0xE0, 0x13, 0x4A, 0x4A, 0x81, 0x14, 0xE0, +0x12, 0x4A, 0x8A, 0x81, 0x11, 0xE0, 0x12, 0x4A, +0xCA, 0x81, 0x0E, 0xE0, 0x11, 0x4A, 0x0A, 0x82, +0x0B, 0xE0, 0x11, 0x4A, 0x4A, 0x82, 0x08, 0xE0, +0x10, 0x4A, 0x8A, 0x82, 0x05, 0xE0, 0x10, 0x4A, +0xCA, 0x82, 0x02, 0xE0, 0x0F, 0x4A, 0x43, 0x00, +0xCA, 0x52, 0x40, 0x1C, 0x80, 0xB2, 0x0C, 0x28, +0xCF, 0xD3, 0x0D, 0xA0, 0x01, 0xF0, 0xC5, 0xFE, +0x10, 0xBD, 0x00, 0x00, 0xCC, 0x02, 0x00, 0x20, +0x90, 0x02, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20, +0x34, 0x01, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20, +0xE4, 0x06, 0x00, 0x20, 0xD8, 0x06, 0x00, 0x20, +0xC0, 0x02, 0x00, 0x20, 0xBC, 0x02, 0x00, 0x20, +0x9C, 0x01, 0x00, 0x20, 0xC4, 0x02, 0x00, 0x20, +0x49, 0x32, 0x43, 0x20, 0x4F, 0x4B, 0x0D, 0x0A, +0x00, 0x00, 0x00, 0x00, 0x70, 0xB5, 0x01, 0x25, +0xCA, 0x07, 0x0A, 0xD0, 0x00, 0x20, 0x70, 0xBD, +0x93, 0x00, 0xC4, 0x58, 0x66, 0x1C, 0x02, 0xD0, +0x1B, 0x18, 0x5B, 0x68, 0x23, 0x60, 0x92, 0x1C, +0x92, 0xB2, 0x8A, 0x42, 0xF4, 0xD3, 0x28, 0x46, +0x70, 0xBD, 0x00, 0x00, 0x10, 0xB5, 0x1D, 0x49, +0x0A, 0x68, 0x1C, 0x48, 0x40, 0x30, 0x02, 0x61, +0x4A, 0x68, 0x42, 0x61, 0x8A, 0x68, 0x82, 0x61, +0xC9, 0x68, 0xC1, 0x61, 0x82, 0x69, 0xF0, 0x21, +0x8A, 0x43, 0x82, 0x61, 0x82, 0x69, 0x0A, 0x43, +0x82, 0x61, 0x41, 0x69, 0x49, 0x04, 0x04, 0xD5, +0x41, 0x69, 0x01, 0x22, 0x52, 0x03, 0x89, 0x1A, +0x41, 0x61, 0x10, 0x48, 0x40, 0x38, 0x01, 0x68, +0x01, 0x22, 0x12, 0x06, 0x11, 0x43, 0x01, 0x60, +0x30, 0x21, 0x0D, 0x48, 0xFF, 0xF7, 0xC6, 0xFF, +0x0B, 0x48, 0x40, 0x21, 0xC0, 0x30, 0xFF, 0xF7, +0xC1, 0xFF, 0x0B, 0x20, 0xFE, 0xF7, 0x46, 0xFF, +0x03, 0x20, 0xFE, 0xF7, 0x43, 0xFF, 0x00, 0x20, +0xFE, 0xF7, 0x40, 0xFF, 0x05, 0x20, 0xFE, 0xF7, +0x3D, 0xFF, 0x09, 0x20, 0xFE, 0xF7, 0x3A, 0xFF, +0x01, 0x20, 0x10, 0xBD, 0x40, 0x14, 0x00, 0x50, +0x5C, 0x39, 0x00, 0x00, 0xFE, 0xB5, 0x27, 0x48, +0x0A, 0x25, 0x45, 0x5F, 0x01, 0x26, 0x29, 0x46, +0x25, 0xA0, 0x01, 0xF0, 0x4A, 0xFE, 0x2A, 0x48, +0x72, 0x04, 0x01, 0x68, 0x29, 0x4F, 0x91, 0x43, +0x00, 0x24, 0x01, 0x60, 0x28, 0x48, 0x00, 0x68, +0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x18, 0xD0, +0x26, 0x49, 0x60, 0x00, 0x0A, 0x5E, 0x26, 0x49, +0x0B, 0x5A, 0xD0, 0x1A, 0x00, 0xB2, 0xA8, 0x42, +0x0B, 0xDA, 0x00, 0x90, 0x21, 0x46, 0x01, 0x95, +0x22, 0xA0, 0x01, 0xF0, 0x2E, 0xFE, 0x38, 0x5D, +0x20, 0x21, 0x08, 0x43, 0x38, 0x55, 0x00, 0x26, +0x03, 0xE0, 0x38, 0x5D, 0xDF, 0x21, 0x08, 0x40, +0x38, 0x55, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C, +0xDC, 0xD3, 0x01, 0x2E, 0x18, 0xD0, 0x14, 0x4A, +0x01, 0x21, 0x10, 0x68, 0x49, 0x04, 0x08, 0x43, +0x10, 0x60, 0x23, 0xA0, 0x01, 0xF0, 0x15, 0xFE, +0x27, 0xA0, 0x01, 0xF0, 0x12, 0xFE, 0x01, 0x21, +0x10, 0x48, 0x01, 0xF0, 0xF5, 0xF9, 0x27, 0xA0, +0x01, 0xF0, 0x0B, 0xFE, 0x01, 0x21, 0x0E, 0x48, +0x01, 0xF0, 0xEE, 0xF9, 0x30, 0x46, 0xFE, 0xBD, +0x27, 0xA0, 0xEB, 0xE7, 0x84, 0x06, 0x00, 0x20, +0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x43, 0x20, +0x43, 0x68, 0x65, 0x63, 0x6B, 0x20, 0x54, 0x48, +0x44, 0x3D, 0x25, 0x64, 0x20, 0x0D, 0x0A, 0x00, +0x0C, 0x05, 0x00, 0x20, 0xD8, 0x04, 0x00, 0x20, +0x98, 0x01, 0x00, 0x20, 0xAC, 0x03, 0x00, 0x20, +0x4C, 0x07, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, +0x20, 0x43, 0x43, 0x20, 0x4E, 0x47, 0x20, 0x50, +0x69, 0x6E, 0x5B, 0x25, 0x64, 0x5D, 0x2C, 0x20, +0x44, 0x69, 0x66, 0x66, 0x3D, 0x25, 0x64, 0x2D, +0x25, 0x64, 0x3D, 0x25, 0x64, 0x2C, 0x20, 0x4C, +0x6F, 0x77, 0x54, 0x48, 0x44, 0x20, 0x3D, 0x20, +0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x43, +0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x5B, +0x4E, 0x47, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, +0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x43, 0x0D, +0x0A, 0x00, 0x00, 0x00, 0x47, 0x6F, 0x6C, 0x64, +0x65, 0x6E, 0x20, 0x4F, 0x70, 0x65, 0x6E, 0x20, +0x43, 0x43, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x43, +0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x5B, +0x50, 0x61, 0x73, 0x73, 0x5D, 0x0D, 0x0A, 0x00, +0xFE, 0xB5, 0x36, 0x49, 0x02, 0x20, 0x08, 0x5E, +0x00, 0x90, 0x0A, 0x20, 0x08, 0x5E, 0x03, 0x21, +0x89, 0x03, 0x48, 0x43, 0x00, 0x14, 0x01, 0x90, +0x31, 0x48, 0x01, 0x27, 0x01, 0x68, 0xB9, 0x43, +0x00, 0x24, 0x01, 0x60, 0x2F, 0x48, 0x00, 0x68, +0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x38, 0xD0, +0x65, 0x00, 0x2D, 0x4B, 0x2D, 0x48, 0x5E, 0x5F, +0x41, 0x5B, 0x00, 0x20, 0x71, 0x1A, 0x09, 0xB2, +0xCA, 0x00, 0x51, 0x1A, 0xCA, 0x17, 0x52, 0x0F, +0x51, 0x18, 0xC9, 0x10, 0x09, 0xB2, 0x59, 0x53, +0x00, 0x9A, 0x91, 0x42, 0x06, 0xDA, 0x0B, 0x46, +0x32, 0x46, 0x21, 0x46, 0x24, 0xA0, 0x01, 0xF0, +0x78, 0xFD, 0x01, 0x20, 0x00, 0x2E, 0x0E, 0xD1, +0x2B, 0x49, 0x2C, 0x4A, 0x49, 0x5B, 0x52, 0x5B, +0x89, 0x1A, 0x0A, 0xB2, 0x01, 0x99, 0x8A, 0x42, +0x05, 0xDA, 0x0B, 0x46, 0x21, 0x46, 0x28, 0xA0, +0x01, 0xF0, 0x67, 0xFD, 0x01, 0xE0, 0x00, 0x28, +0x06, 0xD0, 0x2F, 0x48, 0x01, 0x22, 0x01, 0x5D, +0x11, 0x43, 0x01, 0x55, 0x00, 0x27, 0x04, 0xE0, +0x2B, 0x48, 0x01, 0x5D, 0x49, 0x08, 0x49, 0x00, +0x01, 0x55, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C, +0xBC, 0xD3, 0x00, 0x99, 0x01, 0x2F, 0x10, 0xD0, +0x26, 0xA0, 0x01, 0xF0, 0x4E, 0xFD, 0x0A, 0x49, +0x01, 0x22, 0x08, 0x68, 0x10, 0x43, 0x08, 0x60, +0x29, 0xA0, 0x01, 0xF0, 0x46, 0xFD, 0x01, 0x21, +0x07, 0x48, 0x01, 0xF0, 0x29, 0xF9, 0x38, 0x46, +0xFE, 0xBD, 0x2A, 0xA0, 0x01, 0xF0, 0x3D, 0xFD, +0xF2, 0xE7, 0x00, 0x00, 0x84, 0x06, 0x00, 0x20, +0x0C, 0x05, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, +0x48, 0x03, 0x00, 0x20, 0x74, 0x04, 0x00, 0x20, +0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x20, 0x54, +0x48, 0x44, 0x20, 0x4F, 0x70, 0x65, 0x6E, 0x20, +0x4E, 0x47, 0x20, 0x44, 0x61, 0x74, 0x61, 0x5B, +0x25, 0x64, 0x5D, 0x5B, 0x25, 0x64, 0x3A, 0x25, +0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0xAC, 0x03, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, +0x4F, 0x70, 0x65, 0x6E, 0x20, 0x4E, 0x47, 0x20, +0x50, 0x69, 0x6E, 0x5B, 0x25, 0x64, 0x5D, 0x2C, +0x20, 0x43, 0x43, 0x5F, 0x44, 0x69, 0x66, 0x66, +0x3D, 0x25, 0x64, 0x2C, 0x20, 0x54, 0x48, 0x44, +0x3D, 0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, 0x00, +0xD8, 0x04, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, +0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x54, 0x48, +0x44, 0x3A, 0x25, 0x64, 0x29, 0x20, 0x5B, 0x4E, +0x47, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0x51, 0x75, 0x69, 0x63, 0x6B, 0x20, 0x2D, 0x20, +0x42, 0x61, 0x73, 0x65, 0x6C, 0x69, 0x6E, 0x65, +0x0D, 0x0A, 0x00, 0x00, 0x4F, 0x70, 0x65, 0x6E, +0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x54, 0x48, +0x44, 0x3A, 0x25, 0x64, 0x29, 0x20, 0x5B, 0x50, +0x61, 0x73, 0x73, 0x5D, 0x0D, 0x0A, 0x00, 0x00, +0xF0, 0xB5, 0x17, 0x49, 0x00, 0x20, 0x7D, 0x27, +0x09, 0x68, 0xFF, 0x00, 0x02, 0x46, 0x89, 0x07, +0x24, 0xD5, 0x14, 0x49, 0x14, 0x4D, 0x09, 0x68, +0xCE, 0x26, 0x0B, 0x18, 0xDB, 0x7E, 0x41, 0x2B, +0x0A, 0xD0, 0x12, 0x4B, 0x44, 0x00, 0x1B, 0x5F, +0xBB, 0x42, 0x00, 0xDA, 0x01, 0x22, 0x2B, 0x5C, +0x9C, 0x07, 0x01, 0xD5, 0x33, 0x40, 0x2B, 0x54, +0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, 0xEC, 0xD3, +0x00, 0x2A, 0x0B, 0xD0, 0x00, 0x20, 0x0A, 0x18, +0xD2, 0x7E, 0x41, 0x2A, 0x02, 0xD0, 0x2A, 0x5C, +0x32, 0x40, 0x2A, 0x54, 0x40, 0x1C, 0xC0, 0xB2, +0x30, 0x28, 0xF4, 0xD3, 0x01, 0x20, 0xF0, 0xBD, +0x0C, 0x05, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, +0xD8, 0x04, 0x00, 0x20, 0xE4, 0x02, 0x00, 0x20, +0x70, 0xB5, 0x20, 0x48, 0x01, 0x25, 0xC4, 0x7D, +0x1F, 0x48, 0x80, 0x88, 0x40, 0x07, 0x01, 0xD4, +0x01, 0x20, 0x70, 0xBD, 0xFF, 0xF7, 0x2C, 0xFB, +0x1C, 0x48, 0xFE, 0xF7, 0x57, 0xFF, 0x00, 0x28, +0x0F, 0xD1, 0x1B, 0xA0, 0x01, 0xF0, 0x91, 0xFC, +0x01, 0x20, 0x20, 0x49, 0x00, 0x25, 0x08, 0x70, +0x00, 0x2C, 0x06, 0xD0, 0x64, 0x1E, 0xE4, 0xB2, +0x21, 0x46, 0x1D, 0xA0, 0x01, 0xF0, 0x85, 0xFC, +0xE8, 0xE7, 0xFE, 0xF7, 0x8D, 0xFF, 0xFF, 0xF7, +0x03, 0xFA, 0xFF, 0xF7, 0xD5, 0xFE, 0x00, 0x28, +0x07, 0xD1, 0x00, 0x25, 0x00, 0x2C, 0x04, 0xD0, +0x64, 0x1E, 0xE4, 0xB2, 0x21, 0x46, 0x1C, 0xA0, +0x0A, 0xE0, 0xFF, 0xF7, 0x1F, 0xFE, 0x00, 0x28, +0x0A, 0xD1, 0x00, 0x25, 0x00, 0x2C, 0x07, 0xD0, +0x64, 0x1E, 0xE4, 0xB2, 0x21, 0x46, 0x1C, 0xA0, +0x01, 0xF0, 0x67, 0xFC, 0x01, 0x25, 0xC9, 0xE7, +0x28, 0x46, 0x70, 0xBD, 0xA8, 0x06, 0x00, 0x20, +0x74, 0x06, 0x00, 0x20, 0xAC, 0x03, 0x00, 0x20, +0x4F, 0x70, 0x65, 0x6E, 0x20, 0x54, 0x65, 0x73, +0x74, 0x20, 0x43, 0x61, 0x6C, 0x69, 0x62, 0x72, +0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x4E, 0x47, +0x21, 0x0D, 0x0A, 0x00, 0xD8, 0x04, 0x00, 0x20, +0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x61, 0x6C, +0x69, 0x62, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, +0x20, 0x52, 0x65, 0x74, 0x72, 0x79, 0x20, 0x28, +0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, 0x00, +0x4F, 0x70, 0x65, 0x6E, 0x20, 0x54, 0x65, 0x73, +0x74, 0x20, 0x52, 0x65, 0x74, 0x72, 0x79, 0x20, +0x28, 0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, +0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x43, 0x20, +0x52, 0x65, 0x74, 0x72, 0x79, 0x20, 0x28, 0x25, +0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0x10, 0xB5, 0x17, 0x4A, 0x11, 0x78, 0x81, 0x42, +0x29, 0xD0, 0x16, 0x49, 0x0B, 0x68, 0x01, 0x24, +0xA4, 0x02, 0x23, 0x43, 0x0B, 0x60, 0x10, 0x70, +0x05, 0x22, 0x40, 0x24, 0x12, 0x49, 0x12, 0x07, +0x80, 0x23, 0x00, 0x28, 0x09, 0xD0, 0x08, 0x68, +0x40, 0x06, 0x0C, 0xD4, 0x08, 0x68, 0x20, 0x43, +0x08, 0x60, 0xD0, 0x68, 0x98, 0x43, 0xD0, 0x60, +0x05, 0xE0, 0xD0, 0x68, 0x18, 0x43, 0xD0, 0x60, +0x08, 0x68, 0xA0, 0x43, 0x08, 0x60, 0x08, 0x68, +0x18, 0x43, 0x08, 0x60, 0xD0, 0x68, 0x01, 0x21, +0xC9, 0x03, 0x08, 0x43, 0xD0, 0x60, 0xE1, 0x20, +0x00, 0x02, 0x01, 0xF0, 0x3B, 0xF8, 0x10, 0xBD, +0x04, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, +0x00, 0x11, 0x00, 0x50, 0x06, 0x49, 0x83, 0x20, +0x08, 0x70, 0x06, 0x49, 0x00, 0x20, 0x08, 0x70, +0x05, 0x48, 0x00, 0x68, 0x05, 0x49, 0x40, 0x05, +0x40, 0x0F, 0x08, 0x73, 0x70, 0x47, 0x00, 0x00, +0x20, 0x08, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20, +0x00, 0x11, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20, +0xF0, 0xB5, 0x04, 0x46, 0x8F, 0xB0, 0xA6, 0x48, +0x0A, 0x90, 0xA5, 0x48, 0xA6, 0x4D, 0x60, 0x30, +0x0B, 0x90, 0xA4, 0x48, 0x03, 0x90, 0xA5, 0x48, +0x05, 0x95, 0x01, 0x68, 0x22, 0x46, 0xA4, 0xA0, +0x01, 0xF0, 0xC7, 0xFB, 0x03, 0x98, 0x08, 0x90, +0xA5, 0x48, 0xA6, 0x4A, 0x00, 0x21, 0x09, 0x95, +0x8B, 0x00, 0x08, 0x9D, 0x49, 0x1C, 0xE8, 0x50, +0x09, 0x9D, 0x89, 0xB2, 0xEA, 0x50, 0x18, 0x29, +0xF6, 0xD3, 0x01, 0x20, 0x80, 0x02, 0x06, 0x90, +0x20, 0x46, 0x07, 0x94, 0x50, 0x30, 0x00, 0xB2, +0x50, 0x3C, 0x0C, 0x90, 0x20, 0xB2, 0x0D, 0x90, +0x00, 0xF0, 0x96, 0xFC, 0x00, 0xF0, 0xC6, 0xFE, +0x00, 0xF0, 0x6E, 0xF9, 0x98, 0x48, 0x00, 0x78, +0xC0, 0x07, 0x18, 0xD1, 0x01, 0x90, 0x00, 0x20, +0x08, 0x9A, 0x81, 0x00, 0x53, 0x58, 0x0A, 0x9A, +0x40, 0x1C, 0x53, 0x50, 0x09, 0x9A, 0x80, 0xB2, +0x53, 0x58, 0x0B, 0x9A, 0x18, 0x28, 0x53, 0x50, +0xF2, 0xD3, 0x00, 0xF0, 0xC7, 0xFD, 0x00, 0xF0, +0xAD, 0xFE, 0x00, 0xF0, 0x55, 0xF9, 0x8C, 0x48, +0x00, 0x78, 0xC0, 0x07, 0x02, 0xD0, 0x00, 0x20, +0x0F, 0xB0, 0xF0, 0xBD, 0x3B, 0x46, 0x89, 0xA0, +0x00, 0x9A, 0x01, 0x99, 0x01, 0xF0, 0x81, 0xFB, +0x00, 0x25, 0x2D, 0xE0, 0x00, 0x2D, 0x2B, 0xD0, +0x01, 0x98, 0x05, 0x28, 0x73, 0xD9, 0x30, 0x2F, +0x71, 0xD2, 0x7A, 0x4C, 0x05, 0x98, 0x40, 0x3C, +0x04, 0x90, 0x60, 0x6B, 0x03, 0x26, 0x36, 0x05, +0x01, 0x21, 0xB0, 0x43, 0x49, 0x05, 0x40, 0x18, +0x60, 0x63, 0x00, 0xF0, 0x9F, 0xFD, 0x00, 0xF0, +0x85, 0xFE, 0x00, 0xF0, 0x2D, 0xF9, 0x60, 0x6B, +0x01, 0x21, 0xB0, 0x43, 0x09, 0x05, 0x40, 0x18, +0x60, 0x63, 0x00, 0x27, 0x00, 0x26, 0x79, 0x48, +0x00, 0x68, 0x80, 0x19, 0xC0, 0x7E, 0x41, 0x28, +0x0E, 0xD0, 0x70, 0x00, 0x04, 0x99, 0x02, 0x90, +0x0C, 0x5A, 0x16, 0x2E, 0x12, 0xD0, 0x1A, 0xE0, +0x00, 0x98, 0x30, 0x28, 0x77, 0xD0, 0x03, 0x98, +0x04, 0x90, 0x00, 0x20, 0x00, 0x90, 0xE9, 0xE7, +0x00, 0x2D, 0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, +0x68, 0xE0, 0x00, 0x98, 0x40, 0x1C, 0x80, 0xB2, +0x00, 0x90, 0x63, 0xE0, 0x6A, 0x48, 0x2C, 0x23, +0x00, 0x68, 0x22, 0x46, 0xC3, 0x5E, 0x31, 0x46, +0x68, 0xA0, 0x01, 0xF0, 0x36, 0xFB, 0x01, 0x98, +0x0A, 0x28, 0x0C, 0xD2, 0x64, 0x49, 0x02, 0x98, +0x09, 0x68, 0x09, 0x5E, 0x07, 0x98, 0x81, 0x42, +0x01, 0xDA, 0x06, 0x98, 0x84, 0x43, 0x06, 0x98, +0x40, 0x08, 0x04, 0x43, 0x32, 0xE0, 0x5E, 0x48, +0x02, 0x99, 0x00, 0x68, 0x41, 0x5E, 0x00, 0x29, +0x01, 0xDB, 0x08, 0x46, 0x00, 0xE0, 0x48, 0x42, +0x00, 0xB2, 0x40, 0x30, 0xC2, 0x17, 0x52, 0x0E, +0x10, 0x18, 0x0D, 0x9A, 0xC0, 0x11, 0x91, 0x42, +0x10, 0xDA, 0x21, 0x05, 0x06, 0xD5, 0x20, 0x1A, +0x84, 0xB2, 0x01, 0x20, 0xC0, 0x02, 0x84, 0x42, +0x11, 0xD3, 0x17, 0xE0, 0x84, 0x42, 0x03, 0xDD, +0x20, 0x1A, 0x84, 0xB2, 0x12, 0xE0, 0x2E, 0xE0, +0x00, 0x24, 0x0F, 0xE0, 0x0C, 0x9A, 0x91, 0x42, +0x15, 0xDD, 0x21, 0x05, 0x05, 0xD5, 0x20, 0x18, +0x84, 0xB2, 0x4D, 0x48, 0x04, 0xE0, 0x04, 0x46, +0x04, 0xE0, 0x20, 0x18, 0x84, 0xB2, 0x4B, 0x48, +0x84, 0x42, 0xF8, 0xD8, 0x00, 0x2D, 0x0F, 0xD0, +0x01, 0x20, 0xC0, 0x02, 0x04, 0x43, 0x05, 0x99, +0x02, 0x98, 0x0C, 0x52, 0x0E, 0xE0, 0x00, 0x2D, +0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, 0xF3, 0xE7, +0x00, 0x98, 0x40, 0x1C, 0x80, 0xB2, 0x00, 0x90, +0x03, 0x99, 0x02, 0x98, 0x0C, 0x52, 0x01, 0x98, +0x05, 0x28, 0xE9, 0xD9, 0x76, 0x1C, 0xB6, 0xB2, +0x30, 0x2E, 0x00, 0xD2, 0x77, 0xE7, 0x6D, 0x1C, +0xED, 0xB2, 0x02, 0x2D, 0x00, 0xD2, 0x51, 0xE7, +0x06, 0x98, 0x40, 0x08, 0x06, 0x90, 0x38, 0xA0, +0x01, 0xF0, 0xCB, 0xFA, 0x00, 0x98, 0x30, 0x28, +0x03, 0xD1, 0x30, 0x2F, 0x01, 0xD1, 0x40, 0x20, +0x01, 0x90, 0x01, 0x98, 0x40, 0x1C, 0x80, 0xB2, +0x01, 0x90, 0x40, 0x28, 0x00, 0xD8, 0x1A, 0xE7, +0x1C, 0x4C, 0x40, 0x3C, 0x60, 0x6B, 0x03, 0x25, +0x2D, 0x05, 0x01, 0x21, 0xA8, 0x43, 0x09, 0x05, +0x40, 0x18, 0x60, 0x63, 0x00, 0xF0, 0xE6, 0xFC, +0x00, 0xF0, 0xCC, 0xFD, 0x00, 0xF0, 0x74, 0xF8, +0x21, 0x4E, 0x01, 0x21, 0x30, 0x68, 0x00, 0xF0, +0x8F, 0xFE, 0x60, 0x6B, 0x01, 0x21, 0xA8, 0x43, +0x49, 0x05, 0x40, 0x18, 0x60, 0x63, 0x00, 0xF0, +0xD5, 0xFC, 0x00, 0xF0, 0xBB, 0xFD, 0x00, 0xF0, +0x63, 0xF8, 0x01, 0x21, 0x30, 0x68, 0x00, 0xF0, +0x7F, 0xFE, 0x60, 0x6B, 0x28, 0x43, 0x60, 0x63, +0x00, 0xF0, 0xC8, 0xFC, 0x00, 0xF0, 0xAE, 0xFD, +0x00, 0xF0, 0x56, 0xF8, 0x01, 0x21, 0x30, 0x68, +0x00, 0xF0, 0x72, 0xFE, 0x01, 0x20, 0xFF, 0xE6, +0x00, 0x20, 0x00, 0x50, 0xF4, 0x05, 0x00, 0x20, +0x00, 0x00, 0x01, 0x20, 0xC0, 0x10, 0x00, 0x50, +0x5B, 0x43, 0x46, 0x42, 0x3A, 0x25, 0x78, 0x3A, +0x25, 0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, +0x00, 0x04, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x0C, +0x30, 0x00, 0x00, 0x20, 0x5B, 0x25, 0x64, 0x3A, +0x25, 0x64, 0x3A, 0x25, 0x64, 0x5D, 0x20, 0x0D, +0x0A, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20, +0x4C, 0x01, 0x00, 0x20, 0x25, 0x64, 0x2C, 0x30, +0x78, 0x25, 0x78, 0x2C, 0x25, 0x64, 0x2C, 0x00, +0xFF, 0x0F, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, +0x0D, 0x0A, 0x00, 0x00, 0x10, 0xB5, 0x0B, 0x49, +0x30, 0x24, 0x00, 0x28, 0x0A, 0x4A, 0x0B, 0x4B, +0x08, 0x68, 0x06, 0xD0, 0x20, 0x43, 0x08, 0x60, +0x09, 0x48, 0x10, 0x60, 0x08, 0x48, 0x60, 0x30, +0x05, 0xE0, 0xA0, 0x43, 0x08, 0x60, 0x07, 0x48, +0x10, 0x60, 0x06, 0x48, 0x60, 0x30, 0x18, 0x60, +0x10, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, +0x4C, 0x01, 0x00, 0x20, 0x50, 0x01, 0x00, 0x20, +0x00, 0x00, 0x04, 0x20, 0x00, 0x10, 0x04, 0x20, +0xF0, 0xB5, 0x11, 0x48, 0x01, 0x68, 0x11, 0x4D, +0x89, 0x06, 0x30, 0x22, 0x60, 0x35, 0x00, 0x29, +0x0E, 0x4B, 0x0F, 0x4C, 0x0F, 0x4E, 0x10, 0x4F, +0x01, 0x68, 0x0A, 0xDA, 0x91, 0x43, 0x01, 0x60, +0x23, 0x60, 0x0E, 0x48, 0x35, 0x60, 0x07, 0x60, +0x0B, 0x48, 0x0D, 0x49, 0x60, 0x30, 0x08, 0x60, +0xF0, 0xBD, 0x11, 0x43, 0x01, 0x60, 0x08, 0x48, +0x27, 0x60, 0x60, 0x30, 0x30, 0x60, 0x07, 0x48, +0x03, 0x60, 0x07, 0x48, 0x05, 0x60, 0xF0, 0xBD, +0x00, 0x10, 0x00, 0x50, 0x00, 0x00, 0x04, 0x20, +0x40, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, +0x00, 0x10, 0x04, 0x20, 0x4C, 0x01, 0x00, 0x20, +0x50, 0x01, 0x00, 0x20, 0x00, 0xB5, 0x09, 0x48, +0x00, 0x78, 0x00, 0x28, 0x08, 0xD0, 0x01, 0x28, +0x09, 0xD0, 0x02, 0x28, 0x03, 0xD1, 0x06, 0x49, +0x04, 0x20, 0x00, 0xF0, 0x65, 0xF8, 0x00, 0xBD, +0x04, 0x49, 0x01, 0x20, 0xF9, 0xE7, 0x04, 0x49, +0x02, 0x20, 0xF6, 0xE7, 0x0D, 0x08, 0x00, 0x20, +0x70, 0x05, 0x00, 0x20, 0x1C, 0x02, 0x00, 0x20, +0x10, 0x05, 0x00, 0x20, 0xF8, 0xB5, 0x07, 0x46, +0x00, 0xF0, 0xD2, 0xFA, 0x21, 0x4D, 0x60, 0x21, +0x28, 0x46, 0xFE, 0xF7, 0x9A, 0xFA, 0x00, 0x24, +0x00, 0xF0, 0xFC, 0xFC, 0xFF, 0xF7, 0xA4, 0xFF, +0xFE, 0xF7, 0x31, 0xFB, 0x00, 0x2C, 0x0C, 0xD0, +0x1B, 0x49, 0x00, 0x20, 0x0E, 0x68, 0x42, 0x00, +0x51, 0x19, 0x0B, 0x88, 0xB2, 0x5A, 0x40, 0x1C, +0x9A, 0x18, 0xC0, 0xB2, 0x0A, 0x80, 0x30, 0x28, +0xF5, 0xD3, 0x64, 0x1C, 0xE4, 0xB2, 0x05, 0x2C, +0xE6, 0xD3, 0x00, 0x20, 0x41, 0x00, 0x49, 0x19, +0x00, 0x22, 0x8A, 0x5E, 0x40, 0x1C, 0x92, 0x10, +0xC0, 0xB2, 0x0A, 0x80, 0x30, 0x28, 0xF5, 0xD3, +0x39, 0x46, 0x0E, 0xA0, 0x01, 0xF0, 0xB9, 0xF9, +0x01, 0x21, 0x28, 0x46, 0x00, 0xF0, 0x9C, 0xFD, +0x01, 0x20, 0xFE, 0xF7, 0xB7, 0xFB, 0x0C, 0x4C, +0x20, 0x78, 0x38, 0x42, 0x08, 0xD1, 0x0B, 0x48, +0x60, 0x22, 0x29, 0x46, 0x00, 0x68, 0xFE, 0xF7, +0x47, 0xFA, 0x20, 0x78, 0x38, 0x43, 0x20, 0x70, +0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, 0x01, 0x20, +0x4C, 0x01, 0x00, 0x20, 0x42, 0x43, 0x5F, 0x4F, +0x4B, 0x5F, 0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, +0x38, 0x00, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, +0xF0, 0xB5, 0x21, 0x48, 0x00, 0x22, 0x00, 0x68, +0x20, 0x4C, 0xA0, 0x42, 0x28, 0xD8, 0x20, 0x48, +0x00, 0x78, 0x04, 0x28, 0x27, 0xD1, 0x1F, 0x4D, +0x1F, 0x4C, 0x2D, 0x68, 0x00, 0x20, 0x2E, 0x18, +0xF6, 0x7E, 0x41, 0x2E, 0x08, 0xD0, 0x43, 0x00, +0x1F, 0x19, 0x00, 0x26, 0xBE, 0x5F, 0xCB, 0x5E, +0xF3, 0x1A, 0x00, 0xD5, 0x5B, 0x42, 0x1B, 0xB2, +0x9A, 0x42, 0x00, 0xDA, 0x1A, 0x46, 0x40, 0x1C, +0xC0, 0xB2, 0x30, 0x28, 0xEB, 0xD3, 0x60, 0x20, +0x40, 0x5D, 0x90, 0x35, 0x40, 0x1C, 0x50, 0x43, +0x6A, 0x7B, 0x2B, 0x7B, 0x00, 0x11, 0x12, 0x02, +0x00, 0xB2, 0x1A, 0x43, 0x82, 0x42, 0x03, 0xDA, +0x0E, 0x49, 0x00, 0x20, 0x08, 0x70, 0xF0, 0xBD, +0x00, 0x20, 0x42, 0x00, 0x8B, 0x5E, 0x00, 0x26, +0xDD, 0x00, 0xEB, 0x1A, 0x15, 0x19, 0xAE, 0x5F, +0x40, 0x1C, 0x9B, 0x19, 0xDB, 0x10, 0xC0, 0xB2, +0x8B, 0x52, 0x30, 0x28, 0xF1, 0xD3, 0xF0, 0xBD, +0x5C, 0x01, 0x00, 0x20, 0x40, 0x77, 0x1B, 0x00, +0x93, 0x01, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, +0x00, 0x00, 0x01, 0x20, 0x38, 0x00, 0x00, 0x20, +0x70, 0xB5, 0x21, 0x48, 0x21, 0x49, 0x00, 0x78, +0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x08, 0xD0, +0x02, 0x28, 0x16, 0xD0, 0x05, 0x28, 0x2F, 0xD1, +0x34, 0xE0, 0x01, 0x25, 0x2C, 0x46, 0x1C, 0x48, +0x02, 0xE0, 0x02, 0x25, 0x1B, 0x48, 0x2C, 0x46, +0x08, 0x60, 0x00, 0x22, 0x1A, 0x49, 0x28, 0x46, +0xFE, 0xF7, 0x38, 0xFB, 0x00, 0x28, 0x18, 0xD0, +0x18, 0x48, 0x00, 0x78, 0x00, 0x28, 0x04, 0xD0, +0x13, 0xE0, 0x04, 0x25, 0x2C, 0x46, 0x16, 0x48, +0xEE, 0xE7, 0x16, 0x48, 0x41, 0x6B, 0x03, 0x22, +0x12, 0x05, 0x11, 0x43, 0x41, 0x63, 0x14, 0x48, +0x00, 0xF0, 0x4C, 0xFA, 0x00, 0x28, 0x0B, 0xD1, +0x20, 0x46, 0xFF, 0xF7, 0x27, 0xFF, 0x00, 0x28, +0x06, 0xD1, 0x01, 0x22, 0x21, 0x46, 0x28, 0x46, +0x00, 0xF0, 0x1E, 0xF8, 0x00, 0x28, 0x04, 0xD0, +0x0C, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, +0x00, 0x20, 0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD, +0x92, 0x01, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, +0x1C, 0x02, 0x00, 0x20, 0x10, 0x05, 0x00, 0x20, +0x00, 0x20, 0x00, 0x50, 0x3B, 0x01, 0x00, 0x20, +0x70, 0x05, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, +0x00, 0x00, 0x01, 0x20, 0x30, 0x00, 0x00, 0x20, +0xF7, 0xB5, 0x07, 0x46, 0x14, 0x48, 0x00, 0x25, +0x05, 0x70, 0x78, 0x07, 0x1D, 0xD0, 0x01, 0x21, +0x02, 0x20, 0x00, 0xF0, 0x45, 0xFB, 0x11, 0x4C, +0x60, 0x6B, 0x03, 0x26, 0x36, 0x05, 0x01, 0x21, +0xB0, 0x43, 0x09, 0x05, 0x40, 0x18, 0x60, 0x63, +0x00, 0x20, 0xFF, 0xF7, 0xFD, 0xFC, 0x00, 0x28, +0x0D, 0xD0, 0x01, 0x98, 0xFF, 0xF7, 0xE6, 0xFE, +0x01, 0x22, 0x09, 0x49, 0x38, 0x46, 0xFE, 0xF7, +0xD9, 0xFA, 0x08, 0x48, 0x05, 0x70, 0x08, 0x48, +0x05, 0x70, 0x01, 0x20, 0xFE, 0xBD, 0x60, 0x6B, +0x30, 0x43, 0x60, 0x63, 0x00, 0x20, 0xFE, 0xBD, +0x90, 0x01, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, +0x00, 0x20, 0x00, 0x50, 0x3B, 0x01, 0x00, 0x20, +0x3C, 0x01, 0x00, 0x20, 0x70, 0xB5, 0x10, 0x4C, +0x60, 0x6B, 0x03, 0x25, 0x2D, 0x05, 0x01, 0x26, +0xA8, 0x43, 0x36, 0x05, 0x80, 0x19, 0x60, 0x63, +0x0C, 0x49, 0x02, 0x20, 0x08, 0x5E, 0xFF, 0xF7, +0xCF, 0xFC, 0x00, 0x28, 0x60, 0x6B, 0x09, 0xD0, +0xA8, 0x43, 0x80, 0x19, 0x60, 0x63, 0x08, 0x49, +0x00, 0x20, 0x08, 0x70, 0x07, 0x49, 0x08, 0x70, +0x01, 0x20, 0x70, 0xBD, 0xA8, 0x43, 0x80, 0x19, +0x60, 0x63, 0x00, 0x20, 0x70, 0xBD, 0x00, 0x00, +0x80, 0x10, 0x00, 0x50, 0xCE, 0x00, 0x00, 0x20, +0x3B, 0x01, 0x00, 0x20, 0x3C, 0x01, 0x00, 0x20, +0xF0, 0xB5, 0xB4, 0x48, 0x01, 0x22, 0x23, 0x23, +0x13, 0x24, 0x92, 0x02, 0x5B, 0x01, 0xA4, 0x01, +0x05, 0x46, 0x1C, 0xC5, 0x29, 0x21, 0x49, 0x01, +0xC1, 0x60, 0xAF, 0x48, 0x07, 0x68, 0x38, 0x46, +0x40, 0x30, 0x45, 0x7C, 0x06, 0x7C, 0x2D, 0x04, +0x36, 0x02, 0x35, 0x43, 0x0C, 0x35, 0x15, 0x60, +0x45, 0x7C, 0x06, 0x7C, 0x2D, 0x04, 0x36, 0x02, +0x35, 0x43, 0x0C, 0x35, 0x55, 0x60, 0x01, 0x25, +0x95, 0x60, 0xD5, 0x60, 0x05, 0x7D, 0xBC, 0x46, +0x6E, 0x1E, 0x6D, 0x08, 0x6D, 0x1E, 0xF6, 0x05, +0xED, 0x05, 0xF6, 0x09, 0xED, 0x0D, 0x2E, 0x43, +0x16, 0x61, 0xA0, 0x4D, 0x55, 0x61, 0x03, 0x25, +0x95, 0x61, 0x03, 0x26, 0x36, 0x02, 0x00, 0x25, +0x16, 0x62, 0xD5, 0x61, 0x06, 0x7E, 0xD6, 0x62, +0x06, 0x7E, 0x87, 0x7E, 0xF6, 0x19, 0x16, 0x63, +0x87, 0x7E, 0x06, 0x7E, 0x7F, 0x00, 0xF6, 0x19, +0x56, 0x63, 0x95, 0x63, 0x06, 0x7E, 0xBC, 0x36, +0xD6, 0x63, 0x06, 0x7E, 0x87, 0x7E, 0xF6, 0x19, +0xBC, 0x36, 0x16, 0x64, 0x07, 0x7E, 0x86, 0x7E, +0x95, 0x64, 0x76, 0x00, 0xBC, 0x36, 0xBE, 0x19, +0x56, 0x64, 0x67, 0x46, 0x55, 0x62, 0x8E, 0x4E, +0x50, 0x37, 0x96, 0x62, 0xBC, 0x46, 0xFE, 0x7B, +0xBF, 0x7B, 0x36, 0x02, 0x3E, 0x43, 0xF6, 0x1C, +0xB7, 0x05, 0x8A, 0x4E, 0xBF, 0x0D, 0xBE, 0x19, +0xD6, 0x64, 0x89, 0x4E, 0x16, 0x65, 0x46, 0x7C, +0x0C, 0x3E, 0xF7, 0xB2, 0x05, 0x26, 0x76, 0x02, +0xBE, 0x19, 0x56, 0x65, 0x06, 0x7D, 0x36, 0x02, +0x21, 0x36, 0x96, 0x65, 0xD5, 0x65, 0x42, 0x7C, +0x06, 0x7C, 0x12, 0x04, 0x36, 0x02, 0x32, 0x43, +0x0C, 0x32, 0x1A, 0x60, 0x42, 0x7C, 0x06, 0x7C, +0x12, 0x04, 0x36, 0x02, 0x32, 0x43, 0x0C, 0x32, +0x5A, 0x60, 0x01, 0x22, 0x9A, 0x60, 0xDA, 0x60, +0x42, 0x7D, 0x56, 0x1E, 0x52, 0x08, 0x52, 0x1E, +0xF6, 0x05, 0xD2, 0x05, 0xF7, 0x09, 0xD2, 0x0D, +0x17, 0x43, 0x72, 0x4A, 0x1F, 0x61, 0x5A, 0x61, +0x03, 0x22, 0xDD, 0x61, 0x9A, 0x61, 0x12, 0x02, +0x1A, 0x62, 0x02, 0x7E, 0xDA, 0x62, 0x02, 0x7E, +0xC6, 0x7E, 0x92, 0x19, 0x1A, 0x63, 0xC6, 0x7E, +0x02, 0x7E, 0x76, 0x00, 0x92, 0x19, 0x9D, 0x63, +0x5A, 0x63, 0x02, 0x7E, 0x67, 0x46, 0xBC, 0x32, +0xDA, 0x63, 0x02, 0x7E, 0xC6, 0x7E, 0x92, 0x19, +0xBC, 0x32, 0x1A, 0x64, 0xC6, 0x7E, 0x02, 0x7E, +0x76, 0x00, 0xBC, 0x36, 0x9D, 0x64, 0x92, 0x19, +0x5D, 0x62, 0x5A, 0x64, 0x60, 0x4A, 0x9A, 0x62, +0xFA, 0x7B, 0xBE, 0x7B, 0x12, 0x02, 0x32, 0x43, +0xD2, 0x1C, 0x96, 0x05, 0x5D, 0x4A, 0xB6, 0x0D, +0xB2, 0x18, 0xDA, 0x64, 0x5C, 0x4A, 0x1A, 0x65, +0x42, 0x7C, 0x0C, 0x3A, 0xD6, 0xB2, 0x05, 0x22, +0x52, 0x02, 0xB2, 0x18, 0x5A, 0x65, 0x42, 0x7D, +0xDD, 0x65, 0x12, 0x02, 0x21, 0x32, 0x9A, 0x65, +0xC2, 0x7C, 0x83, 0x7C, 0x12, 0x04, 0x1B, 0x02, +0x1A, 0x43, 0x0C, 0x32, 0x23, 0x46, 0x1A, 0x60, +0xC2, 0x7C, 0x83, 0x7C, 0x12, 0x04, 0x1B, 0x02, +0x1A, 0x43, 0x0C, 0x32, 0x23, 0x46, 0x5A, 0x60, +0x82, 0x7D, 0x52, 0x1E, 0xD3, 0x05, 0xDB, 0x0D, +0x22, 0x46, 0x93, 0x60, 0x82, 0x7D, 0x52, 0x08, +0x52, 0x1E, 0xD2, 0x05, 0x23, 0x46, 0xD2, 0x0D, +0x1D, 0x61, 0xDA, 0x60, 0x47, 0x4A, 0x5A, 0x61, +0x26, 0x46, 0x01, 0x23, 0xF5, 0x61, 0xB3, 0x61, +0x13, 0x12, 0x33, 0x62, 0x43, 0x7E, 0xF3, 0x62, +0x43, 0x7E, 0x06, 0x7F, 0x9B, 0x19, 0x26, 0x46, +0x33, 0x63, 0x06, 0x7F, 0x43, 0x7E, 0x76, 0x00, +0x9B, 0x19, 0x26, 0x46, 0xB5, 0x63, 0xF5, 0x63, +0x35, 0x64, 0x73, 0x63, 0x65, 0x64, 0x38, 0x4B, +0xA5, 0x64, 0x3F, 0x33, 0x65, 0x62, 0xA3, 0x62, +0x36, 0x4B, 0xDB, 0x1C, 0xE3, 0x64, 0x36, 0x4B, +0x23, 0x65, 0xC6, 0x7C, 0x0C, 0x3E, 0xF7, 0xB2, +0x05, 0x26, 0x76, 0x02, 0xBF, 0x19, 0x67, 0x65, +0x87, 0x7D, 0x7F, 0x08, 0x3F, 0x02, 0x21, 0x37, +0xA7, 0x65, 0xE5, 0x65, 0xC4, 0x7C, 0x80, 0x7C, +0x24, 0x04, 0x00, 0x02, 0x04, 0x43, 0x0C, 0x34, +0x27, 0x48, 0x0C, 0x60, 0x00, 0x68, 0x40, 0x30, +0xC4, 0x7C, 0x87, 0x7C, 0x24, 0x04, 0x3F, 0x02, +0x3C, 0x43, 0x0C, 0x34, 0x4C, 0x60, 0xC4, 0x7D, +0x64, 0x1E, 0xE4, 0x05, 0xE4, 0x0D, 0x8C, 0x60, +0xC4, 0x7D, 0x0D, 0x61, 0x64, 0x08, 0x64, 0x1E, +0xE4, 0x05, 0xE4, 0x0D, 0xCC, 0x60, 0x4A, 0x61, +0x01, 0x22, 0xCD, 0x61, 0x8A, 0x61, 0x12, 0x02, +0x0A, 0x62, 0x42, 0x7E, 0xCA, 0x62, 0x42, 0x7E, +0x44, 0x7F, 0x12, 0x19, 0x0A, 0x63, 0x44, 0x7F, +0x42, 0x7E, 0x64, 0x00, 0x12, 0x19, 0x8D, 0x63, +0x4A, 0x63, 0x42, 0x7E, 0xBC, 0x32, 0xCA, 0x63, +0x42, 0x7E, 0x44, 0x7F, 0x12, 0x19, 0xBC, 0x32, +0x0A, 0x64, 0x42, 0x7F, 0x44, 0x7E, 0x52, 0x00, +0xBC, 0x32, 0xA2, 0x18, 0x8D, 0x64, 0x4A, 0x64, +0x01, 0x22, 0x92, 0x04, 0x4A, 0x62, 0x0C, 0x4A, +0x3F, 0x32, 0x8A, 0x62, 0x0B, 0x4A, 0x0B, 0x65, +0xD2, 0x1C, 0xCA, 0x64, 0xC2, 0x7C, 0x0C, 0x3A, +0xD2, 0xB2, 0x92, 0x19, 0x4A, 0x65, 0xC0, 0x7D, +0xCD, 0x65, 0x40, 0x08, 0x00, 0x02, 0x21, 0x30, +0x88, 0x65, 0xF0, 0xBD, 0xD0, 0x05, 0x00, 0x20, +0x98, 0x01, 0x00, 0x20, 0x03, 0x00, 0x03, 0x00, +0x00, 0x3F, 0x3F, 0x3F, 0x00, 0x30, 0xBC, 0x00, +0x00, 0x26, 0x31, 0x00, 0x01, 0x00, 0x01, 0x00, +0x10, 0xB5, 0x00, 0xF0, 0x2F, 0xFA, 0x00, 0xF0, +0x5D, 0xF9, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, +0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x00, 0x20, +0xFF, 0xF7, 0xAC, 0xFC, 0x09, 0x48, 0x01, 0x78, +0x09, 0x48, 0x00, 0x29, 0x04, 0xD0, 0x01, 0x68, +0xFF, 0x22, 0x01, 0x32, 0x11, 0x43, 0x01, 0x60, +0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x91, 0x43, +0x01, 0x60, 0x04, 0x49, 0x02, 0x20, 0x08, 0x72, +0x10, 0xBD, 0x00, 0x00, 0x31, 0x00, 0x00, 0x20, +0x00, 0x10, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20, +0x10, 0xB5, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, +0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x09, 0x48, +0x07, 0x49, 0x41, 0x60, 0x08, 0x49, 0x81, 0x60, +0xFF, 0xF7, 0x52, 0xFE, 0x80, 0x21, 0x07, 0x48, +0xFD, 0xF7, 0x93, 0xFF, 0x80, 0x21, 0x06, 0x48, +0xFD, 0xF7, 0x8F, 0xFF, 0x10, 0xBD, 0x00, 0x00, +0x1F, 0x1F, 0x5F, 0x1F, 0x00, 0x10, 0x00, 0x50, +0x1F, 0x1F, 0x1F, 0x1F, 0xDC, 0x08, 0x00, 0x20, +0x9C, 0x01, 0x00, 0x20, 0x10, 0xB5, 0x0F, 0x49, +0x0A, 0x78, 0x0F, 0x49, 0x00, 0x2A, 0x09, 0x78, +0x04, 0xD0, 0x01, 0x2A, 0x07, 0xD0, 0x02, 0x2A, +0x12, 0xD1, 0x09, 0xE0, 0xC9, 0x07, 0x0F, 0xD0, +0x60, 0x22, 0x0A, 0x49, 0x08, 0xE0, 0x89, 0x07, +0x0A, 0xD5, 0x60, 0x22, 0x08, 0x49, 0x03, 0xE0, +0x49, 0x07, 0x05, 0xD5, 0x60, 0x22, 0x07, 0x49, +0xFD, 0xF7, 0x4E, 0xFF, 0x01, 0x20, 0x10, 0xBD, +0x00, 0x20, 0x10, 0xBD, 0x92, 0x01, 0x00, 0x20, +0x38, 0x00, 0x00, 0x20, 0x1C, 0x02, 0x00, 0x20, +0x10, 0x05, 0x00, 0x20, 0x70, 0x05, 0x00, 0x20, +0x10, 0xB5, 0x13, 0x49, 0x13, 0x4B, 0x09, 0x68, +0x13, 0x4A, 0x40, 0x31, 0x00, 0x28, 0x0F, 0xD0, +0x01, 0x28, 0x12, 0xD0, 0x02, 0x28, 0x15, 0xD0, +0x03, 0x28, 0x08, 0xD1, 0x48, 0x7E, 0x84, 0x1E, +0x1C, 0x80, 0x49, 0x7F, 0x4B, 0x00, 0xC9, 0x18, +0x89, 0x1C, 0x40, 0x18, 0x10, 0x80, 0x10, 0xBD, +0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, 0x89, 0x7E, +0xF4, 0xE7, 0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, +0xC9, 0x7E, 0xEF, 0xE7, 0x48, 0x7E, 0x84, 0x1E, +0x1C, 0x80, 0x09, 0x7F, 0xEA, 0xE7, 0x00, 0x00, +0x98, 0x01, 0x00, 0x20, 0x3A, 0x00, 0x00, 0x20, +0x3C, 0x00, 0x00, 0x20, 0xF8, 0xB5, 0x05, 0x46, +0x20, 0x48, 0x00, 0x21, 0x01, 0x60, 0x41, 0x60, +0x1F, 0x49, 0x01, 0x20, 0x08, 0x70, 0x1F, 0x48, +0x1F, 0x4C, 0x00, 0x78, 0x00, 0x28, 0x06, 0xD0, +0x2D, 0x26, 0x09, 0x27, 0x01, 0x28, 0x0D, 0xD0, +0x02, 0x28, 0x29, 0xD1, 0x17, 0xE0, 0x1B, 0xA0, +0x00, 0xF0, 0x5B, 0xFE, 0x5A, 0x20, 0x00, 0x2D, +0x20, 0x70, 0x01, 0xD0, 0x00, 0x20, 0x0B, 0xE0, +0x01, 0x20, 0x09, 0xE0, 0x18, 0xA0, 0x00, 0xF0, +0x50, 0xFE, 0x00, 0x2D, 0x02, 0xD0, 0x26, 0x70, +0x02, 0x20, 0x01, 0xE0, 0x27, 0x70, 0x03, 0x20, +0x00, 0xF0, 0x0C, 0xF9, 0x10, 0xE0, 0x14, 0xA0, +0x00, 0xF0, 0x43, 0xFE, 0x00, 0x2D, 0x02, 0xD0, +0x26, 0x70, 0x02, 0x20, 0x01, 0xE0, 0x27, 0x70, +0x03, 0x20, 0x00, 0xF0, 0xFF, 0xF8, 0x11, 0x48, +0x81, 0x6A, 0x11, 0x4A, 0x11, 0x40, 0x81, 0x62, +0x05, 0x20, 0x10, 0x49, 0x00, 0x02, 0x08, 0x60, +0xF8, 0xBD, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, +0x31, 0x00, 0x00, 0x20, 0x92, 0x01, 0x00, 0x20, +0x94, 0x01, 0x00, 0x20, 0x41, 0x63, 0x74, 0x69, +0x76, 0x65, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0x49, 0x64, 0x6C, 0x65, 0x0D, 0x0A, 0x00, 0x00, +0x47, 0x65, 0x73, 0x74, 0x75, 0x72, 0x65, 0x0D, +0x0A, 0x00, 0x00, 0x00, 0xC0, 0x11, 0x00, 0x50, +0xFF, 0xFF, 0x00, 0xF8, 0x00, 0x10, 0x00, 0x50, +0x70, 0xB5, 0x19, 0x4D, 0x19, 0x4C, 0x28, 0x70, +0x02, 0x46, 0x21, 0x78, 0x18, 0xA0, 0x00, 0xF0, +0x08, 0xFE, 0x1B, 0x49, 0x01, 0x20, 0x08, 0x70, +0x00, 0xF0, 0x20, 0xF9, 0x00, 0xF0, 0x4E, 0xF8, +0x01, 0x20, 0xFD, 0xF7, 0x45, 0xFF, 0xFF, 0xF7, +0xF5, 0xFB, 0x01, 0x20, 0xFF, 0xF7, 0x86, 0xFF, +0x00, 0xF0, 0x42, 0xFC, 0xFE, 0xF7, 0x5C, 0xFE, +0x28, 0x78, 0x20, 0x70, 0x05, 0x28, 0x0F, 0xD0, +0xFF, 0xF7, 0xAA, 0xFC, 0x00, 0x28, 0x0C, 0xD0, +0x0E, 0x48, 0x00, 0x7A, 0x00, 0x28, 0x03, 0xD1, +0x00, 0x21, 0x02, 0x20, 0x00, 0xF0, 0x48, 0xF8, +0x0B, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, +0x00, 0x20, 0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD, +0x92, 0x01, 0x00, 0x20, 0x0D, 0x08, 0x00, 0x20, +0x53, 0x4D, 0x3D, 0x5B, 0x25, 0x64, 0x3A, 0x25, +0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0x91, 0x01, 0x00, 0x20, 0x20, 0x00, 0x00, 0x20, +0x30, 0x00, 0x00, 0x20, 0x08, 0x49, 0x02, 0x20, +0x08, 0x72, 0x08, 0x48, 0x01, 0x78, 0x08, 0x48, +0x00, 0x29, 0x01, 0x68, 0x04, 0xD0, 0x01, 0x22, +0x92, 0x02, 0x91, 0x43, 0x01, 0x60, 0x70, 0x47, +0x01, 0x22, 0x11, 0x43, 0xFA, 0xE7, 0x00, 0x00, +0x20, 0x00, 0x00, 0x20, 0x31, 0x00, 0x00, 0x20, +0x00, 0x10, 0x00, 0x50, 0x10, 0xB5, 0x08, 0x48, +0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x11, 0x43, +0x01, 0x60, 0x00, 0x68, 0x05, 0x4C, 0xC0, 0x07, +0x03, 0xD0, 0x02, 0x20, 0x20, 0x72, 0x00, 0xF0, +0xC1, 0xF8, 0x00, 0x20, 0x20, 0x72, 0x10, 0xBD, +0x00, 0x10, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20, +0x70, 0xB5, 0x05, 0x00, 0x0E, 0x46, 0x16, 0xD0, +0xFF, 0xF7, 0x82, 0xFE, 0x00, 0x24, 0x6D, 0x1E, +0x07, 0xE0, 0x00, 0xF0, 0xAF, 0xF8, 0xFF, 0xF7, +0x57, 0xFB, 0xFF, 0xF7, 0xC3, 0xFF, 0x64, 0x1C, +0xE4, 0xB2, 0xAC, 0x42, 0xF5, 0xDB, 0x00, 0xF0, +0xA5, 0xF8, 0xFF, 0xF7, 0x4D, 0xFB, 0x00, 0x2E, +0x02, 0xD0, 0xFF, 0xF7, 0xCF, 0xFF, 0x70, 0xBD, +0xFF, 0xF7, 0xB4, 0xFF, 0x70, 0xBD, 0x00, 0x00, +0x30, 0xB5, 0x01, 0x24, 0x1C, 0x4A, 0xA4, 0x07, +0x23, 0x13, 0x1B, 0x49, 0x40, 0x32, 0x00, 0x28, +0x21, 0xD0, 0x01, 0x28, 0x1F, 0xD0, 0x02, 0x28, +0x01, 0xD0, 0x03, 0x28, 0x1A, 0xD1, 0x25, 0x68, +0x1D, 0x43, 0x25, 0x60, 0x62, 0x23, 0x93, 0x61, +0xD3, 0x61, 0x14, 0x4B, 0xCB, 0x63, 0x14, 0x49, +0x09, 0x68, 0x40, 0x31, 0x02, 0x28, 0x48, 0x7E, +0x17, 0xD0, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D, +0x10, 0x60, 0x4B, 0x7E, 0x48, 0x7F, 0x41, 0x00, +0x40, 0x18, 0x80, 0x1C, 0x18, 0x18, 0x40, 0x05, +0x40, 0x0D, 0x90, 0x60, 0x30, 0xBD, 0x20, 0x68, +0x98, 0x43, 0x20, 0x60, 0x01, 0x20, 0x90, 0x61, +0xD0, 0x61, 0x00, 0x20, 0xC8, 0x63, 0x10, 0x60, +0xF3, 0xE7, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D, +0x10, 0x60, 0x4B, 0x7E, 0x08, 0x7F, 0xE6, 0xE7, +0x80, 0x11, 0x00, 0x50, 0x01, 0x00, 0x01, 0x00, +0x98, 0x01, 0x00, 0x20, 0x70, 0xB5, 0x04, 0x46, +0x81, 0x00, 0x25, 0x48, 0x41, 0x58, 0x25, 0x48, +0x0A, 0x68, 0x42, 0x60, 0x4A, 0x68, 0x82, 0x60, +0x8A, 0x68, 0x02, 0x61, 0xCA, 0x68, 0x42, 0x61, +0x21, 0x4D, 0x08, 0x69, 0x28, 0x62, 0x1F, 0x4A, +0x48, 0x69, 0xC0, 0x32, 0x90, 0x61, 0x88, 0x69, +0xD0, 0x61, 0x48, 0x6A, 0x10, 0x62, 0x88, 0x6A, +0x50, 0x62, 0x1A, 0x48, 0xCB, 0x69, 0x40, 0x38, +0xC3, 0x60, 0x0B, 0x6A, 0x03, 0x61, 0xCB, 0x6A, +0x93, 0x62, 0x0B, 0x6B, 0xD3, 0x63, 0x16, 0x4B, +0x4E, 0x6B, 0x80, 0x3B, 0x1E, 0x61, 0x8E, 0x6B, +0x5E, 0x62, 0xCE, 0x6B, 0xD6, 0x62, 0x0A, 0x6C, +0x1A, 0x60, 0x4A, 0x6C, 0x5A, 0x61, 0x8A, 0x6C, +0x9A, 0x62, 0xCA, 0x6C, 0xAA, 0x62, 0x0D, 0x4B, +0x0A, 0x6D, 0x40, 0x33, 0x5A, 0x63, 0x0B, 0x4B, +0x4A, 0x6D, 0x80, 0x33, 0x9A, 0x60, 0x0B, 0x4A, +0x8B, 0x6D, 0x13, 0x60, 0xC9, 0x6D, 0x51, 0x60, +0xC2, 0x68, 0x09, 0x49, 0x0A, 0x60, 0x00, 0x69, +0x48, 0x60, 0x20, 0x46, 0xFF, 0xF7, 0x50, 0xFE, +0x20, 0x46, 0xFF, 0xF7, 0x71, 0xFF, 0x70, 0xBD, +0xD0, 0x05, 0x00, 0x20, 0x40, 0x10, 0x00, 0x50, +0xC0, 0x11, 0x00, 0x50, 0x00, 0x19, 0x00, 0x50, +0x20, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x20, +0x40, 0x04, 0x00, 0x23, 0x0C, 0x4A, 0x0D, 0x49, +0x0D, 0x4C, 0x05, 0xE0, 0x0D, 0x78, 0xED, 0x07, +0x01, 0xD0, 0x13, 0x72, 0x09, 0xE0, 0x40, 0x1E, +0x15, 0x7A, 0x02, 0x2D, 0x02, 0xD0, 0x25, 0x68, +0xED, 0x07, 0x02, 0xD0, 0x00, 0x28, 0xF1, 0xD1, +0x01, 0xE0, 0x00, 0x28, 0x03, 0xD1, 0x13, 0x72, +0x04, 0xA0, 0x00, 0xF0, 0xC6, 0xFC, 0x70, 0xBD, +0x20, 0x00, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20, +0x00, 0x10, 0x00, 0x50, 0x57, 0x53, 0x46, 0x20, +0x54, 0x4F, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0xF8, 0xB5, 0x1D, 0x48, 0x01, 0x25, 0x01, 0x68, +0x00, 0x24, 0x02, 0x22, 0x91, 0x43, 0x1B, 0x4F, +0x1B, 0x4E, 0x01, 0x60, 0x1B, 0x48, 0x00, 0x68, +0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x14, 0xD0, +0x19, 0x48, 0x61, 0x00, 0x42, 0x5E, 0x04, 0x20, +0x38, 0x5E, 0x82, 0x42, 0x09, 0xDA, 0x21, 0x46, +0x16, 0xA0, 0x00, 0xF0, 0x9E, 0xFC, 0x30, 0x5D, +0x02, 0x21, 0x08, 0x43, 0x30, 0x55, 0x00, 0x25, +0x03, 0xE0, 0x30, 0x5D, 0xFD, 0x21, 0x08, 0x40, +0x30, 0x55, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C, +0xE0, 0xD3, 0x15, 0x49, 0x01, 0x2D, 0x79, 0x5E, +0x09, 0xD0, 0x14, 0xA0, 0x00, 0xF0, 0x89, 0xFC, +0x05, 0x49, 0x02, 0x22, 0x08, 0x68, 0x10, 0x43, +0x08, 0x60, 0x28, 0x46, 0xF8, 0xBD, 0x16, 0xA0, +0x00, 0xF0, 0x7F, 0xFC, 0xF9, 0xE7, 0x00, 0x00, +0x0C, 0x05, 0x00, 0x20, 0x84, 0x06, 0x00, 0x20, +0xD8, 0x04, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, +0xE4, 0x02, 0x00, 0x20, 0x53, 0x68, 0x6F, 0x72, +0x74, 0x20, 0x4E, 0x47, 0x20, 0x44, 0x61, 0x74, +0x61, 0x5B, 0x25, 0x64, 0x5D, 0x20, 0x3D, 0x20, +0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0x04, 0x00, 0x00, 0x00, 0x53, 0x68, 0x6F, 0x72, +0x74, 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x54, +0x48, 0x44, 0x3A, 0x25, 0x64, 0x29, 0x20, 0x5B, +0x4E, 0x47, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, +0x53, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x54, 0x65, +0x73, 0x74, 0x28, 0x54, 0x48, 0x44, 0x3A, 0x25, +0x64, 0x29, 0x20, 0x5B, 0x50, 0x61, 0x73, 0x73, +0x5D, 0x0D, 0x0A, 0x00, 0x70, 0xB5, 0x0F, 0x48, +0x01, 0x25, 0xC4, 0x7F, 0x0E, 0x48, 0x80, 0x88, +0x00, 0x07, 0x01, 0xD4, 0x01, 0x20, 0x70, 0xBD, +0xFE, 0xF7, 0xB6, 0xFB, 0xFE, 0xF7, 0x1E, 0xFA, +0xFF, 0xF7, 0x7E, 0xFF, 0x00, 0x28, 0x0A, 0xD1, +0x00, 0x25, 0x00, 0x2C, 0x07, 0xD0, 0x64, 0x1E, +0xE4, 0xB2, 0x21, 0x46, 0x05, 0xA0, 0x00, 0xF0, +0x2C, 0xFC, 0x01, 0x25, 0xEE, 0xE7, 0x28, 0x46, +0x70, 0xBD, 0x00, 0x00, 0xA8, 0x06, 0x00, 0x20, +0x74, 0x06, 0x00, 0x20, 0x53, 0x68, 0x6F, 0x72, +0x74, 0x20, 0x52, 0x65, 0x74, 0x72, 0x79, 0x20, +0x28, 0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, +0xF8, 0xB5, 0x1B, 0x4E, 0x05, 0x46, 0x0C, 0x46, +0x80, 0x21, 0x30, 0x46, 0xFD, 0xF7, 0xC9, 0xFC, +0x00, 0x2C, 0x04, 0xD0, 0x31, 0x46, 0x28, 0x46, +0x00, 0xF0, 0x42, 0xFA, 0x04, 0xE0, 0x80, 0x22, +0x29, 0x46, 0x30, 0x46, 0xFD, 0xF7, 0xA4, 0xFC, +0x29, 0x46, 0x12, 0xA0, 0x00, 0xF0, 0x01, 0xFC, +0x00, 0x25, 0x14, 0x4F, 0x13, 0xE0, 0x00, 0x24, +0x08, 0xE0, 0x68, 0x43, 0x00, 0x19, 0x40, 0x00, +0x31, 0x5E, 0x11, 0xA0, 0x00, 0xF0, 0xF5, 0xFB, +0x64, 0x1C, 0xE4, 0xB2, 0x38, 0x68, 0x00, 0x7E, +0xA0, 0x42, 0xF2, 0xD8, 0x0E, 0xA0, 0x00, 0xF0, +0xEC, 0xFB, 0x6D, 0x1C, 0xED, 0xB2, 0x38, 0x68, +0x40, 0x7E, 0xA8, 0x42, 0xE7, 0xD8, 0x0A, 0xA0, +0x00, 0xF0, 0xE3, 0xFB, 0xF8, 0xBD, 0x00, 0x00, +0x4C, 0x00, 0x00, 0x20, 0x49, 0x6D, 0x61, 0x67, +0x65, 0x3A, 0x30, 0x78, 0x25, 0x78, 0x0D, 0x0A, +0x00, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20, +0x25, 0x36, 0x64, 0x2C, 0x00, 0x00, 0x00, 0x00, +0x0D, 0x0A, 0x00, 0x00, 0x08, 0x49, 0x8A, 0x78, +0x52, 0x1E, 0x8A, 0x70, 0x4B, 0x78, 0x0A, 0x1D, +0xD2, 0x5C, 0x02, 0x70, 0x48, 0x78, 0x40, 0x1C, +0x48, 0x70, 0x48, 0x78, 0x10, 0x28, 0x01, 0xD1, +0x00, 0x20, 0x48, 0x70, 0x70, 0x47, 0x00, 0x00, +0xE0, 0x05, 0x00, 0x20, 0xF8, 0xB5, 0x01, 0x27, +0x05, 0x46, 0xBF, 0x07, 0x38, 0x68, 0x08, 0x21, +0x08, 0x43, 0x38, 0x60, 0x01, 0x23, 0x15, 0x48, +0x80, 0x22, 0x02, 0x60, 0x14, 0x48, 0x00, 0x21, +0x81, 0x70, 0x01, 0x70, 0x41, 0x70, 0xC3, 0x70, +0x12, 0x4B, 0x98, 0x68, 0x02, 0x21, 0x88, 0x43, +0x98, 0x60, 0xF8, 0x68, 0x10, 0x43, 0xF8, 0x60, +0x0F, 0x4C, 0x61, 0x61, 0x0F, 0x48, 0x00, 0x68, +0xE9, 0x00, 0x46, 0x06, 0x0E, 0x48, 0xFD, 0xF7, +0x0D, 0xFC, 0xE0, 0x60, 0x30, 0x20, 0xA0, 0x60, +0x06, 0x49, 0x80, 0x20, 0x80, 0x39, 0x08, 0x60, +0x08, 0x20, 0x78, 0x60, 0xE0, 0x68, 0x68, 0x43, +0xC1, 0x00, 0x08, 0xA0, 0x00, 0xF0, 0x89, 0xFB, +0xF8, 0xBD, 0x00, 0x00, 0x80, 0xE1, 0x00, 0xE0, +0xE0, 0x05, 0x00, 0x20, 0x40, 0x09, 0x00, 0x50, +0x00, 0x02, 0x00, 0x50, 0x00, 0x11, 0x00, 0x50, +0x00, 0x36, 0x6E, 0x01, 0x55, 0x41, 0x52, 0x54, +0x28, 0x25, 0x64, 0x29, 0x21, 0x0D, 0x0A, 0x00, +0x70, 0xB5, 0x14, 0x4A, 0x91, 0x78, 0x14, 0x4C, +0x0E, 0x29, 0x02, 0xD3, 0x61, 0x68, 0x49, 0x07, +0xFC, 0xD5, 0x61, 0x69, 0x02, 0x25, 0xA9, 0x43, +0x61, 0x61, 0x91, 0x78, 0x00, 0x26, 0x10, 0x29, +0x0D, 0xD2, 0x0C, 0x49, 0x13, 0x78, 0x09, 0x1D, +0xC8, 0x54, 0x10, 0x78, 0x40, 0x1C, 0x10, 0x70, +0x10, 0x78, 0x10, 0x28, 0x00, 0xD1, 0x16, 0x70, +0x90, 0x78, 0x40, 0x1C, 0x90, 0x70, 0xD0, 0x78, +0x00, 0x28, 0x03, 0xD0, 0xD6, 0x70, 0x20, 0x46, +0xFF, 0xF7, 0x80, 0xFF, 0x60, 0x69, 0x28, 0x43, +0x60, 0x61, 0x70, 0xBD, 0xE0, 0x05, 0x00, 0x20, +0x00, 0x02, 0x00, 0x50, 0xFE, 0xB5, 0x2F, 0x48, +0x10, 0x26, 0x12, 0x27, 0x86, 0x5F, 0xC7, 0x5F, +0x2D, 0x48, 0x01, 0x90, 0x2D, 0x48, 0x01, 0x25, +0x01, 0x68, 0x2A, 0x04, 0x91, 0x43, 0x00, 0x24, +0x01, 0x60, 0x2B, 0x48, 0x00, 0x68, 0x00, 0x19, +0xC0, 0x7E, 0x41, 0x28, 0x26, 0xD0, 0x01, 0x99, +0x60, 0x00, 0x09, 0x5A, 0x4A, 0x05, 0x52, 0x0D, +0x26, 0x49, 0x53, 0x05, 0x0A, 0x52, 0x03, 0xD5, +0x89, 0x23, 0x9B, 0x00, 0xD2, 0x1A, 0x0A, 0x52, +0x23, 0x4A, 0x0B, 0x5A, 0x12, 0x5A, 0xD0, 0x1A, +0x00, 0xB2, 0xB0, 0x42, 0x01, 0xDC, 0xB8, 0x42, +0x0B, 0xDA, 0x00, 0x90, 0x21, 0x46, 0x1F, 0xA0, +0x00, 0xF0, 0x17, 0xFB, 0x23, 0x48, 0x10, 0x22, +0x01, 0x5D, 0x11, 0x43, 0x01, 0x55, 0x00, 0x25, +0x04, 0xE0, 0x20, 0x48, 0xEF, 0x22, 0x01, 0x5D, +0x11, 0x40, 0x01, 0x55, 0x64, 0x1C, 0xE4, 0xB2, +0x30, 0x2C, 0xCE, 0xD3, 0x01, 0x2D, 0x18, 0xD0, +0x10, 0x4A, 0x01, 0x20, 0x11, 0x68, 0x00, 0x04, +0x01, 0x43, 0x19, 0xA0, 0x11, 0x60, 0x00, 0xF0, +0xFC, 0xFA, 0x1C, 0xA0, 0x00, 0xF0, 0xF9, 0xFA, +0x01, 0x21, 0x0C, 0x48, 0xFF, 0xF7, 0xDC, 0xFE, +0x1A, 0xA0, 0x00, 0xF0, 0xF2, 0xFA, 0x01, 0x21, +0x09, 0x48, 0xFF, 0xF7, 0xD5, 0xFE, 0x28, 0x46, +0xFE, 0xBD, 0x19, 0xA0, 0x00, 0xF0, 0xE9, 0xFA, +0xEB, 0xE7, 0x00, 0x00, 0x84, 0x06, 0x00, 0x20, +0x00, 0x20, 0x00, 0x50, 0x0C, 0x05, 0x00, 0x20, +0x98, 0x01, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20, +0xEC, 0x06, 0x00, 0x20, 0x55, 0x43, 0x5B, 0x25, +0x64, 0x5D, 0x20, 0x4E, 0x47, 0x21, 0x20, 0x25, +0x64, 0x2D, 0x25, 0x64, 0x3D, 0x25, 0x64, 0x0D, +0x0A, 0x00, 0x00, 0x00, 0xD8, 0x04, 0x00, 0x20, +0x55, 0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, +0x5B, 0x4E, 0x47, 0x5D, 0x3A, 0x30, 0x78, 0x25, +0x78, 0x0D, 0x0A, 0x00, 0x55, 0x43, 0x0D, 0x0A, +0x00, 0x00, 0x00, 0x00, 0x47, 0x6F, 0x6C, 0x64, +0x65, 0x6E, 0x20, 0x55, 0x43, 0x0D, 0x0A, 0x00, +0x55, 0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, +0x5B, 0x50, 0x61, 0x73, 0x73, 0x5D, 0x0D, 0x0A, +0x00, 0x00, 0x00, 0x00, 0x70, 0xB5, 0x0E, 0x48, +0x01, 0x25, 0x44, 0x79, 0x0D, 0x48, 0x80, 0x88, +0xC0, 0x06, 0x01, 0xD4, 0x01, 0x20, 0x70, 0xBD, +0xFF, 0xF7, 0x5C, 0xFF, 0x00, 0x28, 0x0C, 0xD1, +0x00, 0x25, 0x00, 0x2C, 0x09, 0xD0, 0x64, 0x1E, +0xE4, 0xB2, 0xFE, 0xF7, 0x03, 0xF9, 0x21, 0x46, +0x05, 0xA0, 0x00, 0xF0, 0x96, 0xFA, 0x01, 0x25, +0xEE, 0xE7, 0x28, 0x46, 0x70, 0xBD, 0x00, 0x00, +0xC8, 0x06, 0x00, 0x20, 0x74, 0x06, 0x00, 0x20, +0x55, 0x43, 0x20, 0x4E, 0x47, 0x20, 0x52, 0x65, +0x74, 0x72, 0x79, 0x20, 0x28, 0x25, 0x64, 0x29, +0x0D, 0x0A, 0x00, 0x00, 0xF8, 0xB5, 0x3D, 0x4F, +0x01, 0x24, 0x38, 0x7B, 0x3C, 0x49, 0x0A, 0x78, +0x3C, 0x4E, 0x3D, 0x4D, 0x90, 0x42, 0x05, 0xD1, +0x28, 0x78, 0x81, 0x28, 0x02, 0xD0, 0x30, 0x78, +0xC0, 0x07, 0x63, 0xD0, 0x00, 0x20, 0x30, 0x70, +0x35, 0x48, 0x00, 0x78, 0x07, 0x28, 0x0C, 0xD3, +0x36, 0x48, 0x00, 0x68, 0x40, 0x05, 0x40, 0x0F, +0x38, 0x73, 0x01, 0x20, 0xFD, 0xF7, 0xD4, 0xFC, +0x00, 0x20, 0x30, 0x70, 0x38, 0x7B, 0x02, 0x28, +0x13, 0xD0, 0x38, 0x7B, 0x2C, 0x4F, 0x38, 0x70, +0x81, 0x20, 0x28, 0x70, 0x2E, 0x4A, 0x01, 0x21, +0x10, 0x88, 0x09, 0x03, 0x08, 0x43, 0x10, 0x80, +0x38, 0x78, 0x03, 0x00, 0xFD, 0xF7, 0x6E, 0xFB, +0x07, 0x25, 0x27, 0x37, 0x3D, 0x0B, 0x18, 0x0B, +0x3D, 0x00, 0x83, 0x20, 0x28, 0x70, 0x05, 0x20, +0xFF, 0xF7, 0x36, 0xFC, 0xF8, 0xBD, 0x25, 0xA0, +0x00, 0xF0, 0x43, 0xFA, 0x01, 0x20, 0xFE, 0xF7, +0x1B, 0xFE, 0x00, 0x20, 0xFF, 0xF7, 0x2C, 0xFC, +0x00, 0x28, 0x1E, 0xD0, 0x01, 0x20, 0x1A, 0xE0, +0x20, 0xA0, 0x00, 0xF0, 0x36, 0xFA, 0x01, 0x20, +0xFE, 0xF7, 0x0E, 0xFE, 0x01, 0x20, 0xFF, 0xF7, +0x1F, 0xFC, 0x00, 0x28, 0x11, 0xD0, 0x04, 0x20, +0x0D, 0xE0, 0x1C, 0xA0, 0x00, 0xE0, 0x1D, 0xA0, +0x00, 0xF0, 0x27, 0xFA, 0x00, 0x20, 0xFE, 0xF7, +0xFF, 0xFD, 0x02, 0x20, 0xFF, 0xF7, 0x10, 0xFC, +0x00, 0x28, 0x02, 0xD0, 0x02, 0x20, 0x28, 0x70, +0x07, 0xE0, 0x00, 0x24, 0x05, 0xE0, 0x83, 0x20, +0x28, 0x70, 0x05, 0x20, 0xFF, 0xF7, 0x04, 0xFC, +0x04, 0x46, 0x2A, 0x78, 0x39, 0x78, 0x13, 0xA0, +0x00, 0xF0, 0x0F, 0xFA, 0x30, 0x78, 0xC0, 0x07, +0x02, 0xD0, 0x81, 0x20, 0x28, 0x70, 0x00, 0x24, +0x20, 0x46, 0xF8, 0xBD, 0x20, 0x00, 0x00, 0x20, +0x39, 0x01, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20, +0x20, 0x08, 0x00, 0x20, 0x00, 0x11, 0x00, 0x50, +0x4A, 0x01, 0x00, 0x20, 0x44, 0x41, 0x0D, 0x0A, +0x00, 0x00, 0x00, 0x00, 0x44, 0x49, 0x0D, 0x0A, +0x00, 0x00, 0x00, 0x00, 0x44, 0x53, 0x54, 0x42, +0x0D, 0x0A, 0x00, 0x00, 0x44, 0x47, 0x0D, 0x0A, +0x00, 0x00, 0x00, 0x00, 0x44, 0x53, 0x50, 0x3D, +0x25, 0x64, 0x2C, 0x50, 0x57, 0x52, 0x3D, 0x25, +0x64, 0x20, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0x70, 0xB5, 0x05, 0x46, 0x01, 0x24, 0xFE, 0xF7, +0x7B, 0xFA, 0x00, 0x2D, 0x09, 0xD0, 0xF0, 0x20, +0xFD, 0xF7, 0xC2, 0xFA, 0xFE, 0xF7, 0x42, 0xFB, +0xFE, 0xF7, 0xD8, 0xFA, 0x00, 0x20, 0xFE, 0xF7, +0xAB, 0xFD, 0xFE, 0xF7, 0xDF, 0xFD, 0xFF, 0xF7, +0xDF, 0xFA, 0x20, 0x46, 0x70, 0xBD, 0x00, 0x00, +0x30, 0xB5, 0x08, 0x4A, 0x14, 0x68, 0x1B, 0x34, +0x00, 0x22, 0xA3, 0x5C, 0x41, 0x2B, 0x03, 0xD0, +0x55, 0x00, 0x45, 0x5B, 0x5B, 0x00, 0xCD, 0x52, +0x52, 0x1C, 0xD2, 0xB2, 0x30, 0x2A, 0xF4, 0xD3, +0x30, 0xBD, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20, +0x70, 0xB5, 0x17, 0x4D, 0x16, 0x20, 0x28, 0x70, +0x68, 0x78, 0x29, 0x46, 0x00, 0x24, 0x14, 0x31, +0x02, 0x28, 0x0C, 0xD8, 0x00, 0x28, 0x05, 0xD1, +0xA8, 0x78, 0x00, 0x28, 0x02, 0xD0, 0x08, 0x46, +0x1C, 0x30, 0x04, 0x70, 0x48, 0x88, 0x00, 0x28, +0x01, 0xD1, 0x03, 0x20, 0x68, 0x71, 0xAC, 0x70, +0x2C, 0x81, 0xEC, 0x80, 0xAC, 0x81, 0x6C, 0x81, +0x4C, 0x80, 0x8C, 0x80, 0x0C, 0x72, 0x09, 0x48, +0x44, 0x70, 0x84, 0x70, 0x04, 0x70, 0x06, 0x48, +0xA0, 0x21, 0x30, 0x30, 0xFD, 0xF7, 0x45, 0xFA, +0xEC, 0x70, 0x5A, 0x20, 0xE8, 0x81, 0x2C, 0x71, +0x2C, 0x61, 0x00, 0xF0, 0x09, 0xFA, 0x70, 0xBD, +0x0C, 0x08, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, +0x06, 0x4A, 0x07, 0x4B, 0x00, 0x21, 0x00, 0x20, +0x40, 0x1C, 0x90, 0x42, 0xFC, 0xDB, 0x49, 0x1C, +0x99, 0x42, 0xF8, 0xDB, 0x03, 0x48, 0x01, 0x21, +0x01, 0x60, 0x70, 0x47, 0x10, 0x27, 0x00, 0x00, +0xB8, 0x0B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, +0x70, 0xB5, 0x7B, 0x24, 0x21, 0x48, 0x24, 0x02, +0x04, 0x60, 0xF7, 0x20, 0xC0, 0x01, 0x20, 0x4A, +0xC1, 0x7C, 0x11, 0x70, 0x1F, 0x4A, 0x01, 0x7D, +0x11, 0x70, 0x1F, 0x4A, 0x41, 0x7D, 0x11, 0x70, +0x1E, 0x49, 0x80, 0x7D, 0x08, 0x70, 0x60, 0x7C, +0x21, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, +0x1B, 0x49, 0x05, 0x46, 0x08, 0x80, 0xE0, 0x7C, +0xA1, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, +0x18, 0x49, 0x08, 0x80, 0x20, 0x7E, 0x61, 0x7E, +0x48, 0x43, 0x17, 0x49, 0x08, 0x80, 0x17, 0x48, +0xFD, 0xF7, 0x07, 0xFA, 0x10, 0x21, 0xC8, 0x41, +0x15, 0x49, 0x08, 0x60, 0x04, 0x22, 0x21, 0x1D, +0x14, 0x48, 0xFD, 0xF7, 0xD9, 0xF9, 0x11, 0x49, +0x06, 0x22, 0x89, 0x1F, 0x12, 0x48, 0xFD, 0xF7, +0xD3, 0xF9, 0x60, 0x7D, 0x22, 0x7D, 0x01, 0x02, +0x11, 0x43, 0x28, 0x46, 0x0A, 0x22, 0x50, 0x43, +0xFD, 0xF7, 0xB6, 0xF9, 0x0D, 0x49, 0x08, 0x80, +0x70, 0xBD, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20, +0x0C, 0x00, 0x00, 0x20, 0x0D, 0x00, 0x00, 0x20, +0x0E, 0x00, 0x00, 0x20, 0x0F, 0x00, 0x00, 0x20, +0x1C, 0x00, 0x00, 0x20, 0x1E, 0x00, 0x00, 0x20, +0x18, 0x00, 0x00, 0x20, 0x10, 0x7B, 0x00, 0x00, +0xD8, 0x06, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20, +0xE4, 0x06, 0x00, 0x20, 0x1A, 0x00, 0x00, 0x20, +0x00, 0x20, 0x0E, 0x49, 0x08, 0x70, 0x17, 0xE0, +0x0C, 0x48, 0x00, 0x78, 0x00, 0x28, 0x02, 0xD0, +0x07, 0x28, 0x0F, 0xD1, 0x0B, 0xE0, 0x00, 0xF0, +0x13, 0xF8, 0x01, 0x28, 0x03, 0xD1, 0x07, 0x20, +0x06, 0x49, 0x08, 0x70, 0x02, 0xE0, 0x03, 0x20, +0x04, 0x49, 0x08, 0x70, 0x03, 0xE0, 0xFD, 0xF7, +0x5B, 0xFA, 0x00, 0xE0, 0x00, 0xBF, 0x00, 0xBF, +0xE6, 0xE7, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x20, +0x10, 0xB5, 0x00, 0x24, 0xFD, 0xF7, 0xBC, 0xFC, +0x00, 0xF0, 0x9A, 0xF9, 0x01, 0x20, 0xFF, 0xF7, +0x03, 0xFF, 0x04, 0x46, 0x00, 0x2C, 0x04, 0xD1, +0x05, 0xA0, 0x00, 0xF0, 0xDE, 0xF8, 0x00, 0x20, +0x10, 0xBD, 0x06, 0x49, 0x06, 0x48, 0x81, 0x70, +0x09, 0x0A, 0xC1, 0x70, 0x01, 0x20, 0xF7, 0xE7, +0x49, 0x4E, 0x49, 0x54, 0x20, 0x4E, 0x47, 0x0D, +0x0A, 0x00, 0x00, 0x00, 0xAA, 0x55, 0x00, 0x00, +0x88, 0x02, 0x00, 0x20, 0xF7, 0xB5, 0x00, 0x25, +0x84, 0xB0, 0x0C, 0x46, 0x16, 0x46, 0x61, 0x27, +0x5C, 0xE0, 0x25, 0x28, 0x54, 0xD1, 0x64, 0x1C, +0x00, 0x22, 0x20, 0x78, 0x13, 0x46, 0x00, 0x28, +0x57, 0xD0, 0x25, 0x28, 0x4C, 0xD0, 0x2D, 0x28, +0x01, 0xD1, 0x64, 0x1C, 0x01, 0x23, 0x02, 0x20, +0x21, 0x78, 0x30, 0x29, 0x07, 0xD1, 0x64, 0x1C, +0x03, 0x43, 0xF9, 0xE7, 0x0A, 0x21, 0x4A, 0x43, +0x30, 0x3A, 0x82, 0x18, 0x64, 0x1C, 0x20, 0x78, +0x01, 0x46, 0x30, 0x39, 0x09, 0x29, 0xF5, 0xD9, +0xC1, 0xB2, 0x73, 0x29, 0x0A, 0xD0, 0x64, 0x28, +0x10, 0xD0, 0x78, 0x28, 0x13, 0xD0, 0x58, 0x28, +0x19, 0xD0, 0x75, 0x28, 0x1F, 0xD0, 0x63, 0x28, +0x23, 0xD0, 0x2E, 0xE0, 0x02, 0xCE, 0x00, 0x29, +0x00, 0xD1, 0x1D, 0xA1, 0x04, 0x98, 0x00, 0xF0, +0xCB, 0xF8, 0x0A, 0xE0, 0x68, 0x46, 0x8C, 0xC0, +0x02, 0xCE, 0x01, 0x23, 0x13, 0xE0, 0x68, 0x46, +0x8C, 0xC0, 0x08, 0xE0, 0x04, 0x98, 0x00, 0xF0, +0x3C, 0xF8, 0x45, 0x19, 0x19, 0xE0, 0x41, 0x20, +0x01, 0x93, 0x00, 0x92, 0x02, 0x90, 0x02, 0xCE, +0x00, 0x23, 0x10, 0x22, 0xF2, 0xE7, 0x68, 0x46, +0x8C, 0xC0, 0x02, 0xCE, 0x00, 0x23, 0x0A, 0x22, +0xEC, 0xE7, 0x02, 0xCE, 0x68, 0x46, 0x01, 0x73, +0x00, 0x21, 0x41, 0x73, 0x03, 0xA9, 0xD9, 0xE7, +0xC1, 0xB2, 0x04, 0x98, 0x00, 0xF0, 0x14, 0xF8, +0x6D, 0x1C, 0x64, 0x1C, 0x20, 0x78, 0x00, 0x28, +0x9F, 0xD1, 0x04, 0x98, 0x00, 0x28, 0x03, 0xD0, +0x04, 0x99, 0x00, 0x20, 0x09, 0x68, 0x08, 0x70, +0x28, 0x46, 0x07, 0xB0, 0xF0, 0xBD, 0x00, 0x00, +0x28, 0x6E, 0x75, 0x6C, 0x6C, 0x29, 0x00, 0x00, +0x10, 0xB5, 0x00, 0x28, 0x05, 0xD0, 0x02, 0x68, +0x11, 0x70, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, +0x10, 0xBD, 0xC8, 0xB2, 0xFF, 0xF7, 0xD4, 0xFC, +0x10, 0xBD, 0xFF, 0xB5, 0x00, 0x27, 0x83, 0xB0, +0x0C, 0x9D, 0x3E, 0x46, 0x08, 0x00, 0x3A, 0x46, +0x05, 0xD0, 0x00, 0x2B, 0x12, 0xD0, 0x05, 0x9B, +0x0A, 0x2B, 0x0B, 0xD0, 0x0E, 0xE0, 0x30, 0x20, +0x69, 0x46, 0x08, 0x70, 0x4A, 0x70, 0x2A, 0x46, +0x0D, 0x9B, 0x03, 0x98, 0x00, 0xF0, 0x6C, 0xF8, +0x07, 0xB0, 0xF0, 0xBD, 0x00, 0x29, 0x01, 0xDA, +0x01, 0x27, 0x40, 0x42, 0x02, 0xAC, 0x69, 0x46, +0x03, 0x34, 0xCA, 0x72, 0x0A, 0xE0, 0x05, 0x99, +0xFD, 0xF7, 0x98, 0xF8, 0x0A, 0x29, 0x02, 0xDB, +0x0E, 0x9A, 0x89, 0x18, 0x3A, 0x39, 0x30, 0x31, +0x64, 0x1E, 0x21, 0x70, 0x00, 0x28, 0xF2, 0xD1, +0x00, 0x2F, 0x0E, 0xD0, 0x00, 0x2D, 0x09, 0xD0, +0x0D, 0x98, 0x80, 0x07, 0x06, 0xD5, 0x2D, 0x21, +0x03, 0x98, 0xFF, 0xF7, 0xB9, 0xFF, 0x76, 0x1C, +0x6D, 0x1E, 0x02, 0xE0, 0x2D, 0x20, 0x64, 0x1E, +0x20, 0x70, 0x2A, 0x46, 0x21, 0x46, 0x0D, 0x9B, +0x03, 0x98, 0x00, 0xF0, 0x3D, 0xF8, 0x80, 0x19, +0xCE, 0xE7, 0x0F, 0xB4, 0x10, 0xB5, 0x03, 0xAA, +0x00, 0x20, 0x02, 0x99, 0xFF, 0xF7, 0x2E, 0xFF, +0x10, 0xBC, 0x08, 0xBC, 0x04, 0xB0, 0x18, 0x47, +0x36, 0x03, 0x35, 0x03, 0x2C, 0x03, 0x9C, 0x03, +0xE9, 0x02, 0x28, 0x03, 0x29, 0x03, 0x1C, 0x03, +0x9D, 0x03, 0x2F, 0x03, 0x4A, 0x03, 0x4D, 0x03, +0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, +0x3D, 0x03, 0x3C, 0x03, 0x2C, 0x03, 0x9C, 0x03, +0xD3, 0x02, 0x28, 0x03, 0x29, 0x03, 0xD6, 0x02, +0x9E, 0x03, 0x30, 0x03, 0x37, 0x03, 0x3A, 0x03, +0xFF, 0x03, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xFF, 0xB5, 0x00, 0x25, 0x20, 0x27, 0x81, 0xB0, +0x0E, 0x46, 0x14, 0x46, 0x00, 0x2A, 0x0E, 0xDD, +0x00, 0x20, 0x01, 0xE0, 0x40, 0x1C, 0x49, 0x1C, +0x0A, 0x78, 0x00, 0x2A, 0xFA, 0xD1, 0xA0, 0x42, +0x01, 0xDB, 0x00, 0x24, 0x00, 0xE0, 0x24, 0x1A, +0x98, 0x07, 0x00, 0xD5, 0x30, 0x27, 0xD8, 0x07, +0x06, 0xD0, 0x0D, 0xE0, 0x39, 0x46, 0x01, 0x98, +0xFF, 0xF7, 0x52, 0xFF, 0x6D, 0x1C, 0x64, 0x1E, +0x00, 0x2C, 0xF7, 0xDC, 0x04, 0xE0, 0x01, 0x98, +0xFF, 0xF7, 0x4A, 0xFF, 0x6D, 0x1C, 0x76, 0x1C, +0x31, 0x78, 0x00, 0x29, 0xF7, 0xD1, 0x05, 0xE0, +0x39, 0x46, 0x01, 0x98, 0xFF, 0xF7, 0x40, 0xFF, +0x6D, 0x1C, 0x64, 0x1E, 0x00, 0x2C, 0xF7, 0xDC, +0x28, 0x46, 0x05, 0xB0, 0xF0, 0xBD, 0x00, 0x00, +0x05, 0x48, 0x01, 0x78, 0x82, 0x29, 0x05, 0xD1, +0xC0, 0x79, 0x01, 0x28, 0x02, 0xD1, 0x03, 0x49, +0xFF, 0x20, 0x08, 0x70, 0x70, 0x47, 0x00, 0x00, +0x20, 0x08, 0x00, 0x20, 0x40, 0x01, 0x00, 0x20, +0x05, 0x48, 0x00, 0x21, 0x01, 0x80, 0x41, 0x80, +0x04, 0x49, 0x81, 0x80, 0xC1, 0x80, 0x04, 0x49, +0x03, 0x20, 0x08, 0x70, 0x70, 0x47, 0x00, 0x00, +0x10, 0x00, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, +0x90, 0x01, 0x00, 0x20, 0x08, 0x49, 0x5A, 0x20, +0x08, 0x70, 0x08, 0x49, 0x00, 0x20, 0x08, 0x70, +0x08, 0x48, 0x07, 0x49, 0x81, 0x80, 0xC1, 0x80, +0x07, 0x49, 0x03, 0x20, 0x08, 0x70, 0x07, 0x49, +0x01, 0x20, 0x08, 0x70, 0x70, 0x47, 0x00, 0x00, +0x94, 0x01, 0x00, 0x20, 0x0F, 0x08, 0x00, 0x20, +0xFF, 0x7F, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, +0x90, 0x01, 0x00, 0x20, 0x91, 0x01, 0x00, 0x20, +0x10, 0xB5, 0xFF, 0xF7, 0xDD, 0xFD, 0xFF, 0xF7, +0xDD, 0xFF, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x40, +0x10, 0x03, 0x42, 0x88, 0x0C, 0x00, 0x00, 0x40, +0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x50, +0x1F, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x50, +0x64, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x50, +0x77, 0x00, 0x01, 0x48, 0x44, 0x09, 0x00, 0x50, +0x39, 0x5A, 0x5B, 0x00, 0x10, 0x06, 0x00, 0x50, +0x00, 0x00, 0x06, 0x07, 0x00, 0x06, 0x00, 0x50, +0x00, 0x00, 0x00, 0x78, 0x08, 0x06, 0x00, 0x50, +0x0C, 0x30, 0x00, 0x00, 0x28, 0x06, 0x00, 0x50, +0x06, 0x00, 0x00, 0x00, 0x2C, 0x06, 0x00, 0x50, +0x0A, 0x66, 0x00, 0x00, 0x30, 0x06, 0x00, 0x50, +0xCC, 0x02, 0x00, 0x20, 0x34, 0x06, 0x00, 0x50, +0x00, 0x20, 0x00, 0x00, 0x44, 0x00, 0x00, 0x50, +0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x50, +0x50, 0x71, 0x00, 0x00, 0x20, 0x00, 0x00, 0x50, +0x24, 0x29, 0x00, 0x00, 0x14, 0x00, 0x00, 0x40, +0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, +0x14, 0x33, 0x43, 0xC8, 0x0C, 0x00, 0x00, 0x40, +0x29, 0x0A, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, +0x10, 0x32, 0x00, 0x00, 0x1C, 0x0E, 0x00, 0x50, +0x03, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x50, +0x14, 0x07, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x50, +0x00, 0x00, 0x00, 0x00, 0x58, 0x10, 0x00, 0x50, +0x01, 0x00, 0x00, 0x00, 0x04, 0x11, 0x00, 0x50, +0x78, 0x11, 0x00, 0x00, 0x0C, 0x11, 0x00, 0x50, +0x01, 0x00, 0x00, 0x00, 0x10, 0x11, 0x00, 0x50, +0x78, 0x01, 0x00, 0x00, 0x14, 0x11, 0x00, 0x50, +0xC8, 0x03, 0x60, 0x00, 0x4C, 0x00, 0x00, 0x50, +0x31, 0x00, 0x00, 0x00, 0x3C, 0x10, 0x00, 0x50, +0x00, 0x00, 0x10, 0x00, 0xB4, 0x10, 0x00, 0x50, +0x00, 0x26, 0x31, 0x00, 0xC0, 0x10, 0x00, 0x50, +0x33, 0x03, 0x33, 0x03, 0xC4, 0x10, 0x00, 0x50, +0x33, 0x03, 0x33, 0x03, 0xC8, 0x10, 0x00, 0x50, +0x0C, 0x0A, 0x00, 0x00, 0xCC, 0x10, 0x00, 0x50, +0x1A, 0x00, 0x00, 0x00, 0xD0, 0x10, 0x00, 0x50, +0x03, 0x19, 0x19, 0x00, 0xF0, 0x11, 0x00, 0x50, +0x12, 0x00, 0x00, 0x00, 0xEC, 0x11, 0x00, 0x50, +0x5C, 0x00, 0x00, 0x00, 0xF4, 0x11, 0x00, 0x50, +0x01, 0x00, 0x01, 0x00, 0x2C, 0x10, 0x00, 0x50, +0x10, 0x00, 0x90, 0x00, 0x30, 0x10, 0x00, 0x50, +0x20, 0x0C, 0x90, 0x00, 0x34, 0x10, 0x00, 0x50, +0x30, 0x0C, 0x30, 0x0C, 0x38, 0x10, 0x00, 0x50, +0xFF, 0x0F, 0x00, 0x00, 0x7C, 0x10, 0x00, 0x50, +0x88, 0x88, 0xFE, 0x88, 0x80, 0x10, 0x00, 0x50, +0x88, 0xFF, 0x00, 0x00, 0x84, 0x10, 0x00, 0x50, +0x55, 0x55, 0x55, 0x55, 0x88, 0x10, 0x00, 0x50, +0x55, 0x55, 0x55, 0x55, 0x8C, 0x10, 0x00, 0x50, +0x55, 0x55, 0x55, 0x55, 0xE8, 0x10, 0x00, 0x50, +0x3F, 0x16, 0x3F, 0x15, 0x04, 0x00, 0x00, 0x40, +0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x3C, 0x3B, 0x00, 0x00, +0x04, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, +0x5C, 0x0A, 0x00, 0x00, 0x44, 0x3B, 0x00, 0x00, +0x0C, 0x00, 0x00, 0x20, 0x54, 0x0E, 0x00, 0x00, +0x6A, 0x0A, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, +0x78, 0x7F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x94, 0x41, 0x4C, +}; +const unsigned char u8_rad_testpara_30[] = { +0xA1, 0x00, 0x03, 0xF3, 0x02, 0x02, 0x40, 0x01, +0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, +0x40, 0x01, 0x68, 0x01, 0x44, 0x01, 0x44, 0x01, +0x05, 0x05, 0x00, 0x00, 0x01, 0x05, 0x41, 0x06, +0x0A, 0x0B, 0x41, 0x0F, 0x10, 0x14, 0x41, 0x15, +0x02, 0x07, 0x41, 0x16, 0x41, 0x41, 0x41, 0x41, +0x41, 0x41, 0x41, 0x04, 0x03, 0x09, 0x41, 0x08, +0x0E, 0x0D, 0x41, 0x13, 0x12, 0x18, 0x41, 0x17, +0x0C, 0x11, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, +0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, +0x0C, 0x3E, 0x0C, 0x3E, 0x08, 0x08, 0x02, 0x02, +0x14, 0x03, 0x08, 0x08, 0x01, 0x01, 0xC8, 0x00, +0x1A, 0x01, 0x08, 0x14, 0x82, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x01, +0xA0, 0x00, 0x64, 0x00, 0x4A, 0x00, 0x0A, 0x78, +0x0F, 0x08, 0x1A, 0x4A, 0x4A, 0x3C, 0x3C, 0x00, +0x00, 0x00, 0x6A, 0x00, 0x38, 0x00, 0x1C, 0x00, +0x06, 0x32, 0x69, 0xE3, 0x0D, 0x00, 0x00, 0x72, +0x45, 0x00, 0x00, 0x06, 0x00, 0xA0, 0x00, 0x78, +0x00, 0x64, 0xDB, 0x13, 0xB5, 0x0C, 0x2D, 0x03, +0xCB, 0x00, 0x14, 0x0A, 0xB1, 0x01, 0xF1, 0x05, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x55, 0xAA, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x09, 0x5D, 0xA5, 0xB2, 0x35, 0x48, 0x5B, 0x66, +}; diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c new file mode 100644 index 0000000000..f0ec4c31a9 --- /dev/null +++ b/raydium/raydium_driver.c @@ -0,0 +1,2122 @@ +/* raydium_driver.c + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#endif /*end of CONFIG_FB*/ + +#include "raydium_driver.h" + +struct raydium_slot_status { + unsigned char pt_id; /*Occupied point ID*/ + unsigned char need_update; /*Mark as info need to be updated*/ + unsigned char pt_report_offset; /*point info offset in report*/ +}; +/*The first 3 elements are currently occupied. therest is new coming points*/ +struct raydium_slot_status gst_slot[MAX_TOUCH_NUM * 2]; +struct raydium_slot_status gst_slot_init = {0xFF, 0, 0}; + +#if (defined(CONFIG_RM_SYSFS_DEBUG)) +const struct attribute_group raydium_attr_group; +#endif /*end of CONFIG_RM_SYSFS_DEBUG*/ + +unsigned char g_u8_addr; +unsigned char g_u8_raydium_flag; +unsigned char g_u8_i2c_mode; +unsigned char g_u8_upgrade_type; +unsigned char g_u8_raw_data_type; +unsigned int g_u32_raw_data_len; /* 128 bytes*/ +unsigned long g_u32_addr; +unsigned int g_u32_length; +unsigned int g_u32_driver_version; +unsigned char *g_rad_fw_image, *g_rad_init_image; +unsigned char *g_rad_boot_image, *g_rad_para_image; +unsigned char *g_rad_testfw_image, *g_rad_testpara_image; +unsigned char g_u8_table_setting, g_u8_table_init; +unsigned char g_u8_resetflag; +unsigned char g_u8_wakeup_flag; +#ifdef ESD_SOLUTION_EN +unsigned char g_u8_checkflag; +#endif +unsigned char g_u8_log_level; +struct raydium_ts_data *g_raydium_ts; +/******************************************************************************* + * Name: raydium_variable_init + * Brief: + * Input: + * Output: + * Return: + *******************************************************************************/ +static void raydium_variable_init(void) +{ + g_u8_addr = RAYDIUM_PDA2_PDA_CFG_ADDR; + g_u8_raydium_flag = NORMAL_MODE; + g_u8_i2c_mode = PDA2_MODE; + g_u8_upgrade_type = 0; + g_u8_raw_data_type = RAYDIUM_FT_UPDATE; + g_u32_raw_data_len = 64 * 2; /* 128 bytes*/ + g_u32_addr = RAYDIUM_CHK_I2C_CMD; + g_u32_length = 1; + g_u8_table_setting = 0; + g_u8_table_init = 0; + g_rad_fw_image = NULL; + g_rad_init_image = NULL; + g_rad_boot_image = NULL; + g_rad_para_image = NULL; + g_rad_testfw_image = NULL; + g_rad_testpara_image = NULL; + g_u32_driver_version = ((RAD_MAIN_VERSION << 24) | + (RAD_MINOR_VERSION << 16) | + (RAD_CUSTOMER_VERSION)); + g_u8_resetflag = false; + g_u8_wakeup_flag = false; +#ifdef ESD_SOLUTION_EN + g_u8_checkflag = false; +#endif + g_u8_log_level = LOG_INFO; +} + + +/******************************************************************************* + * Name: raydium_gpio_configure + * Brief: + * Input: + * Output: + * Return: + *******************************************************************************/ + +static int raydium_gpio_configure(bool on) +{ + int i32_err = 0; + + if (on) { + if (gpio_is_valid(g_raydium_ts->irq_gpio)) { + i32_err = gpio_request(g_raydium_ts->irq_gpio, + "raydium_irq_gpio"); + if (i32_err) { + LOGD(LOG_ERR, "[touch]irq gpio request failed"); + goto err_irq_gpio_req; + } + + i32_err = gpio_direction_input(g_raydium_ts->irq_gpio); + if (i32_err) { + LOGD(LOG_ERR, "[touch]set_direction for irq gpio failed\n"); + goto err_irq_gpio_dir; + } + } + if (gpio_is_valid(g_raydium_ts->rst_gpio)) { + i32_err = gpio_request(g_raydium_ts->rst_gpio, + "raydium_rst_gpio"); + if (i32_err) { + LOGD(LOG_ERR, "[touch]rst gpio request failed"); + goto err_irq_gpio_req; + } + + i32_err = gpio_direction_output(g_raydium_ts->rst_gpio, 1); + if (i32_err) { + LOGD(LOG_ERR, + "[touch]set_direction for irq gpio failed\n"); + goto err_rst_gpio_dir; + } + } + } else { + if (gpio_is_valid(g_raydium_ts->irq_gpio)) + gpio_free(g_raydium_ts->irq_gpio); + } + return 0; +err_rst_gpio_dir: + if (gpio_is_valid(g_raydium_ts->rst_gpio)) + gpio_free(g_raydium_ts->rst_gpio); + return i32_err; +err_irq_gpio_dir: + if (gpio_is_valid(g_raydium_ts->irq_gpio)) + gpio_free(g_raydium_ts->irq_gpio); +err_irq_gpio_req: + return i32_err; +} + +/******************************************************************************* + * Name: raydium_ts_pinctrl_init + * Brief: + * Input: + * Output: + * Return: + *******************************************************************************/ +#ifdef MSM_NEW_VER +static int raydium_ts_pinctrl_init(void) +{ + int i32_ret; + + /* Get pinctrl if target uses pinctrl */ + g_raydium_ts->ts_pinctrl = devm_pinctrl_get(&(g_raydium_ts->client->dev)); + if (IS_ERR_OR_NULL(g_raydium_ts->ts_pinctrl)) { + i32_ret = PTR_ERR(g_raydium_ts->ts_pinctrl); + LOGD(LOG_ERR, "[touch]target does not use pinctrl %d\n", i32_ret); + goto err_pinctrl_get; + } + + g_raydium_ts->pinctrl_state_active + = pinctrl_lookup_state(g_raydium_ts->ts_pinctrl, PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(g_raydium_ts->pinctrl_state_active)) { + i32_ret = PTR_ERR(g_raydium_ts->pinctrl_state_active); + LOGD(LOG_ERR, "[touch]Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, i32_ret); + goto err_pinctrl_lookup; + } + + g_raydium_ts->pinctrl_state_suspend + = pinctrl_lookup_state(g_raydium_ts->ts_pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(g_raydium_ts->pinctrl_state_suspend)) { + i32_ret = PTR_ERR(g_raydium_ts->pinctrl_state_suspend); + LOGD(LOG_ERR, "[touch]Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, i32_ret); + goto err_pinctrl_lookup; + } + + g_raydium_ts->pinctrl_state_release + = pinctrl_lookup_state(g_raydium_ts->ts_pinctrl, + PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(g_raydium_ts->pinctrl_state_release)) { + i32_ret = PTR_ERR(g_raydium_ts->pinctrl_state_release); + LOGD(LOG_ERR, "[touch]Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, i32_ret); + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(g_raydium_ts->ts_pinctrl); +err_pinctrl_get: + g_raydium_ts->ts_pinctrl = NULL; + return i32_ret; +} +#endif/*end of MSM_NEW_VER*/ +#ifdef ESD_SOLUTION_EN +static int raydium_hw_reset_fun(struct i2c_client *client) +{ + int i32_ret = SUCCESS; + + LOGD(LOG_INFO, "[touch]HW reset\n"); + g_u8_raydium_flag |= ENG_MODE; + + g_u8_resetflag = true; + /*HW reset*/ + gpio_set_value(g_raydium_ts->rst_gpio, 1); + gpio_set_value(g_raydium_ts->rst_gpio, 0); + msleep(RAYDIUM_RESET_INTERVAL_MSEC); + gpio_set_value(g_raydium_ts->rst_gpio, 1); + + g_u8_i2c_mode = PDA2_MODE; + + i32_ret = wait_irq_state(client, 300, 2000); + if (i32_ret != ERROR) + msleep(35); + + g_u8_raydium_flag &= ~ENG_MODE; + + LOGD(LOG_INFO, "[touch]Raydium HW reset : %d\n", i32_ret); + return i32_ret; +} +#endif + +int raydium_i2c_write_pda_via_pda2(struct i2c_client *client, + unsigned int u32_addr, unsigned char *u8_w_data, + unsigned short u16_length) +{ + int i32_ret = -1; + /*unsigned char u8_retry;*/ + unsigned char u8_mode = 0x00; + unsigned char u8_buf[MAX_WRITE_PACKET_SIZE + 6]; + + struct i2c_msg msg[] = { + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_WRITE, + .len = u16_length + 6, + .buf = u8_buf, + }, + }; + if (u16_length > MAX_WRITE_PACKET_SIZE) + return -EINVAL; + + + /*I2C access for register need to word mode*/ + + if ((u16_length == 4) && + ((u32_addr & 0x50000000) || (u32_addr & 0x40000000))) + u8_mode = I2C_PDA2_WORD_MODE; + else + u8_mode = I2C_PDA2_BYTE_MODE; + + if (u16_length > MAX_WRITE_PACKET_SIZE) + return -EINVAL; + g_u8_i2c_mode = PDA2_MODE; + + u8_buf[0] = RAYDIUM_I2C_PDA_CMD; + u8_buf[1] = (unsigned char)u32_addr; + u8_buf[2] = (unsigned char)(u32_addr >> 8); + u8_buf[3] = (unsigned char)(u32_addr >> 16); + u8_buf[4] = (unsigned char)(u32_addr >> 24); + u8_buf[5] = u8_mode; + + memcpy(&u8_buf[6], u8_w_data, u16_length); + + /*for (u8_retry = 0; u8_retry < SYN_I2C_RETRY_TIMES; u8_retry++) {*/ + if (i2c_transfer(client->adapter, msg, 1) == 1) { + i32_ret = u16_length; + /*break;*/ + } + i32_ret = u16_length; + /*usleep_range(500, 1500);*/ + /*}*/ + + /*if (u8_retry == SYN_I2C_RETRY_TIMES) {*/ + /* LOGD(LOG_ERR, "[touch]%s: I2C write over retry limit\n", __func__);*/ + /* i32_ret = -EIO;*/ + /*}*/ + + return i32_ret; + +} +int raydium_i2c_read_pda_via_pda2(struct i2c_client *client, + unsigned int u32_addr, unsigned char *u8_r_data, + unsigned short u16_length) +{ + int i32_ret; + /*unsigned char u8_retry;*/ + unsigned char u8_mode = 0x00; + unsigned char u8_buf[6]; + + struct i2c_msg msg[] = { + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_WRITE, + .len = 6, + .buf = u8_buf, + }, + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_READ, + .len = u16_length, + .buf = u8_r_data, + }, + }; + + if ((u32_addr & 0x50000000) || (u32_addr & 0x40000000)) + u8_mode = I2C_PDA2_WORD_MODE; + else + u8_mode = I2C_PDA2_BYTE_MODE; + + g_u8_i2c_mode = PDA2_MODE; + u8_buf[0] = RAYDIUM_I2C_PDA_CMD; + u8_buf[1] = (unsigned char)u32_addr; + u8_buf[2] = (unsigned char)(u32_addr >> 8); + u8_buf[3] = (unsigned char)(u32_addr >> 16); + u8_buf[4] = (unsigned char)(u32_addr >> 24); + u8_buf[5] = u8_mode; + + /*for (u8_retry = 0; u8_retry < SYN_I2C_RETRY_TIMES; u8_retry++) {*/ + if (i2c_transfer(g_raydium_ts->client->adapter, msg, 2) == 2) { + i32_ret = u16_length; + /*break;*/ + } + i32_ret = u16_length; + /*usleep_range(500, 1500);*/ + /*}*/ + + /*if (u8_retry == SYN_I2C_RETRY_TIMES) {*/ + /* LOGD(LOG_ERR, "[touch]%s: I2C read over retry limit\n", __func__);*/ + /* i32_ret = -EIO;*/ + /*}*/ + + return i32_ret; +} +int raydium_i2c_pda_set_address(unsigned int u32_address, + unsigned char u8_mode) +{ + int i32_ret = 0; + unsigned char u8_retry; + unsigned char u8_buf[RAD_I2C_PDA_ADDRESS_LENGTH]; + struct i2c_client *client = g_raydium_ts->client; + + client->addr = RAYDIUM_I2C_EID; + u8_buf[0] = (u32_address & 0x0000FF00) >> 8; + u8_buf[1] = (u32_address & 0x00FF0000) >> 16; + u8_buf[2] = (u32_address & 0xFF000000) >> 24; + u8_buf[3] = u8_mode; + + for (u8_retry = 0; u8_retry < SYN_I2C_RETRY_TIMES; u8_retry++) { + i32_ret = i2c_master_send(client, u8_buf, + RAD_I2C_PDA_ADDRESS_LENGTH); + if (i32_ret != RAD_I2C_PDA_ADDRESS_LENGTH) { + LOGD(LOG_ERR, "[touch]%s: I2C retry %d\n", + __func__, u8_retry + 1); + usleep_range(500, 1500); + } else { + break; + } + } + + return (i32_ret == RAD_I2C_PDA_ADDRESS_LENGTH) ? i32_ret : -EIO; +} + +/*device attribute raydium_i2c_pda2_mode used*/ +int raydium_i2c_pda_read(struct i2c_client *client, + unsigned int u32_addr, unsigned char *u8_r_data, + unsigned short u16_length) +{ + int i32_ret; + unsigned char u8_retry; + unsigned char u8_mode = 0x00; + unsigned char u8_buf; + + struct i2c_msg msg[] = { + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_WRITE, + .len = 1, + .buf = &u8_buf, + }, + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_READ, + .len = u16_length, + .buf = u8_r_data, + }, + }; + + if (u16_length == 4) + u8_mode |= RAD_I2C_PDA_MODE_ENABLE | + RAD_I2C_PDA_2_MODE_DISABLE | + RAD_I2C_PDA_MODE_WORD_MODE; + else + u8_mode |= RAD_I2C_PDA_MODE_ENABLE | + RAD_I2C_PDA_2_MODE_DISABLE; + + u8_mode |= 0x03; + + u8_buf = u32_addr & MASK_8BIT; + + i32_ret = raydium_i2c_pda_set_address(u32_addr, u8_mode); + if (i32_ret != RAD_I2C_PDA_ADDRESS_LENGTH) + goto exit; + usleep_range(50, 80); + + for (u8_retry = 0; u8_retry < SYN_I2C_RETRY_TIMES; u8_retry++) { + if (i2c_transfer(g_raydium_ts->client->adapter, msg, 2) == 2) { + i32_ret = u16_length; + break; + } + LOGD(LOG_ERR, "%s: I2C retry %d\n", __func__, u8_retry + 1); + usleep_range(500, 1500); + } + + if (u8_retry == SYN_I2C_RETRY_TIMES) { + LOGD(LOG_ERR, "%s: I2C read over retry limit\n", __func__); + i32_ret = -EIO; + } +exit: + return i32_ret; +} + +int raydium_i2c_pda_write(struct i2c_client *client, + unsigned int u32_addr, unsigned char *u8_w_data, + unsigned short u16_length) +{ + int i32_ret; + unsigned char u8_retry; + unsigned char u8_mode = 0x00; + unsigned char u8_buf[MAX_WRITE_PACKET_SIZE + 1]; + + struct i2c_msg msg[] = { + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_WRITE, + .len = u16_length + 1, + .buf = u8_buf, + }, + }; + + if (u16_length > MAX_WRITE_PACKET_SIZE) + return -EINVAL; + + if (u16_length == 4) + u8_mode |= RAD_I2C_PDA_MODE_ENABLE | + RAD_I2C_PDA_2_MODE_DISABLE | + RAD_I2C_PDA_MODE_WORD_MODE; + else + u8_mode |= RAD_I2C_PDA_MODE_ENABLE | + RAD_I2C_PDA_2_MODE_DISABLE; + + u8_buf[0] = u32_addr & MASK_8BIT; + memcpy(&u8_buf[1], u8_w_data, u16_length); + + i32_ret = raydium_i2c_pda_set_address(u32_addr, u8_mode); + if (i32_ret != RAD_I2C_PDA_ADDRESS_LENGTH) + goto exit; + usleep_range(50, 80); + + for (u8_retry = 0; u8_retry < SYN_I2C_RETRY_TIMES; u8_retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) { + i32_ret = u16_length; + break; + } + LOGD(LOG_ERR, "[touch]%s: I2C retry %d\n", __func__, u8_retry + 1); + usleep_range(500, 1500); + } + + if (u8_retry == SYN_I2C_RETRY_TIMES) { + LOGD(LOG_ERR, "[touch]%s: I2C write over retry limit\n", __func__); + i32_ret = -EIO; + } +exit: + return i32_ret; +} + +int handle_i2c_pda_read(struct i2c_client *client, + unsigned int u32_addr, unsigned char *u8_r_data, + unsigned short u16_length) +{ + if ((g_u8_i2c_mode & PDA_MODE) != 0) { + if (raydium_i2c_pda_read(client, u32_addr, u8_r_data, u16_length) == ERROR) { + LOGD(LOG_ERR, "[touch] handle_ic_write I2C NG!\r\n"); + return ERROR; + } + } else { + if (raydium_i2c_read_pda_via_pda2(client, u32_addr, u8_r_data, u16_length) == ERROR) { + LOGD(LOG_ERR, "[touch] handle_ic_write I2C via_pda2 NG!\r\n"); + return ERROR; + } + } + return SUCCESS; +} + +int handle_i2c_pda_write(struct i2c_client *client, + unsigned int u32_addr, unsigned char *u8_w_data, + unsigned short u16_length) +{ + if ((g_u8_i2c_mode & PDA_MODE) != 0) { + if (raydium_i2c_pda_write(client, u32_addr, u8_w_data, u16_length) == ERROR) { + LOGD(LOG_ERR, "[touch] handle_ic_write I2C NG!\r\n"); + return ERROR; + } + } else { + if (raydium_i2c_write_pda_via_pda2(client, u32_addr, u8_w_data, u16_length) == ERROR) { + LOGD(LOG_ERR, "[touch] handle_ic_write I2C via_pda2 NG!\r\n"); + return ERROR; + } + } + return SUCCESS; +} +int raydium_i2c_pda2_set_page(struct i2c_client *client, + unsigned int is_suspend, + unsigned char u8_page) +{ + int i32_ret = -1; + unsigned char u8_retry; + unsigned int u8_ret = (is_suspend) ? 10 : 2; + unsigned char u8_buf[RAYDIUM_I2C_PDA2_PAGE_LENGTH]; + + struct i2c_msg msg[] = { + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_WRITE, + .len = RAYDIUM_I2C_PDA2_PAGE_LENGTH, + .buf = u8_buf, + }, + }; + + u8_buf[0] = RAYDIUM_PDA2_PAGE_ADDR; + u8_buf[1] = u8_page; + for (; u8_ret > 0; u8_ret--) { + for (u8_retry = 0; u8_retry < SYN_I2C_RETRY_TIMES; u8_retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) { + i32_ret = RAYDIUM_I2C_PDA2_PAGE_LENGTH; + break; + } + usleep_range(500, 1500); + } + if (i32_ret == RAYDIUM_I2C_PDA2_PAGE_LENGTH) + break; + usleep_range(2000, 5000); + } + + if (u8_ret == 0) { + LOGD(LOG_ERR, "[touch]%s: I2C write over retry limit\n", __func__); + i32_ret = -EIO; + } + + return i32_ret; +} + +int raydium_i2c_pda2_read(struct i2c_client *client, + unsigned char u8_addr, + unsigned char *u8_r_data, + unsigned short u16_length) +{ + int i32_ret = -1; + unsigned char u8_retry; + + struct i2c_msg msg[] = { + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_WRITE, + .len = 1, + .buf = &u8_addr, + }, + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_READ, + .len = u16_length, + .buf = u8_r_data, + }, + }; + g_u8_i2c_mode = PDA2_MODE; + for (u8_retry = 0; u8_retry < SYN_I2C_RETRY_TIMES; u8_retry++) { + if (i2c_transfer(g_raydium_ts->client->adapter, msg, 2) == 2) { + i32_ret = u16_length; + break; + } + usleep_range(500, 1500); + } + + if (u8_retry == SYN_I2C_RETRY_TIMES) { + LOGD(LOG_ERR, "[touch]%s: I2C read over retry limit\n", __func__); + i32_ret = -EIO; + } + + return i32_ret; +} + +int raydium_i2c_pda2_write(struct i2c_client *client, + unsigned char u8_addr, + unsigned char *u8_w_data, + unsigned short u16_length) +{ + int i32_ret = -1; + unsigned char u8_retry; + unsigned char u8_buf[MAX_WRITE_PACKET_SIZE + 1]; + + struct i2c_msg msg[] = { + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_WRITE, + .len = u16_length + 1, + .buf = u8_buf, + }, + }; + + if (u16_length > MAX_WRITE_PACKET_SIZE) + return -EINVAL; + g_u8_i2c_mode = PDA2_MODE; + u8_buf[0] = u8_addr; + memcpy(&u8_buf[1], u8_w_data, u16_length); + + for (u8_retry = 0; u8_retry < SYN_I2C_RETRY_TIMES; u8_retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) { + i32_ret = u16_length; + break; + } + usleep_range(500, 1500); + } + + if (u8_retry == SYN_I2C_RETRY_TIMES) { + LOGD(LOG_ERR, "[touch]%s: I2C write over retry limit\n", __func__); + i32_ret = -EIO; + } + + return i32_ret; +} + +void raydium_irq_control(bool enable) +{ + if (enable) { + if (g_raydium_ts->irq_enabled) { + /*mutex_unlock(&ts->lock);*/ + LOGD(LOG_INFO, "[touch]Already enable irq\n"); + return; + } + + /* Clear interrupts first */ + if (g_raydium_ts->blank != FB_BLANK_POWERDOWN) { + if (g_u8_i2c_mode == PDA2_MODE) { + mutex_lock(&g_raydium_ts->lock); + if (raydium_i2c_pda2_set_page(g_raydium_ts->client, + g_raydium_ts->is_suspend, + RAYDIUM_PDA2_PAGE_0) < 0) + LOGD(LOG_ERR, "[touch]set page fail%s\n", + __func__); + mutex_unlock(&g_raydium_ts->lock); + usleep_range(500, 1500); + } + } + while (g_raydium_ts->irq_desc->depth > 0) { + LOGD(LOG_INFO, "[touch]irq enable\n"); + g_raydium_ts->irq_enabled = true; + enable_irq(g_raydium_ts->irq); + } + } else { + if (g_raydium_ts->irq_enabled) { + if (g_raydium_ts->irq_desc->depth == 0) { + disable_irq(g_raydium_ts->irq); + g_raydium_ts->irq_enabled = false; + LOGD(LOG_INFO, "[touch]irq disable\n"); + } + } + } +} + +unsigned char raydium_disable_i2c_deglitch(void) +{ + unsigned int u32_buf = 0; + unsigned char u8_retry = 3, u8_comfirm_time = 3; + unsigned char u8_check = 0, u8_i = 0; + unsigned int u32_i2c_deglitch = 0x07060000; + unsigned char u8_buf[4]; + + while (u8_retry--) { + u32_buf = 0; + handle_i2c_pda_read(g_raydium_ts->client, RAYDIUM_CHK_I2C_CMD, + (unsigned char *)(&u32_buf), 4); + if ((u32_buf & 0xFFFF0000) == 0xF3030000) + u8_check++; + } + if (u8_check == 3) { + LOGD(LOG_INFO, "[touch]PDA2 OK\r\n"); + return SUCCESS; + } + + g_u8_i2c_mode = PDA_MODE; + u8_retry = 100; + while (u8_retry--) { + u8_check = 0; + for (u8_i = 0; u8_i < u8_comfirm_time; u8_i++) { + /*check I2C*/ + u32_buf = 0; + if (handle_i2c_pda_read(g_raydium_ts->client, + RAYDIUM_PDA_I2CENG, + (unsigned char *)(&u32_buf), 4) == ERROR) { + LOGD(LOG_ERR, "[touch]%s: 1.handle_ic_read I2C NG!\r\n", __func__); + break; + } + + if (u32_buf == u32_i2c_deglitch) + u8_check++; + else + break; + } + + if (u8_check == u8_comfirm_time) + break; + + if (handle_i2c_pda_write(g_raydium_ts->client, RAYDIUM_PDA_I2CENG, + (unsigned char *)(&u32_i2c_deglitch), 4) == ERROR) { + LOGD(LOG_ERR, "[touch]%s:handle_ic_write I2C NG!\r\n", __func__); + continue; + } + + u8_check = 0; + for (u8_i = 0; u8_i < u8_comfirm_time; u8_i++) { + /*check I2C*/ + u32_buf = 0; + if (handle_i2c_pda_read(g_raydium_ts->client, + RAYDIUM_PDA_I2CENG, + (unsigned char *)(&u32_buf), 4) == ERROR) { + LOGD(LOG_ERR, "[touch]%s:2.handle_ic_read I2C NG!\r\n", __func__); + break; + } + + if (u32_buf == u32_i2c_deglitch) + u8_check++; + else + break; + } + + if (u8_check == u8_comfirm_time) + break; + } + + if (u8_retry == 0) + return ERROR; + + u32_buf = 0x03; + if (handle_i2c_pda_write(g_raydium_ts->client, RAYDIUM_REG_GPIO_DEGLITCH, + (unsigned char *)(&u32_buf), 4) == ERROR) { + LOGD(LOG_ERR, "[touch]%s:3.handle_ic_write I2C NG!\r\n", __func__); + return ERROR; + } + + /*Disable PDA*/ + handle_i2c_pda_read(g_raydium_ts->client, RAYDIUM_PDA_I2CREG, u8_buf, 4); + u8_buf[0] |= RAD_ENABLE_PDA2 | RAD_ENABLE_SI2; + handle_i2c_pda_write(g_raydium_ts->client, RAYDIUM_PDA_I2CREG, u8_buf, 4); + raydium_i2c_pda_set_address(0x50000628, DISABLE); + + g_u8_i2c_mode = PDA2_MODE; + + return SUCCESS; +} + +#ifdef CONFIG_RM_SYSFS_DEBUG + +int raydium_i2c_mode_control(struct i2c_client *client, + unsigned char u8_mode) +{ + unsigned char u8_buf[4]; + + switch (u8_mode) { + case 0: /* Disable INT flag */ + LOGD(LOG_INFO, "[touch]RAD INT flag : %d\n", g_raydium_ts->irq_enabled); + disable_irq(g_raydium_ts->irq); + g_raydium_ts->irq_enabled = false; + LOGD(LOG_INFO, "[touch]RAD irq disable\n"); + break; + case 1: /* Enable INT flag */ + LOGD(LOG_INFO, "[touch]RAD INT flag : %d\n", g_raydium_ts->irq_enabled); + enable_irq(g_raydium_ts->irq); + g_raydium_ts->irq_enabled = true; + LOGD(LOG_INFO, "[touch]RAD irq enable\n"); + break; + case 2: /* Disable INT by raydium_irq_control */ + raydium_irq_control(DISABLE); + break; + case 3: /* Enable INT by raydium_irq_control */ + raydium_irq_control(ENABLE); + break; + case 4: /* Show RAD INT depth */ + LOGD(LOG_INFO, "[touch]RAD INT depth : %d\n", g_raydium_ts->irq_desc->depth); + break; + case 7: + raydium_i2c_pda2_set_page(client, + g_raydium_ts->is_suspend, RAYDIUM_PDA2_2_PDA); + g_u8_i2c_mode = PDA_MODE; + LOGD(LOG_INFO, "[touch]Disable PDA2_MODE\n"); + break; + case 8: + raydium_i2c_pda_read(client, RAYDIUM_PDA_I2CREG, u8_buf, 4); + u8_buf[0] |= RAD_ENABLE_PDA2 | RAD_ENABLE_SI2; + raydium_i2c_pda_write(client, RAYDIUM_PDA_I2CREG, u8_buf, 4); + raydium_i2c_pda_set_address(RAYDIUM_PDA_I2CREG, DISABLE); + + g_u8_i2c_mode = PDA2_MODE; + LOGD(LOG_INFO, "[touch]Enable PDA2_MODE\n"); + break; + } + return 0; +} + + +const struct attribute_group raydium_attr_group = { + .attrs = raydium_attributes +}; + +/*create sysfs for debug update firmware*/ +static int raydium_create_sysfs(struct i2c_client *client) +{ + int ret = -1; + + ret = sysfs_create_group(&(client->dev.kobj), &raydium_attr_group); + if (ret) { + LOGD(LOG_ERR, "[touch]failed to register sysfs\n"); + sysfs_remove_group(&client->dev.kobj, &raydium_attr_group); + ret = -EIO; + } else { + LOGD(LOG_INFO, "[touch]create raydium sysfs attr_group successful\n"); + } + return ret; +} + +static void raydium_release_sysfs(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &raydium_attr_group); +} +#endif /*end of CONFIG_RM_SYSFS_DEBUG*/ + +#ifdef ESD_SOLUTION_EN +int raydium_esd_check(void) +{ + int i32_ret = 0; + unsigned char u8_esd_status[MAX_TCH_STATUS_PACKET_SIZE]; + + mutex_lock(&g_raydium_ts->lock); + if (g_u8_i2c_mode == PDA2_MODE) { + i32_ret = raydium_i2c_pda2_set_page(g_raydium_ts->client, + g_raydium_ts->is_suspend, + RAYDIUM_PDA2_PAGE_0); + if (i32_ret < 0) + goto exit; + /*read esd status*/ + i32_ret = raydium_i2c_pda2_read(g_raydium_ts->client, + RAYDIUM_PDA2_TCH_RPT_STATUS_ADDR, + u8_esd_status, MAX_TCH_STATUS_PACKET_SIZE); + if (i32_ret < 0) { + LOGD(LOG_ERR, "[touch]%s: failed to read data: %d\n", + __func__, __LINE__); + goto exit; + } + + if (u8_esd_status[POS_FW_STATE] != 0x1A && + u8_esd_status[POS_FW_STATE] != 0xAA) { + if (g_u8_resetflag == true) { + LOGD(LOG_ERR, "[touch]%s -> filter abnormal irq\n" + , __func__); + goto exit; + } + LOGD(LOG_ERR, "[touch]%s -> abnormal irq, FW state = 0x%x\n", + __func__, u8_esd_status[POS_FW_STATE]); + g_u8_resetflag = false; + i32_ret = -1; + goto exit; + + } + g_u8_resetflag = false; + } +exit: + mutex_unlock(&g_raydium_ts->lock); + LOGD(LOG_INFO, "[touch]%s\n", __func__); + return i32_ret; +} +#endif + + + + +static int raydium_touch_report(unsigned char *p_u8_buf, + unsigned char u8_points_amount) +{ + unsigned char u8_i, u8_j, u8_offset = 0, u8_pt_id; + signed short i16_wx, i16_wy; + /* number of touch points */ + unsigned char u8_touch_count = 0; + //DECLARE_BITMAP(ids, g_raydium_ts->u8_max_touchs); + unsigned long *ids = NULL; + + ids = kzalloc(sizeof(*ids)*BITS_TO_LONGS(g_raydium_ts->u8_max_touchs), GFP_KERNEL); + if (!ids) + return -ENOMEM; + + bitmap_zero(ids, g_raydium_ts->u8_max_touchs); + + for (u8_i = 0; u8_i < (g_raydium_ts->u8_max_touchs * 2); u8_i++) { + gst_slot[u8_i].need_update = 0; + gst_slot[u8_i].pt_report_offset = 0; + } + + /*Check incoming point info*/ + for (u8_i = 0; u8_i < u8_points_amount; u8_i++) { + u8_pt_id = p_u8_buf[POS_PT_ID + u8_i * LEN_PT]; + /* Current*/ + for (u8_j = 0; u8_j < g_raydium_ts->u8_max_touchs; u8_j++) { + if (u8_pt_id == gst_slot[u8_j].pt_id) { + gst_slot[u8_j].need_update = 1; + gst_slot[u8_j].pt_report_offset = u8_i; + break; + } + } + /* New coming*/ + if (u8_j == g_raydium_ts->u8_max_touchs) { + for (u8_j = g_raydium_ts->u8_max_touchs; + u8_j < (g_raydium_ts->u8_max_touchs * 2); u8_j++) { + if (!gst_slot[u8_j].need_update) { + gst_slot[u8_j].pt_id = u8_pt_id; + gst_slot[u8_j].need_update = 1; + gst_slot[u8_j].pt_report_offset = u8_i; + LOGD(LOG_INFO, "[touch]x:%d,y:%d\n", + p_u8_buf[POS_X_L + u8_offset] | + p_u8_buf[POS_X_H + u8_offset] << 8, + p_u8_buf[POS_Y_L + u8_offset] | + p_u8_buf[POS_Y_H + u8_offset] << 8); + break; + } + } + } + } + + /*Release slot with non-occupied point*/ + for (u8_i = 0; u8_i < g_raydium_ts->u8_max_touchs; u8_i++) { + if (!gst_slot[u8_i].need_update) { + input_mt_slot(g_raydium_ts->input_dev, u8_i); + input_mt_report_slot_state(g_raydium_ts->input_dev, + MT_TOOL_FINGER, false); + gst_slot[u8_i].pt_id = 0xFF; + gst_slot[u8_i].pt_report_offset = 0; + gst_slot[u8_i].need_update = 0; + } + } + /*Assign new one to non-occupied slot*/ + for (u8_i = g_raydium_ts->u8_max_touchs; + u8_i < (g_raydium_ts->u8_max_touchs * 2); u8_i++) { + if (gst_slot[u8_i].need_update) { + for (u8_j = 0; u8_j < g_raydium_ts->u8_max_touchs; u8_j++) { + if (!gst_slot[u8_j].need_update) { + gst_slot[u8_j] = gst_slot[u8_i]; + gst_slot[u8_i] = gst_slot_init; + break; + } + } + } else { + break; + } + } + for (u8_i = 0; u8_i < g_raydium_ts->u8_max_touchs; u8_i++) { + if (gst_slot[u8_i].need_update) { + u8_offset = gst_slot[u8_i].pt_report_offset * LEN_PT; + g_raydium_ts->x_pos[u8_i] = p_u8_buf[POS_X_L + u8_offset] | + p_u8_buf[POS_X_H + u8_offset] << BYTE_SHIFT; + g_raydium_ts->y_pos[u8_i] = p_u8_buf[POS_Y_L + u8_offset] | + p_u8_buf[POS_Y_H + u8_offset] << BYTE_SHIFT; + g_raydium_ts->pressure = p_u8_buf[POS_PRESSURE_L + u8_offset] | + p_u8_buf[POS_PRESSURE_H + u8_offset] << BYTE_SHIFT; + i16_wx = p_u8_buf[POS_WX_L + u8_offset] | + p_u8_buf[POS_WX_H + u8_offset] << BYTE_SHIFT; + i16_wy = p_u8_buf[POS_WY_L + u8_offset] | + p_u8_buf[POS_WY_H + u8_offset] << BYTE_SHIFT; + + input_mt_slot(g_raydium_ts->input_dev, u8_i); + input_mt_report_slot_state(g_raydium_ts->input_dev, + MT_TOOL_FINGER, true); + __set_bit(u8_i, ids); + + input_report_abs(g_raydium_ts->input_dev, + ABS_MT_POSITION_X, g_raydium_ts->x_pos[u8_i]); + input_report_abs(g_raydium_ts->input_dev, + ABS_MT_POSITION_Y, g_raydium_ts->y_pos[u8_i]); + input_report_abs(g_raydium_ts->input_dev, + ABS_MT_PRESSURE, g_raydium_ts->pressure); + input_report_abs(g_raydium_ts->input_dev, + ABS_MT_TOUCH_MAJOR, max(i16_wx, i16_wy)); + input_report_abs(g_raydium_ts->input_dev, + ABS_MT_TOUCH_MINOR, min(i16_wx, i16_wy)); + LOGD(LOG_DEBUG, "[touch:%d]x:%d,y:%d\n", + u8_i, + p_u8_buf[POS_X_L + u8_offset] | + p_u8_buf[POS_X_H + u8_offset] << 8, + p_u8_buf[POS_Y_L + u8_offset] | + p_u8_buf[POS_Y_H + u8_offset] << 8); + u8_touch_count++; + } + } + input_report_key(g_raydium_ts->input_dev, + BTN_TOUCH, u8_touch_count > 0); + input_report_key(g_raydium_ts->input_dev, + BTN_TOOL_FINGER, u8_touch_count > 0); + + for (u8_i = 0; u8_i < g_raydium_ts->u8_max_touchs; u8_i++) { + if (test_bit(u8_i, ids)) + continue; + input_mt_slot(g_raydium_ts->input_dev, u8_i); + input_mt_report_slot_state(g_raydium_ts->input_dev, + MT_TOOL_FINGER, false); + } + + input_sync(g_raydium_ts->input_dev); + kfree(ids); + + return 0; +} + +int raydium_read_touchdata(unsigned char *p_u8_tp_status, unsigned char *p_u8_buf) +{ + int i32_ret = 0; + unsigned char u8_points_amount; + static unsigned char u8_seq_no; + unsigned char u8_retry; + unsigned char u8_read_size; + unsigned char u8_read_buf[MAX_REPORT_PACKET_SIZE]; + + u8_retry = 3; + + mutex_lock(&g_raydium_ts->lock); + while (u8_retry != 0) { + i32_ret = raydium_i2c_pda2_set_page(g_raydium_ts->client, + g_raydium_ts->is_suspend, RAYDIUM_PDA2_PAGE_0); + if (i32_ret < 0) { + msleep(250); + u8_retry--; + } else + break; + } + if (u8_retry == 0) { + LOGD(LOG_ERR, "[touch]%s: failed to set page\n", __func__); + + goto reset_error; + } + + memset(u8_read_buf, 0, MAX_REPORT_PACKET_SIZE); + memset(p_u8_buf, 0, MAX_REPORT_PACKET_SIZE); + memset(p_u8_tp_status, 0, MAX_TCH_STATUS_PACKET_SIZE); + u8_read_size = 4 + MAX_TOUCH_NUM * LEN_PT + 1; + /*read touch point information*/ + i32_ret = raydium_i2c_pda2_read(g_raydium_ts->client, + RAYDIUM_PDA2_TCH_RPT_STATUS_ADDR, + u8_read_buf, u8_read_size); + if (i32_ret < 0) { + LOGD(LOG_ERR, "[touch]%s: failed to read data: %d\n", + __func__, __LINE__); + goto exit_error; + } + memcpy(p_u8_tp_status, &u8_read_buf[0], MAX_TCH_STATUS_PACKET_SIZE); + +#ifdef ESD_SOLUTION_EN + if (p_u8_tp_status[POS_FW_STATE] != 0x1A && + p_u8_tp_status[POS_FW_STATE] != 0xAA) { + if (g_u8_resetflag == true) { + LOGD(LOG_ERR, "[touch]%s -> filter irq, FW state = 0x%x\n", + __func__, p_u8_tp_status[POS_FW_STATE]); + i32_ret = -1; + g_u8_resetflag = false; + goto exit_error; + } + LOGD(LOG_ERR, "[touch]%s -> abnormal irq, FW state = 0x%x\n", + __func__, p_u8_tp_status[POS_FW_STATE]); + i32_ret = -1; + goto reset_error; + + } +#endif + /* inform IC to prepare next report*/ + if (u8_seq_no == p_u8_tp_status[POS_SEQ]) { + p_u8_tp_status[POS_SEQ] = 0; + i32_ret = raydium_i2c_pda2_write(g_raydium_ts->client, + RAYDIUM_PDA2_TCH_RPT_STATUS_ADDR, p_u8_tp_status, 1); + if (i32_ret < 0) { + LOGD(LOG_ERR, "[touch]%s: write data failed: %d\n", __func__, i32_ret); + goto exit_error; + } + LOGD(LOG_WARNING, "[touch]%s -> report not updated.\n", __func__); + goto exit_error; + } + u8_seq_no = p_u8_tp_status[POS_SEQ]; + p_u8_tp_status[POS_SEQ] = 0; + i32_ret = raydium_i2c_pda2_write(g_raydium_ts->client, + RAYDIUM_PDA2_TCH_RPT_STATUS_ADDR, p_u8_tp_status, 1); + if (i32_ret < 0) { + LOGD(LOG_ERR, "[touch]%s: write data failed: %d\n", __func__, i32_ret); + goto exit_error; + } + + u8_points_amount = p_u8_tp_status[POS_PT_AMOUNT]; + + if (u8_points_amount > MAX_TOUCH_NUM) + goto exit_error; + memcpy(p_u8_buf, &u8_read_buf[4], u8_points_amount * LEN_PT); + + raydium_touch_report(p_u8_buf, u8_points_amount); + +exit_error: + mutex_unlock(&g_raydium_ts->lock); + + return i32_ret; + +reset_error: + mutex_unlock(&g_raydium_ts->lock); +#ifdef ESD_SOLUTION_EN + u8_retry = 3; + while (u8_retry != 0) { + i32_ret = raydium_hw_reset_fun(g_raydium_ts->client); + LOGD(LOG_ERR, "[touch]%s: HW reset\n", __func__); + if (i32_ret < 0) { + msleep(100); + u8_retry--; + } else + break; + } +#endif + return i32_ret; +} + +static void raydium_work_handler(struct work_struct *work) +{ + int i32_ret = 0; + unsigned char u8_tp_status[MAX_TCH_STATUS_PACKET_SIZE]; + unsigned char u8_buf[MAX_REPORT_PACKET_SIZE]; + +#ifdef GESTURE_EN + unsigned char u8_i; + + if (g_u8_i2c_mode == PDA2_MODE) { + i32_ret = raydium_read_touchdata(u8_tp_status, u8_buf); + if (i32_ret < 0) { + LOGD(LOG_ERR, "[touch]%s, read_touchdata error, ret:%d\n", + __func__, i32_ret); + return; + } + } + /*when display on can use palm to suspend*/ + if (g_raydium_ts->blank == FB_BLANK_UNBLANK) { + if (u8_tp_status[POS_GES_STATUS] == RAD_PALM_ENABLE) { + if (g_raydium_ts->is_palm == 0) { + /* release all touches*/ + for (u8_i = 0; u8_i < g_raydium_ts->u8_max_touchs; + u8_i++) { + input_mt_slot(g_raydium_ts->input_dev, + u8_i); + input_mt_report_slot_state( + g_raydium_ts->input_dev, + MT_TOOL_FINGER, false); + } + input_mt_report_pointer_emulation( + g_raydium_ts->input_dev, + false); + /*press sleep key*/ + input_report_key(g_raydium_ts->input_dev, + KEY_SLEEP, true); + input_sync(g_raydium_ts->input_dev); + + LOGD(LOG_INFO, "[touch]palm_status = %d.\n", + u8_tp_status[POS_GES_STATUS]); + + g_raydium_ts->is_palm = 1; + /*goto exit;*/ + } + } else if ((u8_tp_status[POS_GES_STATUS] + == RAD_PALM_DISABLE) + && (g_raydium_ts->is_palm == 1)) { + LOGD(LOG_INFO, "[touch]leave palm mode.\n"); + input_report_key(g_raydium_ts->input_dev, + KEY_SLEEP, false); + input_sync(g_raydium_ts->input_dev); + + /*raydium_irq_control(raydium_ts, DISABLE);*/ + g_raydium_ts->is_palm = 0; + /*goto exit;*/ + } + } else if (g_raydium_ts->blank == FB_BLANK_VSYNC_SUSPEND || + g_raydium_ts->blank == FB_BLANK_POWERDOWN) { + /*need check small area*/ + if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP + && g_u8_wakeup_flag == false) { + input_report_key(g_raydium_ts->input_dev, KEY_POWER, true); + usleep_range(9500, 10500); + input_sync(g_raydium_ts->input_dev); + + input_report_key(g_raydium_ts->input_dev, KEY_POWER, false); + input_sync(g_raydium_ts->input_dev); + LOGD(LOG_INFO, "[touch]display wake up with g_u8_resetflag true\n"); + /*goto exit;*/ + } + } +#else + if (g_u8_i2c_mode == PDA2_MODE) { + i32_ret = raydium_read_touchdata(u8_tp_status, u8_buf); + if (i32_ret < 0) { + LOGD(LOG_ERR, "[touch]%s, read_touchdata error, ret:%d\n", + __func__, i32_ret); + } + } +#endif +} + + +/*The raydium device will signal the host about TRIGGER_FALLING. + *Processed when the interrupt is asserted. + */ +static irqreturn_t raydium_ts_interrupt(int irq, void *dev_id) +{ + bool result = false; + + LOGD(LOG_DEBUG, "[touch]%s\n", __func__); + /*For bootloader wrt/erase flash and software reset interrupt*/ + if ((g_u8_raydium_flag & ENG_MODE) != 0) { + LOGD(LOG_INFO, "[touch]RAD_ENG_MODE\n"); + g_u8_raydium_flag |= INT_FLAG; + } else { + if (!work_pending(&g_raydium_ts->work)) { + /* Clear interrupts*/ + result = queue_work(g_raydium_ts->workqueue, + &g_raydium_ts->work); + + if (!result) { + /*queue_work fail*/ + LOGD(LOG_ERR, "[touch]queue_work fail.\n"); + } + + + } else { + /*work pending*/ + mutex_lock(&g_raydium_ts->lock); + if (raydium_i2c_pda2_set_page(g_raydium_ts->client, + g_raydium_ts->is_suspend, + RAYDIUM_PDA2_PAGE_0) < 0) { + + LOGD(LOG_ERR, "[touch]%s: failed to set page in work_pending\n", + __func__); + } + mutex_unlock(&g_raydium_ts->lock); + + LOGD(LOG_WARNING, "[touch]work_pending\n"); + } + } + return IRQ_HANDLED; +} + +static int raydium_check_i2c_ready(unsigned short *u16_i2c_data) +{ + unsigned char u8_buf[4]; + int i32_ret = ERROR; + + mutex_lock(&g_raydium_ts->lock); + + if (g_u8_i2c_mode == PDA2_MODE) { + i32_ret = handle_i2c_pda_read(g_raydium_ts->client, + RAYDIUM_CHK_I2C_CMD, u8_buf, 4); + if (i32_ret < 0) + goto exit_error; + + if (u8_buf[3] != 0xF3) { + LOGD(LOG_ERR, "[touch]PDA2 read i2c fail\n"); + g_u8_i2c_mode = PDA_MODE; + i32_ret = handle_i2c_pda_read(g_raydium_ts->client, + RAYDIUM_CHK_I2C_CMD, u8_buf, 4); + if (i32_ret < 0) + goto exit_error; + } + } else { + i32_ret = handle_i2c_pda_read(g_raydium_ts->client, + RAYDIUM_CHK_I2C_CMD, u8_buf, 4); + if (i32_ret < 0) + goto exit_error; + + } + + *u16_i2c_data = u8_buf[3] << 8 | u8_buf[2]; + + LOGD(LOG_INFO, "[touch]RAD check I2C : 0x%02X%02X\n", u8_buf[3], u8_buf[2]); + +exit_error: + mutex_unlock(&g_raydium_ts->lock); + return i32_ret; +} + +#if defined(CONFIG_PM) +static void raydium_ts_do_suspend(void) +{ + unsigned char u8_i = 0; + + if (g_u8_raw_data_type == 0) + g_u8_resetflag = false; + if (g_raydium_ts->is_suspend == 1) { + LOGD(LOG_WARNING, "[touch]Already in suspend state\n"); + return; + } + + /*#ifndef GESTURE_EN*/ + raydium_irq_control(DISABLE); + /*#endif*/ + + /*clear workqueue*/ + if (!cancel_work_sync(&g_raydium_ts->work)) + LOGD(LOG_DEBUG, "[touch]workqueue is empty!\n"); + + LOGD(LOG_INFO, "[touch]%s.\n", __func__); + + /* release all touches */ + for (u8_i = 0; u8_i < g_raydium_ts->u8_max_touchs; u8_i++) { + input_mt_slot(g_raydium_ts->input_dev, u8_i); + input_mt_report_slot_state(g_raydium_ts->input_dev, + MT_TOOL_FINGER, + false); + } + input_mt_report_pointer_emulation(g_raydium_ts->input_dev, false); + input_sync(g_raydium_ts->input_dev); + +#ifdef GESTURE_EN + if (device_may_wakeup(&g_raydium_ts->client->dev)) { + LOGD(LOG_INFO, "[touch]Device may wakeup\n"); + if (!enable_irq_wake(g_raydium_ts->irq)) + g_raydium_ts->irq_wake = true; + + } else { + LOGD(LOG_INFO, "[touch]Device not wakeup\n"); + } + raydium_irq_control(ENABLE); +#endif + + g_raydium_ts->is_suspend = 1; +} + +static void raydium_ts_do_resume(void) +{ +#ifdef ESD_SOLUTION_EN + int i32_ret = 0; + unsigned char u8_retry = 0; +#endif + + + LOGD(LOG_INFO, "[touch]%s, %d.\n", __func__, g_raydium_ts->is_suspend); + if (g_raydium_ts->is_suspend == 0) { + LOGD(LOG_WARNING, "[touch]Already in resume state\n"); + return; + } + + /* clear interrupts*/ + mutex_lock(&g_raydium_ts->lock); + if (raydium_i2c_pda2_set_page(g_raydium_ts->client, + g_raydium_ts->is_suspend, RAYDIUM_PDA2_PAGE_0) < 0) { + LOGD(LOG_ERR, "[touch]%s: failed to set page\n", __func__); + mutex_unlock(&g_raydium_ts->lock); + return; + } + mutex_unlock(&g_raydium_ts->lock); + + /* clear workqueue*/ + if (!cancel_work_sync(&g_raydium_ts->work)) + LOGD(LOG_DEBUG, "[ raydium ]workqueue is empty!\n"); +#ifdef ESD_SOLUTION_EN + if (g_u8_checkflag == true) { + i32_ret = raydium_esd_check(); + if (i32_ret < 0) { + u8_retry = 3; + while (u8_retry != 0) { + i32_ret = raydium_hw_reset_fun(g_raydium_ts->client); + if (i32_ret < 0) { + msleep(100); + u8_retry--; + } else + break; + } + + } + g_u8_checkflag = false; + } +#endif + raydium_irq_control(ENABLE); +#ifdef GESTURE_EN + if (device_may_wakeup(&g_raydium_ts->client->dev)) { + LOGD(LOG_INFO, "[touch]Device may wakeup\n"); + if (g_raydium_ts->irq_wake) { + disable_irq_wake(g_raydium_ts->irq); + g_raydium_ts->irq_wake = false; + } + } else + LOGD(LOG_INFO, "[touch]Device not wakeup\n"); +#endif + + g_raydium_ts->is_suspend = 0; +} + +static int raydium_ts_suspend(struct device *dev) +{ + raydium_ts_do_suspend(); + return 0; +} + +static int raydium_ts_resume(struct device *dev) +{ + raydium_ts_do_resume(); + return 0; +} + +static const struct dev_pm_ops raydium_ts_pm_ops = { +#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)) + .suspend = raydium_ts_suspend, + .resume = raydium_ts_resume, +#endif /*end of CONFIG_PM*/ +}; + +/*used for touch lock feature*/ +static int raydium_ts_open(struct input_dev *input_dev) +{ + //int i32_ret = 0; + + LOGD(LOG_INFO, "[touch]%s()+\n", __func__); + + LOGD(LOG_INFO, "[touch]ts->blank:%x\n", g_raydium_ts->blank); + + if (g_raydium_ts->is_sleep == 1) { + mutex_lock(&g_raydium_ts->lock); + if (gpio_is_valid(g_raydium_ts->rst_gpio)) { + + g_u8_resetflag = true; + gpio_set_value(g_raydium_ts->rst_gpio, 1); + gpio_set_value(g_raydium_ts->rst_gpio, 0); + msleep(RAYDIUM_RESET_INTERVAL_MSEC);/*5ms*/ + gpio_set_value(g_raydium_ts->rst_gpio, 1); + msleep(RAYDIUM_RESET_DELAY_MSEC);/*100ms*/ + g_u8_i2c_mode = PDA2_MODE; + } + mutex_unlock(&g_raydium_ts->lock); + raydium_irq_control(ENABLE); + g_raydium_ts->is_sleep = 0; + LOGD(LOG_INFO, "[touch]disable touch lock.\n"); + } + //return i32_ret; + return 0; +} + +static void raydium_ts_close(struct input_dev *input_dev) +{ + int i32_ret = 0; + unsigned char u8_i = 0; + unsigned char u8_wbuffer[1]; + + LOGD(LOG_INFO, "[touch]%s()+\n", __func__); + + if (g_raydium_ts->is_sleep == 1) { + LOGD(LOG_INFO, "[touch]touch lock already enabled.\n"); + return; + } + + raydium_irq_control(DISABLE); + + for (u8_i = 0; u8_i < g_raydium_ts->u8_max_touchs; u8_i++) { + input_mt_slot(g_raydium_ts->input_dev, u8_i); + input_mt_report_slot_state(g_raydium_ts->input_dev, + MT_TOOL_FINGER, + false); + } + input_mt_report_pointer_emulation(g_raydium_ts->input_dev, false); + input_sync(g_raydium_ts->input_dev); + mutex_lock(&g_raydium_ts->lock); + i32_ret = raydium_i2c_pda2_set_page(g_raydium_ts->client, + g_raydium_ts->is_suspend, RAYDIUM_PDA2_PAGE_0); + if (i32_ret < 0) { + LOGD(LOG_ERR, "[touch]ret:%d\n", i32_ret); + goto exit_i2c_error; + } + u8_wbuffer[0] = RAYDIUM_HOST_CMD_PWR_SLEEP; + i32_ret = raydium_i2c_pda2_write(g_raydium_ts->client, + RAYDIUM_PDA2_HOST_CMD_ADDR, + u8_wbuffer, + 1); + if (i32_ret < 0) { + LOGD(LOG_ERR, "[touch]ret:%d\n", i32_ret); + goto exit_i2c_error; + } + + mutex_unlock(&g_raydium_ts->lock); + g_raydium_ts->is_sleep = 1; + LOGD(LOG_INFO, "[touch]enable touch lock.\n"); + return; + +exit_i2c_error: + mutex_unlock(&g_raydium_ts->lock); + raydium_irq_control(ENABLE); +} + +#else +static int raydium_ts_suspend(struct device *dev) +{ + return 0; +} + +static int raydium_ts_resume(struct device *dev) +{ + return 0; +} +#endif /*end of CONFIG_FB*/ + +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, + void *data) +{ + struct fb_event *evdata = data; + int *blank; + + if (evdata && evdata->data && event == FB_EVENT_BLANK && + g_raydium_ts && g_raydium_ts->client) { + blank = evdata->data; + g_raydium_ts->blank = (*blank); + switch (*blank) { + + /*screen on*/ + case FB_BLANK_UNBLANK: + LOGD(LOG_INFO, "[touch]FB_BLANK_UNBLANK\n"); +#ifdef GESTURE_EN + + /* clear palm status */ + + g_raydium_ts->is_palm = 0; +#endif + raydium_ts_resume(&g_raydium_ts->client->dev); + break; + + /*screen off*/ + case FB_BLANK_POWERDOWN: + LOGD(LOG_INFO, "[touch]FB_BLANK_POWERDOWN\n"); +#ifdef GESTURE_EN + + /* clear palm status */ + + g_raydium_ts->is_palm = 0; +#endif + raydium_ts_suspend(&g_raydium_ts->client->dev); + break; + + /*ambient mode*/ + case FB_BLANK_VSYNC_SUSPEND: + LOGD(LOG_INFO, "[touch]FB_BLANK_VSYNC_SUSPEND\n"); +#ifdef GESTURE_EN + + /* clear palm status */ + + g_raydium_ts->is_palm = 0; +#endif + + raydium_ts_suspend(&g_raydium_ts->client->dev); + break; + + default: + break; + } + } + + return 0; +} + +static void raydium_register_notifier(void) +{ + memset(&g_raydium_ts->fb_notif, 0, sizeof(g_raydium_ts->fb_notif)); + g_raydium_ts->fb_notif.notifier_call = fb_notifier_callback; + + /* register on the fb notifier and work with fb*/ + if (fb_register_client(&g_raydium_ts->fb_notif)) + LOGD(LOG_ERR, "[touch]register notifier failed\n"); +} + +static void raydium_unregister_notifier(void) +{ + fb_unregister_client(&g_raydium_ts->fb_notif); +} +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void raydium_ts_early_suspend(struct early_suspend *handler) +{ + + raydium_ts_do_suspend(); +} + +static void raydium_ts_late_resume(struct early_suspend *handler) +{ + raydium_ts_do_resume(); +} +#endif /*end of CONFIG_FB*/ + +#ifdef CONFIG_OF +static int raydium_get_dt_coords(struct device *dev, char *name, + struct raydium_ts_platform_data *pdata) +{ + u32 coords[COORDS_ARR_SIZE]; + struct property *prop; + struct device_node *np = dev->of_node; + int coords_size, rc; + + prop = of_find_property(np, name, NULL); + if (!prop) + return -EINVAL; + + if (!prop->value) + return -ENODATA; + + + coords_size = prop->length / sizeof(u32); + if (coords_size != COORDS_ARR_SIZE) { + LOGD(LOG_ERR, "[touch]invalid %s\n", name); + return -EINVAL; + } + + rc = of_property_read_u32_array(np, name, coords, coords_size); + if (rc && (rc != -EINVAL)) { + LOGD(LOG_ERR, "[touch]unable to read %s\n", name); + return rc; + } + + if (!strcmp(name, "raydium,display-coords")) { + pdata->x_min = coords[0]; + pdata->y_min = coords[1]; + pdata->x_max = coords[2]; + pdata->y_max = coords[3]; + } else { + LOGD(LOG_ERR, "[touch]unsupported property %s\n", name); + return -EINVAL; + } + + return 0; +} + +static int raydium_parse_dt(struct device *dev, + struct raydium_ts_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + int rc = 0; + u32 temp_val = 0; + + pdata->name = RAYDIUM_NAME; + + rc = raydium_get_dt_coords(dev, "raydium,display-coords", pdata); + if (rc) + return rc; + + + /* reset, irq gpio info */ + pdata->reset_gpio = of_get_named_gpio_flags(np, + "raydium,reset-gpio", + 0, + &pdata->reset_gpio_flags); + //if (pdata->reset_gpio < 0) + if ((s32)(pdata->reset_gpio) < 0) + return pdata->reset_gpio; + + + pdata->irq_gpio = of_get_named_gpio_flags(np, + "raydium,irq-gpio", + 0, + &pdata->irq_gpio_flags); + //if (pdata->irq_gpio < 0) + if ((s32)(pdata->irq_gpio) < 0) + return pdata->irq_gpio; + + + rc = of_property_read_u32(np, + "raydium,hard-reset-delay-ms", &temp_val); + if (!rc) + pdata->hard_rst_dly = temp_val; + else + return rc; + + + rc = of_property_read_u32(np, + "raydium,soft-reset-delay-ms", &temp_val); + if (!rc) + pdata->soft_rst_dly = temp_val; + else + return rc; + + + rc = of_property_read_u32(np, "raydium,num-max-touches", &temp_val); + if (!rc) + pdata->num_max_touches = temp_val; + else + return rc; +#ifdef FW_MAPPING_BYID_EN + rc = of_property_read_u32(np, "raydium,fw_id", &temp_val); + if (!rc) + pdata->fw_id = temp_val; + else + return rc; +#endif + return 0; +} +#else +static int raydium_parse_dt(struct device *dev, + struct raydium_ts_platform_data *pdata) +{ + return -ENODEV; +} +#endif /*end of CONFIG_OF*/ + +static void raydium_input_set(struct input_dev *input_dev) +{ + int ret = 0; + unsigned char i; + + input_dev->name = "raydium_ts";/*name need same with .idc*/ + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &g_raydium_ts->client->dev; + input_dev->open = raydium_ts_open;/*touch lock*/ + input_dev->close = raydium_ts_close; + input_set_drvdata(input_dev, g_raydium_ts); + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + LOGD(LOG_INFO, "[touch]set abs prarams x[%d], y[%d]\n", + g_raydium_ts->x_max, g_raydium_ts->y_max); + + /* Multitouch input params setup */ + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, + g_raydium_ts->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, + g_raydium_ts->y_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, PRESS_MAX, 0, 0); + input_set_abs_params(input_dev, + ABS_MT_TOUCH_MAJOR, 0, WIDTH_MAX, 0, 0); + input_set_abs_params(input_dev, + ABS_MT_TOUCH_MINOR, 0, WIDTH_MAX, 0, 0); + + ret = input_mt_init_slots(input_dev, MAX_TOUCH_NUM, + INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); + if (ret) + LOGD(LOG_ERR, "[touch]failed to initialize MT slots: %d\n", ret); + + for (i = 0; i < (MAX_TOUCH_NUM * 2); i++) + gst_slot[i] = gst_slot_init; + +} +static int raydium_set_resolution(void) +{ + unsigned char u8_buf[4]; + int i32_ret = -1; + unsigned int u32_x, u32_y; + + mutex_lock(&g_raydium_ts->lock); + + i32_ret = raydium_i2c_pda2_set_page(g_raydium_ts->client, + g_raydium_ts->is_suspend, + RAYDIUM_PDA2_PAGE_0); + if (i32_ret < 0) + goto exit_error; + + i32_ret = raydium_i2c_pda2_read(g_raydium_ts->client, + RAYDIUM_PDA2_DISPLAY_INFO_ADDR, + u8_buf, 4); + if (i32_ret < 0) + goto exit_error; + + u32_x = u8_buf[3] << 8 | u8_buf[2]; + u32_y = u8_buf[1] << 8 | u8_buf[0]; + + LOGD(LOG_INFO, "[touch]RAD display info x:%d, y:%d\n", u32_x, u32_y); + + if (u32_x > 100 && u32_y > 100 && + u32_x < 600 && u32_y < 600) { + g_raydium_ts->x_max = u32_x - 1; + g_raydium_ts->y_max = u32_y - 1; + } + +exit_error: + mutex_unlock(&g_raydium_ts->lock); + return i32_ret; +} + +static int raydium_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct raydium_ts_platform_data *pdata = + (struct raydium_ts_platform_data *)client->dev.platform_data; + + struct input_dev *input_dev; + unsigned short u16_i2c_data; + int ret = 0; + + LOGD(LOG_INFO, "[touch] probe\n"); + + if (client->dev.of_node) { + pdata = devm_kzalloc(&client->dev, + sizeof(struct raydium_ts_platform_data), + GFP_KERNEL); + if (!pdata) { + LOGD(LOG_ERR, "[touch]failed to allocate memory\n"); + return -ENOMEM; + } + + ret = raydium_parse_dt(&client->dev, pdata); + if (ret) { + LOGD(LOG_ERR, "[touch]device tree parsing failed\n"); + goto parse_dt_failed; + } + } else + pdata = client->dev.platform_data; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + ret = -ENODEV; + goto exit_check_functionality_failed; + } + + g_raydium_ts = devm_kzalloc(&client->dev, + sizeof(struct raydium_ts_data), + GFP_KERNEL); + if (!g_raydium_ts) { + LOGD(LOG_ERR, "[touch]failed to allocate input driver data\n"); + return -ENOMEM; + } + + raydium_variable_init(); + + mutex_init(&g_raydium_ts->lock); + + i2c_set_clientdata(client, g_raydium_ts); + g_raydium_ts->irq_enabled = false; + g_raydium_ts->irq_wake = false; + + g_raydium_ts->irq_gpio = pdata->irq_gpio; + g_raydium_ts->rst_gpio = pdata->reset_gpio; + client->irq = g_raydium_ts->irq_gpio; + g_raydium_ts->u8_max_touchs = pdata->num_max_touches; + g_raydium_ts->client = client; + g_raydium_ts->x_max = pdata->x_max - 1; + g_raydium_ts->y_max = pdata->y_max - 1; + g_raydium_ts->is_suspend = 0; + g_raydium_ts->is_sleep = 0; +#ifdef GESTURE_EN + g_raydium_ts->is_palm = 0; +#endif + g_raydium_ts->fw_version = 0; + device_init_wakeup(&client->dev, 1); + +#ifdef MSM_NEW_VER + ret = raydium_ts_pinctrl_init(); + if (!ret && g_raydium_ts->ts_pinctrl) { + /* + * Pinctrl handle is optional. If pinctrl handle is found + * let pins to be configured in active state. If not + * found continue further without error. + */ + ret = pinctrl_select_state(g_raydium_ts->ts_pinctrl, + g_raydium_ts->pinctrl_state_active); + if (ret < 0) + LOGD(LOG_ERR, "[touch]failed to set pin to active state\n"); + } +#endif /*end of MSM_NEW_VER*/ + + ret = raydium_gpio_configure(true); + if (ret < 0) { + LOGD(LOG_ERR, "[touch]failed to configure the gpios\n"); + goto err_gpio_req; + } + /*modify dtsi to 360*/ + msleep(pdata->soft_rst_dly); + if (raydium_disable_i2c_deglitch() == ERROR) { + LOGD(LOG_ERR, "[touch]disable i2c deglicth NG!\r\n"); + ret = -ENODEV; + goto exit_check_i2c; + } + + /*print touch i2c ready*/ + ret = raydium_check_i2c_ready(&u16_i2c_data); + if (ret < 0) { + LOGD(LOG_ERR, "[touch]Check I2C failed\n"); + ret = -ENODEV; + goto exit_check_i2c; + } + + /*input device initialization*/ + input_dev = input_allocate_device(); + if (!input_dev) { + ret = -ENOMEM; + LOGD(LOG_ERR, "[touch]failed to allocate input device\n"); + goto exit_input_dev_alloc_failed; + } + + raydium_set_resolution(); + + g_raydium_ts->input_dev = input_dev; + raydium_input_set(input_dev); + + ret = input_register_device(input_dev); + if (ret) { + LOGD(LOG_ERR, "[touch]failed to register input device: %s\n", + dev_name(&client->dev)); + goto exit_input_register_device_failed; + } + +#ifdef GESTURE_EN + input_set_capability(input_dev, EV_KEY, KEY_SLEEP); + input_set_capability(input_dev, EV_KEY, KEY_POWER); +#endif + + /*suspend/resume routine*/ +#if defined(CONFIG_FB) + raydium_register_notifier(); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + /*Early-suspend level*/ + g_raydium_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + g_raydium_ts->early_suspend.suspend = raydium_ts_early_suspend; + g_raydium_ts->early_suspend.resume = raydium_ts_late_resume; + register_early_suspend(&g_raydium_ts->early_suspend); +#endif/*end of CONFIG_FB*/ + +#ifdef CONFIG_RM_SYSFS_DEBUG + raydium_create_sysfs(client); +#endif/*end of CONFIG_RM_SYSFS_DEBUG*/ + + INIT_WORK(&g_raydium_ts->work, raydium_work_handler); + + g_raydium_ts->workqueue = create_singlethread_workqueue("raydium_ts"); + /*irq_gpio = 13 irqflags = 108*/ + LOGD(LOG_INFO, "[touch]pdata irq : %d\n", g_raydium_ts->irq_gpio); + LOGD(LOG_INFO, "[touch]client irq : %d, pdata flags : %d\n", + client->irq, pdata->irqflags); + + g_raydium_ts->irq = gpio_to_irq(pdata->irq_gpio); + ret = request_threaded_irq(g_raydium_ts->irq, NULL, raydium_ts_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_NO_SUSPEND, + client->dev.driver->name, g_raydium_ts); + + if (ret < 0) { + LOGD(LOG_ERR, "[touch]raydium_probe: request irq failed\n"); + goto exit_irq_request_failed; + } + + g_raydium_ts->irq_desc = irq_to_desc(g_raydium_ts->irq); + g_raydium_ts->irq_enabled = true; + + /*disable_irq then enable_irq for avoid Unbalanced enable for IRQ */ + + /*raydium_irq_control(ts, ENABLE);*/ + + LOGD(LOG_INFO, "[touch]RAD Touch driver ver :0x%X\n", g_u32_driver_version); + + /*fw update check*/ + ret = raydium_fw_update_check(u16_i2c_data); + if (ret < 0) { + LOGD(LOG_ERR, "[touch]FW update check failed\n"); + ret = -ENODEV; + goto exit_irq_request_failed; + } + return 0; + +exit_irq_request_failed: +#if defined(CONFIG_FB) + raydium_unregister_notifier(); +#endif/*end of CONFIG_FB*/ + + cancel_work_sync(&g_raydium_ts->work); + input_unregister_device(input_dev); + +exit_input_register_device_failed: + input_free_device(input_dev); + +exit_input_dev_alloc_failed: +exit_check_i2c: + if (gpio_is_valid(pdata->reset_gpio)) + gpio_free(pdata->reset_gpio); + + if (gpio_is_valid(pdata->irq_gpio)) + gpio_free(pdata->irq_gpio); + +err_gpio_req: +#ifdef MSM_NEW_VER + if (g_raydium_ts->ts_pinctrl) { + if (IS_ERR_OR_NULL(g_raydium_ts->pinctrl_state_release)) { + devm_pinctrl_put(g_raydium_ts->ts_pinctrl); + g_raydium_ts->ts_pinctrl = NULL; + } else { + ret = pinctrl_select_state(g_raydium_ts->ts_pinctrl, + g_raydium_ts->pinctrl_state_release); + if (ret) + LOGD(LOG_ERR, "[touch]pinctrl_select_state failed\n"); + } + } +#endif/*end of MSM_NEW_VER*/ + +parse_dt_failed: +exit_check_functionality_failed: + return ret; + +} + +static int raydium_ts_remove(struct i2c_client *client) +{ + +#if defined(CONFIG_FB) + raydium_unregister_notifier(); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&g_raydium_ts->early_suspend); +#endif/*end of CONFIG_FB*/ + input_unregister_device(g_raydium_ts->input_dev); + input_free_device(g_raydium_ts->input_dev); + gpio_free(g_raydium_ts->rst_gpio); + +#ifdef CONFIG_RM_SYSFS_DEBUG + raydium_release_sysfs(client); +#endif /*end of CONFIG_RM_SYSFS_DEBUG*/ + + free_irq(client->irq, g_raydium_ts); + + if (gpio_is_valid(g_raydium_ts->rst_gpio)) + gpio_free(g_raydium_ts->rst_gpio); + + if (gpio_is_valid(g_raydium_ts->irq_gpio)) + gpio_free(g_raydium_ts->irq_gpio); + + cancel_work_sync(&g_raydium_ts->work); + destroy_workqueue(g_raydium_ts->workqueue); + + + //kfree(g_raydium_ts); + + i2c_set_clientdata(client, NULL); + return 0; +} + +static const struct i2c_device_id raydium_ts_id[] = { + {RAYDIUM_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, raydium_ts_id); + +#ifdef CONFIG_OF +static const struct of_device_id raydium_match_table[] = { + { .compatible = "raydium,raydium-ts",}, + { }, +}; +#else +#define raydium_match_table NULL +#endif/*end of CONFIG_OF*/ + +static struct i2c_driver raydium_ts_driver = { + .probe = raydium_ts_probe, + .remove = raydium_ts_remove, + .id_table = raydium_ts_id, + .driver = { + .name = RAYDIUM_NAME, + .owner = THIS_MODULE, + .of_match_table = raydium_match_table, +#if defined(CONFIG_PM) + .pm = &raydium_ts_pm_ops, +#endif/*end of CONFIG_PM*/ + }, +}; + +static int __init raydium_ts_init(void) +{ + int ret; + + ret = i2c_add_driver(&raydium_ts_driver); + return ret; +} + +static void __exit raydium_ts_exit(void) +{ + i2c_del_driver(&raydium_ts_driver); +} + +module_init(raydium_ts_init); +module_exit(raydium_ts_exit); + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("Raydium TouchScreen driver"); +MODULE_LICENSE("GPL"); diff --git a/raydium/raydium_driver.h b/raydium/raydium_driver.h new file mode 100644 index 0000000000..60ed6f4d37 --- /dev/null +++ b/raydium/raydium_driver.h @@ -0,0 +1,432 @@ +/* raydium_driver.h + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + + +#ifndef __LINUX_RAYDIUM_H +#define __LINUX_RAYDIUM_H +#define RAYDIUM_NAME "raydium_ts" +#define COORDS_ARR_SIZE 4 +#define I2C_VTG_MIN_UV 1800000 +#define I2C_VTG_MAX_UV 1800000 +#define RAD_MAIN_VERSION 0x01 +#define RAD_MINOR_VERSION 0x01 +#define RAD_CUSTOMER_VERSION 0x0100 + +#if defined(CONFIG_TOUCHSCREEN_RM_TS) +/* IC timing control arguments */ +#define RAYDIUM_POWERON_DELAY_USEC 500 +#define RAYDIUM_RESET_INTERVAL_MSEC 5 +#define RAYDIUM_RESET_RESTORE_USEC 200 +#define RAYDIUM_RESET_DELAY_MSEC 100 + +/* I2C bus slave address(ID) */ +#define RAYDIUM_I2C_EID (0x5A) +#define RAYDIUM_I2C_NID (0x39) +#define RAYDIUM_I2C_PDA_CMD 0x66 + +/* I2C R/W configuration literal */ +#define RAYDIUM_I2C_WRITE I2C_SMBUS_WRITE +#define RAYDIUM_I2C_READ I2C_SMBUS_READ +#define SYN_I2C_RETRY_TIMES 1 +#define MAX_WRITE_PACKET_SIZE 128 +#define MAX_READ_PACKET_SIZE 128 + +/* PDA address and bit definition*/ +#define RAD_READ_FT_DATA_CMD 0x2000019C +/* 1byte, disable:0x00 ; enable:0x20*/ +#define RAD_GESTURE_STATE_CMD 0x200005F4 +#define RAD_GESTURE_DISABLE 0x00 +#define RAD_GESTURE_ENABLE 0x20 +/* 4bytes, [0]:ready ; [1]:type ; [2]:direction*/ +#define RAD_GESTURE_RESULT_CMD 0x200005F0 +#define RAD_ENABLE_PDA2 0x04 +#define RAD_ENABLE_SI2 0x02 + +/* PDA literal */ +#define MASK_8BIT 0xFF +#define RAD_I2C_PDA_ADDRESS_LENGTH 4 +#define PDA_MODE 0x01 +#define PDA2_MODE 0x02 +#define RAD_I2C_PDA_MODE_DISABLE 0x00 +#define RAD_I2C_PDA_MODE_ENABLE 0x80 +/* Using byte mode due to data might be not word-aligment */ +#define RAD_I2C_PDA_MODE_WORD_MODE 0x40 +#define RAD_I2C_PDA_2_MODE_DISABLE 0x20 + +#define I2C_PDA2_BYTE_MODE 0x03 +#define I2C_PDA2_WORD_MODE 0x43 + +#define RAD_PALM_DISABLE 0x00 +#define RAD_PALM_ENABLE 0x01 +#define RAD_WAKE_UP 0x02 +#define RAYDIUM_TEST_FW 0x80 +#define RAYDIUM_TEST_PARA 0x40 +#define RAYDIUM_BOOTLOADER 0x20 +#define RAYDIUM_FIRMWARE 0x10 +#define RAYDIUM_PARA 0x08 +#define RAYDIUM_COMP 0x04 +#define RAYDIUM_BASELINE 0x02 +#define RAYDIUM_INIT 0x01 +#define FAIL 0 +#define ERROR -1 +#define SUCCESS 1 +#define DISABLE 0 +#define ENABLE 1 + +/* PDA2 setting */ +/* Page 0 ~ Page A */ +#define MAX_PAGE_AMOUNT 11 + +/* PDA2 address and setting definition*/ +#define RAYDIUM_PDA2_TCH_RPT_STATUS_ADDR 0x00 /* only in Page 0 */ +#define RAYDIUM_PDA2_TCH_RPT_ADDR 0x01 /* only in Page 0 */ +#define RAYDIUM_PDA2_HOST_CMD_ADDR 0x02 /* only in Page 0 */ +#define RAYDIUM_PDA2_PALM_AREA_ADDR 0x03 /* only in Page 0 */ +#define RAYDIUM_PDA2_GESTURE_RPT_ADDR 0x04 /* only in Page 0 */ +#define RAYDIUM_PDA2_PALM_STATUS_ADDR 0x05 /* only in Page 0 */ +#define RAYDIUM_PDA2_FW_VERSION_ADDR 0x06 /* only in Page 0 */ +#define RAYDIUM_PDA2_PANEL_VERSION_ADDR 0x07 /* only in Page 0 */ +#define RAYDIUM_PDA2_DISPLAY_INFO_ADDR 0x08 /* only in Page 0 */ +#define RAYDIUM_PDA2_PDA_CFG_ADDR 0x09 /* only in Page 0 */ +#define RAYDIUM_PDA2_RAWDATA_ADDR 0x0B /* only in Page 0 */ +/* Page 0 ~ Page 9 will be directed to Page 0 */ +#define RAYDIUM_PDA2_PAGE_ADDR 0x0A +#define RAYDIUM_PDA2_PAGE_0 0x00 +/* temporary switch to PDA once */ +#define RAYDIUM_PDA2_ENABLE_PDA 0x0A +/* permanently switch to PDA mode */ +#define RAYDIUM_PDA2_2_PDA (MAX_PAGE_AMOUNT + 2) + +/* Raydium host cmd */ +#define RAYDIUM_HOST_CMD_NO_OP 0x00 +#define RAYDIUM_HOST_CMD_PWR_SLEEP 0x30 +#define RAYDIUM_HOST_CMD_DISPLAY_MODE 0x33 +#define RAYDIUM_HOST_CMD_CALIBRATION 0x5C +#define RAYDIUM_HOST_CMD_TP_MODE 0x60 +#define RAYDIUM_HOST_CMD_FT_MODE 0x61 + +/* Raydium Register define */ +#define RAYDIUM_PDA_BOOTVERSION 0x00000080 +#define RAYDIUM_PDA_FIRMWAREADDR 0x00000800 +#define RAYDIUM_PDA_PARAADDR 0x00007B00 +#define RAYDIUM_PDA_FIRMWARELENGTH 0x00007300 +#define RAYDIUM_PDA_PARALENGTH 0x00000178 +#define RAYDIUM_PDA_CRCLENGTH 0x00007474 +#define RAYDIUM_PDA_SYNCDATA 0x20000200 +#define RAYDIUM_PDA_BOOTENG1 0x20000204 +#define RAYDIUM_PDA_BOOTENG2 0x20000208 +#define RAYDIUM_PDA_BOOTENG3 0x2000020C +#define RAYDIUM_PDA_BOOTENG4 0x20000210 +#define RAYDIUM_PDA_BOOTSTATE 0x20000214 +#define RAYDIUM_PDA_BOOTMODE 0x20000218 +#define RAYDIUM_PDA_BLKEN 0x40000000 +#define RAYDIUM_PDA_BLKRST 0x40000004 +#define RAYDIUM_PDA_MISCIER 0x40000014 +#define RAYDIUM_PDA_I2CENG 0x50000610 +#define RAYDIUM_PDA_FLASHPRO 0x50000624 +#define RAYDIUM_PDA_I2CREG 0x50000628 +#define RAYDIUM_PDA_PRAMLOCK 0x50000900 +#define RAYDIUM_PDA_PRAMTYPE 0x50000904 +#define RAYDIUM_PDA_PRAMADDR 0x50000908 +#define RAYDIUM_PDA_PRAMLENGTH 0x5000090C +#define RAYDIUM_PDA_FLHADDR 0x50000910 +#define RAYDIUM_PDA_FLHCTL 0x50000914 +#define RAYDIUM_PDA_BOOTREG 0x50000918 +#define RAYDIUM_PDA_FLKEY1 0x50000934 +#define RAYDIUM_PDA_FLKEY2 0x50000938 +#define RAYDIUM_PDA_FLDATA 0x5000093C +#define RAYDIUM_PDA_PRGCHKSUMENG 0x5000094C +#define RAYDIUM_PDA_PRGCHKSUMADDR 0x50000974 +#define RAYDIUM_PDA_PRGCHKSUMRESULT 0x50000978 +#define RAYDIUM_CHK_I2C_CMD 0x500009BC +#define RAYDIUM_REG_GPIO_DEGLITCH 0x50000E1C + +#define I2CTB_LOCK (0x00000001<<6) +#define BOTLR_LOCK (0x00000001<<5) +#define USEFW_LOCK (0x00000001<<4) +#define CONFIG_LOCK (0x00000001<<3) +#define COMP_LOCK (0x00000001<<2) +#define BASEL_LOCK (0x00000001<<1) +#define INICO_LOCK (0x00000001<<0) + +/* ['h5000_0904], [32'h0000_0000], Program RAM store type, PRAM_STORE_TYPE */ +#define BOTLR_AREA (0x00000001<<5) +#define USEFW_AREA (0x00000001<<4) +#define CONFIG_AREA (0x00000001<<3) +#define COMP_AREA (0x00000001<<2) +#define BASEL_AREA (0x00000001<<1) +#define INICO_AREA (0x00000001<<0) + + +/* PDA2 literal */ +/* entry byte + target page byte */ +#define RAYDIUM_I2C_PDA2_PAGE_LENGTH 2 + + +/* Touch report */ +#define MAX_TOUCH_NUM 2 +#define MAX_REPORT_PACKET_SIZE 35 +#define MAX_TCH_STATUS_PACKET_SIZE 4 +#define PRESS_MAX 0xFFFF +#define WIDTH_MAX 0xFFFF +#define BYTE_SHIFT 8 +#define TOUCH_PRESS 0 +#define TOUCH_RELEASE 1 +#define TOUCH_MOVE 2 +#define TOUCH_COVER 3 +#define TOUCH_SHORTCLICK 4 + +/* FW update literal */ +#define RAYDIUM_FW_BIN_PATH_LENGTH 256 + +#define RAD_BOOT_3X_SIZE 0x800 +#define RAD_INIT_3X_SIZE 0x80 +#define RAD_FW_3X_SIZE 0x7300 +#define RAD_PARA_3X_SIZE 0x174 +#define RAD_TESTFW_3X_SIZE (RAD_FW_3X_SIZE + RAD_PARA_3X_SIZE + 4) + +#define RAD_CMD_UPDATE_BIN 0x80 +#define RAD_CMD_UPDATE_END 0x81 +#define RAD_CMD_BURN_FINISH 0x82 + +/* FT APK literal */ +#define RAD_HOST_CMD_POS 0x00 +#define RAD_FT_CMD_POS 0x01 +#define RAD_FT_CMD_LENGTH 0x02 + +/* FT APK data type */ +#define RAYDIUM_FT_UPDATE 0x00 + +/*Raydium system flag*/ +#define INT_FLAG 0x01 +#define ENG_MODE 0x02 +#define NORMAL_MODE 0x00 +/* define display mode */ +#define ACTIVE_MODE 0x00 +#define AMBIENT_MODE 0x01 +#define SLEEP_MODE 0x02 + +/* Enable sysfs */ +#define CONFIG_RM_SYSFS_DEBUG + +/* Gesture switch */ +#define GESTURE_EN + +/* Enable FW update */ +/* #define FW_UPDATE_EN */ +/* #define FW_MAPPING_EN */ +#define MSM_NEW_VER + +/* enable ESD */ +/* #define ESD_SOLUTION_EN */ + +#define RAD_SELFTEST +#define PARA_FW_VERSION_OFFSET 4 + +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" + +struct raydium_ts_data { + unsigned int irq; + unsigned int irq_gpio; + unsigned int rst_gpio; + unsigned int x_max; + unsigned int y_max; + unsigned int x_pos[2]; + unsigned int y_pos[2]; + unsigned int pressure; + unsigned int is_suspend; + unsigned int is_sleep; +#ifdef GESTURE_EN + unsigned int is_palm; +#endif + unsigned char u8_max_touchs; + + struct i2c_client *client; + struct input_dev *input_dev; + struct mutex lock; + struct work_struct work; + struct workqueue_struct *workqueue; + struct irq_desc *irq_desc; + bool irq_enabled; + bool irq_wake; + +#if defined(CONFIG_FB) + struct notifier_block fb_notif; + int blank; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif /*end of CONFIG_FB*/ + + /*struct regulator *vdd;*/ + struct regulator *vcc_i2c; + unsigned int fw_version; + unsigned short id; + char *vcc_name; +#ifdef MSM_NEW_VER + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; +#endif /*end of MSM_NEW_VER*/ + + +}; +struct raydium_platform_data { + char *vdd_name; + int irq_gpio_number; + int reset_gpio_number; + int x_max; + int y_max; +}; + +struct raydium_ts_platform_data { + char *name; + u32 irqflags; + u32 irq_gpio; + u32 irq_gpio_flags; + u32 reset_gpio; + u32 reset_gpio_flags; + u32 x_max; + u32 y_max; + u32 x_min; + u32 y_min; + u32 hard_rst_dly; + u32 soft_rst_dly; + u32 num_max_touches; + u32 fw_id; +}; + +/* TODO: Using struct+memcpy instead of array+offset*/ +enum raydium_pt_report_status { + POS_SEQ = 0,/*1:touch, 0:no touch*/ + POS_PT_AMOUNT, + POS_GES_STATUS, + POS_FW_STATE, +}; + +enum raydium_pt_report_idx { + POS_PT_ID = 0, + POS_X_L, + POS_X_H, + POS_Y_L, + POS_Y_H, + POS_PRESSURE_L, + POS_PRESSURE_H, + POS_WX_L, + POS_WX_H, + POS_WY_L, + POS_WY_H, + LEN_PT = 11 +}; + +enum raydium_log_level { + LOG_NONE = 0, + LOG_ALERT, + LOG_ERR, + LOG_WARNING, + LOG_INFO, + LOG_DEBUG = 5 +}; +extern int raydium_read_touchdata(unsigned char *tp_status, unsigned char *buf); +extern int raydium_mem_table_setting(void); +extern int wait_fw_state(struct i2c_client *client, unsigned int u32_addr, + unsigned int u32_state, unsigned long u32_delay_us, + unsigned short u16_retry); +extern int wait_irq_state(struct i2c_client *client, + unsigned int u32_retry_time, + unsigned int u32_delay_us); +extern void raydium_irq_control(bool enable); + +extern int raydium_i2c_mode_control(struct i2c_client *client, + unsigned char u8_mode); +extern int raydium_i2c_pda_read(struct i2c_client *client, + unsigned int u32_addr, unsigned char *u8_r_data, + unsigned short u16_length); +extern int raydium_i2c_pda_write(struct i2c_client *client, + unsigned int u32_addr, unsigned char *u8_w_data, + unsigned short u16_length); +extern int handle_i2c_pda_write(struct i2c_client *client, + unsigned int u32_addr, unsigned char *u8_w_data, + unsigned short u16_length); +extern int handle_i2c_pda_read(struct i2c_client *client, + unsigned int u32_addr, unsigned char *u8_r_data, + unsigned short u16_length); +extern int raydium_i2c_pda2_read(struct i2c_client *client, + unsigned char u8_addr, + unsigned char *u8_r_data, + unsigned short u16_length); +extern int raydium_i2c_pda2_write(struct i2c_client *client, + unsigned char u8_addr, + unsigned char *u8_w_data, + unsigned short u16_length); +extern int raydium_i2c_pda2_set_page(struct i2c_client *client, + unsigned int is_suspend, + unsigned char u8_page); +extern int raydium_i2c_write_pda_via_pda2(struct i2c_client *client, + unsigned int u32_addr, unsigned char *u8_w_data, + unsigned short u16_length); +extern int raydium_i2c_read_pda_via_pda2(struct i2c_client *client, + unsigned int u32_addr, unsigned char *u8_r_data, + unsigned short u16_length); +extern unsigned char raydium_disable_i2c_deglitch(void); +extern unsigned char raydium_selftest_stop_mcu(struct i2c_client *client); +extern int raydium_burn_comp(struct i2c_client *client); +extern int raydium_burn_fw(struct i2c_client *client); + +extern int raydium_load_test_fw(struct i2c_client *client); +extern int raydium_fw_update_check(unsigned short u16_i2c_data); +extern int raydium_i2c_pda_set_address(unsigned int u32_address, + unsigned char u8_mode); +extern void raydium_mem_table_init(unsigned short u16_id); +extern int raydium_id_init(unsigned char u8_type); + +#ifdef RAD_SELFTEST +extern int raydium_do_selftest(struct raydium_ts_data *ts); +#endif +int raydium_esd_check(void); + +extern struct attribute *raydium_attributes[]; +extern const struct attribute_group raydium_attr_group; + +extern unsigned char g_u8_raydium_flag; +extern unsigned char g_u8_addr; +extern unsigned char g_u8_i2c_mode; +extern unsigned char g_u8_upgrade_type; +extern unsigned char g_u8_raw_data_type; +extern unsigned int g_u32_raw_data_len; /* 128 bytes*/ +extern unsigned int g_u32_length; +extern unsigned long g_u32_addr; +extern unsigned char *g_rad_fw_image, *g_rad_init_image; +extern unsigned char *g_rad_boot_image, *g_rad_para_image; +extern unsigned char *g_rad_testfw_image, *g_rad_testpara_image; +extern unsigned char g_u8_table_setting, g_u8_table_init; +extern unsigned int g_u32_driver_version; +extern unsigned char g_u8_resetflag; +extern struct raydium_ts_data *g_raydium_ts; +extern unsigned char g_u8_log_level; + +#define LOGD(a, fmt, ...) {\ + if (a <= g_u8_log_level) {\ + pr_info(pr_fmt(fmt), ##__VA_ARGS__);\ + } \ +} +#endif +#endif /*__LINUX_RAYDIUM_H*/ + diff --git a/raydium/raydium_fw_update.c b/raydium/raydium_fw_update.c new file mode 100644 index 0000000000..7b18038765 --- /dev/null +++ b/raydium/raydium_fw_update.c @@ -0,0 +1,1135 @@ +/* raydium_fw_update.c + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "raydium_driver.h" +#include "rad_fw_image_30.h" +#if defined(FW_MAPPING_EN) +#include "rad_fw_image_31.h" +#endif + +void raydium_mem_table_init(unsigned short u16_id) +{ + LOGD(LOG_INFO, "[touch]Raydium table init 0x%x\n", u16_id); + + g_rad_boot_image = kzalloc(RAD_BOOT_3X_SIZE, GFP_KERNEL); + g_rad_init_image = kzalloc(RAD_INIT_3X_SIZE, GFP_KERNEL); + g_rad_fw_image = kzalloc(RAD_FW_3X_SIZE, GFP_KERNEL); + g_rad_para_image = kzalloc(RAD_PARA_3X_SIZE + 4, GFP_KERNEL); + g_rad_testfw_image = kzalloc(RAD_TESTFW_3X_SIZE, GFP_KERNEL); + g_rad_testpara_image = kzalloc(RAD_PARA_3X_SIZE + 4, + GFP_KERNEL); + g_u8_table_init = SUCCESS; +} + +int raydium_mem_table_setting(void) +{ + int i32_ret = SUCCESS; + + LOGD(LOG_INFO, "[touch]Raydium ID is 0x%x\n", g_raydium_ts->id); + switch (g_raydium_ts->id) { + case RAD_30: + memcpy(g_rad_boot_image, u8_rad_boot_30, RAD_BOOT_3X_SIZE); + memcpy(g_rad_init_image, u8_rad_init_30, RAD_INIT_3X_SIZE); + memcpy(g_rad_fw_image, u8_rad_fw_30, RAD_FW_3X_SIZE); + memcpy(g_rad_testfw_image, u8_rad_testfw_30, RAD_FW_3X_SIZE); + memcpy(g_rad_testfw_image + RAD_FW_3X_SIZE, u8_rad_testpara_30 + , RAD_PARA_3X_SIZE + 4); + memcpy(g_rad_para_image, u8_rad_para_30, RAD_PARA_3X_SIZE + 4); + memcpy(g_rad_testpara_image, u8_rad_testpara_30, + RAD_PARA_3X_SIZE + 4); + break; +#if defined(FW_MAPPING_EN) + case RAD_31: + memcpy(g_rad_boot_image, u8_rad_boot_31, RAD_BOOT_3X_SIZE); + memcpy(g_rad_init_image, u8_rad_init_31, RAD_INIT_3X_SIZE); + memcpy(g_rad_fw_image, u8_rad_fw_31, RAD_FW_3X_SIZE); + memcpy(g_rad_testfw_image, u8_rad_testfw_31, RAD_FW_3X_SIZE); + memcpy(g_rad_testfw_image + RAD_FW_3X_SIZE, u8_rad_testpara_31, + RAD_PARA_3X_SIZE + 4); + memcpy(g_rad_para_image, u8_rad_para_31, RAD_PARA_3X_SIZE + 4); + memcpy(g_rad_testpara_image, u8_rad_testpara_31, + RAD_PARA_3X_SIZE + 4); + break; +#endif + default: + LOGD(LOG_WARNING, "[touch]mapping ic setting use default fw\n"); + memcpy(g_rad_boot_image, u8_rad_boot_30, RAD_BOOT_3X_SIZE); + memcpy(g_rad_init_image, u8_rad_init_30, RAD_INIT_3X_SIZE); + memcpy(g_rad_fw_image, u8_rad_fw_30, RAD_FW_3X_SIZE); + memcpy(g_rad_testfw_image, u8_rad_testfw_30, RAD_FW_3X_SIZE); + memcpy(g_rad_testfw_image + RAD_FW_3X_SIZE, u8_rad_testpara_30 + , RAD_PARA_3X_SIZE + 4); + memcpy(g_rad_para_image, u8_rad_para_30, RAD_PARA_3X_SIZE + 4); + memcpy(g_rad_testpara_image, u8_rad_testpara_30, + RAD_PARA_3X_SIZE + 4); + g_raydium_ts->id = RAD_30; + i32_ret = SUCCESS; + break; + } + + g_u8_table_setting = 0; + return i32_ret; +} + +int raydium_id_init(unsigned char u8_type) +{ + int i32_ret = ERROR; + + i32_ret = 0; + switch (u8_type) { + case 0: + g_raydium_ts->id = RAD_30; + i32_ret = SUCCESS; + break; +#if defined(FW_MAPPING_EN) + case 1: + g_raydium_ts->id = RAD_31; + i32_ret = SUCCESS; + break; +#endif + } + return i32_ret; +} + +unsigned int bits_reverse(unsigned int u32_num, unsigned int bit_num) +{ + unsigned int reverse = 0, u32_i; + + for (u32_i = 0; u32_i < bit_num; u32_i++) { + if (u32_num & (1 << u32_i)) + reverse |= 1 << ((bit_num - 1) - u32_i); + } + return reverse; +} + +unsigned int rc_crc32(const char *buf, unsigned int u32_len, + unsigned int u32_crc) +{ + unsigned int u32_i; + unsigned char u8_flash_byte, u8_current, u8_j; + + for (u32_i = 0; u32_i < u32_len; u32_i++) { + u8_flash_byte = buf[u32_i]; + u8_current = (unsigned char)bits_reverse(u8_flash_byte, 8); + for (u8_j = 0; u8_j < 8; u8_j++) { + if ((u32_crc ^ u8_current) & 0x01) + u32_crc = (u32_crc >> 1) ^ 0xedb88320; + else + u32_crc >>= 1; + u8_current >>= 1; + } + } + return u32_crc; +} + +int wait_fw_state(struct i2c_client *client, unsigned int u32_addr, + unsigned int u32_state, unsigned long u32_delay_us, + unsigned short u16_retry) +{ + unsigned char u8_buf[4]; + unsigned int u32_read_data; + unsigned int u32_min_delay_us = u32_delay_us - 500; + unsigned int u32_max_delay_us = u32_delay_us + 500; + + do { + if (handle_i2c_pda_read(client, u32_addr, u8_buf, 4) == ERROR) + return ERROR; + + memcpy(&u32_read_data, u8_buf, 4); + u16_retry--; + usleep_range(u32_min_delay_us, u32_max_delay_us); + } while ((u32_read_data != u32_state) && (u16_retry != 0)); + + if (u32_read_data != u32_state) { + LOGD(LOG_ERR, "[touch]confirm data error : 0x%x\n", u32_read_data); + return ERROR; + } + + return SUCCESS; +} + +int wait_irq_state(struct i2c_client *client, unsigned int retry_time, + unsigned int u32_delay_us) +{ + int i32_ret = SUCCESS; + unsigned int u32_retry; + unsigned int u32_irq_value; + unsigned int u32_min_delay_us = u32_delay_us - 500; + unsigned int u32_max_delay_us = u32_delay_us + 500; + + u32_retry = retry_time; + u32_irq_value = 0; + while (u32_retry != 0 && u32_irq_value != 1) { + u32_irq_value = gpio_get_value(g_raydium_ts->irq_gpio); + usleep_range(u32_min_delay_us, u32_max_delay_us); + u32_retry--; + } + LOGD(LOG_DEBUG, "[touch]irq_value is %d\n", u32_irq_value); + + if (u32_retry == 0) { + LOGD(LOG_ERR, "[touch]%s, FW not ready, retry error!\n", __func__); + i32_ret = ERROR; + } + + return i32_ret; +} + +int raydium_do_software_reset(struct i2c_client *client) +{ + int i32_ret = SUCCESS; + + unsigned char u8_buf[4]; + + /*SW reset*/ + g_u8_resetflag = true; + memset(u8_buf, 0, sizeof(u8_buf)); + u8_buf[0] = 0x01; + LOGD(LOG_INFO, "[touch]SW reset\n"); + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_BLKRST, u8_buf, 4); + if (i32_ret < 0) + goto exit; + + msleep(35); + if (raydium_disable_i2c_deglitch() == ERROR) { + LOGD(LOG_ERR, "[touch] 1.disable_i2c_deglitch_3x NG!\r\n"); + goto exit; + } +exit: + return i32_ret; +} + +int set_skip_load(struct i2c_client *client) +{ + int i32_ret = SUCCESS; + unsigned int u32_retry_time = 1000; + unsigned char u8_buf[4]; + + /*Skip load*/ + memset(u8_buf, 0, sizeof(u8_buf)); + u8_buf[0] = 0x10; + u8_buf[1] = 0x08; + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_BOOTREG, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + i32_ret = raydium_do_software_reset(client); + if (i32_ret < 0) + LOGD(LOG_ERR, "[touch]%s, SW reset error!\n", __func__); + msleep(35); + + i32_ret = wait_fw_state(client, RAYDIUM_PDA_BOOTSTATE, 0x82, 2000, u32_retry_time); + if (i32_ret < 0) + LOGD(LOG_ERR, "[touch]%s, wait_fw_state error!\n", __func__); +exit_upgrade: + return i32_ret; +} + +/*check pram crc32*/ +static int raydium_check_pram_crc_3X(struct i2c_client *client, + unsigned int u32_addr, + unsigned int u32_len) +{ + int i32_ret = SUCCESS; + unsigned int u32_crc_addr = u32_addr + u32_len; + unsigned int u32_end_addr = u32_crc_addr - 1; + unsigned int u32_crc_result, u32_read_data; + unsigned int u32_retry = 1000; + unsigned char u8_buf[4], u8_retry = 3; + + memset(u8_buf, 0, sizeof(u8_buf)); + u8_buf[0] = (unsigned char)(u32_addr & 0xFF); + u8_buf[1] = (unsigned char)((u32_addr & 0xFF00) >> 8); + u8_buf[2] = (unsigned char)(u32_end_addr & 0xFF); + u8_buf[3] = (unsigned char)((u32_end_addr & 0xFF00) >> 8); + + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_PRGCHKSUMADDR, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + i32_ret = handle_i2c_pda_read(client, RAYDIUM_PDA_PRGCHKSUMENG, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + u8_buf[3] |= 0x81; + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_PRGCHKSUMENG, u8_buf, 4); + + while (u8_buf[3] != 0x80 && u32_retry != 0) { + i32_ret = handle_i2c_pda_read(client, RAYDIUM_PDA_PRGCHKSUMENG, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + u32_retry--; + usleep_range(4500, 5500); + } + if (u32_retry == 0) { + LOGD(LOG_ERR, "[touch]%s, Cal CRC not ready, retry error!\n", + __func__); + i32_ret = ERROR; + } + + i32_ret = handle_i2c_pda_read(client, RAYDIUM_PDA_PRGCHKSUMRESULT, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + memcpy(&u32_crc_result, u8_buf, 4); + i32_ret = handle_i2c_pda_read(client, u32_crc_addr, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + memcpy(&u32_read_data, u8_buf, 4); + + while (u32_read_data != u32_crc_result && u8_retry > 0) { + i32_ret = handle_i2c_pda_read(client, RAYDIUM_PDA_PRGCHKSUMRESULT, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + memcpy(&u32_crc_result, u8_buf, 4); + usleep_range(1500, 2500); + u8_retry--; + } + if (u32_read_data != u32_crc_result) { + LOGD(LOG_ERR, "[touch]check pram crc fail!!\n"); + LOGD(LOG_ERR, "[touch]u32_read_data 0x%x\n", u32_read_data); + LOGD(LOG_ERR, "[touch]u32_crc_result 0x%x\n", u32_crc_result); + i32_ret = ERROR; + goto exit_upgrade; + } else if (u8_retry != 3) { + LOGD(LOG_DEBUG, "[touch]check pram crc pass!!\n"); + LOGD(LOG_DEBUG, "[touch]u8_retry : %d\n", u8_retry); + LOGD(LOG_DEBUG, "[touch]u32_read_data 0x%x\n", u32_read_data); + LOGD(LOG_DEBUG, "[touch]u32_crc_result 0x%x\n", u32_crc_result); + i32_ret = ERROR; + goto exit_upgrade; + } else { + LOGD(LOG_DEBUG, "[touch]check pram crc pass!!\n"); + LOGD(LOG_DEBUG, "[touch]u8_retry : %d\n", u8_retry); + LOGD(LOG_DEBUG, "[touch]u32_read_data 0x%x\n", u32_read_data); + LOGD(LOG_DEBUG, "[touch]u32_crc_result 0x%x\n", u32_crc_result); + i32_ret = SUCCESS; + } + +exit_upgrade: + return i32_ret; +} + +/* upgrade firmware with image file */ +static int raydium_write_to_pram_3X(struct i2c_client *client, + unsigned int u32_fw_addr, + unsigned char u8_type) +{ + int i32_ret = ERROR; + unsigned int u32_fw_size = 0; + unsigned char *p_u8_firmware_data = NULL; + unsigned int u32_write_offset = 0; + unsigned short u16_write_length = 0; + + switch (u8_type) { + case RAYDIUM_INIT: + u32_fw_size = 0x80; + p_u8_firmware_data = g_rad_init_image; + break; + case RAYDIUM_BOOTLOADER: + u32_fw_size = 0x800; + p_u8_firmware_data = g_rad_boot_image; + break; + + case RAYDIUM_PARA: + u32_fw_size = RAYDIUM_PDA_PARALENGTH; + p_u8_firmware_data = g_rad_para_image; + break; + + case RAYDIUM_FIRMWARE: + u32_fw_size = RAYDIUM_PDA_FIRMWARELENGTH; + p_u8_firmware_data = g_rad_fw_image; + break; + case RAYDIUM_TEST_FW: + u32_fw_size = RAYDIUM_PDA_FIRMWARELENGTH + RAYDIUM_PDA_PARALENGTH; + p_u8_firmware_data = g_rad_testfw_image; + break; + + default: + goto exit_upgrate; + } + + u32_write_offset = 0; + while (u32_write_offset < u32_fw_size) { + if ((u32_write_offset + MAX_WRITE_PACKET_SIZE) < u32_fw_size) + u16_write_length = MAX_WRITE_PACKET_SIZE; + else + u16_write_length = + (unsigned short)(u32_fw_size - u32_write_offset); + + i32_ret = handle_i2c_pda_write( + client, + (u32_fw_addr + u32_write_offset), + (p_u8_firmware_data + u32_write_offset), + u16_write_length); + if (i32_ret < 0) + goto exit_upgrate; + + u32_write_offset += (unsigned long)u16_write_length; + } + u32_fw_addr += u32_write_offset; + +exit_upgrate: + if (i32_ret < 0) { + LOGD(LOG_ERR, "[touch]upgrade failed\n"); + return i32_ret; + } + LOGD(LOG_INFO, "[touch]upgrade success\n"); + return SUCCESS; +} + +unsigned char raydium_stop_mcu_3x(unsigned char u8_is_tp_reset) +{ + unsigned short u16_time_out = 100; + unsigned int u32_read_data; + unsigned int u32_write = 0; + + if (u8_is_tp_reset) { + gpio_set_value(g_raydium_ts->rst_gpio, 1); + gpio_set_value(g_raydium_ts->rst_gpio, 0); + msleep(RAYDIUM_RESET_INTERVAL_MSEC); + gpio_set_value(g_raydium_ts->rst_gpio, 1); + + g_u8_i2c_mode = PDA2_MODE; + msleep(35); + if (raydium_disable_i2c_deglitch() == ERROR) { + LOGD(LOG_ERR, "[touch] disable_i2c_deglitch_3x NG!\r\n"); + return ERROR; + } + } + LOGD(LOG_INFO, "[touch]%s\r\n", __func__); + + u32_write = 0x30; + if (handle_i2c_pda_write(g_raydium_ts->client, RAYDIUM_PDA_BOOTREG + , (unsigned char *)&u32_write, 4) == ERROR) { + return ERROR; + } + + u32_write = 0x01; + if (handle_i2c_pda_write(g_raydium_ts->client, RAYDIUM_PDA_BLKRST, (unsigned char *)&u32_write, 4 + ) == ERROR) { + return ERROR; + } + msleep(100); + + if (raydium_disable_i2c_deglitch() == ERROR) { + LOGD(LOG_ERR, "[touch] disable_i2c_deglitch_3x NG!\r\n"); + return ERROR; + } + LOGD(LOG_INFO, "[touch] stop_mcu 2\r\n"); + + if (handle_i2c_pda_read(g_raydium_ts->client, RAYDIUM_PDA_BOOTREG + , (unsigned char *)(&u32_read_data), 4) == ERROR) { + return ERROR; + } + + while ((u32_read_data & 0x2000) == 0 && u16_time_out-- > 0) { + msleep(20); + if (handle_i2c_pda_read(g_raydium_ts->client, RAYDIUM_PDA_BOOTREG + , (unsigned char *)(&u32_read_data), 4) == ERROR) { + return ERROR; + } + } + + LOGD(LOG_DEBUG, "[touch]Stop MCU=0x%X(0x%x)(%d)!!\r\n", u32_read_data, (u32_read_data & 0x2000), u16_time_out); + + if ((u32_read_data & 0x2000) == 0) + return ERROR; + + return SUCCESS; +} + +/* Raydium fireware upgrade flow */ +static int raydium_fw_upgrade_3X(struct i2c_client *client, + unsigned char u8_type, + unsigned char u8_check_crc) +{ + int i32_ret = 0; + unsigned char u8_buf[4]; + unsigned short u16_retry = 1000; + unsigned int u32_write = 0; + + /*##### wait for boot-loader start #####*/ + LOGD(LOG_INFO, "[touch]Type is %x\n", u8_type); + + /*read Boot version*/ + if (handle_i2c_pda_read(client, RAYDIUM_PDA_BOOTVERSION, u8_buf, 4) == ERROR) + return ERROR; + u32_write = (u8_buf[3] << 24) | (u8_buf[2] << 16) | (u8_buf[1] << 8) | u8_buf[0]; + LOGD(LOG_INFO, "[touch]bootloader ver: 0x%x\r\n", u32_write); + + if (u8_type != RAYDIUM_COMP) { + if (raydium_stop_mcu_3x(0) == ERROR) + return ERROR; + } + + /*#start write data to PRAM*/ + if (u8_type == RAYDIUM_FIRMWARE) { + /* unlock PRAM */ + u8_buf[0] = (BOTLR_LOCK | COMP_LOCK | BASEL_LOCK | INICO_LOCK); + i32_ret = handle_i2c_pda_write(client, + RAYDIUM_PDA_PRAMLOCK, + u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + i32_ret = raydium_write_to_pram_3X(client, RAYDIUM_PDA_FIRMWAREADDR, + RAYDIUM_FIRMWARE); + if (i32_ret < 0) + goto exit_upgrade; + + i32_ret = raydium_write_to_pram_3X(client, RAYDIUM_PDA_PARAADDR, + RAYDIUM_PARA); + if (i32_ret < 0) + goto exit_upgrade; + + i32_ret = raydium_check_pram_crc_3X(client, RAYDIUM_PDA_FIRMWAREADDR, + RAYDIUM_PDA_CRCLENGTH); + if (i32_ret < 0) + goto exit_upgrade; + + } else if (u8_type == RAYDIUM_BOOTLOADER) { + /* unlock PRAM */ + u8_buf[0] = (BOTLR_LOCK | CONFIG_LOCK | COMP_LOCK | BASEL_LOCK | INICO_LOCK); + + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_PRAMLOCK, + u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + i32_ret = raydium_write_to_pram_3X(client, 0x0800, + RAYDIUM_BOOTLOADER); + if (i32_ret < 0) + goto exit_upgrade; + i32_ret = raydium_write_to_pram_3X(client, 0x1000, + RAYDIUM_INIT); + if (i32_ret < 0) + goto exit_upgrade; + + i32_ret = raydium_check_pram_crc_3X(client, 0x800, + 0x7FC); + if (i32_ret < 0) + goto exit_upgrade; + i32_ret = raydium_check_pram_crc_3X(client, 0x1000, + 0x7C); + if (i32_ret < 0) + goto exit_upgrade; + } + + if (u8_type != RAYDIUM_COMP) { + /*release mcu hold*/ + /*Skip load*/ + i32_ret = set_skip_load(client); + if (i32_ret < 0) + LOGD(LOG_ERR, "[touch]%s, set skip_load error!\n", __func__); + /*#confirm in burn mode*/ + if (wait_fw_state(client, RAYDIUM_PDA_BOOTSTATE, 0x82, + 2000, u16_retry) == ERROR) { + LOGD(LOG_ERR, "[touch]Error, confirm in burn mode 1\n"); + i32_ret = ERROR; + goto exit_upgrade; + } + + } + + /*#setting burning mode*/ + memset(u8_buf, 0, sizeof(u8_buf)); + u8_buf[0] = 0x01; + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_BOOTENG1, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + memset(u8_buf, 0, sizeof(u8_buf)); + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_BOOTENG2, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + memset(u8_buf, 0, sizeof(u8_buf)); + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_BOOTENG3, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + memset(u8_buf, 0, sizeof(u8_buf)); + u8_buf[0] = 0x01; + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_BOOTMODE, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + /*#confirm in burn mode*/ + if (wait_fw_state(client, RAYDIUM_PDA_BOOTSTATE, 0xFF, + 2000, u16_retry) == ERROR) { + LOGD(LOG_ERR, "[touch]bootloader wrt empty flash fail\r\n"); + LOGD(LOG_ERR, "[touch]bootloader confirm in burn mode fail, Type=%d\r\n", u8_type); + i32_ret = ERROR; + goto exit_upgrade; + } + + /* burning setting */ + memset(u8_buf, 0, sizeof(u8_buf)); + u8_buf[0] = 0x10; + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_BOOTREG, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + memset(u8_buf, 0, sizeof(u8_buf)); + u8_buf[0] = u8_type; + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_PRAMTYPE, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + /*#set PRAM length (at 'h5000_090C)*/ + if (u8_type == RAYDIUM_COMP) { + memset(u8_buf, 0, sizeof(u8_buf)); + u8_buf[0] = 0x78; + u8_buf[1] = 0x7c; + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_PRAMADDR, + u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + } else if (u8_type == RAYDIUM_FIRMWARE) { + memset(u8_buf, 0, sizeof(u8_buf)); + u8_buf[0] = 0x00; + u8_buf[1] = 0x08; + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_PRAMADDR, + u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + } else if (u8_type == RAYDIUM_BOOTLOADER) { + memset(u8_buf, 0, sizeof(u8_buf)); + u8_buf[0] = 0x00; + u8_buf[1] = 0x08; + i32_ret = handle_i2c_pda_write(client, 0x50000908, + u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + } + + + /*#set sync_data(RAYDIUM_PDA_SYNCDATA) = 0 as WRT data finish*/ + memset(u8_buf, 0, sizeof(u8_buf)); + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_SYNCDATA, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + /*#wait for sync_data finish*/ + if (wait_fw_state(client, RAYDIUM_PDA_BOOTENG4, 168, 1000, + u16_retry) == ERROR) { + LOGD(LOG_ERR, "[touch]Error, wait for input unlock key\n"); + i32_ret = ERROR; + goto exit_upgrade; + } + + /*#flash unlock key*/ + memset(u8_buf, 0, sizeof(u8_buf)); + u8_buf[0] = 0xd7; + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_FLKEY2, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + memset(u8_buf, 0, sizeof(u8_buf)); + u8_buf[0] = 0xa5; + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_FLKEY1, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + memset(u8_buf, 0, sizeof(u8_buf)); + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_FLKEY1, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + memset(u8_buf, 0, sizeof(u8_buf)); + u8_buf[0] = 0xa5; + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_FLKEY1, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + memset(u8_buf, 0, sizeof(u8_buf)); + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_FLKEY2, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + memset(u8_buf, 0, sizeof(u8_buf)); + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_FLASHPRO, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + /*#ready to burn flash*/ + memset(u8_buf, 0, sizeof(u8_buf)); + u8_buf[0] = 0xa8; + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_BOOTSTATE, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + /*#clr sync_data(RAYDIUM_PDA_SYNCDATA) = 0 as finish*/ + memset(u8_buf, 0, sizeof(u8_buf)); + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_SYNCDATA, u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + /* wait erase/wrt finish + * confirm burning_state result (gu8I2CSyncData.burning_state = + * BURNING_WRT_FLASH_FINISH at RAYDIUM_PDA_BOOTENG3) + */ + if (wait_fw_state(client, RAYDIUM_PDA_BOOTENG3, 6, 8000, + u16_retry) == ERROR) { + LOGD(LOG_ERR, "[touch]Error, wait erase/wrt finish\n"); + i32_ret = ERROR; + goto exit_upgrade; + } + LOGD(LOG_INFO, "[touch]Burn flash ok\n"); + + if (u8_check_crc) { + memset(u8_buf, 0, sizeof(u8_buf)); + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_SYNCDATA, + u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + /*#wait software reset finish*/ + msleep(35); + + /* wait sw reset finished RAYDIUM_PDA_BOOTSTATE = 0x82 */ + if (wait_fw_state(client, RAYDIUM_PDA_BOOTSTATE, 0x82, 2000, + u16_retry) == ERROR) { + LOGD(LOG_ERR, "[touch]Error, wait sw reset finished\n"); + i32_ret = ERROR; + goto exit_upgrade; + } + + /*#set test_mode = 1 start to check crc*/ + memset(u8_buf, 0, sizeof(u8_buf)); + u8_buf[0] = 0x01; + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_BOOTMODE, + u8_buf, 4); + if (i32_ret < 0) + goto exit_upgrade; + + /*#wait crc check finish*/ + if (wait_fw_state(client, RAYDIUM_PDA_BOOTENG2, 2, + 2000, u16_retry) + == ERROR) { + LOGD(LOG_ERR, "[touch]Error, wait crc check finish\n"); + i32_ret = ERROR; + goto exit_upgrade; + } + + /*#crc check pass RAYDIUM_PDA_BOOTSTATE = 0x81*/ + if (wait_fw_state(client, RAYDIUM_PDA_BOOTSTATE, 0x81, + 2000, u16_retry) + == ERROR) { + LOGD(LOG_ERR, "[touch]Error, confirm crc result\n"); + i32_ret = ERROR; + goto exit_upgrade; + } + /*#run to next step*/ + LOGD(LOG_INFO, "[touch]Type 0x%x => Pass\n", u8_type); + + /* sw rest */ + + i32_ret = raydium_do_software_reset(client); + if (i32_ret < 0) + goto exit_upgrade; + + g_u8_i2c_mode = PDA2_MODE; + + + LOGD(LOG_INFO, "[touch]Burn FW finish!\n"); + } + +exit_upgrade: + return i32_ret; +} + +static int raydium_boot_upgrade_3X(struct i2c_client *client, unsigned char u8_is_tp_reset) +{ + int i32_ret = SUCCESS; + unsigned char u8_buf[4]; + unsigned short u16_retry = 1000; + + /*set mcu hold*/ + memset(u8_buf, 0, sizeof(u8_buf)); + if (raydium_stop_mcu_3x(u8_is_tp_reset) == ERROR) + return ERROR; + /*WRT boot-loader to PRAM first*/ + memset(u8_buf, 0, sizeof(u8_buf)); + u8_buf[0] = 0x1F; + i32_ret = handle_i2c_pda_write(client, RAYDIUM_PDA_PRAMLOCK, u8_buf, 4); + + /*Sending bootloader*/ + i32_ret = raydium_write_to_pram_3X(client, 0x0000, + RAYDIUM_BOOTLOADER); + if (i32_ret < 0) + goto exit_upgrade; + + i32_ret = raydium_check_pram_crc_3X(client, 0x000, 0x7FC); + if (i32_ret < 0) + goto exit_upgrade; + + /*Sending initial code*/ + i32_ret = raydium_write_to_pram_3X(client, 0x7f80, + RAYDIUM_INIT); + if (i32_ret < 0) + goto exit_upgrade; + + i32_ret = raydium_check_pram_crc_3X(client, 0x7F80, 0x7C); + if (i32_ret < 0) + goto exit_upgrade; + + /*release mcu hold*/ + /*Skip load*/ + i32_ret = set_skip_load(client); + if (i32_ret < 0) + LOGD(LOG_ERR, "[touch]%s, set skip_load error!\n", __func__); + msleep(35); + if (wait_fw_state(client, RAYDIUM_PDA_BOOTSTATE, 0x82, 1000, + u16_retry) == ERROR) { + LOGD(LOG_ERR, "[touch]Error, wait for input unlock key\n"); + i32_ret = ERROR; + goto exit_upgrade; + } +exit_upgrade: + return i32_ret; +} + + +int raydium_burn_fw(struct i2c_client *client) +{ + int i32_ret = ERROR; + + g_u8_resetflag = true; + if ((g_raydium_ts->id & 0x3000) != 0) { + LOGD(LOG_INFO, "[touch]start burn function!\n"); + i32_ret = raydium_boot_upgrade_3X(client, 1); + if (i32_ret < 0) + goto exit_upgrade; + + i32_ret = raydium_fw_upgrade_3X(client, RAYDIUM_BOOTLOADER, 0); + if (i32_ret < 0) + goto exit_upgrade; + + i32_ret = raydium_fw_upgrade_3X(client, RAYDIUM_FIRMWARE, 1); + if (i32_ret < 0) + goto exit_upgrade; + } else { + LOGD(LOG_ERR, "[touch]FW ID ERROR!\n"); + } + +exit_upgrade: + return i32_ret; +} + +int raydium_fw_update_check(unsigned short u16_i2c_data) +{ + + unsigned char u8_rbuffer[4]; + + unsigned int u32_fw_version, u32_image_version; + int i32_ret = ERROR; + + mutex_lock(&g_raydium_ts->lock); + i32_ret = raydium_i2c_pda2_set_page(g_raydium_ts->client, + g_raydium_ts->is_suspend, + RAYDIUM_PDA2_PAGE_0); + if (i32_ret < 0) + goto exit_error; + + i32_ret = raydium_i2c_pda2_read(g_raydium_ts->client, + RAYDIUM_PDA2_FW_VERSION_ADDR, + u8_rbuffer, + 4); + if (i32_ret < 0) + goto exit_error; + + mutex_unlock(&g_raydium_ts->lock); + + u32_fw_version = (u8_rbuffer[0] << 24) | (u8_rbuffer[1] << 16) | + (u8_rbuffer[2] << 8) | u8_rbuffer[3]; + LOGD(LOG_INFO, "[touch]RAD FW ver 0x%.8x\n", u32_fw_version); + + g_raydium_ts->fw_version = u32_fw_version; + + g_raydium_ts->id = ((u16_i2c_data & 0xF) << 12) | + ((u8_rbuffer[0] & 0xF) << 8) | u8_rbuffer[1]; + + raydium_mem_table_init(g_raydium_ts->id); + if (raydium_mem_table_setting() == SUCCESS) { + + u32_image_version = (g_rad_para_image[PARA_FW_VERSION_OFFSET] << 24) | + (g_rad_para_image[PARA_FW_VERSION_OFFSET + 1] << 16) | + (g_rad_para_image[PARA_FW_VERSION_OFFSET + 2] << 8) | + g_rad_para_image[PARA_FW_VERSION_OFFSET + 3]; + + LOGD(LOG_INFO, "[touch]RAD Image FW ver : 0x%x\n", u32_image_version); + } else { + LOGD(LOG_ERR, "[touch]Mem setting failed, Stop fw upgrade!\n"); + return FAIL; + } + +#ifdef FW_UPDATE_EN + if (u32_fw_version != u32_image_version) { + LOGD(LOG_INFO, "[touch]FW need update.\n"); + g_u8_raydium_flag |= ENG_MODE; + + i32_ret = raydium_burn_fw(g_raydium_ts->client); + if (i32_ret < 0) + LOGD(LOG_ERR, "[touch]FW update fail:%d\n", i32_ret); + + g_u8_raydium_flag &= ~ENG_MODE; + mutex_lock(&g_raydium_ts->lock); + i32_ret = raydium_i2c_pda2_set_page(g_raydium_ts->client, + g_raydium_ts->is_suspend, + RAYDIUM_PDA2_PAGE_0); + if (i32_ret < 0) + goto exit_error; + + i32_ret = raydium_i2c_pda2_read(g_raydium_ts->client, + RAYDIUM_PDA2_FW_VERSION_ADDR, + u8_rbuffer, + 4); + if (i32_ret < 0) + goto exit_error; + + mutex_unlock(&g_raydium_ts->lock); + u32_fw_version = (u8_rbuffer[0] << 24) | + (u8_rbuffer[1] << 16) | + (u8_rbuffer[2] << 8) | + u8_rbuffer[3]; + LOGD(LOG_INFO, "[touch]RAD FW ver is 0x%x\n", + u32_fw_version); + g_raydium_ts->fw_version = u32_fw_version; + } else + LOGD(LOG_INFO, "[touch]FW is the latest version.\n"); +#endif + + + return i32_ret; + +exit_error: + mutex_unlock(&g_raydium_ts->lock); + return i32_ret; +} +int raydium_burn_comp(struct i2c_client *client) +{ + int i32_ret = ERROR; + + i32_ret = raydium_boot_upgrade_3X(client, 0); + if (i32_ret < 0) + goto exit_upgrade; + + i32_ret = set_skip_load(client); + if (i32_ret < 0) + goto exit_upgrade; + + + i32_ret = raydium_fw_upgrade_3X(client, RAYDIUM_COMP, 1); + if (i32_ret < 0) + goto exit_upgrade; + + i32_ret = SUCCESS; + +exit_upgrade: + return i32_ret; +} + +int raydium_check_fw_ready(struct i2c_client *client) +{ + int i32_ret = SUCCESS; + unsigned int u32_retry = 400; + unsigned char u8_buf[4]; + + u8_buf[1] = 0; + while (u8_buf[1] != 0x40 && u32_retry != 0) { + i32_ret = handle_i2c_pda_read(client, RAYDIUM_PDA_BOOTREG, u8_buf, 4); + if (i32_ret < 0) + goto exit; + + u32_retry--; + usleep_range(4500, 5500); + } + + if (u32_retry == 0) { + LOGD(LOG_ERR, "[touch]%s, FW not ready, retry error!\n", __func__); + i32_ret = ERROR; + } else { + LOGD(LOG_INFO, "[touch]%s, FW is ready!!\n", __func__); + usleep_range(4500, 5500); + } + +exit: + return i32_ret; +} +/* upgrade firmware with image file */ +int raydium_fw_upgrade_with_image(struct i2c_client *client, + unsigned int u32_fw_addr, + unsigned char u8_type) +{ + int i32_ret = ERROR; + unsigned int u32_fw_size = 0; + unsigned char *p_u8_firmware_data = NULL; + unsigned int u32_write_offset = 0; + unsigned short u16_write_length = 0; + unsigned int u32_checksum = 0xFFFFFFFF; + + switch (u8_type) { + case RAYDIUM_INIT: + u32_fw_size = 0x1fc; + p_u8_firmware_data = g_rad_init_image; + break; + case RAYDIUM_PARA: + u32_fw_size = 0x158; + p_u8_firmware_data = g_rad_para_image; + break; + case RAYDIUM_FIRMWARE: + u32_fw_size = 0x61fc; + p_u8_firmware_data = g_rad_fw_image; + break; + case RAYDIUM_BOOTLOADER: + u32_fw_size = 0x7FC; + p_u8_firmware_data = g_rad_boot_image; + break; + case RAYDIUM_TEST_FW: + u32_fw_size = 0x635C; + p_u8_firmware_data = g_rad_testfw_image; + break; + } + + LOGD(LOG_DEBUG, "[touch]CRC 0x%08X\n", + *(unsigned int *)(p_u8_firmware_data + u32_fw_size)); + + u32_checksum = rc_crc32(p_u8_firmware_data, + u32_fw_size, u32_checksum); + u32_checksum = bits_reverse(u32_checksum, 32); + memcpy((p_u8_firmware_data + u32_fw_size), &u32_checksum, 4); + LOGD(LOG_DEBUG, "[touch]CRC result 0x%08X\n", u32_checksum); + + u32_fw_size += 4; + + u32_write_offset = 0; + while (u32_write_offset < u32_fw_size) { + if ((u32_write_offset + MAX_WRITE_PACKET_SIZE) < u32_fw_size) + u16_write_length = MAX_WRITE_PACKET_SIZE; + else + u16_write_length = + (unsigned short) + (u32_fw_size - u32_write_offset); + + i32_ret = handle_i2c_pda_write( + client, + (u32_fw_addr + u32_write_offset), + (p_u8_firmware_data + u32_write_offset), + u16_write_length); + if (i32_ret < 0) + goto exit_upgrate; + + u32_write_offset += (unsigned long)u16_write_length; + } + u32_fw_addr += u32_write_offset; + +exit_upgrate: + if (i32_ret < 0) { + LOGD(LOG_ERR, "[touch]upgrade failed\n"); + return i32_ret; + } + LOGD(LOG_INFO, "[touch]upgrade success\n"); + return SUCCESS; +} +int raydium_load_test_fw(struct i2c_client *client) +{ + unsigned int u32_read_data; + unsigned int u32_write = 0; + int i32_ret = ERROR; + + /* set skip load & MCU hold then SW reset*/ + /* sync_data:210;cmd_type:210h;ret_data:214h;test_mode:218h*/ + if (raydium_stop_mcu_3x(1) == ERROR) + goto ERROR_EXIT; + + /*WRT boot-loader to PRAM first*/ + u32_write = 1; + if (handle_i2c_pda_write(client, RAYDIUM_PDA_PRAMLOCK, (unsigned char *)&u32_write, 4) == ERROR) + return ERROR; + + /*Sending bootloader*/ + + i32_ret = raydium_write_to_pram_3X(client, 0x0000, + RAYDIUM_BOOTLOADER); + if (i32_ret < 0) + goto ERROR_EXIT; + + i32_ret = raydium_check_pram_crc_3X(client, 0x000, 0x7FC); + if (i32_ret < 0) + goto ERROR_EXIT; + + i32_ret = raydium_write_to_pram_3X(client, RAYDIUM_PDA_FIRMWAREADDR, + RAYDIUM_TEST_FW); + if (i32_ret < 0) + goto ERROR_EXIT; + + i32_ret = raydium_check_pram_crc_3X(client, RAYDIUM_PDA_FIRMWAREADDR, + RAYDIUM_PDA_CRCLENGTH); + if (i32_ret < 0) + goto ERROR_EXIT; + + /*clear CC & BL info*/ + u32_write = 0x00000000; + if (handle_i2c_pda_write(client, 0x7F78, (unsigned char *)&u32_write, 4) == ERROR) + return ERROR; + + /* set skip load & MCU hold then SW reset*/ + /* sync_data:210;cmd_type:210h;ret_data:214h;test_mode:218h*/ + /* read bootloader version*/ + if (handle_i2c_pda_read(client, 0x80, (unsigned char *)(&u32_read_data), 4) == ERROR) + goto ERROR_EXIT; + LOGD(LOG_INFO, "[touch]Read bootloader ver=0x%x!!\r\n", u32_read_data); + + u32_write = 0x0410; + if (handle_i2c_pda_write(client, RAYDIUM_PDA_BOOTREG, (unsigned char *)&u32_write, 4) == ERROR) + return ERROR; + if (handle_i2c_pda_read(client, RAYDIUM_PDA_BOOTREG, (unsigned char *)(&u32_read_data), 4) == ERROR) + goto ERROR_EXIT; + LOGD(LOG_DEBUG, "[touch]write MCU STATUS=0x%x!!\r\n", u32_read_data); + /*wait sw rst finish*/ + u32_write = 1; + + handle_i2c_pda_write(client, RAYDIUM_PDA_BLKRST, (unsigned char *)&u32_write, 4); + msleep(35); + + if (handle_i2c_pda_read(client, RAYDIUM_PDA_BOOTREG, (unsigned char *)(&u32_read_data), 4) == ERROR) + goto ERROR_EXIT; + LOGD(LOG_INFO, "[touch]Read MCU STATUS=0x%x!!\r\n", u32_read_data); + if (handle_i2c_pda_read(client, 0x200006E4, (unsigned char *)(&u32_read_data), 4) == ERROR) + goto ERROR_EXIT; + LOGD(LOG_INFO, "[touch]Read test fw version=0x%x!!\r\n", u32_read_data); + + return SUCCESS; + +ERROR_EXIT: + + return ERROR; +} + diff --git a/raydium/raydium_selftest.c b/raydium/raydium_selftest.c new file mode 100644 index 0000000000..1ec50d4dca --- /dev/null +++ b/raydium/raydium_selftest.c @@ -0,0 +1,597 @@ +/* raydium_selftest.c + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drv_interface.h" +#include "chip_raydium/ic_drv_interface.h" +#include "raydium_selftest.h" +#include "chip_raydium/ic_drv_global.h" +#include "tpselftest_30.h" +#if defined(FW_MAPPING_EN) +#include "tpselftest_21.h" +#endif + +#define RM_SELF_TEST_CUSTOMER_VERSION 0x01 +#define RM_SELF_TEST_PLATFORM_VERSION 0x01 +#define RM_SELF_TEST_PROJECT_VERSION 0x40 +#define RM_SELF_TEST_MAIN_VERSION 1 +#define RM_SELF_TEST_SUB_VERSION 0 + +#define RESULT_SUCCESS 0 +#define RESULT_NG 1 + +#define RELATIVE_PATH 0 + +#define RM_SELF_TEST_MAX_STR_LENGTH 1000 + +unsigned char g_u8_normal_fw_version_buf[4]; +char str_ini_path[100]; + +static int self_test_all(void) +{ + int ret = 0; + + g_u8_raydium_flag |= ENG_MODE; + handle_ic_test(); + ret = g_u32_wearable_test_result; + + /*g_u8_raydium_flag &= ~ENG_MODE;*/ + DEBUGOUT("%s\r\n", __func__); + + return ret; +} + +int self_test_save_to_file(char *file_name, char *p_string, short len) +{ + struct file *filp = NULL; + mm_segment_t old_fs; + + filp = filp_open(file_name, O_RDWR | O_CREAT | O_APPEND, 0666); + if (IS_ERR(filp)) { + DEBUGOUT("can't open file:%s\n", RM_SELF_TEST_LOGFILE); + return 0; + } + old_fs = get_fs(); + set_fs(KERNEL_DS); + filp->f_op->write(filp, p_string, len, &filp->f_pos); + set_fs(old_fs); + filp_close(filp, NULL); + + return 1; +} + +#if 1 +static int raydium_check_ini_version(void) +{ + int ret = 0; + unsigned int u32_test_version; + + memcpy(&u32_test_version, &g_rad_testpara_image[4], 4); + + if (u32_test_version != g_st_test_para_resv.u32_test_fw_version) { + DEBUGOUT("test fw versio 0x%X != ini version 0x%X\n" + , u32_test_version, g_st_test_para_resv.u32_test_fw_version); + ret = WEARABLE_FT_TEST_RESULT_TEST_FW_VER_NG; + } + if (g_u16_dev_id == DEVICE_ID_3X) { + + g_u32_dongle_flash_ini_addr = F303_DONGLE_FLASH_INI_ADDR; + g_u32_ini_threshold_addr = F303_INI_THRESHOLD_ADDR; + g_u32_ini_para_addr = F303_INI_PARA_ADDR; + g_u32_ini_raw_data_3_cc_addr = F303_INI_RAW_DATA_3_CC_ADDR; + g_u32_ini_uc_cc_addr = F303_INI_UC_CC_ADDR; + g_u32_initial_code_start_addr = F303_INITIAL_CODE_START_ADDR; + DEBUGOUT("[out_set_ic_version] F303 Set INI ADDR!\r\n"); + } else if (g_u16_dev_id == DEVICE_ID_2X) { + g_u32_dongle_flash_ini_addr = F302_DONGLE_FLASH_INI_ADDR; + g_u32_ini_threshold_addr = F302_INI_THRESHOLD_ADDR; + g_u32_ini_para_addr = F302_INI_PARA_ADDR; + g_u32_ini_raw_data_3_cc_addr = F302_INI_RAW_DATA_3_CC_ADDR; + g_u32_ini_uc_cc_addr = F302_INI_UC_CC_ADDR; + g_u32_initial_code_start_addr = F302_INITIAL_CODE_START_ADDR; + DEBUGOUT("[out_set_ic_version] F302 Set INI ADDR!\r\n"); + } + return ret; +} +#else +static int raydium_check_ini_version(void) +{ + int ret = 0; + unsigned int u32_test_version, u32_version_20, u32_version_21; + + memcpy(&u32_test_version, &g_st_test_para_resv.u32_test_fw_version, 4); + + if (g_u16_dev_id == DEVICE_ID_2X) { + switch (g_raydium_ts->id) { + case RAD_SELFTEST_20: + memcpy(&u32_version_20, &u8_rad_testpara_20[4], 4); + DEBUGOUT("ini version 0x%X, 20 version 0x%X\n" + , u32_test_version, u32_version_20); + + if (u32_test_version == u32_version_20) + DEBUGOUT("map version= 0x%X\r\n", u32_version_20); + else + ret = WEARABLE_FT_TEST_RESULT_TEST_FW_VER_NG; + case RAD_SELFTEST_21: + memcpy(&u32_version_21, &u8_rad_testpara_21[4], 4); + DEBUGOUT("ini version 0x%X, 21 version 0x%X\n" + , u32_test_version, u32_version_21); + + if (u32_test_version == u32_version_21) + DEBUGOUT("map version= 0x%X\r\n", u32_version_21); + else + ret = WEARABLE_FT_TEST_RESULT_TEST_FW_VER_NG; + } + } + + return ret; +} +#endif +static int self_test_init(void) +{ + int ret = 0; + unsigned int u32_dev_id; + + g_u8_drv_interface = I2C_INTERFACE; + g_u16_dev_id = DEVICE_ID_3X; + + if (handle_ic_read(RAYDIUM_CHK_I2C_CMD, 4, (unsigned char *)&u32_dev_id, g_u8_drv_interface, I2C_WORD_MODE) == ERROR) { + ret = WEARABLE_FT_TEST_RESULT_SYSFS_NG; + return ret; + } + g_u16_dev_id = ((u32_dev_id & 0xFFFF0000) >> 16); + + if (g_u16_dev_id == DEVICE_ID_2X) { + handle_ic_read(0x00006a04, 4, g_u8_normal_fw_version_buf, g_u8_drv_interface, I2C_WORD_MODE); + DEBUGOUT("FW Version=0x%.2X%.2X%.2X%.2X\n", g_u8_normal_fw_version_buf[0], g_u8_normal_fw_version_buf[1], + g_u8_normal_fw_version_buf[3], g_u8_normal_fw_version_buf[2]); + } else if (g_u16_dev_id == DEVICE_ID_3X) { + handle_ic_read(0x00007b04, 4, g_u8_normal_fw_version_buf, g_u8_drv_interface, I2C_WORD_MODE); + DEBUGOUT("FW Version=0x%.2X%.2X%.2X%.2X\n", g_u8_normal_fw_version_buf[0], g_u8_normal_fw_version_buf[1], + g_u8_normal_fw_version_buf[3], g_u8_normal_fw_version_buf[2]); + } else { + DEBUGOUT("read ic namd fail\n"); + ret = WEARABLE_FT_TEST_RESULT_TEST_INIT_NG; + return ret; + } + + if (raydium_check_ini_version() != 0) + ret = WEARABLE_FT_TEST_RESULT_TEST_FW_VER_NG; + + return ret; +} + +int self_test_save_test_raw_data_to_file(int i32_ng_type) +{ + /*struct tm *time_infor;*/ + /*time_t raw_time;*/ + char write_string[1000]; + unsigned char u8_i, u8_j; + short *p_i16_buf = (short *)g_u16_raw_data_tmp; + + /*FW Version*/ + memset(write_string, 0, strlen(write_string)); + if (g_u16_dev_id != 0) { + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, + "FW Version=0x%.2X%.2X%.2X%.2X\n", + g_u8_normal_fw_version_buf[0], g_u8_normal_fw_version_buf[1], + g_u8_normal_fw_version_buf[3], g_u8_normal_fw_version_buf[2]); + } + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + + /*Version*/ + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "Selftest Version=%x.%x.%x.%x.%x\n\n", + RM_SELF_TEST_CUSTOMER_VERSION, RM_SELF_TEST_PLATFORM_VERSION, + RM_SELF_TEST_PROJECT_VERSION, RM_SELF_TEST_MAIN_VERSION, + RM_SELF_TEST_SUB_VERSION); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + + /*Test result*/ + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "Test Result = 0x%08X\n\n", + i32_ng_type); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + + if (i32_ng_type == 0) { + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "All pass\n\n\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + } else { + memset(write_string, 0, strlen(write_string)); + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_SYSFS_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "System NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_I2C_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "I2C NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_INT_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "INT NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_RESET_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "RESET NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_PRAM_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "PRAM NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_NORMAL_FW_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "NORMAL_FW_NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_OPEN_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "OPEN NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_SHORT_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "SHORT NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_BURN_CC_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "BURN CC NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_GET_DATA_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "GET DATA NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_FLASH_ID_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "FLASH ID NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_NORMAL_FW_VER_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "NORMAL FW VER NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_TEST_FW_VER_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "TEST FW VER NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_TEST_INIT_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "TEST INIT NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_LOAD_TESTFW_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "LOAD TESTFW NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_BURN_FW_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "BURN FW NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_SINGLE_CC_OPEN_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "Open NG (Single Pin CC) "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_SINGLE_CC_SHORT_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "Short NG (Single Pin CC) "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_UB_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "Uniformity Baseline NG "); + } + + if (i32_ng_type & WEARABLE_FT_TEST_RESULT_UC_NG) { + snprintf(write_string + strlen(write_string), + RM_SELF_TEST_MAX_STR_LENGTH, "Uniformity CC NG "); + } + + snprintf(write_string + strlen(write_string), RM_SELF_TEST_MAX_STR_LENGTH, "\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + } + + /*Threshold*/ + memset(write_string, 0, strlen(write_string)); + /*snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "0x%02X, 0x%02X\n0x%02X, + 0x%02X\n0x%02X, 0x%02X\n0x%02X, 0x%02X\n0x%02X, 0x%02X\n0x%02X, 0x%02X\n0x%02X, + 0x%02X\n0x%02X, 0x%02X\n0x%02X, 0x%02X\n", + (g_st_test_thd.i16_ft_test_open_lower_thd >> 8) & 0xFF, (g_st_test_thd.i16_ft_test_open_lower_thd & 0xFF), + (g_st_test_thd.i16_ft_test_short_upper_thd >> 8) & 0xFF, (g_st_test_thd.i16_ft_test_short_upper_thd & 0xFF), + (g_st_test_thd.i16_ft_test_short_lower_thd >> 8) & 0xFF, (g_st_test_thd.i16_ft_test_short_lower_thd & 0xFF), + (g_st_test_thd.i16_ft_test_single_cc_upper_thd >> 8) & 0xFF, (g_st_test_thd.i16_ft_test_single_cc_upper_thd & 0xFF), + (g_st_test_thd.i16_ft_test_single_cc_lower_thd >> 8) & 0xFF, (g_st_test_thd.i16_ft_test_single_cc_lower_thd & 0xFF), + (g_st_test_thd.i16_ft_test_uniformity_bl_upper_thd >> 8) & 0xFF, (g_st_test_thd.i16_ft_test_uniformity_bl_upper_thd & 0xFF), + (g_st_test_thd.i16_ft_test_uniformity_bl_lower_thd >> 8) & 0xFF, (g_st_test_thd.i16_ft_test_uniformity_bl_lower_thd & 0xFF), + (g_st_test_thd.i16_ft_test_uniformity_cc_upper_thd >> 8) & 0xFF, (g_st_test_thd.i16_ft_test_uniformity_cc_upper_thd & 0xFF), + (g_st_test_thd.i16_ft_test_uniformity_cc_lower_thd >> 8) & 0xFF, (g_st_test_thd.i16_ft_test_uniformity_cc_lower_thd & 0xFF));*/ + + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + + + for (u8_i = 0; u8_i < MAX_SENSING_PIN_NUM; u8_i++) { + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "0x%2X,", + g_u16_uc_golden_cc_buf[u8_i]); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + } + + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + + for (u8_i = 0; u8_i < MAX_SENSING_PIN_NUM; u8_i++) { + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "0x%2X,", + g_u16_raw_data3_golden_cc_buf[u8_i]); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + } + + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "\n\n\n\n\n\n\n\n\n\n\n\n\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + + if ((i32_ng_type & 0xFFF0FBF8) < 4) { + + if ((g_st_test_info.u16_ft_test_item & (IC_TEST_ITEMS_SHORT)) != 0) { + /*Raw data*/ + /*Raw data slow*/ + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "\r\n\n\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "Raw Data 1\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + /*Raw data1*/ + memset(p_i16_buf, 0, MAX_IMAGE_BUFFER_SIZE * 2); + for (u8_i = 0; u8_i < MAX_IMAGE_BUFFER_SIZE; u8_i++) + if (g_u8_wearable_pin_map[u8_i] != F303_NA_P) + p_i16_buf[g_u8_wearable_pin_map[u8_i]] = g_i16_raw_data_1_short_buf[u8_i]; + + for (u8_j = 0; u8_j < g_u8_channel_y; u8_j++) { + for (u8_i = 0; u8_i < g_u8_channel_x; u8_i++) { + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, + "%05d,", p_i16_buf[u8_i + u8_j * g_u8_channel_x]); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + } + + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "\r\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + + } + } + if ((g_st_test_info.u16_ft_test_item & (IC_TEST_ITEMS_OPEN)) != 0) { + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "\r\n\n\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + + /*Raw data slow*/ + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "Raw Data 2\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + /*Raw data2*/ + memset(p_i16_buf, 0, MAX_IMAGE_BUFFER_SIZE * 2); + for (u8_i = 0; u8_i < MAX_IMAGE_BUFFER_SIZE; u8_i++) + if (g_u8_wearable_pin_map[u8_i] != F303_NA_P) + p_i16_buf[g_u8_wearable_pin_map[u8_i]] = g_i16_raw_data_2_open_buf[u8_i]; + + for (u8_j = 0; u8_j < g_u8_channel_y; u8_j++) { + for (u8_i = 0; u8_i < g_u8_channel_x; u8_i++) { + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, + "%05d,", p_i16_buf[u8_i + u8_j * g_u8_channel_x]); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + } + + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "\r\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + + } + } + if ((g_st_test_info.u16_ft_test_item & (IC_TEST_ITEMS_OPEN | IC_TEST_ITEMS_SHORT)) != 0) { + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "\r\n\n\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + + /*Raw data 3*/ + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "Raw Data 3\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + /*Raw data3*/ + memset(p_i16_buf, 0, MAX_IMAGE_BUFFER_SIZE * 2); + for (u8_i = 0; u8_i < MAX_IMAGE_BUFFER_SIZE; u8_i++) + if (g_u8_wearable_pin_map[u8_i] != F303_NA_P) + p_i16_buf[g_u8_wearable_pin_map[u8_i]] = g_u16_raw_data3_cc_buf[u8_i]; + + for (u8_j = 0; u8_j < g_u8_channel_y; u8_j++) { + for (u8_i = 0; u8_i < g_u8_channel_x; u8_i++) { + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, + "%05d,", p_i16_buf[u8_i + u8_j * g_u8_channel_x]); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + } + + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "\r\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + + } + } + if ((g_st_test_info.u16_ft_test_item & (IC_TEST_ITEMS_UC)) != 0) { + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "\r\n\n\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + + /*Raw data Uniformity CC*/ + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "Raw Data_UC\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + /*Raw data uc*/ + memset(p_i16_buf, 0, MAX_IMAGE_BUFFER_SIZE * 2); + for (u8_i = 0; u8_i < MAX_IMAGE_BUFFER_SIZE; u8_i++) + if (g_u8_wearable_pin_map[u8_i] != F303_NA_P) + p_i16_buf[g_u8_wearable_pin_map[u8_i]] = g_u16_uc_buf[u8_i]; + + DEBUGOUT("Image:0x%x\r\n", p_i16_buf[0]); + + for (u8_j = 0; u8_j < g_u8_channel_y; u8_j++) { + for (u8_i = 0; u8_i < g_u8_channel_x; u8_i++) { + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, + "%05d,", p_i16_buf[u8_i + u8_j * g_u8_channel_x]); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + } + + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "\r\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + + } + } + } + + memset(write_string, 0, strlen(write_string)); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "\r\n\n\n"); + self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); + + return 1; +} + +int self_test_read_setting_from_file(void) +{ + unsigned short u16_offset = 0; + + DEBUGOUT("[touch]g_raydium_ts->id = 0x%x\r\n", g_raydium_ts->id); + switch (g_raydium_ts->id) { + case RAD_SELFTEST_30: + u16_offset = 0; + memcpy(&g_u8_ini_flash[u16_offset], &u8_test_info_30, sizeof(u8_test_info_30)); + u16_offset += 16; + memcpy(&g_u8_ini_flash[u16_offset], &i8_ft_test_thd_30, sizeof(i8_ft_test_thd_30)); + u16_offset += 36; + memcpy(&g_u8_ini_flash[u16_offset], &u8_test_para_30, sizeof(u8_test_para_30)); + u16_offset += 48; + u16_offset += 128;/*reserve for BL*/ + memcpy(&g_u8_ini_flash[u16_offset], &u8_raw_data_3_cc_30, sizeof(u8_raw_data_3_cc_30)); + u16_offset += 128; + memcpy(&g_u8_ini_flash[u16_offset], &u8_raw_uc_cc_30, sizeof(u8_raw_uc_cc_30)); + u16_offset += 128; + + memcpy((void *)(&g_st_test_para_resv), &u8_test_para_30[0], sizeof(g_st_test_para_resv)); + DEBUGOUT("ini length = %d\r\n", u16_offset); + break; +#if defined(FW_MAPPING_EN) + case RAD_SELFTEST_31: + u16_offset = 0; + memcpy(&g_u8_ini_flash[u16_offset], &u8_test_info_31, sizeof(u8_test_info_31)); + u16_offset += 16; + memcpy(&g_u8_ini_flash[u16_offset], &i8_ft_test_thd_31, sizeof(i8_ft_test_thd_31)); + u16_offset += 36; + memcpy(&g_u8_ini_flash[u16_offset], &u8_test_para_31, sizeof(u8_test_para_31)); + u16_offset += 48; + u16_offset += 128;/*reserve for BL*/ + memcpy(&g_u8_ini_flash[u16_offset], &u8_raw_data_3_cc_31, sizeof(u8_raw_data_3_cc_31)); + u16_offset += 128; + memcpy(&g_u8_ini_flash[u16_offset], &u8_raw_uc_cc_31, sizeof(u8_raw_uc_cc_31)); + u16_offset += 128; + + memcpy((void *)(&g_st_test_para_resv), &u8_test_para_31[0], sizeof(g_st_test_para_resv)); + DEBUGOUT("ini length = %d\r\n", u16_offset); + break; +#endif + } + + return 0; +} + +int raydium_do_selftest(struct raydium_ts_data *ts) +{ + int ret = RESULT_SUCCESS; + unsigned int time_start, time_end, time_start2, time_end2; + + time_start = get_system_time(); + + pr_info("Selftest Version=%x.%x.%x.%x.%x\n", RM_SELF_TEST_CUSTOMER_VERSION, RM_SELF_TEST_PLATFORM_VERSION, + RM_SELF_TEST_PROJECT_VERSION, RM_SELF_TEST_MAIN_VERSION, RM_SELF_TEST_SUB_VERSION); + + self_test_read_setting_from_file(); + ic_drv_init(); + set_raydium_ts_data(ts); + + ret = self_test_init(); + if (ret != 0) + DEBUGOUT("mapping ic fw fail\n"); + else { + DEBUGOUT("Test all\n"); + ret |= self_test_all(); + } + + if (ret != WEARABLE_FT_TEST_RESULT_SYSFS_NG) { + gpio_touch_hw_reset(); + g_u8_raydium_flag &= ~ENG_MODE; + } + + raydium_i2c_mode_control(ts->client, ENABLE_TOUCH_INT); +#if ENABLE_TIME_MEASURMENT + time_start2 = get_system_time(); +#endif + self_test_save_test_raw_data_to_file(ret); + +#if ENABLE_TIME_MEASURMENT + time_end2 = get_system_time(); + DEBUGOUT("Write log Finish(%ums)\n", time_end2 - time_start2); +#endif + if (ret != 0) { + DEBUGOUT("Selftest Test Result=0x%x\n", ret); + ret = RESULT_NG; + DEBUGOUT("Selftest Result=%d\n", ret); + } else { + DEBUGOUT("Selftest Pass ^_^!!!\n"); + ret = RESULT_SUCCESS; + } + + time_end = get_system_time(); + DEBUGOUT("All Test Finish(%ums)\n", time_end - time_start); + + + return ret; +} diff --git a/raydium/raydium_selftest.h b/raydium/raydium_selftest.h new file mode 100644 index 0000000000..c6bfd79582 --- /dev/null +++ b/raydium/raydium_selftest.h @@ -0,0 +1,34 @@ +/* raydium_selftest.h + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + +#define DISABLE_PDA2_MODE_AND_INT 0 +#define ENABLE_PDA2_MODE_AND_INT 1 +#define DISABLE_TOUCH_INT 2 +#define ENABLE_TOUCH_INT 3 + +#define ENABLE_TIME_MEASURMENT 1 +/*Raydium system flag*/ +#define RAYDIUM_INTERRUPT_FLAG 0x01 + + +#define RM_SELF_TEST_THRESHOLD_FILE "/data/selftest/tpselftest.ini" +#define RM_SELF_TEST_LOGFILE "/sdcard/selftest.csv" + + +extern const unsigned char u8_rad_testpara_20[]; + diff --git a/raydium/raydium_sysfs.c b/raydium/raydium_sysfs.c new file mode 100644 index 0000000000..34eadf9885 --- /dev/null +++ b/raydium/raydium_sysfs.c @@ -0,0 +1,1454 @@ +/*raydium_sysfs.c + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "raydium_driver.h" + +static ssize_t raydium_touch_calibration_show(struct device *dev, + struct device_attribute *attr, + char *p_i8_buf) +{ + unsigned char u8_rbuffer[1]; + unsigned short u16_len = 0; + int i32_ret = -1; + unsigned char u8_retry = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + if (g_raydium_ts->is_suspend) + LOGD(LOG_DEBUG, "[touch]RAD is_suspend at %s\n", __func__); + + g_u8_raydium_flag |= ENG_MODE; + + mutex_lock(&g_raydium_ts->lock); + + i32_ret = raydium_i2c_pda2_set_page(client, + g_raydium_ts->is_suspend, + RAYDIUM_PDA2_PAGE_0); + if (i32_ret < 0) + goto exit_i2c_error; + + u8_rbuffer[0] = RAYDIUM_HOST_CMD_CALIBRATION; + i32_ret = raydium_i2c_pda2_write(client, RAYDIUM_PDA2_HOST_CMD_ADDR, + u8_rbuffer, 1); + if (i32_ret < 0) + goto exit_i2c_error; + + do { + if (u8_rbuffer[0] == RAYDIUM_HOST_CMD_NO_OP) + break; + + msleep(1000); + + i32_ret = raydium_i2c_pda2_read(client, + RAYDIUM_PDA2_HOST_CMD_ADDR, + u8_rbuffer, 1); + if (i32_ret < 0) + goto exit_i2c_error; + + LOGD(LOG_INFO, "[touch]RAD %s return 0x%02x!!\n", + __func__, u8_rbuffer[0]); + } while (u8_retry++ < (SYN_I2C_RETRY_TIMES * 2)); + + memcpy(p_i8_buf, u8_rbuffer, 1); + + u16_len = strlen(p_i8_buf); + i32_ret = u16_len + 1; + +exit_i2c_error: + mutex_unlock(&g_raydium_ts->lock); + g_u8_raydium_flag &= ~ENG_MODE; + return i32_ret; +} + +static ssize_t raydium_i2c_pda_access_show(struct device *dev, + struct device_attribute *attr, + char *p_i8_buf) +{ + unsigned char u8_rbuffer[4]; + unsigned short u16_len = 0; + int i32_ret = -1; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + if (g_u32_length > 4) + return -EINVAL; + memset(u8_rbuffer, 0x00, 4); + mutex_lock(&g_raydium_ts->lock); + i32_ret = raydium_i2c_pda_read(client, + g_u32_addr, + u8_rbuffer, + g_u32_length); + mutex_unlock(&g_raydium_ts->lock); + if (i32_ret < 0) + return i32_ret; + + snprintf(p_i8_buf, PAGE_SIZE, "0x%08X : 0x%02X%02X%02X%02X\n", + (unsigned int)g_u32_addr, u8_rbuffer[3], u8_rbuffer[2], + u8_rbuffer[1], u8_rbuffer[0]); + u16_len = strlen(p_i8_buf); + + return u16_len + 1; +} + + +static ssize_t raydium_i2c_pda_access_via_pda2_show(struct device *dev, + struct device_attribute *attr, + char *p_i8_buf) +{ + unsigned char u8_rbuffer[4]; + unsigned short u16_len = 0; + int i32_ret = -1; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + if (g_u32_length > 4) + return -EINVAL; + memset(u8_rbuffer, 0x00, 4); + mutex_lock(&g_raydium_ts->lock); + i32_ret = handle_i2c_pda_read(client, + g_u32_addr, + u8_rbuffer, + g_u32_length); + mutex_unlock(&g_raydium_ts->lock); + if (i32_ret < 0) + return i32_ret; + + snprintf(p_i8_buf, PAGE_SIZE, "0x%08X : 0x%02X%02X%02X%02X\n", + (unsigned int)g_u32_addr, u8_rbuffer[3], u8_rbuffer[2], + u8_rbuffer[1], u8_rbuffer[0]); + u16_len = strlen(p_i8_buf); + + return u16_len + 1; +} + +static ssize_t raydium_check_i2c_show(struct device *dev, + struct device_attribute *attr, + char *p_i8_buf) +{ + unsigned char u8_rbuffer[4]; + unsigned short u16_len = 0; + int i32_ret = -1; + + if (g_raydium_ts->is_suspend) + LOGD(LOG_DEBUG, "[touch]RAD is_suspend at %s\n", __func__); + + mutex_lock(&g_raydium_ts->lock); + i32_ret = handle_i2c_pda_read(g_raydium_ts->client, + RAYDIUM_CHK_I2C_CMD, u8_rbuffer, 4); + if (i32_ret < 0) + goto exit_i2c_error; + + snprintf(p_i8_buf, PAGE_SIZE, "[touch]RAD Touch check i2c: %02X%02X\n", + u8_rbuffer[3], u8_rbuffer[2]); + u16_len = strlen(p_i8_buf); + i32_ret = u16_len + 1; + +exit_i2c_error: + mutex_unlock(&g_raydium_ts->lock); + return i32_ret; +} + +static ssize_t raydium_hw_reset_show(struct device *dev, + struct device_attribute *attr, + char *p_i8_buf) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + int i32_ret = SUCCESS; + + + LOGD(LOG_INFO, "[touch]HW reset\n"); + g_u8_resetflag = true; + g_u8_raydium_flag |= ENG_MODE; + + /*HW reset*/ + if (gpio_is_valid(g_raydium_ts->rst_gpio)) { + gpio_set_value(g_raydium_ts->rst_gpio, 1); + gpio_set_value(g_raydium_ts->rst_gpio, 0); + msleep(RAYDIUM_RESET_INTERVAL_MSEC); + gpio_set_value(g_raydium_ts->rst_gpio, 1); + } + + g_u8_i2c_mode = PDA2_MODE; + if (raydium_disable_i2c_deglitch() == ERROR) + LOGD(LOG_ERR, "[touch]disable_i2c_deglitch_3x NG!\r\n"); + + i32_ret = wait_irq_state(client, 1000, 2000); + if (i32_ret != ERROR) + msleep(35); + + g_u8_raydium_flag &= ~ENG_MODE; + + snprintf(p_i8_buf, PAGE_SIZE, "Raydium HW Reset : %d\n", i32_ret); + LOGD(LOG_INFO, "[touch]%s\n", p_i8_buf); + return strlen(p_i8_buf) + 1; +} + +static ssize_t raydium_palm_status_show(struct device *dev, + struct device_attribute *attr, + char *p_i8_buf) +{ + unsigned short u16_len = 0; + + unsigned char u8_tp_status[MAX_TCH_STATUS_PACKET_SIZE]; + unsigned char u8_tp_buf[MAX_REPORT_PACKET_SIZE]; + + raydium_read_touchdata(u8_tp_status, u8_tp_buf); + snprintf(p_i8_buf, PAGE_SIZE, "[touch] palm_status : %d\n", + u8_tp_status[POS_GES_STATUS]); + + u16_len = strlen(p_i8_buf); + return u16_len + 1; +} +static ssize_t raydium_touch_lock_store(struct device *dev, + struct device_attribute *attr, + const char *p_i8_buf, size_t count) +{ + int i32_ret = 0; + unsigned char u8_mode; + unsigned char u8_wbuffer[1]; + + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + if (g_raydium_ts->is_suspend) + LOGD(LOG_DEBUG, "[touch]RAD is_suspend at %s\n", __func__); + + /* receive command line arguments string */ + if (count > 2) + return -EINVAL; + + i32_ret = kstrtou8(p_i8_buf, 16, &u8_mode); + if (i32_ret < 0) + return i32_ret; + g_u8_raydium_flag |= ENG_MODE; + mutex_lock(&g_raydium_ts->lock); + + switch (u8_mode) { + case 0: /* Disable Touch lock */ + + if (g_raydium_ts->is_sleep != 1) + break; + g_u8_resetflag = true; + if (gpio_is_valid(g_raydium_ts->rst_gpio)) { + gpio_set_value(g_raydium_ts->rst_gpio, 1); + gpio_set_value(g_raydium_ts->rst_gpio, 0); + msleep(RAYDIUM_RESET_INTERVAL_MSEC);/*5ms*/ + gpio_set_value(g_raydium_ts->rst_gpio, 1); + msleep(RAYDIUM_RESET_DELAY_MSEC);/*100ms*/ + } + LOGD(LOG_INFO, "[touch]RAD %s disable touch lock!!\n", __func__); + + g_raydium_ts->is_sleep = 0; + break; + + case 1: /* Enable Touch lock */ + + if (g_raydium_ts->is_sleep == 1) + break; + i32_ret = raydium_i2c_pda2_set_page(client, + g_raydium_ts->is_suspend, + RAYDIUM_PDA2_PAGE_0); + if (i32_ret < 0) + goto exit_i2c_error; + /*fw enter sleep mode*/ + u8_wbuffer[0] = RAYDIUM_HOST_CMD_PWR_SLEEP; + i32_ret = raydium_i2c_pda2_write(client, + RAYDIUM_PDA2_HOST_CMD_ADDR, + u8_wbuffer, + 1); + if (i32_ret < 0) + goto exit_i2c_error; + + LOGD(LOG_INFO, "[touch]RAD %s enable touch lock!!\n", __func__); + g_raydium_ts->is_sleep = 1; + break; + } + +exit_i2c_error: + mutex_unlock(&g_raydium_ts->lock); + g_u8_raydium_flag &= ~ENG_MODE; + return count; +} + +static ssize_t raydium_check_driver_version_show(struct device *dev, + struct device_attribute *attr, + char *p_i8_buf) +{ + /*unsigned char rbuffer[4];*/ + unsigned short u16_len = 0; + int i32_ret = -1; + + snprintf(p_i8_buf, PAGE_SIZE, "RAD Driver Ver: 0x%X\n", + g_u32_driver_version); + + u16_len = strlen(p_i8_buf); + i32_ret = u16_len + 1; + return i32_ret; +} + +static ssize_t raydium_check_fw_version_show(struct device *dev, + struct device_attribute *attr, + char *p_i8_buf) +{ + unsigned char u8_rbuffer[4]; + unsigned short u16_len = 0; + int i32_ret = -1; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + unsigned int fw_version, image_version; + + if (g_raydium_ts->is_suspend) + LOGD(LOG_DEBUG, "[touch]RAD is_suspend at %s\n", __func__); + + g_u8_raydium_flag |= ENG_MODE; + mutex_lock(&g_raydium_ts->lock); + + i32_ret = raydium_i2c_pda2_set_page(client, + g_raydium_ts->is_suspend, + RAYDIUM_PDA2_PAGE_0); + if (i32_ret < 0) + goto exit_i2c_error; + i32_ret = raydium_i2c_pda2_read(client, RAYDIUM_PDA2_FW_VERSION_ADDR, + u8_rbuffer, 4); + if (i32_ret < 0) + goto exit_i2c_error; + snprintf(p_i8_buf, PAGE_SIZE, "RAD Touch FW Ver : %02X%02X%02X%02X\n", + u8_rbuffer[0], u8_rbuffer[1], u8_rbuffer[2], u8_rbuffer[3]); + + fw_version = (u8_rbuffer[0] << 24) + | (u8_rbuffer[1] << 16) + | (u8_rbuffer[2] << 8) + | u8_rbuffer[3]; + LOGD(LOG_INFO, "[touch]RAD FW ver : 0x%x\n", fw_version); + + image_version = (g_rad_para_image[PARA_FW_VERSION_OFFSET] << 24) | + (g_rad_para_image[PARA_FW_VERSION_OFFSET + 1] << 16) | + (g_rad_para_image[PARA_FW_VERSION_OFFSET + 2] << 8) | + g_rad_para_image[PARA_FW_VERSION_OFFSET + 3]; + + LOGD(LOG_INFO, "[touch]RAD Image FW ver : 0x%x\n", image_version); + + mutex_unlock(&g_raydium_ts->lock); + g_u8_raydium_flag &= ~ENG_MODE; + + if (fw_version != image_version) + LOGD(LOG_INFO, "[touch]%s, FW need upgrade.\n", __func__); + + u16_len = strlen(p_i8_buf); + i32_ret = u16_len + 1; + goto exit_upgrade; + +exit_i2c_error: + mutex_unlock(&g_raydium_ts->lock); + g_u8_raydium_flag &= ~ENG_MODE; + +exit_upgrade: + return i32_ret; +} + +static ssize_t raydium_check_panel_version_show(struct device *dev, + struct device_attribute *attr, + char *p_i8_buf) +{ + unsigned char u8_rbuffer[8]; + unsigned short u16_len = 0; + int i32_ret = -1; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + if (g_raydium_ts->is_suspend) + LOGD(LOG_DEBUG, "[touch]RAD is_suspend at %s\n", __func__); + g_u8_raydium_flag |= ENG_MODE; + mutex_lock(&g_raydium_ts->lock); + + i32_ret = raydium_i2c_pda2_set_page(client, + g_raydium_ts->is_suspend, + RAYDIUM_PDA2_PAGE_0); + if (i32_ret < 0) + goto exit_i2c_error; + i32_ret = raydium_i2c_pda2_read(client, + RAYDIUM_PDA2_PANEL_VERSION_ADDR, + u8_rbuffer, 8); + if (i32_ret < 0) + goto exit_i2c_error; + snprintf(p_i8_buf, PAGE_SIZE, + "RAD Touch Panel Version : %02X%02X%02X%02X%02X%02X\n", + u8_rbuffer[0], u8_rbuffer[1], u8_rbuffer[2], + u8_rbuffer[3], u8_rbuffer[4], u8_rbuffer[5]); + mutex_unlock(&g_raydium_ts->lock); + g_u8_raydium_flag &= ~ENG_MODE; + + u16_len = strlen(p_i8_buf); + i32_ret = u16_len + 1; + goto exit_upgrade; + +exit_i2c_error: + mutex_unlock(&g_raydium_ts->lock); + g_u8_raydium_flag &= ~ENG_MODE; +exit_upgrade: + return i32_ret; +} + +static ssize_t raydium_fw_upgrade_store(struct device *dev, + struct device_attribute *attr, + const char *p_i8_buf, size_t count) +{ + int i32_ret = 0; + + /* receive command line arguments string */ + if (count > 2) + return -EINVAL; + + i32_ret = kstrtou8(p_i8_buf, 16, &g_u8_upgrade_type); + if (i32_ret < 0) + return i32_ret; + + return count; +} + +static ssize_t raydium_fw_upgrade_show(struct device *dev, + struct device_attribute *attr, + char *p_i8_buf) +{ + int i32_ret = 0, i32_result = FAIL; + unsigned short u16_len = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + LOGD(LOG_INFO, "[touch]RAD burn type is %d\n", g_u8_upgrade_type); + + g_u8_raydium_flag |= ENG_MODE; + + if ((g_u8_table_setting == 1) && (g_u8_table_init == 1)) + raydium_mem_table_setting(); + + if (g_u8_upgrade_type == 1) { + i32_ret = raydium_burn_fw(client); + if (i32_ret < 0) + goto exit_upgrade; + + i32_result = SUCCESS; + } else if (g_u8_upgrade_type == 2) { + i32_ret = raydium_burn_comp(client); + if (i32_ret < 0) + goto exit_upgrade; + + i32_result = SUCCESS; + } +#ifdef RAD_SELFTEST + else if (g_u8_upgrade_type == 4) { + i32_ret = raydium_load_test_fw(client); + if (i32_ret < 0) + goto exit_upgrade; + + i32_result = SUCCESS; + } +#endif + +exit_upgrade: + LOGD(LOG_DEBUG, "[touch]g_u8_raydium_flag : %d", g_u8_raydium_flag); + g_u8_raydium_flag &= ~ENG_MODE; + g_u8_upgrade_type = 0; + + snprintf(p_i8_buf, PAGE_SIZE, "FW Upgrade result : %d\n", i32_result); + u16_len = strlen(p_i8_buf); + return u16_len + 1; +} +static ssize_t raydium_i2c_pda2_page_store(struct device *dev, + struct device_attribute *attr, + const char *p_i8_buf, size_t count) +{ + int i32_ret = 0; + unsigned char u8_page = 0; + char *temp_buf, *token, *free_temp_buf, *free_token; + const char *delim = " ,"; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + /* receive command line arguments string */ + if (count < 2) + return -EINVAL; + temp_buf = kzalloc(count + 1, GFP_KERNEL); + if (temp_buf == NULL) + return -ENOMEM; + + token = kzalloc(count + 1, GFP_KERNEL); + if (token == NULL) { + kfree(temp_buf); + return -ENOMEM; + } + + free_temp_buf = temp_buf; + free_token = token; + + strlcpy(temp_buf, p_i8_buf, count); + + token = strsep(&temp_buf, delim); + + if (temp_buf) { + LOGD(LOG_ERR, "[touch]input error, extra auguments!n"); + i32_ret = -EINVAL; + goto exit_error; + } + i32_ret = kstrtou8(token, 16, &u8_page); + + if (i32_ret < 0) + goto exit_error; + + mutex_lock(&g_raydium_ts->lock); + + i32_ret = raydium_i2c_pda2_set_page(client, g_raydium_ts->is_suspend, u8_page); + if (i32_ret < 0) + goto exit_set_error; + + /* TODO: Page check, Due to ISR will change page back to Page_0. + * Or disable IRQ during PDA2 access period + */ + +exit_set_error: + mutex_unlock(&g_raydium_ts->lock); + +exit_error: + kfree(free_token); + kfree(free_temp_buf); + return count; +} + +static ssize_t raydium_mem_store(struct device *dev, + struct device_attribute *attr, + const char *p_i8_buf, size_t count) +{ + int i32_ret = 0; + unsigned char u8_type = 0; + unsigned int u32_image_version; + + /* receive command line arguments string */ + if (count > 2) + return -EINVAL; + + LOGD(LOG_INFO, "[touch]%s\n", __func__); + + i32_ret = kstrtou8(p_i8_buf, 16, &u8_type); + if (i32_ret < 0) + return i32_ret; + + if (u8_type > 3) { + LOGD(LOG_ERR, "[touch]Input invalid value!!\n"); + return ERROR; + } + + if (g_rad_boot_image) { + kfree(g_rad_boot_image); + g_rad_boot_image = NULL; + } + if (g_rad_init_image) { + kfree(g_rad_init_image); + g_rad_init_image = NULL; + } + if (g_rad_fw_image) { + kfree(g_rad_fw_image); + g_rad_fw_image = NULL; + } + if (g_rad_para_image) { + ree(g_rad_para_image); + g_rad_para_image = NULL; + } + if (g_rad_testfw_image) { + kfree(g_rad_testfw_image); + g_rad_testfw_image = NULL; + } + if (g_rad_testpara_image) { + free(g_rad_testpara_image); + g_rad_testpara_image = NULL; + } + if (!raydium_id_init(u8_type)) { + LOGD(LOG_ERR, "[touch]Set Raydium id failed!\n"); + return count; + } + + raydium_mem_table_init(g_raydium_ts->id); + if (raydium_mem_table_setting()) { + u32_image_version = (g_rad_para_image[PARA_FW_VERSION_OFFSET] << 24) | + (g_rad_para_image[PARA_FW_VERSION_OFFSET + 1] << 16) | + (g_rad_para_image[PARA_FW_VERSION_OFFSET + 2] << 8) | + g_rad_para_image[PARA_FW_VERSION_OFFSET + 3]; + + LOGD(LOG_INFO, "[touch]RAD Image FW ver : 0x%x\n", u32_image_version); + } else + LOGD(LOG_ERR, "[touch]Mem init failed!\n"); + if (g_rad_para_image) { + kfree(g_rad_para_image); + g_rad_para_image = NULL; + } + + return count; +} + +static ssize_t raydium_i2c_raw_data_store(struct device *dev, + struct device_attribute *attr, + const char *p_i8_buf, size_t count) +{ + int i32_ret = 0; + char *temp_buf, *token, *free_temp_buf, *free_token; + const char *delim = " ,"; + unsigned char u8_w_data[RAD_FT_CMD_LENGTH]; + + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + /* receive command line arguments string */ + if (count < 2) + return -EINVAL; + temp_buf = kzalloc(count + 1, GFP_KERNEL); + if (temp_buf == NULL) { + LOGD(LOG_ERR, "[touch]kzalloc temp_buf failed\n"); + return -ENOMEM; + } + + token = kzalloc(count + 1, GFP_KERNEL); + if (token == NULL) { + kfree(temp_buf); + return -ENOMEM; + } + + free_temp_buf = temp_buf; + free_token = token; + + strlcpy(temp_buf, p_i8_buf, count); + + token = strsep(&temp_buf, delim); + + i32_ret = kstrtou8(token, 16, &g_u8_raw_data_type); + + token = strsep(&temp_buf, delim); + if (token) { + i32_ret = kstrtouint(token, 16, &g_u32_raw_data_len); + if (i32_ret < 0) + goto exit_error; + + + } else { /* without length info*/ + i32_ret = -EINVAL; + goto exit_error; + } + + if (temp_buf) { /* too much arguments*/ + i32_ret = -E2BIG; + goto exit_error; + } + + memset(u8_w_data, 0x00, RAD_FT_CMD_LENGTH); + + mutex_lock(&g_raydium_ts->lock); + i32_ret = raydium_i2c_pda2_set_page(client, + g_raydium_ts->is_suspend, + RAYDIUM_PDA2_PAGE_0); + if (i32_ret < 0) { + mutex_unlock(&g_raydium_ts->lock); + goto exit_error; + } + + g_u8_resetflag = true; + + u8_w_data[RAD_HOST_CMD_POS] = RAYDIUM_HOST_CMD_NO_OP; + u8_w_data[RAD_FT_CMD_POS] = g_u8_raw_data_type; + + i32_ret = raydium_i2c_pda2_write(client, RAYDIUM_PDA2_HOST_CMD_ADDR, + u8_w_data, RAD_FT_CMD_LENGTH); + mutex_unlock(&g_raydium_ts->lock); + if (i32_ret < 0) + goto exit_error; + + if (g_u8_raw_data_type == 0) { + msleep(20); + g_u8_resetflag = false; + } +exit_error: + kfree(free_token); + kfree(free_temp_buf); + + return count; +} + +static ssize_t raydium_i2c_raw_data_show(struct device *dev, + struct device_attribute *attr, + char *p_i8_buf) +{ + unsigned char u8_rbuffer[MAX_READ_PACKET_SIZE]; + unsigned int u32_target_addr; + unsigned int u32_offset; + unsigned short u16_read_length; + + int i32_ret = -1; + int i32_retry = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = + (struct raydium_ts_data *)i2c_get_clientdata(client); + unsigned char u8_retry_limit = (ts->is_suspend) ? 100 : 30; + + memset(u8_rbuffer, 0x00, MAX_READ_PACKET_SIZE); + + /* make sure update flag was set*/ + for (i32_retry = 0; i32_retry < u8_retry_limit; i32_retry++) { + mutex_lock(&ts->lock); + i32_ret = raydium_i2c_pda2_set_page(client, + ts->is_suspend, + RAYDIUM_PDA2_PAGE_0); + if (i32_ret < 0) { + mutex_unlock(&ts->lock); + goto exit_i2c_error; + } + + i32_ret = raydium_i2c_pda2_read(client, + RAYDIUM_PDA2_HOST_CMD_ADDR, + u8_rbuffer, + RAD_FT_CMD_LENGTH); + mutex_unlock(&ts->lock); + if (i32_ret < 0) + goto exit_flag_error; + + if ((u8_rbuffer[RAD_FT_CMD_POS] & RAYDIUM_FT_UPDATE) == + RAYDIUM_FT_UPDATE) + break; + + usleep_range(500, 1500); + } + + if (i32_retry == u8_retry_limit) { + i32_ret = -EAGAIN; + goto exit_flag_error; + } + + u32_offset = 0; + u16_read_length = 0; + while (u32_offset < g_u32_raw_data_len) { + if ((u32_offset + MAX_READ_PACKET_SIZE) < + g_u32_raw_data_len) + u16_read_length = MAX_READ_PACKET_SIZE; + else + u16_read_length = + (unsigned short)(g_u32_raw_data_len - u32_offset); + + u32_target_addr = RAD_READ_FT_DATA_CMD + u32_offset; + + mutex_lock(&(ts->lock)); + + /*using byte mode to read 4 bytes*/ + i32_ret = handle_i2c_pda_read(client, + u32_target_addr, + u8_rbuffer, + u16_read_length); + + mutex_unlock(&(ts->lock)); + if (i32_ret < 0) + goto exit_flag_error; + + memcpy((p_i8_buf + u32_offset), u8_rbuffer, u16_read_length); + + u32_offset += u16_read_length; + } + + /* clear update flag to get next one*/ + u8_rbuffer[RAD_HOST_CMD_POS] = RAYDIUM_HOST_CMD_NO_OP; + u8_rbuffer[RAD_FT_CMD_POS] = g_u8_raw_data_type; + mutex_lock(&ts->lock); + i32_ret = raydium_i2c_pda2_write(client, RAYDIUM_PDA2_HOST_CMD_ADDR, + u8_rbuffer, RAD_FT_CMD_LENGTH); + mutex_unlock(&ts->lock); + if (i32_ret < 0) + goto exit_flag_error; + + return g_u32_raw_data_len; +exit_i2c_error: + mutex_unlock(&(ts->lock)); +exit_flag_error: + return i32_ret; +} + +static ssize_t raydium_i2c_pda_access_store(struct device *dev, + struct device_attribute *attr, + const char *p_i8_buf, size_t count) +{ + int i32_ret = 0; + char *temp_buf, *token, *free_temp_buf, *free_token; + const char *delim = " ,"; + unsigned char u8_w_data[MAX_WRITE_PACKET_SIZE]; + unsigned int u32_data_count = 0; + unsigned int u32_data_index = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + /* receive command line arguments string */ + if (count < 2) + return -EINVAL; + + temp_buf = kzalloc(count + 1, GFP_KERNEL); + if (temp_buf == NULL) + return -ENOMEM; + + token = kzalloc(count + 1, GFP_KERNEL); + if (token == NULL) { + kfree(temp_buf); + return -ENOMEM; + } + + free_temp_buf = temp_buf; + free_token = token; + + strlcpy(temp_buf, p_i8_buf, count); + + token = strsep(&temp_buf, delim); + + i32_ret = kstrtoul(token, 16, &g_u32_addr); + + token = strsep(&temp_buf, delim); + if (token) + i32_ret = kstrtouint(token, 16, &u32_data_count); + else + goto exit_error; + if (g_u32_length > MAX_WRITE_PACKET_SIZE) + return -EINVAL; + g_u32_length = u32_data_count; + + memset(u8_w_data, 0x00, MAX_WRITE_PACKET_SIZE); + + if (temp_buf && u32_data_count) { + u32_data_index = 0; + while (u32_data_count) { + token = strsep(&temp_buf, delim); + i32_ret = kstrtou8(token, 16, + &u8_w_data[u32_data_index++]); + if (i32_ret < 0) + goto exit_error; + u32_data_count--; + } + mutex_lock(&g_raydium_ts->lock); + i32_ret = raydium_i2c_pda_write(client, g_u32_addr, + u8_w_data, g_u32_length); + mutex_unlock(&g_raydium_ts->lock); + } + +exit_error: + kfree(free_token); + kfree(free_temp_buf); + return count; +} +static ssize_t raydium_i2c_pda_access_via_pda2_store(struct device *dev, + struct device_attribute *attr, + const char *p_i8_buf, size_t count) +{ + int i32_ret = 0; + char *temp_buf, *token, *free_temp_buf, *free_token; + const char *delim = " ,"; + unsigned char u8_w_data[MAX_WRITE_PACKET_SIZE]; + unsigned int u32_data_count = 0; + unsigned int u32_data_index = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + /* receive command line arguments string */ + if (count < 2) + return -EINVAL; + + temp_buf = kzalloc(count + 1, GFP_KERNEL); + if (temp_buf == NULL) + return -ENOMEM; + + token = kzalloc(count + 1, GFP_KERNEL); + if (token == NULL) { + kfree(temp_buf); + return -ENOMEM; + } + + free_temp_buf = temp_buf; + free_token = token; + + strlcpy(temp_buf, p_i8_buf, count); + + token = strsep(&temp_buf, delim); + + i32_ret = kstrtoul(token, 16, &g_u32_addr); + + token = strsep(&temp_buf, delim); + if (token) + i32_ret = kstrtouint(token, 16, &u32_data_count); + else + goto exit_error; + if (g_u32_length > MAX_WRITE_PACKET_SIZE) + return -EINVAL; + g_u32_length = u32_data_count; + + memset(u8_w_data, 0x00, MAX_WRITE_PACKET_SIZE); + + if (temp_buf && u32_data_count) { + u32_data_index = 0; + while (u32_data_count) { + token = strsep(&temp_buf, delim); + i32_ret = kstrtou8(token, 16, + &u8_w_data[u32_data_index++]); + if (i32_ret < 0) + goto exit_error; + u32_data_count--; + } + mutex_lock(&g_raydium_ts->lock); + i32_ret = handle_i2c_pda_write(client, g_u32_addr, + u8_w_data, g_u32_length); + mutex_unlock(&g_raydium_ts->lock); + } + +exit_error: + kfree(free_token); + kfree(free_temp_buf); + return count; +} + +static ssize_t raydium_i2c_pda2_mode_store(struct device *dev, + struct device_attribute *attr, + const char *p_i8_buf, size_t count) +{ + int i32_ret = 0; + unsigned char u8_mode; + + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + if (g_raydium_ts->is_suspend) + LOGD(LOG_DEBUG, "[touch]RAD is_suspend at %s\n", __func__); + + /* receive command line arguments string */ + if (count > 2) + return -EINVAL; + + i32_ret = kstrtou8(p_i8_buf, 16, &u8_mode); + if (i32_ret < 0) + return i32_ret; + i32_ret = raydium_i2c_mode_control(client, u8_mode); + if (i32_ret < 0) + return i32_ret; + + return count; +} + +static ssize_t raydium_i2c_pda2_access_show(struct device *dev, + struct device_attribute *attr, + char *p_i8_buf) +{ + unsigned char u8_rbuffer[4]; + unsigned short u16_len = 0; + int i32_ret = -1; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + if (g_u32_length > 4) + return -EINVAL; + memset(u8_rbuffer, 0x00, 4); + + mutex_lock(&g_raydium_ts->lock); + i32_ret = raydium_i2c_pda2_read(client, g_u8_addr, + u8_rbuffer, g_u32_length); + mutex_unlock(&g_raydium_ts->lock); + if (i32_ret < 0) + return i32_ret; + + snprintf(p_i8_buf, PAGE_SIZE, "0x%04X : 0x%02X%02X%02X%02X\n", + g_u8_addr, u8_rbuffer[3], u8_rbuffer[2], + u8_rbuffer[1], u8_rbuffer[0]); + u16_len = strlen(p_i8_buf); + + return u16_len + 1; +} + +static ssize_t raydium_i2c_pda2_access_store(struct device *dev, + struct device_attribute *attr, + const char *p_i8_buf, size_t count) +{ + int i32_ret = 0; + char *temp_buf, *token, *free_temp_buf, *free_token; + const char *delim = " ,"; + unsigned char u8_w_data[MAX_WRITE_PACKET_SIZE]; + unsigned int u32_data_count = 0; + unsigned int u32_data_index = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + /* receive command line arguments string */ + if (count < 2) + return -EINVAL; + temp_buf = kzalloc(count + 1, GFP_KERNEL); + if (temp_buf == NULL) { + LOGD(LOG_ERR, "[touch]kzalloc temp_buf failed\n"); + return -ENOMEM; + } + + token = kzalloc(count + 1, GFP_KERNEL); + if (token == NULL) { + kfree(temp_buf); + return -ENOMEM; + } + + free_temp_buf = temp_buf; + free_token = token; + + strlcpy(temp_buf, p_i8_buf, count); + + token = strsep(&temp_buf, delim); + + i32_ret = kstrtou8(token, 16, &g_u8_addr); + + token = strsep(&temp_buf, delim); + if (token) + i32_ret = kstrtouint(token, 16, &u32_data_count); + else { + i32_ret = -EINVAL; + goto exit_error; + } + + if (u32_data_count > MAX_WRITE_PACKET_SIZE) { + i32_ret = -EINVAL; + goto exit_error; + } + + memset(u8_w_data, 0x00, MAX_WRITE_PACKET_SIZE); + + g_u32_length = u32_data_count; + + if (temp_buf && u32_data_count) { + u32_data_index = 0; + while (u32_data_count) { + token = strsep(&temp_buf, delim); + i32_ret = kstrtou8(token, 16, + &u8_w_data[u32_data_index++]); + if (i32_ret < 0) + goto exit_error; + u32_data_count--; + } + + mutex_lock(&g_raydium_ts->lock); + i32_ret = raydium_i2c_pda2_write(client, g_u8_addr, + u8_w_data, g_u32_length); + mutex_unlock(&g_raydium_ts->lock); + if (i32_ret < 0) + goto exit_error; + } + +exit_error: + kfree(free_token); + kfree(free_temp_buf); + return count; +} + +static ssize_t raydium_receive_fw_store(struct device *dev, + struct device_attribute *attr, + const char *p_i8_buf, size_t count) +{ + int i32_ret = 0; + const char *delim = " ,"; + char *token, *temp_buf, *free_token = NULL, *free_temp_buf = NULL; + static unsigned char *p_u8_firmware_data; + + unsigned char u8_cmd; + unsigned long u32_len; + static unsigned char u8_type; + static unsigned int u32_index; + + if (count == 20) { /*check FW type*/ + temp_buf = kzalloc(32, GFP_KERNEL); + if (temp_buf == NULL) { + LOGD(LOG_ERR, "[touch]kzalloc temp_buf failed\n"); + return -ENOMEM; + } + + token = kzalloc(32, GFP_KERNEL); + if (token == NULL) { + kfree(temp_buf); + return -ENOMEM; + } + + free_token = token; + free_temp_buf = temp_buf; + + snprintf(temp_buf, PAGE_SIZE, "%s", p_i8_buf); + token = strsep(&temp_buf, delim); + i32_ret = kstrtou8(token, 16, &u8_cmd); + if (i32_ret < 0) { + LOGD(LOG_ERR, "[touch]kstrtou8 failed\n"); + kfree(free_token); + free_token = NULL; + kfree(free_temp_buf); + free_temp_buf = NULL; + } + + token = strsep(&temp_buf, delim); + i32_ret = kstrtou8(token, 16, &u8_type); + if (i32_ret < 0) { + LOGD(LOG_ERR, "[touch]kstrtou8 failed\n"); + kfree(temp_buf); + kfree(token); + } + + token = strsep(&temp_buf, delim); + i32_ret = kstrtoul(token, 16, &u32_len); + if (i32_ret < 0) { + LOGD(LOG_ERR, "[touch]kstrtou8 failed\n"); + kfree(temp_buf); + kfree(token); + } + + LOGD(LOG_INFO, "[touch]uc_cmd=0x%x, uc_type=0x%x, u16_len=0x%x\n", + u8_cmd, u8_type, (unsigned int)u32_len); + + if (u8_cmd == RAD_CMD_UPDATE_BIN) { /*check FW length*/ + u32_index = 0; + if (u8_type == RAYDIUM_BOOTLOADER) { + memset(g_rad_boot_image, 0, u32_len); + p_u8_firmware_data = g_rad_boot_image; + } else if (u8_type == RAYDIUM_INIT) { + memset(g_rad_init_image, 0, u32_len); + p_u8_firmware_data = g_rad_init_image; + } else if (u8_type == RAYDIUM_PARA) { + memset(g_rad_para_image, 0, u32_len); + p_u8_firmware_data = g_rad_para_image; + } else if (u8_type == RAYDIUM_FIRMWARE) { + memset(g_rad_fw_image, 0, u32_len); + p_u8_firmware_data = g_rad_fw_image; + } else if (u8_type == RAYDIUM_TEST_PARA) { + memset(g_rad_testpara_image, 0, u32_len); + p_u8_firmware_data = g_rad_testpara_image; + } else if (u8_type == RAYDIUM_TEST_FW) { + memset(g_rad_testfw_image, 0, u32_len); + p_u8_firmware_data = g_rad_testfw_image; + } + + } else if (u8_cmd == RAD_CMD_UPDATE_END) { /*set buffer finish*/ + if (u8_type == RAYDIUM_TEST_FW) { + memcpy((g_rad_testfw_image + RAD_FW_3X_SIZE), + g_rad_testpara_image, RAD_PARA_3X_SIZE + 4); + } + + u32_index = 0; + g_u8_table_setting = 0; + + } else if (u8_cmd == RAD_CMD_BURN_FINISH) { /*free buffer*/ + u8_type = 0; + u32_index = 0; + g_u8_table_setting = 1; + } + + if (free_temp_buf) { + kfree(free_temp_buf); + free_temp_buf = NULL; + } + if (free_token) { + kfree(free_token); + free_token = NULL; + } + } else if (count > 10) { /*start copy FW to array*/ + memcpy((p_u8_firmware_data + u32_index), p_i8_buf, count); + u32_index += count; + } else + LOGD(LOG_ERR, "[touch]other case, count=%d\n", count); + + return count; +} + +static ssize_t raydium_log_level_store(struct device *dev, + struct device_attribute *attr, + const char *p_i8_buf, size_t count) +{ + int i32_ret = 0; + unsigned char u8_level = 0; + + /* receive command line arguments string */ + if (count > 2) + return -EINVAL; + i32_ret = kstrtou8(p_i8_buf, 16, &u8_level); + if (i32_ret < 0) + return i32_ret; + g_u8_log_level = u8_level; + LOGD(LOG_ERR, "[touch]g_u8_log_level = %d\r\n", g_u8_log_level); + return count; +} + + +#ifdef RAD_SELFTEST +static ssize_t raydium_reset_control_store(struct device *dev, + struct device_attribute *attr, + const char *p_i8_buf, size_t count) +{ + int i32_ret = 0; + unsigned char u8_high; + + if (g_raydium_ts->is_suspend) + LOGD(LOG_DEBUG, "[touch]RAD is_suspend at %s\n", __func__); + + /* receive command line arguments string */ + if (count > 2) + return -EINVAL; + + i32_ret = kstrtou8(p_i8_buf, 16, &u8_high); + if (i32_ret < 0) + return i32_ret; + + g_u8_i2c_mode = PDA2_MODE; + g_u8_resetflag = true; + + if (u8_high) { + LOGD(LOG_INFO, "[touch]RAD %s set reset gpio to high!!\n", + __func__); + gpio_set_value(g_raydium_ts->rst_gpio, 1); + } else { + LOGD(LOG_INFO, "[touch]RAD %s set reset gpio to low!!\n", + __func__); + gpio_set_value(g_raydium_ts->rst_gpio, 0); + } + return count; +} + + +static ssize_t raydium_irq_state_show(struct device *dev, + struct device_attribute *attr, + char *p_i8_buf) +{ + unsigned int u32_irq_value; + + u32_irq_value = gpio_get_value(g_raydium_ts->irq_gpio); + + snprintf(p_i8_buf, PAGE_SIZE, "%d", u32_irq_value); + LOGD(LOG_DEBUG, "%s\n", p_i8_buf); + return strlen(p_i8_buf) + 1; +} + + +static ssize_t raydium_flag_show(struct device *dev, + struct device_attribute *attr, + char *p_i8_buf) +{ + unsigned short u16_len = 0; + + snprintf(p_i8_buf, PAGE_SIZE, "%d", g_u8_raydium_flag); + LOGD(LOG_DEBUG, "[touch]RAD flag : %d\n", g_u8_raydium_flag); + u16_len = strlen(p_i8_buf); + + return u16_len + 1; +} + +static ssize_t raydium_flag_store(struct device *dev, + struct device_attribute *attr, + const char *p_i8_buf, size_t count) +{ + int i32_ret = 0; + unsigned char u8_flag = 0; + + /* receive command line arguments string */ + if (count > 2) + return -EINVAL; + i32_ret = kstrtou8(p_i8_buf, 16, &u8_flag); + if (i32_ret < 0) + return i32_ret; + g_u8_raydium_flag = u8_flag; + return count; +} + +static ssize_t raydium_selftest_show(struct device *dev, + struct device_attribute *attr, + char *p_i8_buf) +{ + int i32_ret = SUCCESS; + + LOGD(LOG_INFO, "[touch]do selftest\n"); + + i32_ret = raydium_do_selftest(g_raydium_ts); + + + snprintf(p_i8_buf, PAGE_SIZE, "Raydium do selftest : %d\n", i32_ret); + + return strlen(p_i8_buf) + 1; +} + +#endif +/* panel calibration cmd (R) + * example:cat raydium_ic_verion + */ +static DEVICE_ATTR(raydium_touch_calibration, 0644, + raydium_touch_calibration_show, + NULL); + +/* check the i2c (R) + * example:cat raydium_check_i2c + */ +static DEVICE_ATTR(raydium_check_i2c, 0644, + raydium_check_i2c_show, + NULL); + +/* upgrade configurate and algo firmware from app.bin (W) + * example:echo "offset num_of_bin length *_app.bin [length *_app.bin]" + * > raydium_fw_upgrade_mode + */ +static DEVICE_ATTR(raydium_fw_upgrade, 0644, + raydium_fw_upgrade_show, + raydium_fw_upgrade_store); + +/* change I2C communication mode (W) + * example:echo 1 > raydium_i2c_pda2_mode ==> enable pda2 mode + * echo 0 > raydium_i2c_pda2_mode ==> disable pda2 mode + */ +static DEVICE_ATTR(raydium_i2c_pda2_mode, 0644, + NULL, + raydium_i2c_pda2_mode_store); + +/* I2C pda mode (R/W) + * example: cat raydium_i2c_pda_access ==> read pda address provided by the + * following cmd + * echo ADDRinHEX [DATAinHEX] > raydium_i2c_pda_access ==> write + * pda address [data] + */ +static DEVICE_ATTR(raydium_i2c_pda_access, 0644, + raydium_i2c_pda_access_show, + raydium_i2c_pda_access_store); +/* I2C pda mode via pda2(R/W) + * example: cat raydium_i2c_pda_access ==> read pda address provided by the + * following cmd + * echo ADDRinHEX [DATAinHEX] > raydium_i2c_pda_access ==> write + * pda address [data] + */ +static DEVICE_ATTR(raydium_i2c_pda_access_via_pda2, 0644, + raydium_i2c_pda_access_via_pda2_show, + raydium_i2c_pda_access_via_pda2_store); + +/* I2C pda2 mode (R/W) + * example: cat raydium_i2c_pda2_access ==> read pda2 address provided by + * the following cmd + * echo ADDRinHEX [DATAinHEX] > raydium_i2c_pda2_access ==> + * write pda2 address [data] + */ +static DEVICE_ATTR(raydium_i2c_pda2_access, 0644, + raydium_i2c_pda2_access_show, + raydium_i2c_pda2_access_store); + +/* I2C pda2 mode page (W) + * example: echo PAGEinHEX > raydium_i2c_pda2_page ==> write pda2 page + */ +static DEVICE_ATTR(raydium_i2c_pda2_page, 0644, + NULL, + raydium_i2c_pda2_page_store); + +/* I2C read/set FT raw data (R/W) + * example: cat raydium_i2c_raw_data ==> read raw data with specific length + * of corresponding type provided by the following cmd + * echo DataTypeinHEX RawDataLengthinHEX > raydium_i2c_raw_data + * ==> set raw data type and its length + */ +static DEVICE_ATTR(raydium_i2c_raw_data, 0644, + raydium_i2c_raw_data_show, + raydium_i2c_raw_data_store); + +/* Touch lock (W) + * example: echo 1 > raydium_i2c_touch_lock ==> enable touch lock + * echo 0 > raydium_i2c_touch_lock ==> disable touch lock + */ +static DEVICE_ATTR(raydium_i2c_touch_lock, 0644, + NULL, + raydium_touch_lock_store); + +/* Log level (W) + * example: echo 1 > raydium_log_level ==> modify log level + */ +static DEVICE_ATTR(raydium_log_level, 0644, + NULL, + raydium_log_level_store); +/* show the fw version (R) + * example:cat raydium_fw_version + */ +static DEVICE_ATTR(raydium_check_fw_version, 0644, + raydium_check_fw_version_show, + NULL); + +/* show the driver version (R) + * example:cat raydium_check_driver_version + */ +static DEVICE_ATTR(raydium_check_driver_version, 0644, + raydium_check_driver_version_show, + NULL); +/* show the panel version (R) + * example:cat raydium_panel_version + */ +static DEVICE_ATTR(raydium_check_panel_version, 0644, + raydium_check_panel_version_show, + NULL); + +static DEVICE_ATTR(raydium_hw_reset, 0644, + raydium_hw_reset_show, + NULL); + +static DEVICE_ATTR(raydium_palm_status, 0644, + raydium_palm_status_show, + NULL); + +static DEVICE_ATTR(raydium_receive_fw_control, 0644, + NULL, + raydium_receive_fw_store); + +static DEVICE_ATTR(raydium_mem_setting, 0644, + NULL, + raydium_mem_store); + +#ifdef RAD_SELFTEST + +/* Read interrupt flag cmd (R) + * example:cat raydium_int_flag + */ +static DEVICE_ATTR(raydium_int_flag, 0644, + raydium_flag_show, + raydium_flag_store); + +static DEVICE_ATTR(raydium_reset_control, 0644, + NULL, + raydium_reset_control_store); + +static DEVICE_ATTR(raydium_irq_state, 0644, + raydium_irq_state_show, + NULL); +static DEVICE_ATTR(raydium_do_selftest, 0644, + raydium_selftest_show, + NULL); +#endif +/*add your attr in here*/ +struct attribute *raydium_attributes[] = { + &dev_attr_raydium_touch_calibration.attr, + &dev_attr_raydium_check_i2c.attr, + &dev_attr_raydium_i2c_pda2_mode.attr, + &dev_attr_raydium_i2c_pda_access.attr, + &dev_attr_raydium_i2c_pda_access_via_pda2.attr, + &dev_attr_raydium_i2c_pda2_access.attr, + &dev_attr_raydium_i2c_pda2_page.attr, + &dev_attr_raydium_i2c_raw_data.attr, + &dev_attr_raydium_i2c_touch_lock.attr, + &dev_attr_raydium_fw_upgrade.attr, + &dev_attr_raydium_check_fw_version.attr, + &dev_attr_raydium_check_panel_version.attr, + &dev_attr_raydium_hw_reset.attr, + &dev_attr_raydium_palm_status.attr, + &dev_attr_raydium_check_driver_version.attr, + &dev_attr_raydium_receive_fw_control.attr, + &dev_attr_raydium_mem_setting.attr, + &dev_attr_raydium_log_level.attr, +#ifdef RAD_SELFTEST + &dev_attr_raydium_irq_state.attr, + &dev_attr_raydium_int_flag.attr, + &dev_attr_raydium_reset_control.attr, + &dev_attr_raydium_do_selftest.attr, +#endif + NULL +}; diff --git a/raydium/tpselftest_30.h b/raydium/tpselftest_30.h new file mode 100644 index 0000000000..2f5ad79a03 --- /dev/null +++ b/raydium/tpselftest_30.h @@ -0,0 +1,54 @@ +/* tpselftest_30.h + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + */ + +#define RAD_SELFTEST_30 0x3202 +unsigned char u8_test_info_30[16] = { +0x03, 0x02, 0x02, 0x06, 0x5D, 0x00, 0x00, 0xAC, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +signed char i8_ft_test_thd_30[36] = { +0x00, 0x00, 0x7F, 0xFE, 0x22, 0x07, 0x00, 0x00, 0x00, 0x00, 0x85, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x7C, 0xFF, 0x17, 0x00, 0x23, 0x00, +0x2C, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +unsigned char u8_test_para_30[48] = { +0x14, 0x05, 0x64, 0x0A, 0x5A, 0x14, 0x50, 0x15, 0x12, 0x00, 0x03, 0x1E, +0x00, 0x03, 0x64, 0x15, 0x12, 0x02, 0x00, 0x00, 0x10, 0xA1, 0x00, 0x02, +0x08, 0x20, 0x02, 0x28, 0x46, 0x00, 0x00, 0x00, 0x40, 0x08, 0x02, 0x00, +0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x01, 0x02, 0x02, 0x40, 0x01 +}; +unsigned char u8_raw_data_3_cc_30[96] = { +0xAB, 0x02, 0xCF, 0x02, 0x97, 0x02, 0x00, 0x00, 0xBD, 0x02, 0x8D, 0x02, +0xAF, 0x02, 0x00, 0x00, 0x82, 0x02, 0xA5, 0x02, 0x61, 0x02, 0x00, 0x00, +0x61, 0x02, 0xFC, 0x02, 0xBC, 0x02, 0x00, 0x00, 0x6B, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xA5, 0x02, 0xD1, 0x02, 0x90, 0x02, 0x00, 0x00, 0xBD, 0x02, 0x87, 0x02, +0xAE, 0x02, 0x00, 0x00, 0x75, 0x02, 0xA1, 0x02, 0x51, 0x02, 0x00, 0x00, +0x5F, 0x02, 0xBF, 0x02, 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +unsigned char u8_raw_uc_cc_30[96] = { +0x7F, 0x02, 0x93, 0x02, 0x5D, 0x02, 0x00, 0x00, 0x83, 0x02, 0x55, 0x02, +0x79, 0x02, 0x00, 0x00, 0x51, 0x02, 0x79, 0x02, 0x39, 0x02, 0x00, 0x00, +0x3D, 0x02, 0xCB, 0x02, 0x8F, 0x02, 0x00, 0x00, 0x4D, 0x02, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x79, 0x02, 0x95, 0x02, 0x55, 0x02, 0x00, 0x00, 0x83, 0x02, 0x51, 0x02, +0x7B, 0x02, 0x00, 0x00, 0x45, 0x02, 0x75, 0x02, 0x2B, 0x02, 0x00, 0x00, +0x3B, 0x02, 0x95, 0x02, 0x65, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; From b0e4b1c1943362da6479c6e9e26f61126a70f132 Mon Sep 17 00:00:00 2001 From: Parade-Github <87209105+Parade-Github@users.noreply.github.com> Date: Wed, 3 Aug 2022 18:03:22 +0530 Subject: [PATCH 034/170] touch : Add files via upload Removed example files and move source to /pt/ Git-commit: e2e4bb9eacc2406cb0351d15e478e68249d5f625 Git-repo: https://github.com/Parade-Github/TTDL/tree/main Change-Id: I07f3464718058d5bba76b7d5be486a79934db354 Signed-off-by: Surya Teja Kudiri --- pt/Makefile | 57 + pt/pt_btn.c | 524 ++ pt/pt_core.c | 19501 ++++++++++++++++++++++++++++++++++++++++ pt/pt_debug.c | 556 ++ pt/pt_device_access.c | 6830 ++++++++++++++ pt/pt_devtree.c | 1128 +++ pt/pt_i2c.c | 570 ++ pt/pt_loader.c | 5956 ++++++++++++ pt/pt_mt_common.c | 970 ++ pt/pt_mta.c | 143 + pt/pt_mtb.c | 144 + pt/pt_pen.c | 572 ++ pt/pt_platform.c | 1281 +++ pt/pt_proximity.c | 781 ++ pt/pt_regs.h | 1875 ++++ pt/pt_spi.c | 657 ++ 16 files changed, 41545 insertions(+) create mode 100644 pt/Makefile create mode 100644 pt/pt_btn.c create mode 100644 pt/pt_core.c create mode 100644 pt/pt_debug.c create mode 100644 pt/pt_device_access.c create mode 100644 pt/pt_devtree.c create mode 100644 pt/pt_i2c.c create mode 100644 pt/pt_loader.c create mode 100644 pt/pt_mt_common.c create mode 100644 pt/pt_mta.c create mode 100644 pt/pt_mtb.c create mode 100644 pt/pt_pen.c create mode 100644 pt/pt_platform.c create mode 100644 pt/pt_proximity.c create mode 100644 pt/pt_regs.h create mode 100644 pt/pt_spi.c diff --git a/pt/Makefile b/pt/Makefile new file mode 100644 index 0000000000..f495e2dbcd --- /dev/null +++ b/pt/Makefile @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the touchscreen drivers. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_TOUCHSCREEN_PARADE) += pt.o +pt-y := pt_core.o pt_mt_common.o +pt-$(CONFIG_TOUCHSCREEN_PARADE_MT_A) += pt_mta.o +pt-$(CONFIG_TOUCHSCREEN_PARADE_MT_B) += pt_mtb.o +pt-$(CONFIG_TOUCHSCREEN_PARADE_BUTTON) += pt_btn.o +pt-$(CONFIG_TOUCHSCREEN_PARADE_PEN) += pt_pen.o +pt-$(CONFIG_TOUCHSCREEN_PARADE_PROXIMITY) += pt_proximity.o +obj-$(CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT) += pt_devtree.o +ifdef CONFIG_TOUCHSCREEN_PARADE +obj-y += pt_platform.o +endif +obj-$(CONFIG_TOUCHSCREEN_PARADE_I2C) += pt_i2c.o +obj-$(CONFIG_TOUCHSCREEN_PARADE_SPI) += pt_spi.o +obj-$(CONFIG_TOUCHSCREEN_PARADE_DEBUG_MDL) += pt_debug.o +obj-$(CONFIG_TOUCHSCREEN_PARADE_LOADER) += pt_loader.o +obj-$(CONFIG_TOUCHSCREEN_PARADE_DEVICE_ACCESS) += pt_device_access.o + +ifeq ($(CONFIG_TOUCHSCREEN_PARADE_DEBUG),y) +CFLAGS_pt_core.o += -DDEBUG +CFLAGS_pt_i2c.o += -DDEBUG +CFLAGS_pt_spi.o += -DDEBUG +CFLAGS_pt_mta.o += -DDEBUG +CFLAGS_pt_mtb.o += -DDEBUG +CFLAGS_pt_mt_common.o += -DDEBUG +CFLAGS_pt_btn.o += -DDEBUG +CFLAGS_pt_pen.o += -DDEBUG +CFLAGS_pt_proximity.o += -DDEBUG +CFLAGS_pt_device_access.o += -DDEBUG +CFLAGS_pt_loader.o += -DDEBUG +CFLAGS_pt_debug.o += -DDEBUG +CFLAGS_pt_devtree.o += -DDEBUG +CFLAGS_pt_platform.o += -DDEBUG +endif + +ifeq ($(CONFIG_TOUCHSCREEN_PARADE_VDEBUG),y) +CFLAGS_pt_core.o += -DVERBOSE_DEBUG +CFLAGS_pt_i2c.o += -DVERBOSE_DEBUG +CFLAGS_pt_spi.o += -DVERBOSE_DEBUG +CFLAGS_pt_mta.o += -DVERBOSE_DEBUG +CFLAGS_pt_mtb.o += -DVERBOSE_DEBUG +CFLAGS_pt_mt_common.o += -DVERBOSE_DEBUG +CFLAGS_pt_btn.o += -DVERBOSE_DEBUG +CFLAGS_pt_pen.o += -DVERBOSE_DEBUG +CFLAGS_pt_proximity.o += -DVERBOSE_DEBUG +CFLAGS_pt_device_access.o += -DVERBOSE_DEBUG +CFLAGS_pt_loader.o += -DVERBOSE_DEBUG +CFLAGS_pt_debug.o += -DVERBOSE_DEBUG +CFLAGS_pt_devtree.o += -DVERBOSE_DEBUG +CFLAGS_pt_platform.o += -DVERBOSE_DEBUG +endif diff --git a/pt/pt_btn.c b/pt/pt_btn.c new file mode 100644 index 0000000000..28d1df077c --- /dev/null +++ b/pt/pt_btn.c @@ -0,0 +1,524 @@ +#ifndef TTDL_KERNEL_SUBMISSION +/* + * pt_btn.c + * Parade TrueTouch(TM) Standard Product CapSense Reports Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.paradetech.com + */ + +#include "pt_regs.h" + +/******************************************************************************* + * FUNCTION: pt_btn_key_action + * + * SUMMARY: Reports key event + * + * PARAMETERS: + * *bd - pointer to button data structure + * btn_no - number of button + * btn_state - state of button + ******************************************************************************/ +static inline void pt_btn_key_action(struct pt_btn_data *bd, + int btn_no, int btn_state) +{ + struct device *dev = bd->dev; + struct pt_sysinfo *si = bd->si; + + if (!si->btn[btn_no].enabled || + si->btn[btn_no].state == btn_state) + return; + + si->btn[btn_no].state = btn_state; + input_report_key(bd->input, si->btn[btn_no].key_code, btn_state); + input_sync(bd->input); + + pt_debug(dev, DL_INFO, "%s: btn=%d key_code=%d %s\n", + __func__, btn_no, si->btn[btn_no].key_code, + btn_state == PT_BTN_PRESSED ? + "PRESSED" : "RELEASED"); +} + +/******************************************************************************* + * FUNCTION: pt_get_btn_touches + * + * SUMMARY: Parse and report key event + * + * PARAMETERS: + * *bd - pointer to button data structure + ******************************************************************************/ +static void pt_get_btn_touches(struct pt_btn_data *bd) +{ + struct pt_sysinfo *si = bd->si; + int num_btns = si->num_btns; + int cur_btn; + int cur_btn_state; + + for (cur_btn = 0; cur_btn < num_btns; cur_btn++) { + /* Get current button state */ + cur_btn_state = (si->xy_data[0] >> (cur_btn * PT_BITS_PER_BTN)) + & PT_NUM_BTN_EVENT_ID; + + pt_btn_key_action(bd, cur_btn, cur_btn_state); + } +} + +/******************************************************************************* + * FUNCTION: pt_btn_lift_all + * + * SUMMARY: Reports button liftoff action + * + * PARAMETERS: + * *bd - pointer to button data structure + ******************************************************************************/ +static void pt_btn_lift_all(struct pt_btn_data *bd) +{ + struct pt_sysinfo *si = bd->si; + int i; + + if (!si || si->num_btns == 0) + return; + + for (i = 0; i < si->num_btns; i++) + pt_btn_key_action(bd, i, PT_BTN_RELEASED); +} + +/******************************************************************************* + * FUNCTION: pt_xy_worker + * + * SUMMARY: Read xy_data for all current CapSense button touches + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *bd - pointer to button data structure + ******************************************************************************/ +static int pt_xy_worker(struct pt_btn_data *bd) +{ + struct pt_sysinfo *si = bd->si; + + /* extract button press/release touch information */ + if (si->num_btns > 0) + pt_get_btn_touches(bd); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_btn_attention + * + * SUMMARY: Wrapper function for pt_xy_worker() that register to TTDL attention + * list. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_btn_attention(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_btn_data *bd = &cd->bd; + int rc; + + if (bd->si->xy_mode[2] != bd->si->desc.btn_report_id) + return 0; + + /* core handles handshake */ + mutex_lock(&bd->btn_lock); + rc = pt_xy_worker(bd); + mutex_unlock(&bd->btn_lock); + if (rc < 0) + pt_debug(dev, DL_ERROR, + "%s: xy_worker error r=%d\n", __func__, rc); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_startup_attention + * + * SUMMARY: Wrapper function for pt_btn_lift_all() that register to TTDL + * attention list. + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_startup_attention(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_btn_data *bd = &cd->bd; + + mutex_lock(&bd->btn_lock); + pt_btn_lift_all(bd); + mutex_unlock(&bd->btn_lock); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_btn_suspend_attention + * + * SUMMARY: Function for button to enter suspend state that as following steps: + * 1) Lift all button + * 2) Set flag with suspend state + * 3) Decrese pm system count + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_btn_suspend_attention(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_btn_data *bd = &cd->bd; + + mutex_lock(&bd->btn_lock); + pt_btn_lift_all(bd); + bd->is_suspended = true; + mutex_unlock(&bd->btn_lock); + + pm_runtime_put(dev); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_btn_resume_attention + * + * SUMMARY: Function for button to leave suspend state that as following steps: + * 1) Increse pm system count + * 2) Clear suspend state flag + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_btn_resume_attention(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_btn_data *bd = &cd->bd; + + pm_runtime_get(dev); + + mutex_lock(&bd->btn_lock); + bd->is_suspended = false; + mutex_unlock(&bd->btn_lock); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_btn_open + * + * SUMMARY: Open method for input device(button) that sets up call back + * functions to TTDL attention list + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *input - pointer to input_dev structure + ******************************************************************************/ +static int pt_btn_open(struct input_dev *input) +{ + struct device *dev = input->dev.parent; + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_btn_data *bd = &cd->bd; + + pm_runtime_get_sync(dev); + + mutex_lock(&bd->btn_lock); + bd->is_suspended = false; + mutex_unlock(&bd->btn_lock); + + pt_debug(dev, DL_INFO, "%s: setup subscriptions\n", __func__); + + /* set up touch call back */ + _pt_subscribe_attention(dev, PT_ATTEN_IRQ, PT_BTN_NAME, + pt_btn_attention, PT_MODE_OPERATIONAL); + + /* set up startup call back */ + _pt_subscribe_attention(dev, PT_ATTEN_STARTUP, PT_BTN_NAME, + pt_startup_attention, 0); + + /* set up suspend call back */ + _pt_subscribe_attention(dev, PT_ATTEN_SUSPEND, PT_BTN_NAME, + pt_btn_suspend_attention, 0); + + /* set up resume call back */ + _pt_subscribe_attention(dev, PT_ATTEN_RESUME, PT_BTN_NAME, + pt_btn_resume_attention, 0); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_btn_close + * + * SUMMARY: Close method for input device(button) that clears call back + * functions from TTDL attention list. + * + * PARAMETERS: + * *input - pointer to input_dev structure + ******************************************************************************/ +static void pt_btn_close(struct input_dev *input) +{ + struct device *dev = input->dev.parent; + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_btn_data *bd = &cd->bd; + + _pt_unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_BTN_NAME, + pt_btn_attention, PT_MODE_OPERATIONAL); + + _pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_BTN_NAME, + pt_startup_attention, 0); + + _pt_unsubscribe_attention(dev, PT_ATTEN_SUSPEND, PT_BTN_NAME, + pt_btn_suspend_attention, 0); + + _pt_unsubscribe_attention(dev, PT_ATTEN_RESUME, PT_BTN_NAME, + pt_btn_resume_attention, 0); + + mutex_lock(&bd->btn_lock); + if (!bd->is_suspended) { + pm_runtime_put(dev); + bd->is_suspended = true; + } + mutex_unlock(&bd->btn_lock); +} + +/******************************************************************************* + * FUNCTION: pt_setup_input_device + * + * SUMMARY: Set up resolution, event signal capabilities and register input + * device for button. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_setup_input_device(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_btn_data *bd = &cd->bd; + int i; + int rc; + + pt_debug(dev, DL_INFO, "%s: Initialize event signals\n", + __func__); + __set_bit(EV_KEY, bd->input->evbit); + pt_debug(dev, DL_INFO, "%s: Number of buttons %d\n", + __func__, bd->si->num_btns); + for (i = 0; i < bd->si->num_btns; i++) { + pt_debug(dev, DL_INFO, "%s: btn:%d keycode:%d\n", + __func__, i, bd->si->btn[i].key_code); + __set_bit(bd->si->btn[i].key_code, bd->input->keybit); + } + + rc = input_register_device(bd->input); + if (rc < 0) + pt_debug(dev, DL_ERROR, + "%s: Error, failed register input device r=%d\n", + __func__, rc); + else + bd->input_device_registered = true; + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_setup_input_attention + * + * SUMMARY: Wrapper function for pt_setup_input_device() register to TTDL + * attention list. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_setup_input_attention(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_btn_data *bd = &cd->bd; + int rc; + + bd->si = _pt_request_sysinfo(dev); + if (!bd->si) + return -1; + + rc = pt_setup_input_device(dev); + + _pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_BTN_NAME, + pt_setup_input_attention, 0); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_btn_probe + * + * SUMMARY: The probe function for button input device + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +int pt_btn_probe(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_btn_data *bd = &cd->bd; + struct pt_platform_data *pdata = dev_get_platdata(dev); + struct pt_btn_platform_data *btn_pdata; + int rc = 0; + + if (!pdata || !pdata->btn_pdata) { + pt_debug(dev, DL_ERROR, + "%s: Missing platform data\n", __func__); + rc = -ENODEV; + goto error_no_pdata; + } + btn_pdata = pdata->btn_pdata; + + mutex_init(&bd->btn_lock); + bd->dev = dev; + bd->pdata = btn_pdata; + + /* Create the input device and register it. */ + pt_debug(dev, DL_INFO, + "%s: Create the input device and register it\n", __func__); + bd->input = input_allocate_device(); + if (!bd->input) { + pt_debug(dev, DL_ERROR, + "%s: Error, failed to allocate input device\n", + __func__); + rc = -ENODEV; + goto error_alloc_failed; + } else + bd->input_device_allocated = true; + + if (bd->pdata->inp_dev_name) + bd->input->name = bd->pdata->inp_dev_name; + else + bd->input->name = PT_BTN_NAME; + scnprintf(bd->phys, sizeof(bd->phys), "%s/input%d", dev_name(dev), + cd->phys_num++); + bd->input->phys = bd->phys; + bd->input->dev.parent = bd->dev; + bd->input->open = pt_btn_open; + bd->input->close = pt_btn_close; + input_set_drvdata(bd->input, bd); + + /* get sysinfo */ + bd->si = _pt_request_sysinfo(dev); + + if (bd->si) { + rc = pt_setup_input_device(dev); + if (rc) + goto error_init_input; + } else { + pt_debug(dev, DL_ERROR, + "%s: Fail get sysinfo pointer from core p=%p\n", + __func__, bd->si); + _pt_subscribe_attention(dev, PT_ATTEN_STARTUP, + PT_BTN_NAME, pt_setup_input_attention, 0); + } + + return 0; + +error_init_input: + input_free_device(bd->input); + bd->input_device_allocated = false; +error_alloc_failed: +error_no_pdata: + pt_debug(dev, DL_ERROR, "%s failed.\n", __func__); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_btn_release + * + * SUMMARY: The release function for button input device + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +int pt_btn_release(struct device *dev) +{ + struct pt_core_data *cd; + struct pt_btn_data *bd; + + /* Ensure valid pointers before de-referencing them */ + if (dev) { + cd = dev_get_drvdata(dev); + if (cd) + bd = &cd->bd; + else + return 0; + } else { + return 0; + } + + /* + * Second call this function may cause kernel panic if probe fail. + * Use input_device_registered & input_device_allocated variable to + * avoid unregister or free unavailable devive. + */ + if (bd && bd->input_device_registered) { + bd->input_device_registered = false; + input_unregister_device(bd->input); + /* Unregistering device will free the device too */ + bd->input_device_allocated = false; + } else if (bd && bd->input_device_allocated) { + bd->input_device_allocated = false; + input_free_device(bd->input); + _pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, + PT_BTN_NAME, pt_setup_input_attention, 0); + } + + return 0; +} +#endif /*!TTDL_KERNEL_SUBMISSION */ diff --git a/pt/pt_core.c b/pt/pt_core.c new file mode 100644 index 0000000000..0ecbe1b537 --- /dev/null +++ b/pt/pt_core.c @@ -0,0 +1,19501 @@ +/* + * pt_core.c + * Parade TrueTouch(TM) Standard Product Core Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.paradetech.com + */ + +#include "pt_regs.h" +#include +#include +#include + +#ifdef PT_PTSBC_SUPPORT +#define PT_CORE_PROBE_STARTUP_DELAY_MS 500 +#endif /* PT_PTSBC_SUPPORT */ + +#define PT_CORE_STARTUP_RETRY_COUNT 3 + +MODULE_FIRMWARE(PT_FW_FILE_NAME); + +static const char *pt_driver_core_name = PT_CORE_NAME; +static const char *pt_driver_core_version = PT_DRIVER_VERSION; +static const char *pt_driver_core_date = PT_DRIVER_DATE; + +struct pt_hid_field { + int report_count; + int report_size; + int size; /* report_count * report_size */ + int offset; + int data_type; + int logical_min; + int logical_max; + /* Usage Page (Hi 16 bit) + Usage (Lo 16 bit) */ + u32 usage_page; + u32 collection_usage_pages[PT_HID_MAX_COLLECTIONS]; + struct pt_hid_report *report; + bool record_field; +}; + +struct pt_hid_report { + u8 id; + u8 type; + int size; + struct pt_hid_field *fields[PT_HID_MAX_FIELDS]; + int num_fields; + int record_field_index; + int header_size; + int record_size; + u32 usage_page; + int log_collection_num; +}; + +struct atten_node { + struct list_head node; + char *id; + struct device *dev; + + int (*func)(struct device *dev); + int mode; +}; + +struct param_node { + struct list_head node; + u8 id; + u32 value; + u8 size; +}; + +struct module_node { + struct list_head node; + struct pt_module *module; + void *data; +}; + +struct pt_hid_cmd { + __le16 descriptor; + u8 opcode; + u8 report_type; + union { + u8 report_id; + u8 power_state; + }; + u8 has_data_register; + size_t write_length; + size_t read_length; + u8 *write_buf; + u8 *read_buf; + u8 wait_interrupt; + u8 reset_cmd; + u16 timeout_ms; +}; + +struct pt_hid_output { + u8 cmd_type; + u16 length; + u8 command_code; + size_t write_length; + u8 *write_buf; + u8 novalidate; + u8 reset_expected; + u16 timeout_ms; +}; + +#define SET_CMD_OPCODE(byte, opcode) SET_CMD_LOW(byte, opcode) +#define SET_CMD_REPORT_TYPE(byte, type) SET_CMD_HIGH(byte, ((type) << 4)) +#define SET_CMD_REPORT_ID(byte, id) SET_CMD_LOW(byte, id) + +#define CREATE_PIP1_FW_CMD(command) \ + .cmd_type = PIP1_CMD_TYPE_FW, \ + .command_code = command + +#define CREATE_PIP1_BL_CMD(command) \ + .cmd_type = PIP1_CMD_TYPE_BL, \ + .command_code = command + +#define PT_MAX_PR_BUF_SIZE 2048 +/******************************************************************************* + * FUNCTION: pt_pr_buf + * + * SUMMARY: Print out the contents of a buffer to kmsg based on the debug level + * + * RETURN: Void + * + * PARAMETERS: + * *dev - pointer to Device structure + * debug_level - requested debug level to print at + * *buf - pointer to buffer to print + * buf_len - size of buf + * *data_name - Descriptive name of data prefixed to data + ******************************************************************************/ +void pt_pr_buf(struct device *dev, u8 debug_level, u8 *buf, + u16 buf_len, const char *data_name) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int i; + int pr_buf_index = 0; + int max_size; + + /* only proceed if valid debug level and there is data to print */ + if (debug_level <= cd->debug_level && buf_len > 0) { + char *pr_buf = kzalloc(PT_MAX_PR_BUF_SIZE, GFP_KERNEL); + + if (!pr_buf) + return; + + /* + * With spaces each printed char takes 3 bytes, subtract + * the length of the data_name and length prefix and divide 3 + */ + pr_buf_index += scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE, "%s [0..%d]: ", + data_name, buf_len); + max_size = (PT_MAX_PR_BUF_SIZE - pr_buf_index) / 3; + for (i = 0; i < buf_len && i < max_size; i++) + pr_buf_index += scnprintf(pr_buf + pr_buf_index, + PT_MAX_PR_BUF_SIZE, "%02X ", buf[i]); + + pt_debug(dev, debug_level, "%s\n", pr_buf); + kfree(pr_buf); + } +} +EXPORT_SYMBOL_GPL(pt_pr_buf); + +#ifdef TTHE_TUNER_SUPPORT +/******************************************************************************* + * FUNCTION: tthe_print + * + * SUMMARY: Format data name and time stamp as the header and format the + * content of input buffer with hex base to "tthe_buf". And then wake up event + * semaphore for tthe debugfs node. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *buf - pointer to input buffer + * buf_len - size of input buffer + * *data_name - pointer to data name + ******************************************************************************/ +static int tthe_print(struct pt_core_data *cd, u8 *buf, int buf_len, + const u8 *data_name) +{ + int name_len = strlen(data_name); + int i, n; + u8 *p; + int remain; + u8 data_name_with_time_stamp[100]; + + /* Prepend timestamp, if requested, to data_name */ + if (cd->show_timestamp) { + scnprintf(data_name_with_time_stamp, + sizeof(data_name_with_time_stamp), + "[%u] %s", pt_get_time_stamp(), data_name); + data_name = data_name_with_time_stamp; + name_len = strlen(data_name); + } + + mutex_lock(&cd->tthe_lock); + if (!cd->tthe_buf) + goto exit; + + /* Add 1 due to the '\n' that is appended at the end */ + if (cd->tthe_buf_len + name_len + buf_len + 1 > cd->tthe_buf_size) + goto exit; + + if (name_len + buf_len == 0) + goto exit; + + remain = cd->tthe_buf_size - cd->tthe_buf_len; + if (remain < name_len) + name_len = remain; + + p = cd->tthe_buf + cd->tthe_buf_len; + memcpy(p, data_name, name_len); + cd->tthe_buf_len += name_len; + p += name_len; + remain -= name_len; + + *p = 0; + for (i = 0; i < buf_len; i++) { + n = scnprintf(p, remain, "%02X ", buf[i]); + if (n <= 0) + break; + p += n; + remain -= n; + cd->tthe_buf_len += n; + } + + n = scnprintf(p, remain, "\n"); + cd->tthe_buf_len += n; +exit: + wake_up(&cd->wait_q); + mutex_unlock(&cd->tthe_lock); + return 0; +} + +/******************************************************************************* + * FUNCTION: _pt_request_tthe_print + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to request to print data to the "tthe_buffer". + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int _pt_request_tthe_print(struct device *dev, u8 *buf, + int buf_len, const u8 *data_name) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + return tthe_print(cd, buf, buf_len, data_name); +} +#endif + +/******************************************************************************* + * FUNCTION: pt_platform_detect_read + * + * SUMMARY: To be passed to platform dectect function to perform a read + * operation. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to Device structure + * *buf - pointer to buffer where the data read will be stored + * size - size to be read + ******************************************************************************/ +static int pt_platform_detect_read(struct device *dev, void *buf, int size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + return pt_adap_read_default(cd, buf, size); +} + +/******************************************************************************* + * FUNCTION: pt_add_parameter + * + * SUMMARY: Adds a parameter that has been altered to the parameter linked list. + * On every reset of the DUT this linked list is traversed and all + * parameters in it are restored to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * param_id - parameter ID to add + * param_value - Value corresponding to the ID + * param_size - Size of param_value + ******************************************************************************/ +static int pt_add_parameter(struct pt_core_data *cd, + u8 param_id, u32 param_value, u8 param_size) +{ + struct param_node *param, *param_new; + + /* Check if parameter already exists in the list */ + spin_lock(&cd->spinlock); + list_for_each_entry(param, &cd->param_list, node) { + if (param->id == param_id) { + /* Update parameter */ + param->value = param_value; + pt_debug(cd->dev, DL_INFO, + "%s: Update parameter id:%d value:%d size:%d\n", + __func__, param_id, param_value, param_size); + goto exit_unlock; + } + } + spin_unlock(&cd->spinlock); + + param_new = kzalloc(sizeof(*param_new), GFP_KERNEL); + if (!param_new) + return -ENOMEM; + + param_new->id = param_id; + param_new->value = param_value; + param_new->size = param_size; + + pt_debug(cd->dev, DL_INFO, + "%s: Add parameter id:%d value:%d size:%d\n", + __func__, param_id, param_value, param_size); + + spin_lock(&cd->spinlock); + list_add(¶m_new->node, &cd->param_list); +exit_unlock: + spin_unlock(&cd->spinlock); + + return 0; +} + +#ifndef TTDL_KERNEL_SUBMISSION +#ifdef TTDL_DIAGNOSTICS +/******************************************************************************* + * FUNCTION: pt_erase_parameter_list + * + * SUMMARY: Empty out the entire parameter linked list of all parameter/value + * pairs. In some test cases this functionality is needed to ensure DUT + * returns to a virgin state after a reset and no parameters are restored. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_erase_parameter_list(struct pt_core_data *cd) +{ + struct param_node *pos, *temp; + + spin_lock(&cd->spinlock); + list_for_each_entry_safe(pos, temp, &cd->param_list, node) { + pt_debug(cd->dev, DL_INFO, + "%s: Parameter Restore List - remove 0x%02x\n", + __func__, pos->id); + list_del(&pos->node); + kfree(pos); + } + spin_unlock(&cd->spinlock); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_count_parameter_list + * + * SUMMARY: Count the items in the RAM parameter restor list + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_count_parameter_list(struct pt_core_data *cd) +{ + struct param_node *pos, *temp; + int entries = 0; + + spin_lock(&cd->spinlock); + list_for_each_entry_safe(pos, temp, &cd->param_list, node) + entries++; + spin_unlock(&cd->spinlock); + + return entries; +} +#endif /* TTDL_DIAGNOSTICS */ +#endif /* !TTDL_KERNEL_SUBMISSION */ + +/******************************************************************************* + * FUNCTION: request_exclusive + * + * SUMMARY: Request exclusive access to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *ownptr - pointer to device + * timeout_ms - Timeout value + ******************************************************************************/ +int request_exclusive(struct pt_core_data *cd, void *ownptr, + int timeout_ms) +{ + int t = msecs_to_jiffies(timeout_ms); + bool with_timeout = (timeout_ms != 0); + + pt_debug(cd->dev, DL_INFO, "%s: Attempt to Request EXCLUSIVE t=%d\n", + __func__, timeout_ms); + + mutex_lock(&cd->system_lock); + if (!cd->exclusive_dev && cd->exclusive_waits == 0) { + cd->exclusive_dev = ownptr; + goto exit; + } + + cd->exclusive_waits++; +wait: + mutex_unlock(&cd->system_lock); + if (with_timeout) { + t = wait_event_timeout(cd->wait_q, !cd->exclusive_dev, t); + if (IS_TMO(t)) { + pt_debug(cd->dev, DL_ERROR, + "%s: tmo waiting exclusive access\n", __func__); + return -ETIME; + } + } else { + wait_event(cd->wait_q, !cd->exclusive_dev); + } + mutex_lock(&cd->system_lock); + if (cd->exclusive_dev) + goto wait; + cd->exclusive_dev = ownptr; + cd->exclusive_waits--; +exit: + mutex_unlock(&cd->system_lock); + pt_debug(cd->dev, DL_DEBUG, "%s: request exclusive ok=%p\n", + __func__, ownptr); + + return 0; +} + +/******************************************************************************* + * FUNCTION: release_exclusive_ + * + * SUMMARY: Release exclusive access to the DUT + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *cd - pointer to core data + * *ownptr - pointer to device + ******************************************************************************/ +static int release_exclusive_(struct pt_core_data *cd, void *ownptr) +{ + pt_debug(cd->dev, DL_INFO, "%s: Attempt to Release EXCLUSIVE\n", + __func__); + if (cd->exclusive_dev != ownptr) + return -EINVAL; + + pt_debug(cd->dev, DL_DEBUG, "%s: exclusive_dev %p freed\n", + __func__, cd->exclusive_dev); + cd->exclusive_dev = NULL; + wake_up(&cd->wait_q); + return 0; +} + +/******************************************************************************* + * FUNCTION: release_exclusive + * + * SUMMARY: Protected wrapper to release_exclusive_() + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *ownptr - pointer to device + ******************************************************************************/ +int release_exclusive(struct pt_core_data *cd, void *ownptr) +{ + int rc; + + mutex_lock(&cd->system_lock); + rc = release_exclusive_(cd, ownptr); + mutex_unlock(&cd->system_lock); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_create_cmd_and_send_ + * + * SUMMARY: Send the HID command to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_cmd - pointer to the HID command to send + ******************************************************************************/ +static int pt_hid_create_cmd_and_send_(struct pt_core_data *cd, + struct pt_hid_cmd *hid_cmd) +{ + int rc = 0; + u8 *cmd; + u16 cmd_length; + u8 cmd_offset = 0; + + if (hid_cmd->descriptor) { + cmd_length = 2; /* hid or report register */ + } else { + cmd_length = + 2 /* command register */ + + 2 /* command */ + + (hid_cmd->report_id >= 0XF ? 1 : 0) /* Report ID */ + + (hid_cmd->has_data_register ? 2 : 0) /* Data register */ + + hid_cmd->write_length; /* Data length */ + } + + cmd = kzalloc(cmd_length, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + /* hid & report descriptor doesn't require other field */ + if (hid_cmd->descriptor) { + memcpy(&cmd[cmd_offset], &hid_cmd->descriptor, cmd_length); + goto skip_other_field; + } + + /* Set Command register */ + memcpy(&cmd[cmd_offset], &cd->hid_desc.command_register, + sizeof(cd->hid_desc.command_register)); + cmd_offset += sizeof(cd->hid_desc.command_register); + + /* Set Command */ + SET_CMD_REPORT_TYPE(cmd[cmd_offset], hid_cmd->report_type); + + if (hid_cmd->report_id >= 0XF) + SET_CMD_REPORT_ID(cmd[cmd_offset], 0xF); + else + SET_CMD_REPORT_ID(cmd[cmd_offset], hid_cmd->report_id); + cmd_offset++; + + SET_CMD_OPCODE(cmd[cmd_offset], hid_cmd->opcode); + cmd_offset++; + + if (hid_cmd->report_id >= 0XF) { + cmd[cmd_offset] = hid_cmd->report_id; + cmd_offset++; + } + + /* Set Data register */ + if (hid_cmd->has_data_register) { + memcpy(&cmd[cmd_offset], &cd->hid_desc.data_register, + sizeof(cd->hid_desc.data_register)); + cmd_offset += sizeof(cd->hid_desc.data_register); + } + + /* Set Data */ + if (hid_cmd->write_length && hid_cmd->write_buf) { + memcpy(&cmd[cmd_offset], hid_cmd->write_buf, + hid_cmd->write_length); + cmd_offset += hid_cmd->write_length; + } + + pt_debug(cd->dev, DL_INFO, + ">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n", + __func__, cmd_length, hid_cmd->report_id); + +skip_other_field: + pt_pr_buf(cd->dev, DL_DEBUG, cmd, cmd_length, ">>> CMD"); + + rc = pt_adap_write_read_specific(cd, cmd_length, cmd, hid_cmd->read_buf, + hid_cmd->read_length); + if (rc) + pt_debug(cd->dev, DL_ERROR, + "%s: Fail pt_adap_transfer\n", __func__); + + kfree(cmd); + return rc; +} +#ifdef TTDL_DIAGNOSTICS +/******************************************************************************* + * FUNCTION: pt_toggle_err_gpio + * + * SUMMARY: Toggles the pre-defined error GPIO + * + * RETURN: n/a + * + * PARAMETERS: + * *cd - pointer to core data + * type - type of err that occurred + ******************************************************************************/ +void pt_toggle_err_gpio(struct pt_core_data *cd, u8 type) +{ + pt_debug(cd->dev, DL_DEBUG, "%s called with type = %d\n", + __func__, type); + if (cd->err_gpio && type == cd->err_gpio_type) { + pt_debug(cd->dev, DL_WARN, "%s: Toggle ERR GPIO\n", __func__); + gpio_direction_output(cd->err_gpio, + !gpio_get_value(cd->err_gpio)); + } +} + +/******************************************************************************* + * FUNCTION: _pt_request_toggle_err_gpio + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to request to toggle the err_gpio + * + * RETURN: n/a + * + * PARAMETERS: + * *cd - pointer to core data + * type - type of err that occurred + ******************************************************************************/ +void _pt_request_toggle_err_gpio(struct device *dev, u8 type) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + pt_toggle_err_gpio(cd, type); +} +#endif /* TTDL_DIAGNOSTICS */ + +/******************************************************************************* + * FUNCTION: pt_hid_exec_cmd_and_wait_ + * + * SUMMARY: Send the HID command to the DUT and wait for the response + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_cmd - pointer to the HID command to send + ******************************************************************************/ +static int pt_hid_exec_cmd_and_wait_(struct pt_core_data *cd, + struct pt_hid_cmd *hid_cmd) +{ + int rc = 0; + int t; + u16 timeout_ms; + int *cmd_state; + + if (hid_cmd->reset_cmd) + cmd_state = &cd->hid_reset_cmd_state; + else + cmd_state = &cd->hid_cmd_state; + + mutex_lock(&cd->system_lock); + *cmd_state = 1; + mutex_unlock(&cd->system_lock); + + rc = pt_hid_create_cmd_and_send_(cd, hid_cmd); + if (rc) + goto error; + + if (hid_cmd->timeout_ms) + timeout_ms = hid_cmd->timeout_ms; + else + timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT; + + t = wait_event_timeout(cd->wait_q, (*cmd_state == 0), + msecs_to_jiffies(timeout_ms)); + if (IS_TMO(t)) { +#ifdef TTDL_DIAGNOSTICS + cd->bus_transmit_error_count++; + pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS); +#endif /* TTDL_DIAGNOSTICS */ + pt_debug(cd->dev, DL_ERROR, + "%s: HID output cmd execution timed out\n", + __func__); + rc = -ETIME; + goto error; + } + + goto exit; + +error: + mutex_lock(&cd->system_lock); + *cmd_state = 0; + mutex_unlock(&cd->system_lock); + +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_exec_cmd_no_wait_ + * + * SUMMARY: The function works to send HID command and can read response + * directly instead of waiting it to be received in interrupt function. It + * assgins the read buffer to receive response in following condition: + * 1) descriptor is assigned to get hid descriptor or report descripter + * 2) output register is assigned for vendor-defined commands (TBD) + * + * NOTE: If no read buffer is assigned, it only perform send action. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_cmd - pointer to the HID command to send + ******************************************************************************/ +static int pt_hid_exec_cmd_no_wait_(struct pt_core_data *cd, + struct pt_hid_cmd *hid_cmd) +{ + int rc = 0; + + if (hid_cmd->descriptor) + hid_cmd->read_buf = cd->response_buf; + + rc = pt_hid_create_cmd_and_send_(cd, hid_cmd); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_send_command + * + * SUMMARY: Wrapper function to call pt_hid_exec_cmd_no_wait_() for HID protocol + * and pt_hid_exec_cmd_and_wait_() for PIP protocol. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_cmd - pointer to the HID command to send + ******************************************************************************/ +static int pt_hid_send_command(struct pt_core_data *cd, + struct pt_hid_cmd *hid_cmd) +{ + if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) + return pt_hid_exec_cmd_no_wait_(cd, hid_cmd); + else + return pt_hid_exec_cmd_and_wait_(cd, hid_cmd); +} + +/******************************************************************************* + * FUNCTION: pt_hid_cmd_reset_ + * + * SUMMARY: Send the HID RESET command to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_hid_cmd_reset_(struct pt_core_data *cd) +{ + struct pt_hid_cmd hid_cmd = { + .opcode = HID_CMD_RESET, + .reset_cmd = 1, + .timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT, + }; + + return pt_hid_send_command(cd, &hid_cmd); +} + +/******************************************************************************* + * FUNCTION: pt_hid_cmd_reset + * + * SUMMARY: Wrapper function for pt_hid_cmd_reset_ that guarantees exclusive + * access. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_hid_cmd_reset(struct pt_core_data *cd) +{ + int rc = 0; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + pt_debug(cd->dev, DL_INFO, "%s: Send HID Reset command\n", __func__); + rc = pt_hid_cmd_reset_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_cmd_set_power_ + * + * SUMMARY: Send hid cmd to set power state for the DUT and wait for response + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * power_state - power state to set(HID_POWER_ON/HID_POWER_SLEEP) + ******************************************************************************/ +static int pt_hid_cmd_set_power_(struct pt_core_data *cd, + u8 power_state) +{ + int rc = 0; + struct pt_hid_cmd hid_cmd = { + .opcode = HID_CMD_SET_POWER, + .timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT, + }; + hid_cmd.power_state = power_state; + + /* The chip won't give response if goes to Deep Standby */ + if (power_state == HID_POWER_STANDBY) { + rc = pt_hid_exec_cmd_no_wait_(cd, &hid_cmd); + if (rc) + pt_debug(cd->dev, DL_ERROR, + "%s: Failed to set power to state:%d\n", + __func__, power_state); + else + cd->fw_sys_mode_in_standby_state = true; + return rc; + } + cd->fw_sys_mode_in_standby_state = false; + + rc = pt_hid_send_command(cd, &hid_cmd); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Failed to set power to state:%d\n", + __func__, power_state); + return rc; + } + + /* HID COMMAND doesn't have a response */ + if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) + return rc; + + /* validate */ + if ((cd->response_buf[2] != HID_RESPONSE_REPORT_ID) + || ((cd->response_buf[3] & 0x3) != power_state) + || ((cd->response_buf[4] & 0xF) != HID_CMD_SET_POWER)) + rc = -EINVAL; + + return rc; +} + +#ifndef TTDL_KERNEL_SUBMISSION +/******************************************************************************* + * FUNCTION: pt_hid_cmd_set_power + * + * SUMMARY: Wrapper function for pt_hid_cmd_set_power_ that guarantees + * exclusive access. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * power_state - power state to set(HID_POWER_ON/HID_POWER_SLEEP) + ******************************************************************************/ +static int pt_hid_cmd_set_power(struct pt_core_data *cd, + u8 power_state) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_cmd_set_power_(cd, power_state); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} +#endif /* !TTDL_KERNEL_SUBMISSION */ + +static const u16 crc_table[16] = { + 0x0000, 0x1021, 0x2042, 0x3063, + 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, + 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, +}; + +/******************************************************************************* + * FUNCTION: _pt_compute_crc + * + * SUMMARY: Calculate CRC by CRC table. + * + * RETURN: + * CRC calculation result + * + * PARAMETERS: + * *buf - pointer to the data array to be calculated + * size - size of data array + ******************************************************************************/ +static u16 _pt_compute_crc(u8 *buf, u32 size) +{ + u16 remainder = 0xFFFF; + u16 xor_mask = 0x0000; + u32 index; + u32 byte_value; + u32 table_index; + u32 crc_bit_width = sizeof(u16) * 8; + + /* Divide the message by polynomial, via the table. */ + for (index = 0; index < size; index++) { + byte_value = buf[index]; + table_index = ((byte_value >> 4) & 0x0F) + ^ (remainder >> (crc_bit_width - 4)); + remainder = crc_table[table_index] ^ (remainder << 4); + table_index = (byte_value & 0x0F) + ^ (remainder >> (crc_bit_width - 4)); + remainder = crc_table[table_index] ^ (remainder << 4); + } + + /* Perform the final remainder CRC. */ + return remainder ^ xor_mask; +} + +u16 ccitt_Table[] = { + 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, +}; + +/******************************************************************************* + * FUNCTION: crc_ccitt_calculate + * + * SUMMARY: Calculate CRC with ccitt standard by CRC table. + * + * RETURN: + * CRC calculation result + * + * PARAMETERS: + * *q - pointer to the data array to be calculated + * len - size of data array + ******************************************************************************/ +static unsigned short crc_ccitt_calculate(unsigned char *q, int len) +{ + unsigned short crc = 0xffff; + + while (len-- > 0) + crc = ccitt_Table[(crc >> 8 ^ *q++) & 0xff] ^ (crc << 8); + return crc; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_cmd_calculate_crc + * + * SUMMARY: Calculate the CRC of a command packet + * + * RETURN: void + * + * PARAMETERS: + * *cmd - pointer to command data + * extra_bytes - Extra bytes included in command length + ******************************************************************************/ +static void pt_pip2_cmd_calculate_crc(struct pip2_cmd_structure *cmd, + u8 extra_bytes) +{ + u8 buf[PT_MAX_PIP2_MSG_SIZE + 1] = {0}; + unsigned short crc; + + buf[0] = cmd->len & 0xff; + buf[1] = (cmd->len & 0xff00) >> 8; + buf[2] = cmd->seq; + buf[3] = cmd->id; + memcpy(&buf[4], cmd->data, cmd->len - extra_bytes); + /* Calculate the CRC for the first 4 bytes above and the data payload */ + crc = crc_ccitt_calculate(buf, 4 + (cmd->len - extra_bytes)); + cmd->crc[0] = (crc & 0xff00) >> 8; + cmd->crc[1] = (crc & 0xff); +} + +/******************************************************************************* + * FUNCTION: pt_pip2_get_next_cmd_seq + * + * SUMMARY: Gets the next sequence number for a PIP2 command. The sequence + * number is a 3 bit value (bits [0-2]) but because TTDL will always have + * the TAG bit set (bit 3), the counter starts at 0x08 and goes to 0x0F. + * If the "force_pip2_seq" holds a valid seq value (0x08-0x0F) then do not + * increment, just use the forced value. + * + * RETURN: Next command sequence number [0x08-0x0F] + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static u8 pt_pip2_get_next_cmd_seq(struct pt_core_data *cd) +{ +#ifdef TTDL_DIAGNOSTICS + if (cd->force_pip2_seq <= 0x07) { + cd->pip2_cmd_tag_seq++; + if (cd->pip2_cmd_tag_seq > 0x0F) + cd->pip2_cmd_tag_seq = 0x08; + } else { + cd->pip2_cmd_tag_seq = cd->force_pip2_seq; + } +#else + cd->pip2_cmd_tag_seq++; + if (cd->pip2_cmd_tag_seq > 0x0F) + cd->pip2_cmd_tag_seq = 0x08; +#endif + return cd->pip2_cmd_tag_seq; +} + +/* + * Following macros are to define the response time (the interval between PIP2 + * command finishes sending and INT pin falls). The unit is in microsecond. + * It has different time settings between the solution GPIO polling and Bus + * polling due to the considration for system load. + */ +#ifdef PT_POLL_RESP_BY_BUS +#define POLL_RETRY_DEFAULT_INTERVAL 50 +#define PIP2_RESP_DEFAULT_TIME_MIN 50 +#define PIP2_RESP_DEFAULT_TIME_MAX (PT_PIP_CMD_DEFAULT_TIMEOUT * 1000) +#define PIP2_RESP_FILE_WRITE_TIME_MIN 220 +#define PIP2_RESP_FILE_IOCTL_TIME_MAX (PT_PIP2_CMD_FILE_ERASE_TIMEOUT * 1000) +#else +#define POLL_RETRY_DEFAULT_INTERVAL 20 +#define PIP2_RESP_DEFAULT_TIME_MIN 20 +#define PIP2_RESP_DEFAULT_TIME_MAX (PT_PIP_CMD_DEFAULT_TIMEOUT * 1000) +#define PIP2_RESP_FILE_WRITE_TIME_MIN 20 +#define PIP2_RESP_FILE_IOCTL_TIME_MAX (PT_PIP2_CMD_FILE_ERASE_TIMEOUT * 1000) +#endif +/* + * id: the command id defined in PIP2 + * response_len: the (maximum) length of response. + * response_time_min: minimum response time in microsecond + * response_time_max: maximum response time in microsecond + */ +static const struct pip2_cmd_response_structure pip2_cmd_response[] = { + {.id = PIP2_CMD_ID_PING, + .response_len = 255, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_STATUS, + .response_len = PIP2_EXTRA_BYTES_NUM + 5, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_CTRL, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PT_PIP2_CMD_FILE_ERASE_TIMEOUT}, + {.id = PIP2_CMD_ID_CONFIG, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_CLEAR, + .response_len = PIP2_EXTRA_BYTES_NUM + 0, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_RESET, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_VERSION, + .response_len = PIP2_EXTRA_BYTES_NUM + 23, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_FILE_OPEN, + .response_len = PIP2_EXTRA_BYTES_NUM + 2, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_FILE_CLOSE, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_FILE_READ, + .response_len = 255, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_FILE_WRITE, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_FILE_WRITE_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_FILE_IOCTL, + .response_len = PIP2_EXTRA_BYTES_NUM + 10, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_FILE_IOCTL_TIME_MAX}, + {.id = PIP2_CMD_ID_FLASH_INFO, + .response_len = PIP2_EXTRA_BYTES_NUM + 17, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_EXECUTE, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_GET_LAST_ERRNO, + .response_len = PIP2_EXTRA_BYTES_NUM + 3, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_EXIT_HOST_MODE, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_READ_GPIO, + .response_len = PIP2_EXTRA_BYTES_NUM + 5, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_EXECUTE_SCAN, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_SET_PARAMETER, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_GET_PARAMETER, + .response_len = PIP2_EXTRA_BYTES_NUM + 7, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_SET_DDI_REG, + .response_len = PIP2_EXTRA_BYTES_NUM + 1, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_GET_DDI_REG, + .response_len = PIP2_EXTRA_BYTES_NUM + 249, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX}, + {.id = PIP2_CMD_ID_END, + .response_len = 255, + .response_time_min = PIP2_RESP_DEFAULT_TIME_MIN, + .response_time_max = PIP2_RESP_DEFAULT_TIME_MAX} +}; + +/******************************************************************************* + * FUNCTION: pt_pip2_get_cmd_response_len + * + * SUMMARY: Gets the expected response length based on the command ID + * + * RETURN: Expected response length + * + * PARAMETERS: + * id - Command ID (-1 means input ID is not in list of PIP2 command) + ******************************************************************************/ +static int pt_pip2_get_cmd_response_len(u8 id) +{ + const struct pip2_cmd_response_structure *p = pip2_cmd_response; + + while ((p->id != id) && (p->id != PIP2_CMD_ID_END)) + p++; + + if (p->id != PIP2_CMD_ID_END) + return p->response_len; + else + return -1; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_get_cmd_resp_time_min + * + * SUMMARY: Gets the minimum response time (the interval between PIP2 command + * finishes sending and INT pin falls) based on the command ID + * + * RETURN: Estimated minimum response time in microsecond + * + * PARAMETERS: + * id - Command ID + ******************************************************************************/ +static u32 pt_pip2_get_cmd_resp_time_min(u8 id) +{ + const struct pip2_cmd_response_structure *p = pip2_cmd_response; + + while ((p->id != id) && (p->id != PIP2_CMD_ID_END)) + p++; + + if (p->id != PIP2_CMD_ID_END) + return p->response_time_min; + else + return PIP2_RESP_DEFAULT_TIME_MIN; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_get_cmd_resp_time_max + * + * SUMMARY: Gets the maximum response time (the interval between PIP2 command + * finishes sending and INT pin falls) based on the command ID + * + * RETURN: Estimated maximum response time in microsecond + * + * PARAMETERS: + * id - Command ID + ******************************************************************************/ +static u32 pt_pip2_get_cmd_resp_time_max(u8 id) +{ + const struct pip2_cmd_response_structure *p = pip2_cmd_response; + + while ((p->id != id) && (p->id != PIP2_CMD_ID_END)) + p++; + + if (p->id != PIP2_CMD_ID_END) + return p->response_time_max; + else + return PIP2_RESP_DEFAULT_TIME_MAX; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_validate_response + * + * SUMMARY: Validate the response of PIP2 command. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *pip2_cmd - pointer to PIP2 command to send + * *read_buf - pointer to response buffer + * actual_read_len - actual read length of the response + ******************************************************************************/ +static int pt_pip2_validate_response(struct pt_core_data *cd, + struct pip2_cmd_structure *pip2_cmd, u8 *read_buf, + u16 actual_read_len) +{ + int rc = 0; + u8 response_seq = 0; + u8 reserved_bits = 0; + u8 cmd_id = 0; + u8 response_bit = 0; + unsigned short calc_crc = 0; + unsigned short resp_crc = 0; + + /* Verify the length of response buffer */ + if (actual_read_len < PT_MIN_PIP2_PACKET_SIZE) { + pt_debug(cd->dev, DL_ERROR, + "%s cmd[0x%02X] read length ERR: read_len = %d\n", + __func__, pip2_cmd->id, actual_read_len); + rc = -EINVAL; + goto exit; + } + + /* Verify the CRC */ + calc_crc = crc_ccitt_calculate(read_buf, actual_read_len - 2); + resp_crc = read_buf[actual_read_len - 2] << 8; + resp_crc |= read_buf[actual_read_len - 1]; + if (resp_crc != calc_crc) { + pt_debug(cd->dev, DL_ERROR, + "%s: cmd[0x%02X] CRC ERR: calc=0x%04X rsp=0x%04X\n", + __func__, pip2_cmd->id, calc_crc, resp_crc); +#ifdef TTDL_DIAGNOSTICS + cd->pip2_crc_error_count++; +#endif /* TTDL_DIAGNOSTICS */ + rc = -EINVAL; + goto exit; + } + + /* Verify the response bit is set */ + response_bit = read_buf[PIP2_RESP_REPORT_ID_OFFSET] & 0x80; + if (!response_bit) { + pt_debug(cd->dev, DL_ERROR, + "%s cmd[0x%02X] response bit ERR: response_bit = %d\n", + __func__, pip2_cmd->id, response_bit); + rc = -EINVAL; + goto exit; + } + + /* Verify the command ID matches from command to response */ + cmd_id = read_buf[PIP2_RESP_REPORT_ID_OFFSET] & 0x7F; + if (cmd_id != pip2_cmd->id) { + pt_debug(cd->dev, DL_ERROR, + "%s cmd[0x%02X] command ID ERR: cmd_id = 0x%02X\n", + __func__, pip2_cmd->id, cmd_id); + rc = -EINVAL; + goto exit; + } + + /* Verify the SEQ number matches from command to response */ + response_seq = read_buf[PIP2_RESP_SEQUENCE_OFFSET] & 0x0F; + if ((pip2_cmd->seq & 0x0F) != response_seq) { + pt_debug(cd->dev, DL_ERROR, + "%s cmd[0x%02X] send_seq = 0x%02X, resp_seq = 0x%02X\n", + __func__, pip2_cmd->id, + pip2_cmd->seq, response_seq); + rc = -EINVAL; + goto exit; + } + + /* Verify the reserved bits are 0 */ + reserved_bits = read_buf[PIP2_RESP_SEQUENCE_OFFSET] & 0xF0; + if (reserved_bits) + pt_debug(cd->dev, DL_WARN, + "%s cmd[0x%02X] reserved_bits = 0x%02X\n", + __func__, pip2_cmd->id, reserved_bits); + +exit: + if (rc) + pt_pr_buf(cd->dev, DL_WARN, cd->input_buf, actual_read_len, + "PIP RSP:"); + return rc; +} + + +/******************************************************************************* + * FUNCTION: pt_hid_output_validate_bl_response + * + * SUMMARY: Validate the response of bootloader command. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_output - pointer to hid output data structure + ******************************************************************************/ +static int pt_hid_output_validate_bl_response( + struct pt_core_data *cd, + struct pt_hid_output *hid_output) +{ + u16 size; + u16 crc; + u8 status; + + size = get_unaligned_le16(&cd->response_buf[0]); + + if (hid_output->reset_expected && !size) + return 0; + + if (cd->response_buf[PIP1_RESP_REPORT_ID_OFFSET] + != PT_PIP_BL_RESPONSE_REPORT_ID) { + pt_debug(cd->dev, DL_ERROR, + "%s: BL output response, wrong report_id\n", __func__); + return -EPROTO; + } + + if (cd->response_buf[4] != PIP1_BL_SOP) { + pt_debug(cd->dev, DL_ERROR, + "%s: BL output response, wrong SOP\n", __func__); + return -EPROTO; + } + + if (cd->response_buf[size - 1] != PIP1_BL_EOP) { + pt_debug(cd->dev, DL_ERROR, + "%s: BL output response, wrong EOP\n", __func__); + return -EPROTO; + } + + crc = _pt_compute_crc(&cd->response_buf[4], size - 7); + if (cd->response_buf[size - 3] != LOW_BYTE(crc) + || cd->response_buf[size - 2] != HI_BYTE(crc)) { + pt_debug(cd->dev, DL_ERROR, + "%s: BL output response, wrong CRC 0x%X\n", + __func__, crc); + return -EPROTO; + } + + status = cd->response_buf[5]; + if (status) { + pt_debug(cd->dev, DL_ERROR, + "%s: BL output response, ERROR:%d\n", + __func__, status); + return -EPROTO; + } + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_validate_app_response + * + * SUMMARY: Validate the response of application command. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_output - pointer to hid output data structure + ******************************************************************************/ +static int pt_hid_output_validate_app_response( + struct pt_core_data *cd, + struct pt_hid_output *hid_output) +{ + int command_code; + u16 size; + + size = get_unaligned_le16(&cd->response_buf[0]); + + if (hid_output->reset_expected && !size) + return 0; + + if (cd->response_buf[PIP1_RESP_REPORT_ID_OFFSET] + != PT_PIP_NON_HID_RESPONSE_ID) { + pt_debug(cd->dev, DL_ERROR, + "%s: APP output response, wrong report_id\n", __func__); + return -EPROTO; + } + + command_code = cd->response_buf[PIP1_RESP_COMMAND_ID_OFFSET] + & PIP1_RESP_COMMAND_ID_MASK; + if (command_code != hid_output->command_code) { + pt_debug(cd->dev, DL_ERROR, + "%s: APP output response, wrong command_code:%X\n", + __func__, command_code); + return -EPROTO; + } + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_check_set_parameter + * + * SUMMARY: Check command input and response for Set Parameter command.And + * store the parameter to the list for resume work if pass the check. + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_output - pointer to hid output data structure + * raw - flag to show if output cmd is user cmd(1:user cmd) + ******************************************************************************/ +static void pt_check_set_parameter(struct pt_core_data *cd, + struct pt_hid_output *hid_output, bool raw) +{ + u8 *param_buf; + u32 param_value = 0; + u8 param_size; + u8 param_id; + int i = 0; + + if (!(cd->cpdata->flags & PT_CORE_FLAG_RESTORE_PARAMETERS)) + return; + + /* Check command input for Set Parameter command */ + if (raw && hid_output->length >= 10 && hid_output->length <= 13 + && !memcmp(&hid_output->write_buf[0], + &cd->hid_desc.output_register, + sizeof(cd->hid_desc.output_register)) + && hid_output->write_buf[4] == + PT_PIP_NON_HID_COMMAND_ID + && hid_output->write_buf[6] == + PIP1_CMD_ID_SET_PARAM) + param_buf = &hid_output->write_buf[7]; + else if (!raw && hid_output->cmd_type == PIP1_CMD_TYPE_FW + && hid_output->command_code == PIP1_CMD_ID_SET_PARAM + && hid_output->write_length >= 3 + && hid_output->write_length <= 6) + param_buf = &hid_output->write_buf[0]; + else + return; + + /* Get parameter ID, size and value */ + param_id = param_buf[0]; + param_size = param_buf[1]; + if (param_size > 4) { + pt_debug(cd->dev, DL_ERROR, + "%s: Invalid parameter size\n", __func__); + return; + } + + param_buf = ¶m_buf[2]; + while (i < param_size) + param_value += *(param_buf++) << (8 * i++); + + /* Check command response for Set Parameter command */ + if (cd->response_buf[2] != PT_PIP_NON_HID_RESPONSE_ID + || (cd->response_buf[4] & + PIP1_RESP_COMMAND_ID_MASK) != + PIP1_CMD_ID_SET_PARAM + || cd->response_buf[5] != param_id + || cd->response_buf[6] != param_size) { + pt_debug(cd->dev, DL_ERROR, + "%s: Set Parameter command not successful\n", + __func__); + return; + } + + pt_add_parameter(cd, param_id, param_value, param_size); +} + +/******************************************************************************* + * FUNCTION: pt_check_command + * + * SUMMARY: Check the output command. The function pt_check_set_parameter() is + * called here to check output command and store parameter to the list. + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_output - pointer to hid output data structure + * raw - flag to show if output cmd is user cmd(1:user cmd) + ******************************************************************************/ +static void pt_check_command(struct pt_core_data *cd, + struct pt_hid_output *hid_output, bool raw) +{ + pt_check_set_parameter(cd, hid_output, raw); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_validate_response + * + * SUMMARY: Validate the response of application or bootloader command. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_output - pointer to hid output data structure + ******************************************************************************/ +static int pt_hid_output_validate_response(struct pt_core_data *cd, + struct pt_hid_output *hid_output) +{ + if (hid_output->cmd_type == PIP1_CMD_TYPE_BL) + return pt_hid_output_validate_bl_response(cd, hid_output); + + return pt_hid_output_validate_app_response(cd, hid_output); + +} + +/******************************************************************************* + * FUNCTION: pt_hid_send_output_user_ + * + * SUMMARY: Blindly send user data to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_output - pointer to the command to send + ******************************************************************************/ +static int pt_hid_send_output_user_(struct pt_core_data *cd, + struct pt_hid_output *hid_output) +{ + int rc = 0; + int cmd; + + if (!hid_output->length || !hid_output->write_buf) + return -EINVAL; + + if (cd->pip2_prot_active) { + cmd = hid_output->write_buf[PIP2_CMD_COMMAND_ID_OFFSET]; + cmd &= PIP2_CMD_COMMAND_ID_MASK; + } else + cmd = hid_output->write_buf[PIP1_CMD_COMMAND_ID_OFFSET]; + + pt_debug(cd->dev, DL_INFO, + ">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n", + __func__, hid_output->length, cmd); + pt_pr_buf(cd->dev, DL_DEBUG, hid_output->write_buf, + hid_output->length, ">>> User CMD"); + + rc = pt_adap_write_read_specific(cd, hid_output->length, + hid_output->write_buf, NULL, 0); + if (rc) + pt_debug(cd->dev, DL_ERROR, + "%s: Fail pt_adap_transfer\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_send_output_user_and_wait_ + * + * SUMMARY: Blindly send user data to the DUT and wait for the response. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_output - pointer to the command to send + ******************************************************************************/ +static int pt_hid_send_output_user_and_wait_(struct pt_core_data *cd, + struct pt_hid_output *hid_output) +{ + int rc = 0; + int t; + + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = PIP1_CMD_ID_USER_CMD + 1; + mutex_unlock(&cd->system_lock); + + rc = pt_hid_send_output_user_(cd, hid_output); + if (rc) + goto error; + + t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0), + msecs_to_jiffies(cd->pip_cmd_timeout)); + if (IS_TMO(t)) { +#ifdef TTDL_DIAGNOSTICS + cd->bus_transmit_error_count++; + pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS); +#endif /* TTDL_DIAGNOSTICS */ + pt_debug(cd->dev, DL_ERROR, + "%s: HID output cmd execution timed out\n", + __func__); + rc = -ETIME; + goto error; + } + + pt_check_command(cd, hid_output, true); + + goto exit; + +error: + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = 0; + mutex_unlock(&cd->system_lock); + +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_check_irq_asserted + * + * SUMMARY: Checks if the IRQ GPIO is asserted or not. There are times when + * the FW can hold the INT line low ~150us after the read is complete. + * NOTE: if irq_stat is not defined this function will return false + * + * RETURN: + * true = IRQ asserted + * false = IRQ not asserted + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static bool pt_check_irq_asserted(struct pt_core_data *cd) +{ +#ifdef ENABLE_WORKAROUND_FOR_GLITCH_AFTER_BL_LAUNCH_APP + /* + * Workaround for FW defect, CDT165308 + * bl_launch app creates a glitch in IRQ line + */ + if (cd->hid_cmd_state == PIP1_BL_CMD_ID_LAUNCH_APP + 1 + && cd->cpdata->irq_stat) { + /* + * in X1S panel and GC1546 panel, the width for the INT + * glitch is about 4us,the normal INT width of response + * will last more than 200us, so use 10us delay + * for distinguish the glitch the normal INT is enough. + */ + udelay(10); + } +#endif + if (cd->cpdata->irq_stat) { + if (cd->cpdata->irq_stat(cd->cpdata, cd->dev) + == PT_IRQ_ASSERTED_VALUE) { + /* Debounce to allow FW to release INT */ + usleep_range(100, 200); + } + if (cd->cpdata->irq_stat(cd->cpdata, cd->dev) + == PT_IRQ_ASSERTED_VALUE) + return true; + else + return false; + } + return true; +} + +/******************************************************************************* + * FUNCTION: pt_flush_bus + * + * SUMMARY: Force flushing the bus by reading len bytes or forced 255 bytes + * Used if IRQ is found to be stuck low + * + * RETURN: Length of bytes read from bus + * + * PARAMETERS: + * *cd - pointer to core data + * flush_type - type of flush + * - PT_FLUSH_BUS_BASED_ON_LEN (two reads) + * - PT_FLUSH_BUS_FULL_256_READ + * *read_buf - pointer to store read data + ******************************************************************************/ +static ssize_t pt_flush_bus(struct pt_core_data *cd, + u8 flush_type, u8 *read_buf) +{ + u8 buf[PT_MAX_PIP2_MSG_SIZE]; + u16 pip_len; + int bytes_read; + int rc = 0; + + if (flush_type == PT_FLUSH_BUS_BASED_ON_LEN) { +#ifdef TTDL_PTVIRTDUT_SUPPORT + if (cd->route_bus_virt_dut) + rc = pt_adap_read_default(cd, buf, + PT_MAX_PIP2_MSG_SIZE); + else + rc = pt_adap_read_default(cd, buf, 2); +#else + rc = pt_adap_read_default(cd, buf, 2); +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + if (rc) { + bytes_read = 0; + goto exit; + } + + pip_len = get_unaligned_le16(&buf[0]); + + if (pip_len == 2 || pip_len >= PT_PIP_1P7_EMPTY_BUF) { +#ifdef TTDL_DIAGNOSTICS + pt_toggle_err_gpio(cd, PT_ERR_GPIO_EMPTY_PACKET); +#endif + bytes_read = 2; + pt_debug(cd->dev, DL_INFO, + "%s: Empty buf detected - len=0x%04X\n", + __func__, pip_len); + } else if (pip_len == 0) { + bytes_read = 0; + pt_debug(cd->dev, DL_INFO, + "%s: Sentinel detected\n", __func__); + } else if (pip_len > PT_MAX_PIP2_MSG_SIZE) { + pt_debug(cd->dev, DL_ERROR, + "%s: Illegal len=0x%04x, force %d byte read\n", + __func__, pip_len, PT_MAX_PIP2_MSG_SIZE); + rc = pt_adap_read_default(cd, buf, + PT_MAX_PIP2_MSG_SIZE); + if (!rc) + bytes_read = PT_MAX_PIP2_MSG_SIZE; + else + bytes_read = 0; + } else { + pt_debug(cd->dev, DL_INFO, + "%s: Flush read of %d bytes...\n", + __func__, pip_len); + +#ifdef TTDL_PTVIRTDUT_SUPPORT + rc = 0; + if (cd->route_bus_virt_dut) + bytes_read = pip_len; + else + rc = pt_adap_read_default(cd, buf, pip_len); +#else + rc = pt_adap_read_default(cd, buf, pip_len); +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + if (!rc) + bytes_read = pip_len; + else + bytes_read = 0; + } + } else { + pt_debug(cd->dev, DL_INFO, + "%s: Forced flush of max %d bytes...\n", + __func__, PT_MAX_PIP2_MSG_SIZE); + rc = pt_adap_read_default(cd, buf, PT_MAX_PIP2_MSG_SIZE); + if (!rc) + bytes_read = PT_MAX_PIP2_MSG_SIZE; + else + bytes_read = 0; + } + + if (read_buf && (bytes_read > 3)) + memcpy(read_buf, buf, bytes_read); + +exit: + return bytes_read; +} + +/******************************************************************************* + * FUNCTION: pt_flush_bus_if_irq_asserted + * + * SUMMARY: This function will flush the active bus if the INT is found to be + * asserted. + * + * RETURN: bytes cleared from bus + * + * PARAMETERS: + * *cd - pointer the core data structure + * flush_type - type of flush + * - PT_FLUSH_BUS_BASED_ON_LEN + * - PT_FLUSH_BUS_FULL_256_READ + ******************************************************************************/ +static int pt_flush_bus_if_irq_asserted(struct pt_core_data *cd, u8 flush_type) +{ + int count = 0; + int bytes_read = 0; + + while (pt_check_irq_asserted(cd) && count < 5) { + count++; + bytes_read = pt_flush_bus(cd, flush_type, NULL); + if (bytes_read) { + pt_debug(cd->dev, DL_WARN, + "%s: Cleared %d bytes off bus\n", + __func__, bytes_read); + } + } + if (pt_check_irq_asserted(cd)) { + pt_debug(cd->dev, DL_ERROR, + "%s: IRQ still asserted, %d bytes read\n", + __func__, bytes_read); + } else { + pt_debug(cd->dev, DL_INFO, + "%s: IRQ cleared, %d bytes read\n", + __func__, bytes_read); + } + return bytes_read; +} + +/******************************************************************************* + * FUNCTION: pt_hid_send_output_ + * + * SUMMARY: Send a touch application command to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_output - pointer to the command to send + ******************************************************************************/ +static int pt_hid_send_output_(struct pt_core_data *cd, + struct pt_hid_output *hid_output) +{ + int rc = 0; + u8 *cmd; + u16 length; + u16 crc; + u8 report_id; + u8 cmd_offset = 0; + u8 cmd_allocated = 0; + +#ifdef FUTURE + /* + * *** TODO - Determine side effects of adding this safety net *** + * If IRQ is already asserted due to a pending report, it must be + * cleared before sending command. + */ + pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN); +#endif + + switch (hid_output->cmd_type) { + case PIP1_CMD_TYPE_FW: + report_id = PT_PIP_NON_HID_COMMAND_ID; + length = 5; + break; + case PIP1_CMD_TYPE_BL: + report_id = PT_PIP_BL_COMMAND_REPORT_ID; + length = 11 /* 5 + SOP + LEN(2) + CRC(2) + EOP */; + break; + default: + return -EINVAL; + } + + length += hid_output->write_length; + + if (length + 2 > PT_PREALLOCATED_CMD_BUFFER) { + cmd = kzalloc(length + 2, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + cmd_allocated = 1; + } else { + cmd = cd->cmd_buf; + } + + /* Set Output register */ + memcpy(&cmd[cmd_offset], &cd->hid_desc.output_register, + sizeof(cd->hid_desc.output_register)); + cmd_offset += sizeof(cd->hid_desc.output_register); + + cmd[cmd_offset++] = LOW_BYTE(length); + cmd[cmd_offset++] = HI_BYTE(length); + cmd[cmd_offset++] = report_id; + cmd[cmd_offset++] = 0x0; /* reserved */ + if (hid_output->cmd_type == PIP1_CMD_TYPE_BL) + cmd[cmd_offset++] = PIP1_BL_SOP; + cmd[cmd_offset++] = hid_output->command_code; + + /* Set Data Length for bootloader */ + if (hid_output->cmd_type == PIP1_CMD_TYPE_BL) { + cmd[cmd_offset++] = LOW_BYTE(hid_output->write_length); + cmd[cmd_offset++] = HI_BYTE(hid_output->write_length); + } + /* Set Data */ + if (hid_output->write_length && hid_output->write_buf) { + memcpy(&cmd[cmd_offset], hid_output->write_buf, + hid_output->write_length); + cmd_offset += hid_output->write_length; + } + if (hid_output->cmd_type == PIP1_CMD_TYPE_BL) { + crc = _pt_compute_crc(&cmd[6], + hid_output->write_length + 4); + cmd[cmd_offset++] = LOW_BYTE(crc); + cmd[cmd_offset++] = HI_BYTE(crc); + cmd[cmd_offset++] = PIP1_BL_EOP; + } + + pt_debug(cd->dev, DL_INFO, + ">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n", + __func__, length + 2, hid_output->command_code); + pt_pr_buf(cd->dev, DL_DEBUG, cmd, length + 2, ">>> CMD"); + rc = pt_adap_write_read_specific(cd, length + 2, cmd, NULL, 0); + + if (rc) + pt_debug(cd->dev, DL_ERROR, + "%s: Fail pt_adap_transfer rc=%d\n", __func__, rc); + + if (cmd_allocated) + kfree(cmd); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip1_send_output_and_wait_ + * + * SUMMARY: Send valid PIP1 command to the DUT and wait for the response. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hid_output - pointer to the command to send + ******************************************************************************/ +static int pt_pip1_send_output_and_wait_(struct pt_core_data *cd, + struct pt_hid_output *hid_output) +{ + int rc = 0; + int t; + u16 timeout_ms; + + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = hid_output->command_code + 1; + mutex_unlock(&cd->system_lock); + + if (hid_output->timeout_ms) + timeout_ms = hid_output->timeout_ms; + else + timeout_ms = PT_PIP1_CMD_DEFAULT_TIMEOUT; + + rc = pt_hid_send_output_(cd, hid_output); + if (rc) + goto error; + + t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0), + msecs_to_jiffies(timeout_ms)); + if (IS_TMO(t)) { +#ifdef TTDL_DIAGNOSTICS + cd->bus_transmit_error_count++; + pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS); +#endif /* TTDL_DIAGNOSTICS */ + pt_debug(cd->dev, DL_ERROR, + "%s: HID output cmd execution timed out (%dms)\n", + __func__, timeout_ms); + rc = -ETIME; + goto error; + } + + if (!hid_output->novalidate) + rc = pt_hid_output_validate_response(cd, hid_output); + + pt_check_command(cd, hid_output, false); + goto exit; + +error: + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = 0; + mutex_unlock(&cd->system_lock); +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_user_cmd_ + * + * SUMMARY: Load the write buffer into a HID structure and send it as a HID cmd + * to the DUT waiting for the response and loading it into the read buffer + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * read_len - expected read length of the response + * *read_buf - pointer to where the response will be loaded + * write_len - length of the write buffer + * *write_buf - pointer to the write buffer + * *actual_read_len - pointer to the actual amount of data read back + ******************************************************************************/ +static int pt_hid_output_user_cmd_(struct pt_core_data *cd, + u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf, + u16 *actual_read_len) +{ + int rc = 0; + u16 size; + struct pt_hid_output hid_output = { + .length = write_len, + .write_buf = write_buf, + }; + +#ifdef TTHE_TUNER_SUPPORT + if (!cd->pip2_send_user_cmd) { + int command_code = 0; + int len; + + /* Print up to cmd ID */ + len = PIP1_CMD_COMMAND_ID_OFFSET + 1; + if (write_len < len) + len = write_len; + else + command_code = write_buf[PIP1_CMD_COMMAND_ID_OFFSET] + & PIP1_CMD_COMMAND_ID_MASK; + + /* Don't print EXEC_PANEL_SCAN & RETRIEVE_PANEL_SCAN commands */ + if (command_code != PIP1_CMD_ID_EXEC_PANEL_SCAN && + command_code != PIP1_CMD_ID_RETRIEVE_PANEL_SCAN) + tthe_print(cd, write_buf, len, "CMD="); + } +#endif + + rc = pt_hid_send_output_user_and_wait_(cd, &hid_output); + if (rc) + return rc; + + /* Get the response size from the first 2 bytes in the response */ + size = get_unaligned_le16(&cd->response_buf[0]); + + /* Ensure size is not greater than max buffer size */ + if (size > PT_MAX_PIP2_MSG_SIZE) + size = PT_MAX_PIP2_MSG_SIZE; + + /* Minimum size to read is the 2 byte len field */ + if (size == 0) + size = 2; + + if (size > read_len) { + pt_debug(cd->dev, DL_ERROR, + "%s: PIP2 len field=%d, requested read_len=%d\n", + __func__, size, read_len); + *actual_read_len = 0; + return -EIO; + } + + memcpy(read_buf, cd->response_buf, size); + *actual_read_len = size; + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_user_cmd + * + * SUMMARY: Protected call to pt_hid_output_user_cmd_ by exclusive access to + * the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * read_len - length of data to read + * *read_buf - pointer to store read data + * write_len - length of data to write + * *write_buf - pointer to buffer to write + * *actual_read_len - pointer to store data length actually read + ******************************************************************************/ +static int pt_hid_output_user_cmd(struct pt_core_data *cd, + u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf, + u16 *actual_read_len) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_user_cmd_(cd, read_len, read_buf, + write_len, write_buf, actual_read_len); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip2_send_cmd + * + * SUMMARY: Writes a PIP2 command packet to DUT, then waits for the + * interrupt and reads response data to read_buf + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to run in protected mode + * id - ID of PIP command + * *data - pointer to PIP data payload + * report_body_len - report length + * *read_buf - pointer to response buffer + * *actual_read_len - pointer to response buffer length + ******************************************************************************/ +static int _pt_request_pip2_send_cmd(struct device *dev, + int protect, u8 id, u8 *data, u16 report_body_len, u8 *read_buf, + u16 *actual_read_len) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pip2_cmd_structure pip2_cmd; + int rc = 0; + int i = 0; + int j = 0; + u16 write_len; + u8 *write_buf = NULL; + u16 read_len; + u8 extra_bytes; + + memset(&pip2_cmd, 0, sizeof(pip2_cmd)); + + /* Hard coded register for PIP2.x */ + pip2_cmd.reg[0] = 0x01; + pip2_cmd.reg[1] = 0x01; + + /* + * For PIP2.1+ the length field value includes itself: + * ADD 6: 2 (LEN) + 1 (SEQ) + 1 (REPORT ID) + 2 (CRC) + * + * The overall write length must include only the register: + * ADD 2: 2 (Register) + */ + extra_bytes = 6; + write_len = 2; + + /* PIP2 the CMD ID is a 7bit field */ + if (id > PIP2_CMD_ID_END) { + pt_debug(dev, DL_WARN, "%s: Invalid PIP2 CMD ID 0x%02X\n", + __func__, id); + rc = -EINVAL; + goto exit; + } + + pip2_cmd.len = report_body_len + extra_bytes; + pip2_cmd.id = id & PIP2_CMD_COMMAND_ID_MASK; + pip2_cmd.seq = pt_pip2_get_next_cmd_seq(cd); + pip2_cmd.data = data; + pt_pip2_cmd_calculate_crc(&pip2_cmd, extra_bytes); + + /* Add the command length to the extra bytes based on PIP version */ + write_len += pip2_cmd.len; + + pt_debug(dev, DL_INFO, "%s Length Field: %d, Write Len: %d", + __func__, pip2_cmd.len, write_len); + + write_buf = kzalloc(write_len, GFP_KERNEL); + if (write_buf == NULL) { + rc = -ENOMEM; + goto exit; + } + + write_buf[i++] = pip2_cmd.reg[0]; + write_buf[i++] = pip2_cmd.reg[1]; + write_buf[i++] = pip2_cmd.len & 0xff; + write_buf[i++] = (pip2_cmd.len & 0xff00) >> 8; + write_buf[i++] = pip2_cmd.seq; + write_buf[i++] = pip2_cmd.id; + + for (j = i; j < i + pip2_cmd.len - extra_bytes; j++) + write_buf[j] = pip2_cmd.data[j-i]; + write_buf[j++] = pip2_cmd.crc[0]; + write_buf[j++] = pip2_cmd.crc[1]; + + read_len = pt_pip2_get_cmd_response_len(pip2_cmd.id); + if (read_len < 0) + read_len = 255; + pt_debug(dev, DL_INFO, + "%s cmd_id[0x%02X] expected response length:%d ", + __func__, pip2_cmd.id, read_len); + + /* + * All PIP2 commands come through this function. + * Set flag for PIP2.x interface to allow response parsing to know + * how to decode the protocol header. + */ + mutex_lock(&cd->system_lock); + cd->pip2_prot_active = true; + cd->pip2_send_user_cmd = true; + mutex_unlock(&cd->system_lock); + + if (protect == PT_CORE_CMD_PROTECTED) + rc = pt_hid_output_user_cmd(cd, read_len, read_buf, + write_len, write_buf, actual_read_len); + else + rc = pt_hid_output_user_cmd_(cd, read_len, read_buf, + write_len, write_buf, actual_read_len); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: nonhid_cmd->user_cmd() Error = %d\n", + __func__, rc); + goto exit; + } + + rc = pt_pip2_validate_response(cd, &pip2_cmd, read_buf, + *actual_read_len); + +exit: + mutex_lock(&cd->system_lock); + cd->pip2_prot_active = false; + cd->pip2_send_user_cmd = false; + mutex_unlock(&cd->system_lock); + kfree(write_buf); + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_send_cmd_no_int + * + * SUMMARY: Writes a PIP2 command packet to DUT, then poll the response and + * reads response data to read_buf if response is available. + * + * NOTE: + * Interrupt MUST be disabled before to call this function. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to run in protected mode + * id - ID of PIP command + * *data - pointer to PIP data payload + * report_body_len - report length + * *read_buf - pointer to response buffer + * *actual_read_len - pointer to response buffer length + ******************************************************************************/ +static int _pt_pip2_send_cmd_no_int(struct device *dev, + int protect, u8 id, u8 *data, u16 report_body_len, u8 *read_buf, + u16 *actual_read_len) +{ + int max_retry = 0; + int retry = 0; + int rc = 0; + int i = 0; + int j = 0; + u16 write_len; + u8 *write_buf = NULL; + u16 read_len; + u16 size = 0; + u8 response_seq = 0; + u8 extra_bytes; + u32 retry_interval = 0; + u32 retry_total_time = 0; + u32 resp_time_min = pt_pip2_get_cmd_resp_time_min(id); + u32 resp_time_max = pt_pip2_get_cmd_resp_time_max(id); + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pip2_cmd_structure pip2_cmd; + + if (protect == PT_CORE_CMD_PROTECTED) { + rc = request_exclusive(cd, + cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + } + + memset(&pip2_cmd, 0, sizeof(pip2_cmd)); + + /* Hard coded register for PIP2.x */ + pip2_cmd.reg[0] = 0x01; + pip2_cmd.reg[1] = 0x01; + + /* + * For PIP2.1+ the length field value includes itself: + * ADD 6: 2 (LEN) + 1 (SEQ) + 1 (REPORT ID) + 2 (CRC) + * + * The overall write length must include only the register: + * ADD 2: 2 (Register) + */ + extra_bytes = 6; + write_len = 2; + + pip2_cmd.len = report_body_len + extra_bytes; + pip2_cmd.id = id; + pip2_cmd.seq = pt_pip2_get_next_cmd_seq(cd); + pip2_cmd.data = data; + pt_pip2_cmd_calculate_crc(&pip2_cmd, extra_bytes); + + /* Add the command length to the extra bytes based on PIP version */ + write_len += pip2_cmd.len; + + write_buf = kzalloc(write_len, GFP_KERNEL); + if (write_buf == NULL) { + rc = -ENOMEM; + goto exit; + } + + write_buf[i++] = pip2_cmd.reg[0]; + write_buf[i++] = pip2_cmd.reg[1]; + write_buf[i++] = pip2_cmd.len & 0xff; + write_buf[i++] = (pip2_cmd.len & 0xff00) >> 8; + write_buf[i++] = pip2_cmd.seq; + write_buf[i++] = pip2_cmd.id; + + for (j = i; j < i + pip2_cmd.len - extra_bytes; j++) + write_buf[j] = pip2_cmd.data[j-i]; + write_buf[j++] = pip2_cmd.crc[0]; + write_buf[j++] = pip2_cmd.crc[1]; + + read_len = pt_pip2_get_cmd_response_len(pip2_cmd.id); + if (read_len < 0) + read_len = 255; + pt_debug(dev, DL_INFO, + "%s: ATM - cmd_id[0x%02X] expected response length:%d ", + __func__, pip2_cmd.id, read_len); + + pt_pr_buf(cd->dev, DL_DEBUG, write_buf, write_len, ">>> NO_INT CMD"); + + rc = pt_adap_write_read_specific(cd, write_len, write_buf, NULL, 0); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: SPI write Error = %d\n", + __func__, rc); + goto exit; + } + +#ifdef PT_POLL_RESP_BY_BUS + /* + * Frequent bus read can increase system load obviously. The expected + * first bus read should be valid and timely. The tollerance for + * usleep_range should be limited. The minimum response delay (between + * command finishes sending and INT pin falls) is less than 50 + * microseconds. So the 10 microseconds should be maximum tollerance + * with the consideration that the unit to calculate the response delay + * is 10 microseconds and more precise is not necessary. Every + * additional 10 microseconds only contribute less than 3 milliseconds + * for whole BL. + */ + usleep_range(resp_time_min, resp_time_min+10); + max_retry = resp_time_max / POLL_RETRY_DEFAULT_INTERVAL; + while ((retry < max_retry) && (retry_total_time < resp_time_max)) { + rc = pt_adap_read_default(cd, read_buf, read_len); + if (rc) { + pt_debug(dev, DL_ERROR, "%s: SPI read Error = %d\n", + __func__, rc); + break; + } + response_seq = read_buf[PIP2_RESP_SEQUENCE_OFFSET]; + size = get_unaligned_le16(&read_buf[0]); + if ((size <= read_len) && + (size >= PIP2_EXTRA_BYTES_NUM) && + (pip2_cmd.seq & 0x07) == (response_seq & 0x07)) { + break; + } + + /* + * To reduce the bus and system load, increase the sleep + * step gradually: + * 1 ~ 19 : step=50 us, sleep_us=[50, 100, 150, 200, ..950] + * 20 ~ 39 : step=1000 us, sleep_us=[1950, 2950, ...20950] + * 40 ~ MAX: step=50 ms, sleep_ms=[71, 121, 191,..] + */ + retry++; + if (retry < 20) { + retry_interval += POLL_RETRY_DEFAULT_INTERVAL; + usleep_range(retry_interval, + retry_interval + POLL_RETRY_DEFAULT_INTERVAL); + } else if (retry < 40) { + retry_interval += 1000; + usleep_range(retry_interval, retry_interval + 1000); + } else { + retry_interval += 50000; + msleep(retry_interval/1000); + } + retry_total_time += retry_interval; + } +#else + /* + * Frequent GPIO read will not increase CPU/system load heavily if the + * interval is longer than 10 us, so it is safe to poll GPIO with a + * fixed interval: 20 us. + */ + usleep_range(resp_time_min, resp_time_min+10); + max_retry = resp_time_max / POLL_RETRY_DEFAULT_INTERVAL; + while ((retry < max_retry) && (retry_total_time < resp_time_max)) { + if (!gpio_get_value(cd->cpdata->irq_gpio)) { + rc = pt_adap_read_default(cd, read_buf, read_len); + size = get_unaligned_le16(&read_buf[0]); + if (rc) + pt_debug(dev, DL_ERROR, + "%s: SPI read Error = %d\n", + __func__, rc); + else if (size > read_len) { + pt_debug(cd->dev, DL_ERROR, + "%s: PIP2 len field=%d, requested read_len=%d\n", + __func__, size, read_len); + rc = -EIO; + } + break; + } + /* + * Poll GPIO with fixed interval 20 us, and tollerance is + * limited to 10 us to speed up the process. + */ + retry_interval = POLL_RETRY_DEFAULT_INTERVAL; + usleep_range(retry_interval, retry_interval+10); + retry_total_time += retry_interval; + } +#endif + + *actual_read_len = size; + + if (rc || (retry >= max_retry) || (retry_total_time >= resp_time_max)) { + pt_debug(dev, DL_ERROR, + "%s cmd[0x%02X] timed out, send_seq=0x%02X, resp_seq=0x%02X\n", + __func__, pip2_cmd.id, pip2_cmd.seq, response_seq); + *actual_read_len = 0; + rc = -EINVAL; + } + + pt_pr_buf(cd->dev, DL_DEBUG, read_buf, *actual_read_len, + "<<< NO_INT Read"); + +exit: + kfree(write_buf); + if (protect == PT_CORE_CMD_PROTECTED) { + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + } + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip_null_ + * + * SUMMARY: Send the PIP "ping"(0x00) command to the DUT and wait for response. + * This function is used by watchdog to check if the fw corrupts. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_pip_null_(struct pt_core_data *cd) +{ + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_NULL), + }; + + return pt_pip1_send_output_and_wait_(cd, &hid_output); +} + +#ifndef TTDL_KERNEL_SUBMISSION +/******************************************************************************* + * FUNCTION: pt_pip_null + * + * SUMMARY: Wrapper function for pt_pip_null_ that guarantees exclusive access. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_pip_null(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_null_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} +#endif /*!TTDL_KERNEL_SUBMISSION */ + +static void pt_stop_wd_timer(struct pt_core_data *cd); +/******************************************************************************* + * FUNCTION: pt_pip_start_bootloader_ + * + * SUMMARY: Sends the PIP command start_bootloader [PIP cmd 0x01] to the DUT + * + * NOTE: The WD MUST be stopped/restarted by the calling Function. Having + * the WD active could cause this function to fail! + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_pip_start_bootloader_(struct pt_core_data *cd) +{ + int rc = 0; + + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_START_BOOTLOADER), + .timeout_ms = PT_PIP1_START_BOOTLOADER_TIMEOUT, + .reset_expected = 1, + }; + + if (cd->watchdog_enabled) { + pt_debug(cd->dev, DL_WARN, + "%s: watchdog isn't stopped before enter bl\n", + __func__); + goto exit; + } + + /* Reset startup status after entering BL, new DUT enum required */ + cd->startup_status = STARTUP_STATUS_START; + pt_debug(cd->dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__); + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Start BL PIP cmd failed. rc = %d\n", + __func__, rc); + } + +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip_start_bootloader + * + * SUMMARY: Protected function to force DUT to enter the BL + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_pip_start_bootloader(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_start_bootloader_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_start_bl + * + * SUMMARY: Function pointer included in core_nonhid_cmds to allow other + * modules to request the DUT to enter the BL + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to run in protected mode + ******************************************************************************/ +static int _pt_request_pip_start_bl(struct device *dev, int protect) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_start_bootloader(cd); + + return pt_pip_start_bootloader_(cd); +} + +/******************************************************************************* + * FUNCTION: pt_pip2_ver_load_ttdata + * + * SUMMARY: Function to load the Version information from the PIP2 VERSION + * command into the core data struct. + * + * RETURN: n/a + * + * PARAMETERS: + * *cd - pointer to core data structure + * len - Length of data in response_buf + ******************************************************************************/ +static void pt_pip2_ver_load_ttdata(struct pt_core_data *cd, u16 len) +{ + struct pt_ttdata *ttdata = &cd->sysinfo.ttdata; + struct pt_pip2_version_full *full_ver; + struct pt_pip2_version *ver; + + /* + * The PIP2 VERSION command can return different lengths of data. + * The additional LOT fields are included when the packet + * size is >= 29 bytes. Older FW sends a reduced packet size. + * NOTE: + * - The FW would swap the BL and FW versions when reporting + * the small packet. + * - Sub Lot bytes 16 and 17 are reserved. + */ + if (len >= 0x1D) { + full_ver = (struct pt_pip2_version_full *) + &cd->response_buf[PIP2_RESP_STATUS_OFFSET]; + + ttdata->pip_ver_major = full_ver->pip2_version_msb; + ttdata->pip_ver_minor = full_ver->pip2_version_lsb; + ttdata->bl_ver_major = full_ver->bl_version_msb; + ttdata->bl_ver_minor = full_ver->bl_version_lsb; + ttdata->fw_ver_major = full_ver->fw_version_msb; + ttdata->fw_ver_minor = full_ver->fw_version_lsb; + /* + * BL PIP 2.02 and greater the version fields are + * swapped + */ + if (ttdata->pip_ver_major >= 2 && ttdata->pip_ver_minor >= 2) { + ttdata->chip_rev = + get_unaligned_le16(&full_ver->chip_rev); + ttdata->chip_id = + get_unaligned_le16(&full_ver->chip_id); + } else { + ttdata->chip_rev = + get_unaligned_le16(&full_ver->chip_id); + ttdata->chip_id = + get_unaligned_le16(&full_ver->chip_rev); + } + memcpy(ttdata->uid, full_ver->uid, PT_UID_SIZE); + + pt_pr_buf(cd->dev, DL_INFO, (u8 *)full_ver, + sizeof(struct pt_pip2_version_full), + "PIP2 VERSION FULL"); + } else { + ver = (struct pt_pip2_version *) + &cd->response_buf[PIP2_RESP_STATUS_OFFSET]; + + ttdata->pip_ver_major = ver->pip2_version_msb; + ttdata->pip_ver_minor = ver->pip2_version_lsb; + ttdata->bl_ver_major = ver->bl_version_msb; + ttdata->bl_ver_minor = ver->bl_version_lsb; + ttdata->fw_ver_major = ver->fw_version_msb; + ttdata->fw_ver_minor = ver->fw_version_lsb; + ttdata->chip_rev = get_unaligned_le16(&ver->chip_rev); + ttdata->chip_id = get_unaligned_le16(&ver->chip_id); + + pt_pr_buf(cd->dev, DL_INFO, (u8 *)ver, + sizeof(struct pt_pip2_version), "PIP2 VERSION"); + } +} + +/******************************************************************************* + * FUNCTION: pt_si_get_ttdata + * + * SUMMARY: Function to load the version information from the system information + * PIP command into the core data struct. + * + * RETURN: n/a + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static void pt_si_get_ttdata(struct pt_core_data *cd) +{ + struct pt_ttdata *ttdata = &cd->sysinfo.ttdata; + struct pt_ttdata_dev *ttdata_dev = + (struct pt_ttdata_dev *) + &cd->response_buf[PIP1_SYSINFO_TTDATA_OFFSET]; + + ttdata->pip_ver_major = ttdata_dev->pip_ver_major; + ttdata->pip_ver_minor = ttdata_dev->pip_ver_minor; + ttdata->bl_ver_major = ttdata_dev->bl_ver_major; + ttdata->bl_ver_minor = ttdata_dev->bl_ver_minor; + ttdata->fw_ver_major = ttdata_dev->fw_ver_major; + ttdata->fw_ver_minor = ttdata_dev->fw_ver_minor; + + ttdata->fw_pid = get_unaligned_le16(&ttdata_dev->fw_pid); + ttdata->fw_ver_conf = get_unaligned_le16(&ttdata_dev->fw_ver_conf); + ttdata->post_code = get_unaligned_le16(&ttdata_dev->post_code); + ttdata->revctrl = get_unaligned_le32(&ttdata_dev->revctrl); + ttdata->jtag_id_l = get_unaligned_le16(&ttdata_dev->jtag_si_id_l); + ttdata->jtag_id_h = get_unaligned_le16(&ttdata_dev->jtag_si_id_h); + + memcpy(ttdata->mfg_id, ttdata_dev->mfg_id, PT_NUM_MFGID); + + pt_pr_buf(cd->dev, DL_INFO, (u8 *)ttdata_dev, + sizeof(struct pt_ttdata_dev), "sysinfo_ttdata"); +} + +/******************************************************************************* + * FUNCTION: pt_si_get_sensing_conf_data + * + * SUMMARY: Function to load the sensing information from the system information + * PIP command into the core data struct. + * + * RETURN: n/a + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static void pt_si_get_sensing_conf_data(struct pt_core_data *cd) +{ + struct pt_sensing_conf_data *scd = &cd->sysinfo.sensing_conf_data; + struct pt_sensing_conf_data_dev *scd_dev = + (struct pt_sensing_conf_data_dev *) + &cd->response_buf[PIP1_SYSINFO_SENSING_OFFSET]; + + scd->electrodes_x = scd_dev->electrodes_x; + scd->electrodes_y = scd_dev->electrodes_y; + scd->origin_x = scd_dev->origin_x; + scd->origin_y = scd_dev->origin_y; + + /* PIP 1.4 (001-82649 *Q) add X_IS_TX bit in X_ORG */ + if (scd->origin_x & 0x02) { + scd->tx_num = scd->electrodes_x; + scd->rx_num = scd->electrodes_y; + } else { + scd->tx_num = scd->electrodes_y; + scd->rx_num = scd->electrodes_x; + } + + /* + * When the Panel ID is coming from an XY pin and not a dedicated + * GPIO, store the PID in pid_for_loader. This cannot be done for all + * other DUTs as the loader will use cd->pid_for_loader to generate + * the bin file name but will ignore it if pid_for_loader is still + * set to PANEL_ID_NOT_ENABLED + */ + if (cd->panel_id_support & + (PT_PANEL_ID_BY_BL | PT_PANEL_ID_BY_SYS_INFO)) { + mutex_lock(&cd->system_lock); + cd->pid_for_loader = scd_dev->panel_id; + mutex_unlock(&cd->system_lock); + } + + scd->panel_id = scd_dev->panel_id; + scd->btn = scd_dev->btn; + scd->scan_mode = scd_dev->scan_mode; + scd->max_tch = scd_dev->max_num_of_tch_per_refresh_cycle; + + scd->res_x = get_unaligned_le16(&scd_dev->res_x); + scd->res_y = get_unaligned_le16(&scd_dev->res_y); + scd->max_z = get_unaligned_le16(&scd_dev->max_z); + scd->len_x = get_unaligned_le16(&scd_dev->len_x); + scd->len_y = get_unaligned_le16(&scd_dev->len_y); + + pt_pr_buf(cd->dev, DL_INFO, (u8 *)scd_dev, + sizeof(struct pt_sensing_conf_data_dev), + "sensing_conf_data"); +} + +/******************************************************************************* + * FUNCTION: pt_si_setup + * + * SUMMARY: Setup the xy_data and xy_mode by allocating the needed memory + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_si_setup(struct pt_core_data *cd) +{ + struct pt_sysinfo *si = &cd->sysinfo; + int max_tch = si->sensing_conf_data.max_tch; + + if (!si->xy_data) + si->xy_data = kzalloc(max_tch * si->desc.tch_record_size, + GFP_KERNEL); + if (!si->xy_data) + return -ENOMEM; + + if (!si->xy_mode) + si->xy_mode = kzalloc(si->desc.tch_header_size, GFP_KERNEL); + if (!si->xy_mode) { + kfree(si->xy_data); + return -ENOMEM; + } + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_si_get_btn_data + * + * SUMMARY: Setup the core data button information based on the response of the + * System Information PIP command. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_si_get_btn_data(struct pt_core_data *cd) +{ + struct pt_sysinfo *si = &cd->sysinfo; + int num_btns = 0; + int num_defined_keys; + u16 *key_table; + int btn; + int i; + int rc = 0; + unsigned int btns = cd->response_buf[PIP1_SYSINFO_BTN_OFFSET] + & PIP1_SYSINFO_BTN_MASK; + size_t btn_keys_size; + + pt_debug(cd->dev, DL_INFO, "%s: get btn data\n", __func__); + + for (i = 0; i < PIP1_SYSINFO_MAX_BTN; i++) { + if (btns & (1 << i)) + num_btns++; + } + si->num_btns = num_btns; + + if (num_btns) { + btn_keys_size = num_btns * sizeof(struct pt_btn); + if (!si->btn) + si->btn = kzalloc(btn_keys_size, GFP_KERNEL); + if (!si->btn) + return -ENOMEM; + + if (cd->cpdata->sett[PT_IC_GRPNUM_BTN_KEYS] == NULL) + num_defined_keys = 0; + else if (cd->cpdata->sett[PT_IC_GRPNUM_BTN_KEYS]->data == NULL) + num_defined_keys = 0; + else + num_defined_keys = cd->cpdata->sett + [PT_IC_GRPNUM_BTN_KEYS]->size; + + for (btn = 0; btn < num_btns && btn < num_defined_keys; btn++) { + key_table = (u16 *)cd->cpdata->sett + [PT_IC_GRPNUM_BTN_KEYS]->data; + si->btn[btn].key_code = key_table[btn]; + si->btn[btn].enabled = true; + } + for (; btn < num_btns; btn++) { + si->btn[btn].key_code = KEY_RESERVED; + si->btn[btn].enabled = true; + } + + return rc; + } + + kfree(si->btn); + si->btn = NULL; + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_si_put_log_data + * + * SUMMARY: Prints all sys info data to kmsg log + * + * RETURN: n/a + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static void pt_si_put_log_data(struct pt_core_data *cd) +{ + struct pt_sysinfo *si = &cd->sysinfo; + struct pt_ttdata *ttdata = &si->ttdata; + struct pt_sensing_conf_data *scd = &si->sensing_conf_data; + int i; + + pt_debug(cd->dev, DL_DEBUG, "%s: pip_ver_major = 0x%02X (%d)\n", + __func__, ttdata->pip_ver_major, ttdata->pip_ver_major); + pt_debug(cd->dev, DL_DEBUG, "%s: pip_ver_minor = 0x%02X (%d)\n", + __func__, ttdata->pip_ver_minor, ttdata->pip_ver_minor); + pt_debug(cd->dev, DL_DEBUG, "%s: fw_pid = 0x%04X (%d)\n", + __func__, ttdata->fw_pid, ttdata->fw_pid); + pt_debug(cd->dev, DL_DEBUG, "%s: fw_ver_major = 0x%02X (%d)\n", + __func__, ttdata->fw_ver_major, ttdata->fw_ver_major); + pt_debug(cd->dev, DL_DEBUG, "%s: fw_ver_minor = 0x%02X (%d)\n", + __func__, ttdata->fw_ver_minor, ttdata->fw_ver_minor); + pt_debug(cd->dev, DL_DEBUG, "%s: revctrl = 0x%08X (%d)\n", + __func__, ttdata->revctrl, ttdata->revctrl); + pt_debug(cd->dev, DL_DEBUG, "%s: fw_ver_conf = 0x%04X (%d)\n", + __func__, ttdata->fw_ver_conf, ttdata->fw_ver_conf); + pt_debug(cd->dev, DL_DEBUG, "%s: bl_ver_major = 0x%02X (%d)\n", + __func__, ttdata->bl_ver_major, ttdata->bl_ver_major); + pt_debug(cd->dev, DL_DEBUG, "%s: bl_ver_minor = 0x%02X (%d)\n", + __func__, ttdata->bl_ver_minor, ttdata->bl_ver_minor); + pt_debug(cd->dev, DL_DEBUG, "%s: jtag_id_h = 0x%04X (%d)\n", + __func__, ttdata->jtag_id_h, ttdata->jtag_id_h); + pt_debug(cd->dev, DL_DEBUG, "%s: jtag_id_l = 0x%04X (%d)\n", + __func__, ttdata->jtag_id_l, ttdata->jtag_id_l); + + for (i = 0; i < PT_NUM_MFGID; i++) + pt_debug(cd->dev, DL_DEBUG, + "%s: mfg_id[%d] = 0x%02X (%d)\n", + __func__, i, ttdata->mfg_id[i], + ttdata->mfg_id[i]); + + pt_debug(cd->dev, DL_DEBUG, "%s: post_code = 0x%04X (%d)\n", + __func__, ttdata->post_code, ttdata->post_code); + pt_debug(cd->dev, DL_DEBUG, "%s: electrodes_x = 0x%02X (%d)\n", + __func__, scd->electrodes_x, scd->electrodes_x); + pt_debug(cd->dev, DL_DEBUG, "%s: electrodes_y = 0x%02X (%d)\n", + __func__, scd->electrodes_y, scd->electrodes_y); + pt_debug(cd->dev, DL_DEBUG, "%s: len_x = 0x%04X (%d)\n", + __func__, scd->len_x, scd->len_x); + pt_debug(cd->dev, DL_DEBUG, "%s: len_y = 0x%04X (%d)\n", + __func__, scd->len_y, scd->len_y); + pt_debug(cd->dev, DL_DEBUG, "%s: res_x = 0x%04X (%d)\n", + __func__, scd->res_x, scd->res_x); + pt_debug(cd->dev, DL_DEBUG, "%s: res_y = 0x%04X (%d)\n", + __func__, scd->res_y, scd->res_y); + pt_debug(cd->dev, DL_DEBUG, "%s: max_z = 0x%04X (%d)\n", + __func__, scd->max_z, scd->max_z); + pt_debug(cd->dev, DL_DEBUG, "%s: origin_x = 0x%02X (%d)\n", + __func__, scd->origin_x, scd->origin_x); + pt_debug(cd->dev, DL_DEBUG, "%s: origin_y = 0x%02X (%d)\n", + __func__, scd->origin_y, scd->origin_y); + pt_debug(cd->dev, DL_DEBUG, "%s: panel_id = 0x%02X (%d)\n", + __func__, scd->panel_id, scd->panel_id); + pt_debug(cd->dev, DL_DEBUG, "%s: btn =0x%02X (%d)\n", + __func__, scd->btn, scd->btn); + pt_debug(cd->dev, DL_DEBUG, "%s: scan_mode = 0x%02X (%d)\n", + __func__, scd->scan_mode, scd->scan_mode); + pt_debug(cd->dev, DL_DEBUG, + "%s: max_num_of_tch_per_refresh_cycle = 0x%02X (%d)\n", + __func__, scd->max_tch, scd->max_tch); + pt_debug(cd->dev, DL_DEBUG, "%s: xy_mode = %p\n", + __func__, si->xy_mode); + pt_debug(cd->dev, DL_DEBUG, "%s: xy_data = %p\n", + __func__, si->xy_data); +} + +/******************************************************************************* + * FUNCTION: pt_get_sysinfo_regs + * + * SUMMARY: Setup all the core data System information based on the response + * of the System Information PIP command. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_get_sysinfo_regs(struct pt_core_data *cd) +{ + struct pt_sysinfo *si = &cd->sysinfo; + int rc; + + rc = pt_si_get_btn_data(cd); + if (rc < 0) + return rc; + + pt_si_get_ttdata(cd); + + pt_si_get_sensing_conf_data(cd); + + pt_si_setup(cd); + + pt_si_put_log_data(cd); + + si->ready = true; + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_free_si_ptrs + * + * SUMMARY: Frees all memory associated with the System Information within + * core data + * + * RETURN: n/a + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static void pt_free_si_ptrs(struct pt_core_data *cd) +{ + struct pt_sysinfo *si = &cd->sysinfo; + + kfree(si->btn); + kfree(si->xy_mode); + kfree(si->xy_data); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_get_sysinfo_ + * + * SUMMARY: Sends the PIP Get SYS INFO command to the DUT and waits for the + * response. + * + * RETURN:: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_hid_output_get_sysinfo_(struct pt_core_data *cd) +{ + int rc = 0; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_SYSINFO), + .timeout_ms = PT_PIP1_CMD_GET_SYSINFO_TIMEOUT, + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + /* Parse the sysinfo data */ + rc = pt_get_sysinfo_regs(cd); + if (rc) + pt_free_si_ptrs(cd); + + return rc; +} + +#ifndef TTDL_KERNEL_SUBMISSION +/******************************************************************************* + * FUNCTION: pt_hid_output_get_sysinfo + * + * SUMMARY: Protected call to pt_hid_output_get_sysinfo_ + * + * RETURN:: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_hid_output_get_sysinfo(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_get_sysinfo_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} +#endif /*!TTDL_KERNEL_SUBMISSION */ + +/******************************************************************************* + * FUNCTION: pt_pip_suspend_scanning_ + * + * SUMMARY: Sends the PIP Suspend Scanning command to the DUT + * + * RETURN:: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_pip_suspend_scanning_(struct pt_core_data *cd) +{ + int rc = 0; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_SUSPEND_SCANNING), + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Suspend Scan PIP cmd failed. rc = %d\n", + __func__, rc); + } + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip_suspend_scanning + * + * SUMMARY: Protected wrapper for calling pt_hid_output_suspend_scanning_ + * + * RETURN:: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_pip_suspend_scanning(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_suspend_scanning_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_suspend_scanning + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to pt_pip_suspend_scanning + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - 0 = call non-protected function + * 1 = call protected function + ******************************************************************************/ +static int _pt_request_pip_suspend_scanning(struct device *dev, + int protect) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_suspend_scanning(cd); + + return pt_pip_suspend_scanning_(cd); +} + +/******************************************************************************* + * FUNCTION: pt_pip_resume_scanning_ + * + * SUMMARY: Sends the PIP Resume Scanning command to the DUT + * + * RETURN:: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_pip_resume_scanning_(struct pt_core_data *cd) +{ + int rc = 0; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_RESUME_SCANNING), + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Resume Scan PIP cmd failed. rc = %d\n", + __func__, rc); + } + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip_resume_scanning + * + * SUMMARY: Protected wrapper for calling pt_pip_resume_scanning_ + * + * RETURN:: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_pip_resume_scanning(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_resume_scanning_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_resume_scanning + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to pt_pip_resume_scanning + * + * RETURN:: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - 0 = call non-protected function + * 1 = call protected function + ******************************************************************************/ +static int _pt_request_pip_resume_scanning(struct device *dev, + int protect) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_resume_scanning(cd); + + return pt_pip_resume_scanning_(cd); +} + +/******************************************************************************* + * FUNCTION: pt_pip_get_param_ + * + * SUMMARY: Sends a PIP command 0x05 Get Parameter to the DUT and returns + * the 32bit parameter value + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * param_id - parameter ID to retrieve + * *value - value of DUT parameter + ******************************************************************************/ +static int pt_pip_get_param_(struct pt_core_data *cd, + u8 param_id, u32 *value) +{ + int write_length = 1; + u8 param[1] = { param_id }; + u8 read_param_id; + int param_size; + u8 *ptr; + int rc = 0; + int i; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_PARAM), + .write_length = write_length, + .write_buf = param, + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + read_param_id = cd->response_buf[5]; + if (read_param_id != param_id) + return -EPROTO; + + param_size = cd->response_buf[6]; + ptr = &cd->response_buf[7]; + *value = 0; + for (i = 0; i < param_size; i++) + *value += ptr[i] << (i * 8); + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pip_get_param + * + * SUMMARY: Protected call to pt_hid_output_get_param_ by a request exclusive + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * param_id - parameter ID to retrieve + * *value - value of DUT parameter + ******************************************************************************/ +static int pt_pip_get_param(struct pt_core_data *cd, + u8 param_id, u32 *value) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_get_param_(cd, param_id, value); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_get_param + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to pt_pip_get_param + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non protected function + * param_id - parameter ID to retrieve + * *value - value of DUT parameter + ******************************************************************************/ +int _pt_request_pip_get_param(struct device *dev, + int protect, u8 param_id, u32 *value) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_get_param(cd, param_id, value); + + return pt_pip_get_param_(cd, param_id, value); +} + +/******************************************************************************* + * FUNCTION: pt_pip_set_param_ + * + * SUMMARY: Sends a PIP command 0x06 Set Parameter to the DUT writing the + * passed in value to flash + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * param_id - parameter ID to set + * value - value to write + * size - size to write + ******************************************************************************/ +static int pt_pip_set_param_(struct pt_core_data *cd, + u8 param_id, u32 value, u8 size) +{ + u8 write_buf[6]; + u8 *ptr = &write_buf[2]; + int rc = 0; + int i; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_SET_PARAM), + .write_buf = write_buf, + }; + + write_buf[0] = param_id; + write_buf[1] = size; + for (i = 0; i < size; i++) { + ptr[i] = value & 0xFF; + value = value >> 8; + } + + hid_output.write_length = 2 + size; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + if (param_id != cd->response_buf[5] || size != cd->response_buf[6]) + return -EPROTO; + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pip_set_param + * + * SUMMARY: Protected call to pt_hid_output_set_param_ by a request exclusive + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * param_id - parameter ID to set + * value - value to write + * size - size to write + ******************************************************************************/ +static int pt_pip_set_param(struct pt_core_data *cd, + u8 param_id, u32 value, u8 size) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_set_param_(cd, param_id, value, size); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_set_param + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to pt_pip_set_param + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * param_id - parameter ID to set + * value - value to write + * size - size to write + ******************************************************************************/ +int _pt_request_pip_set_param(struct device *dev, int protect, + u8 param_id, u32 value, u8 size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_set_param(cd, param_id, value, size); + + return pt_pip_set_param_(cd, param_id, value, size); +} + +/******************************************************************************* + * FUNCTION: _pt_pip_enter_easywake_state_ + * + * SUMMARY: Sends a PIP command 0x09 Enter EasyWake State to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * data - easywake guesture (Only used for PIP1.6 and earlier) + * *return_data - return status if easywake was entered + ******************************************************************************/ +static int pt_hid_output_enter_easywake_state_( + struct pt_core_data *cd, u8 data, u8 *return_data) +{ + int write_length = 1; + u8 param[1] = { data }; + int rc = 0; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_ENTER_EASYWAKE_STATE), + .write_length = write_length, + .write_buf = param, + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + *return_data = cd->response_buf[5]; + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip_verify_config_block_crc_ + * + * SUMMARY: Sends the PIP "Verify Data Block CRC" (0x20) command to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer the core data structure + * ebid - enumerated block ID + * *status - PIP command status + * calculated_crc - calculated CRC + * stored_crc - stored CRC in config area + ******************************************************************************/ +static int pt_pip_verify_config_block_crc_( + struct pt_core_data *cd, u8 ebid, u8 *status, + u16 *calculated_crc, u16 *stored_crc) +{ + int write_length = 1; + u8 param[1] = { ebid }; + u8 *ptr; + int rc = 0; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC), + .write_length = write_length, + .write_buf = param, + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + ptr = &cd->response_buf[5]; + *status = ptr[0]; + *calculated_crc = get_unaligned_le16(&ptr[1]); + *stored_crc = get_unaligned_le16(&ptr[3]); + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pip_verify_config_block_crc + * + * SUMMARY: Protected call to pt_hid_output_verify_config_block_crc_() within + * an exclusive access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer the core data structure + * ebid - enumerated block ID + * *status - PIP command status + * calculated_crc - calculated CRC + * stored_crc - stored CRC in config area + ******************************************************************************/ +static int pt_pip_verify_config_block_crc( + struct pt_core_data *cd, u8 ebid, u8 *status, + u16 *calculated_crc, u16 *stored_crc) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_verify_config_block_crc_(cd, ebid, status, + calculated_crc, stored_crc); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_verify_config_block_crc + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to + * pt_pip_verify_config_block_crc_ + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * ebid - enumerated block ID + * *status - PIP command status + * calculated_crc - calculated CRC + * stored_crc - stored CRC in config area + ******************************************************************************/ +static int _pt_request_pip_verify_config_block_crc( + struct device *dev, int protect, u8 ebid, u8 *status, + u16 *calculated_crc, u16 *stored_crc) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_verify_config_block_crc(cd, ebid, + status, calculated_crc, stored_crc); + + return pt_pip_verify_config_block_crc_(cd, ebid, + status, calculated_crc, stored_crc); +} + +/******************************************************************************* + * FUNCTION: pt_pip_get_config_row_size_ + * + * SUMMARY: Sends the PIP "Get Data Row Size" (0x21) command to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * protect - flag to call protected or non-protected + * *row_size - pointer to store the retrieved row size + ******************************************************************************/ +static int pt_pip_get_config_row_size_(struct pt_core_data *cd, + u16 *row_size) +{ + int rc = 0; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_CONFIG_ROW_SIZE), + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + *row_size = get_unaligned_le16(&cd->response_buf[5]); + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pip_get_config_row_size + * + * SUMMARY: Protected call to pt_hid_output_get_config_row_size_ within + * an exclusive access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * protect - flag to call protected or non-protected + * *row_size - pointer to store the retrieved row size + ******************************************************************************/ +static int pt_pip_get_config_row_size(struct pt_core_data *cd, + u16 *row_size) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_get_config_row_size_(cd, row_size); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_get_config_row_size + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to + * pt_pip_get_config_row_size_ + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * *row_size - pointer to store the retrieved row size + ******************************************************************************/ +static int _pt_request_pip_get_config_row_size(struct device *dev, + int protect, u16 *row_size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_get_config_row_size(cd, row_size); + + return pt_pip_get_config_row_size_(cd, row_size); +} + +/******************************************************************************* + * FUNCTION: pt_pip1_read_data_block_ + * + * SUMMARY: Sends the PIP "Read Data Block" (0x22) command to the DUT and print + * output data to the "read_buf" and update "crc". + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * row_number - row number + * length - length of data to read + * ebid - block id + * *actual_read_len - Actual data length read + * *read_buf - pointer to the buffer to store read data + * read_buf_size - size of read_buf + * *crc - pointer to store CRC of row data + ******************************************************************************/ +static int pt_pip1_read_data_block_(struct pt_core_data *cd, + u16 row_number, u16 length, u8 ebid, u16 *actual_read_len, + u8 *read_buf, u16 read_buf_size, u16 *crc) +{ + int read_ebid; + int status; + int rc = 0; + int write_length = 5; + u8 write_buf[5]; + u8 cmd_offset = 0; + u16 calc_crc; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_READ_DATA_BLOCK), + .write_length = write_length, + .write_buf = write_buf, + }; + + write_buf[cmd_offset++] = LOW_BYTE(row_number); + write_buf[cmd_offset++] = HI_BYTE(row_number); + write_buf[cmd_offset++] = LOW_BYTE(length); + write_buf[cmd_offset++] = HI_BYTE(length); + write_buf[cmd_offset++] = ebid; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + status = cd->response_buf[5]; + if (status) + return status; + + read_ebid = cd->response_buf[6]; + if ((read_ebid != ebid) || (cd->response_buf[9] != 0)) + return -EPROTO; + + *actual_read_len = get_unaligned_le16(&cd->response_buf[7]); + if (length == 0 || *actual_read_len == 0) + return 0; + + if (read_buf_size >= *actual_read_len) + memcpy(read_buf, &cd->response_buf[10], *actual_read_len); + else + return -EPROTO; + + *crc = get_unaligned_le16(&cd->response_buf[*actual_read_len + 10]); + + /* Validate Row Data CRC */ + calc_crc = _pt_compute_crc(read_buf, *actual_read_len); + if (*crc == calc_crc) { + return 0; + } else { + pt_debug(cd->dev, DL_ERROR, + "%s: CRC Mismatch packet=0x%04X calc=0x%04X\n", + __func__, *crc, calc_crc); + return -EPROTO; + } +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_read_data_block + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to pt_pip1_read_data_block_ + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * row_number - row number + * length - length of data to read + * ebid - block id + * *actual_read_len - Actual data length read + * *read_buf - pointer to the buffer to store read data + * *crc - pointer to store CRC of row data + ******************************************************************************/ +static int _pt_request_pip_read_data_block(struct device *dev, + u16 row_number, u16 length, u8 ebid, u16 *actual_read_len, + u8 *read_buf, u16 read_buf_size, u16 *crc) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + return pt_pip1_read_data_block_(cd, row_number, length, + ebid, actual_read_len, read_buf, read_buf_size, crc); +} + +/******************************************************************************* + * FUNCTION: pt_pip1_write_data_block_ + * + * SUMMARY: Sends the PIP "Write Data Block" (0x23) command to the DUT and + * write data to the data block. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * row_number - row in config block to write to + * write_length - length of data to write + * ebid - enumerated block ID + * *write_buf - pointer to buffer to write + * *security_key - pointer to security key to allow write + * *actual_write_len - pointer to store data length actually written + ******************************************************************************/ +static int pt_pip1_write_data_block_(struct pt_core_data *cd, + u16 row_number, u16 write_length, u8 ebid, u8 *write_buf, + u8 *security_key, u16 *actual_write_len) +{ + /* row_number + write_len + ebid + security_key + crc */ + int full_write_length = 2 + 2 + 1 + write_length + 8 + 2; + u8 *full_write_buf; + u8 cmd_offset = 0; + u16 crc; + int status; + int rc = 0; + int read_ebid; + u8 *data; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_WRITE_DATA_BLOCK), + .write_length = full_write_length, + .timeout_ms = PT_PIP1_CMD_WRITE_CONF_BLOCK_TIMEOUT, + }; + + full_write_buf = kzalloc(full_write_length, GFP_KERNEL); + if (!full_write_buf) + return -ENOMEM; + + hid_output.write_buf = full_write_buf; + full_write_buf[cmd_offset++] = LOW_BYTE(row_number); + full_write_buf[cmd_offset++] = HI_BYTE(row_number); + full_write_buf[cmd_offset++] = LOW_BYTE(write_length); + full_write_buf[cmd_offset++] = HI_BYTE(write_length); + full_write_buf[cmd_offset++] = ebid; + data = &full_write_buf[cmd_offset]; + memcpy(data, write_buf, write_length); + cmd_offset += write_length; + memcpy(&full_write_buf[cmd_offset], security_key, 8); + cmd_offset += 8; + crc = _pt_compute_crc(data, write_length); + full_write_buf[cmd_offset++] = LOW_BYTE(crc); + full_write_buf[cmd_offset++] = HI_BYTE(crc); + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + goto exit; + + status = cd->response_buf[5]; + if (status) { + rc = -EINVAL; + goto exit; + } + + read_ebid = cd->response_buf[6]; + if (read_ebid != ebid) { + rc = -EPROTO; + goto exit; + } + + *actual_write_len = get_unaligned_le16(&cd->response_buf[7]); + +exit: + kfree(full_write_buf); + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_write_data_block + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to pt_pip1_write_data_block_ + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * row_number - row in config block to write to + * write_length - length of data to write + * ebid - enumerated block ID + * *write_buf - pointer to buffer to write + * *security_key - pointer to security key to allow write + * *actual_write_len - pointer to store data length actually written + ******************************************************************************/ +static int _pt_request_pip_write_data_block(struct device *dev, + u16 row_number, u16 write_length, u8 ebid, + u8 *write_buf, u8 *security_key, u16 *actual_write_len) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + return pt_pip1_write_data_block_(cd, row_number, + write_length, ebid, write_buf, security_key, + actual_write_len); +} + +/******************************************************************************* + * FUNCTION: pt_pip_get_data_structure_ + * + * SUMMARY: Sends the PIP "Retrieve Data Structure" (0x24) command to the DUT + * returning a structure of data defined by data_id + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * read_offset - read pointer offset + * read_length - length of data to read + * data_id - data ID to read + * *status - pointer to store the read response status + * *data_format - pointer to store format of data read + * *actual_read_len - pointer to store data length actually read + * *data - pointer to store data read + ******************************************************************************/ +static int pt_pip_get_data_structure_( + struct pt_core_data *cd, u16 read_offset, u16 read_length, + u8 data_id, u8 *status, u8 *data_format, u16 *actual_read_len, + u8 *data) +{ + int rc = 0; + u16 total_read_len = 0; + u16 read_len; + u16 off_buf = 0; + u8 write_buf[5]; + u8 read_data_id; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_DATA_STRUCTURE), + .write_length = 5, + .write_buf = write_buf, + }; + +again: + write_buf[0] = LOW_BYTE(read_offset); + write_buf[1] = HI_BYTE(read_offset); + write_buf[2] = LOW_BYTE(read_length); + write_buf[3] = HI_BYTE(read_length); + write_buf[4] = data_id; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + if (cd->response_buf[5] != PT_CMD_STATUS_SUCCESS) + goto set_status; + + read_data_id = cd->response_buf[6]; + if (read_data_id != data_id) + return -EPROTO; + + read_len = get_unaligned_le16(&cd->response_buf[7]); + if (read_len && data) { + memcpy(&data[off_buf], &cd->response_buf[10], read_len); + + total_read_len += read_len; + + if (read_len < read_length) { + read_offset += read_len; + off_buf += read_len; + read_length -= read_len; + goto again; + } + } + + if (data_format) + *data_format = cd->response_buf[9]; + if (actual_read_len) + *actual_read_len = total_read_len; +set_status: + if (status) + *status = cd->response_buf[5]; + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip_get_data_structure + * + * SUMMARY: Protected call to pt_hid_output_get_data_structure within + * an exclusive access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * read_offset - read pointer offset + * read_length - length of data to read + * data_id - data ID to read + * *status - pointer to store the read response status + * *data_format - pointer to store format of data read + * *actual_read_len - pointer to store data length actually read + * *data - pointer to store data read + ******************************************************************************/ +static int pt_pip_get_data_structure( + struct pt_core_data *cd, u16 read_offset, u16 read_length, + u8 data_id, u8 *status, u8 *data_format, u16 *actual_read_len, + u8 *data) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_get_data_structure_(cd, read_offset, + read_length, data_id, status, data_format, + actual_read_len, data); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_get_data_structure + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to + * pt_pip_get_data_structure + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * read_offset - read pointer offset + * read_length - length of data to read + * data_id - data ID to read + * *status - pointer to store the read response status + * *data_format - pointer to store format of data read + * *actual_read_len - pointer to store data length actually read + * *data - pointer to store data read + ******************************************************************************/ +static int _pt_request_pip_get_data_structure(struct device *dev, + int protect, u16 read_offset, u16 read_length, u8 data_id, + u8 *status, u8 *data_format, u16 *actual_read_len, u8 *data) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_get_data_structure(cd, + read_offset, read_length, data_id, status, + data_format, actual_read_len, data); + + return pt_pip_get_data_structure_(cd, + read_offset, read_length, data_id, status, + data_format, actual_read_len, data); +} + +/******************************************************************************* + * FUNCTION: _pt_manage_local_cal_data + * + * SUMMARY: This function manages storing or restoring a copy of the Firmware + * CALIBRATION data. It stores it in a local static array and can be + * cleared, loaded or used to restore the CAL data back to the running FW. + * The CAL data is read or restored by use of the PIP1 commands: + * - READ_DATA_BLOCK (0x22) + * - WRITE_DATA_BLOCK (0x23) + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * action - One of the following actions: + * - PT_CAL_DATA_SAVE + * - PT_CAL_DATA_RESTORE + * - PT_CAL_DATA_CLEAR + * - PT_CAL_DATA_SIZE + * *size - pointer to the number of bytes transferred + * *crc - pointer to Chip ID CRC that the CAL data was retrieved from + ******************************************************************************/ +static int _pt_manage_local_cal_data(struct device *dev, u8 action, u16 *size, + unsigned short *crc) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_ttdata *ttdata = &cd->sysinfo.ttdata; + unsigned short calc_id_crc = 0; + static u8 *cal_cache_data; + static u16 cal_cache_len; + static unsigned short cal_cache_chip_id; + int rc = 0; + u8 *tmp_data = NULL; + u8 row_number = 0; + u8 prefix[20]; + u16 cal_size = 0; + u16 transfer_size; + u16 act_trans_len = 0; + u16 byte_offset = 0; + u16 cal_blk_size; + u16 total_rows; + u16 remain_bytes; + u16 data_block_crc; + u16 buf_size = 12; + + pt_debug(dev, DL_INFO, "%s: ATM - CAL Cache action=%d\n", + __func__, action); + switch (action) { + case PT_CAL_DATA_SAVE: + /* Read the size of the CAL block and calculate # rows */ + tmp_data = kzalloc(buf_size, GFP_KERNEL); + if (!tmp_data) { + rc = -ENOMEM; + goto exit; + } + /* + * Don't check rc as doing a read size will give a false + * error on the CRC check. + */ + rc = pt_pip1_read_data_block_(cd, row_number, 0, PT_CAL_EBID, + &act_trans_len, tmp_data, buf_size, &data_block_crc); + cal_blk_size = act_trans_len; + kfree(tmp_data); + + pt_debug(dev, DL_INFO, + "%s: CAL Cache size=%d FW CAL Size=%d\n", + __func__, cal_cache_len, cal_blk_size); + + /* Safety net to ensure we didn't read incorrect size */ + if (cal_blk_size > PT_CAL_DATA_MAX_SIZE) { + pt_debug(dev, DL_ERROR, "%s: Alloc struct Failed\n", + __func__); + rc = 1; + goto exit; + } + + /* Panels could have diff CAL sizes, Re-allocate the cache */ + if (cal_blk_size != cal_cache_len) { + kfree(cal_cache_data); + cal_cache_data = kzalloc(cal_blk_size + 2, + GFP_KERNEL); + if (!cal_cache_data) { + rc = -ENOMEM; + goto exit; + } + + pt_debug(dev, DL_INFO, "%s: CAL Cache Allocated\n", + __func__); + } + memset(&cal_cache_data[0], 0, cal_blk_size + 2); + + /* Calculate how many rows [0-n] (PIP Transactions) */ + total_rows = (cal_blk_size / PT_CAL_DATA_ROW_SIZE) - 1; + remain_bytes = cal_blk_size % PT_CAL_DATA_ROW_SIZE; + /* Add row if we have a last partial row */ + if (remain_bytes > 0) + total_rows++; + pt_debug(dev, DL_INFO, + "%s: CAL size=%d rows=[0-%d] partial row bytes=%d\n", + __func__, cal_blk_size, total_rows, remain_bytes); + + /* Read all rows unless an error occurs */ + rc = 0; + while (rc == 0 && row_number <= total_rows) { + act_trans_len = 0; + if (remain_bytes > 0 && row_number == total_rows) + transfer_size = remain_bytes; + else + transfer_size = PT_CAL_DATA_ROW_SIZE; + + rc = pt_pip1_read_data_block_(cd, row_number, + transfer_size, PT_CAL_EBID, + &act_trans_len, + &cal_cache_data[byte_offset], cal_blk_size + 2, + &data_block_crc); + if (rc) { + /* Error occurred, exit loop */ + cal_size = 0; + break; + } + + pt_debug(dev, DL_INFO, + "%s: CAL read rc=%d actual read len=%d\n", + __func__, rc, act_trans_len); + + byte_offset += act_trans_len; + cal_size = byte_offset; + + scnprintf(prefix, sizeof(prefix), "%s[%d]", "CAL DATA ROW", row_number); + pt_pr_buf(dev, DL_INFO, + &cal_cache_data[byte_offset - act_trans_len], + act_trans_len, prefix); + + row_number++; + } + + if (cal_size > 0) { + /* Save a CRC of the chip info the CAL was saved from */ + calc_id_crc = crc_ccitt_calculate( + (u8 *)&ttdata->chip_rev, 4 + PT_UID_SIZE); + cal_cache_chip_id = calc_id_crc; + cal_cache_len = cal_size; + pt_debug(dev, DL_INFO, + "%s: CAL Cache: CRC=0x%04X Total Size=%d\n", + __func__, calc_id_crc, cal_size); + } + + *size = cal_size; + *crc = calc_id_crc; + break; + case PT_CAL_DATA_RESTORE: + cal_size = cal_cache_len; + while ((rc == 0) && (byte_offset < cal_size)) { + if (cal_size - byte_offset > PT_CAL_DATA_ROW_SIZE) + transfer_size = PT_CAL_DATA_ROW_SIZE; + else + transfer_size = cal_size - byte_offset; + + rc = pt_pip1_write_data_block_(cd, row_number, + transfer_size, PT_CAL_EBID, + &cal_cache_data[byte_offset], + (u8 *)pt_data_block_security_key, + &act_trans_len); + + byte_offset += act_trans_len; + pt_debug(dev, DL_INFO, "%s: CAL write byte offset=%d\n", + __func__, byte_offset); + + scnprintf(prefix, sizeof(prefix), "%s[%d]", "CAL DATA ROW", row_number); + pt_pr_buf(dev, DL_INFO, + &cal_cache_data[byte_offset - act_trans_len], + act_trans_len, prefix); + + + if ((byte_offset > cal_size) || + (act_trans_len != transfer_size)) + rc = -EIO; + row_number++; + } + *size = byte_offset; + *crc = cal_cache_chip_id; + break; + case PT_CAL_DATA_CLEAR: + if (cal_cache_data) + memset(&cal_cache_data[0], 0, cal_cache_len); + cal_cache_len = 0; + cal_cache_chip_id = 0; + *size = 0; + *crc = 0; + break; + case PT_CAL_DATA_INFO: + default: + *size = cal_cache_len; + *crc = cal_cache_chip_id; + pt_debug(dev, DL_INFO, + "%s: CAL Cache: CRC=%04X Total Size=%d\n", + __func__, cal_cache_chip_id, + cal_cache_len); + break; + } + +exit: + pt_debug(dev, DL_INFO, + "%s: CAL Cache exit: rc=%d CRC=0x%04X Total Size=%d\n", + __func__, rc, *crc, *size); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip_run_selftest_ + * + * SUMMARY: Sends the PIP "Run Self Test" (0x26) command to the DUT + * to execute a FW built in self test + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * test_id - enumerated test ID to run + * write_idacs_to_flash - flag whether to write new IDACS to flash + * *status - pointer to store the read response status + * *summary_results - pointer to store the results summary + * *results_available - pointer to store if results are available + *****************************************************************************/ +static int pt_pip_run_selftest_( + struct pt_core_data *cd, u8 test_id, + u8 write_idacs_to_flash, u8 *status, u8 *summary_result, + u8 *results_available) +{ + int rc = 0; + u8 write_buf[2]; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_RUN_SELF_TEST), + .write_length = 2, + .write_buf = write_buf, + .timeout_ms = PT_PIP1_CMD_RUN_SELF_TEST_TIMEOUT, + }; + + write_buf[0] = test_id; + write_buf[1] = write_idacs_to_flash; + + if (cd->active_dut_generation == DUT_PIP2_CAPABLE) + hid_output.write_length = 1; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + if (status) + *status = cd->response_buf[5]; + if (summary_result) + *summary_result = cd->response_buf[6]; + /* results_available only available before PIP 1.03 */ + if (cd->sysinfo.ready && !IS_PIP_VER_GE(&cd->sysinfo, 1, 3)) { + if (results_available) + *results_available = cd->response_buf[7]; + } + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip_run_selftest + * + * SUMMARY: Protected call to pt_hid_output_run_selftest within + * an exclusive access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * test_id - enumerated test ID to run + * write_idacs_to_flash - flag whether to write new IDACS to flash + * *status - pointer to store the read response status + * *summary_results - pointer to store the results summary + * *results_available - pointer to store if results are available + ******************************************************************************/ +static int pt_pip_run_selftest( + struct pt_core_data *cd, u8 test_id, + u8 write_idacs_to_flash, u8 *status, u8 *summary_result, + u8 *results_available) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_run_selftest_(cd, test_id, + write_idacs_to_flash, status, summary_result, + results_available); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_run_selftest + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to pt_pip_run_selftest + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * test_id - enumerated test ID to run + * write_idacs_to_flash - flag whether to write new IDACS to flash + * *status - pointer to store the read response status + * *summary_results - pointer to store the results summary + * *results_available - pointer to store if results are available + ******************************************************************************/ +static int _pt_request_pip_run_selftest(struct device *dev, + int protect, u8 test_id, u8 write_idacs_to_flash, u8 *status, + u8 *summary_result, u8 *results_available) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_run_selftest(cd, test_id, + write_idacs_to_flash, status, summary_result, + results_available); + + return pt_pip_run_selftest_(cd, test_id, + write_idacs_to_flash, status, summary_result, + results_available); +} + +/******************************************************************************* + * FUNCTION: _pt_pip_get_selftest_result_ + * + * SUMMARY: Sends the PIP "Get Self Test Results" (0x27) command to the DUT + * to retrieve the self test results from the self test already executed + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * read_offset - read pointer offset + * read_length - length of data to read + * test_id - enumerated test ID to read selftest results from + * *status - pointer to store the read response status + * *actual_read_len - pointer to store data length actually read + * *status - pointer to where the cmd response statas is stored + ******************************************************************************/ +static int pt_pip_get_selftest_result_( + struct pt_core_data *cd, u16 read_offset, u16 read_length, + u8 test_id, u8 *status, u16 *actual_read_len, u8 *data) +{ + int rc = 0; + u16 total_read_len = 0; + u16 read_len; + u16 off_buf = 0; + u8 write_buf[5]; + u8 read_test_id; + bool repeat; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_SELF_TEST_RESULT), + .write_length = 5, + .write_buf = write_buf, + }; + + /* + * Do not repeat reading for Auto Shorts test + * when PIP version < 1.3 + */ + repeat = IS_PIP_VER_GE(&cd->sysinfo, 1, 3) + || test_id != PT_ST_ID_AUTOSHORTS; + +again: + write_buf[0] = LOW_BYTE(read_offset); + write_buf[1] = HI_BYTE(read_offset); + write_buf[2] = LOW_BYTE(read_length); + write_buf[3] = HI_BYTE(read_length); + write_buf[4] = test_id; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + if (cd->response_buf[5] != PT_CMD_STATUS_SUCCESS) + goto set_status; + + read_test_id = cd->response_buf[6]; + if (read_test_id != test_id) + return -EPROTO; + + read_len = get_unaligned_le16(&cd->response_buf[7]); + if (read_len && data) { + memcpy(&data[off_buf], &cd->response_buf[10], read_len); + + total_read_len += read_len; + + if (repeat && read_len < read_length) { + read_offset += read_len; + off_buf += read_len; + read_length -= read_len; + goto again; + } + } + + if (actual_read_len) + *actual_read_len = total_read_len; +set_status: + if (status) + *status = cd->response_buf[5]; + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_pip_get_selftest_result + * + * SUMMARY: Protected call to pt_hid_output_get_selftest_result by exclusive + * access to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * read_offset - read pointer offset + * read_length - length of data to read + * test_id - enumerated test ID to read selftest results from + * *status - pointer to store the read response status + * *actual_read_len - pointer to store data length actually read + * *status - pointer to where the cmd response statas is stored + ******************************************************************************/ +static int pt_pip_get_selftest_result( + struct pt_core_data *cd, u16 read_offset, u16 read_length, + u8 test_id, u8 *status, u16 *actual_read_len, u8 *data) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_get_selftest_result_(cd, read_offset, + read_length, test_id, status, actual_read_len, data); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_get_selftest_result + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to pt_pip_get_selftest_result + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * read_offset - read pointer offset + * read_length - length of data to read + * test_id - enumerated test ID to read selftest results from + * *status - pointer to store the read response status + * *actual_read_len - pointer to store data length actually read + * *data - pointer to where the data read is stored + ******************************************************************************/ +static int _pt_request_pip_get_selftest_result(struct device *dev, + int protect, u16 read_offset, u16 read_length, u8 test_id, + u8 *status, u16 *actual_read_len, u8 *data) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_get_selftest_result(cd, read_offset, + read_length, test_id, status, actual_read_len, + data); + + return pt_pip_get_selftest_result_(cd, read_offset, + read_length, test_id, status, actual_read_len, + data); +} + +/******************************************************************************* + * FUNCTION: _pt_pip_load_self_test_param + * + * SUMMARY: Sends the PIP "Load Self Test Parameters" (0x25) command to the DUT + * to load parameters needed by a self test + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * self_test_id - enumerated test ID for which the parmeters belong + * load_offset - mem offset to where to load parameters + * load_length - length of parameter data to load + * *parameters - pointer to list of parameter data + * *status - pointer to store the response status + * *ret_test_id - pointer to returned test id the parameters were stored + * *act_load_len - pointer to store the actual load length that was writen + ******************************************************************************/ +static int pt_pip_load_self_test_param_(struct pt_core_data *cd, + u8 self_test_id, u16 load_offset, u16 load_length, + u8 *parameters, u8 *status, u8 *ret_test_id, u16 *act_load_len) +{ + int rc = 0; + int i; + u8 write_buf[PT_MAX_PIP1_MSG_SIZE]; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_LOAD_SELF_TEST_PARAM), + .write_length = 5 + load_length, + .write_buf = write_buf, + .timeout_ms = PT_PIP1_CMD_DEFAULT_TIMEOUT, + }; + + write_buf[0] = LOW_BYTE(load_offset); + write_buf[1] = HI_BYTE(load_offset); + write_buf[2] = LOW_BYTE(load_length); + write_buf[3] = HI_BYTE(load_length); + write_buf[4] = self_test_id; + for (i = 0; i < load_length; i++) + write_buf[i + 5] = parameters[i]; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + if (status) + *status = cd->response_buf[5]; + if (ret_test_id) + *ret_test_id = cd->response_buf[6]; + if (act_load_len) + *act_load_len = get_unaligned_le16(&cd->response_buf[7]); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip_load_self_test_param + * + * SUMMARY: Protected call to pt_pip_load_self_test_param_ within an exclusive + * access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * self_test_id - enumerated test ID for which the parmeters belong + * load_offset - mem offset to where to load parameters + * load_length - length of parameter data to load + * *parameters - pointer to list of parameter data + * *status - pointer to store the response status + * *ret_test_id - pointer to returned test id the parameters were stored + * *act_load_len - pointer to store the actual load length that was writen + ******************************************************************************/ +static int pt_pip_load_self_test_param(struct pt_core_data *cd, + u8 self_test_id, u16 load_offset, u16 load_length, + u8 *parameters, u8 *status, u8 *ret_test_id, u16 *act_load_len) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_load_self_test_param_(cd, self_test_id, load_offset, + load_length, parameters, status, ret_test_id, act_load_len); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_load_self_test_param + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to + * pt_pip_load_self_test_param + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * self_test_id - enumerated test ID for which the parmeters belong + * load_offset - mem offset to where to load parameters + * load_length - length of parameter data to load + * *parameters - pointer to list of parameter data + * *status - pointer to store the response status + * *ret_test_id - pointer to returned test id the parameters were stored + * *act_load_len - pointer to store the actual load length that was writen + ******************************************************************************/ +static int _pt_request_pip_load_self_test_param(struct device *dev, + int protect, u8 self_test_id, u16 load_offset, u16 load_length, + u8 *parameters, u8 *status, u8 *ret_test_id, u16 *act_load_len) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_load_self_test_param(cd, self_test_id, + load_offset, load_length, parameters, status, ret_test_id, + act_load_len); + + return pt_pip_load_self_test_param_(cd, self_test_id, load_offset, + load_length, parameters, status, ret_test_id, act_load_len); +} + +/******************************************************************************* + * FUNCTION: pt_pip_calibrate_ext_ + * + * SUMMARY: Send the PIP1 Extended Calibrate command (0x30) to the DUT waiting + * for the response + * + * NOTE: This calibrate command requires the DUT to support PIP version >= 1.10 + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *cal_data - pointer to extended calibration data structure + * *status - pointer to where the command response status is stored + ******************************************************************************/ +static int pt_pip_calibrate_ext_(struct pt_core_data *cd, + struct pt_cal_ext_data *cal_data, u8 *status) +{ + int rc = 0; + int write_length = 4; + u8 write_buf[4]; + u16 size = 0; + unsigned short crc = 0; + + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_CALIBRATE_DEVICE_EXTENDED), + .write_length = write_length, + .write_buf = write_buf, + .timeout_ms = PT_PIP1_CMD_CALIBRATE_EXT_TIMEOUT, + }; + + if (cal_data == NULL) + return -EINVAL; + + memcpy(write_buf, cal_data, sizeof(struct pt_cal_ext_data)); + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + if (status) + *status = cd->response_buf[5]; + + /* + * When doing a calibration on a flashless DUT, save CAL data in + * the TTDL cache on any successful calibration + */ + if (*status == 0 && cd->cal_cache_in_host) { + pt_debug(cd->dev, DL_INFO, "%s: Retrieve and Save CAL\n", + __func__); + rc = _pt_manage_local_cal_data(cd->dev, PT_CAL_DATA_SAVE, + &size, &crc); + if (rc) + pt_debug(cd->dev, DL_ERROR, + "%s: Error Saving CAL rc=%d\n", __func__, rc); + else + pt_debug(cd->dev, DL_INFO, + "%s: Saved CAL: chip ID=0x%04X size=%d\n", + __func__, crc, size); + } + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pip_calibrate_ext + * + * SUMMARY: Protected call to pt_pip_calibrate_ext_ by exclusive access to the + * DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *cal_data - pointer to extended calibration data structure + * *status - pointer to where the command response status is stored + ******************************************************************************/ +static int pt_pip_calibrate_ext(struct pt_core_data *cd, + struct pt_cal_ext_data *cal_data, u8 *status) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_calibrate_ext_(cd, cal_data, status); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_calibrate_ext + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to pt_pip_calibrate_ext + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * *cal_data - pointer to extended calibration data structure + * *status - pointer to where the command response status is stored + ******************************************************************************/ +static int _pt_request_pip_calibrate_ext(struct device *dev, + int protect, struct pt_cal_ext_data *cal_data, u8 *status) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_calibrate_ext(cd, cal_data, status); + + return pt_pip_calibrate_ext_(cd, cal_data, status); +} + +/******************************************************************************* + * FUNCTION: pt_pip_calibrate_idacs_ + * + * SUMMARY: Send the PIP Calibrate IDACs command (0x28) to the DUT waiting + * for the response + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * mode - sense mode to calibrate (0-5) + * *status - pointer to where the command response status is stored + ******************************************************************************/ +static int pt_pip_calibrate_idacs_(struct pt_core_data *cd, + u8 mode, u8 *status) +{ + int rc = 0; + int write_length = 1; + u8 write_buf[1]; + u8 cmd_offset = 0; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_CALIBRATE_IDACS), + .write_length = write_length, + .write_buf = write_buf, + .timeout_ms = PT_PIP1_CMD_CALIBRATE_IDAC_TIMEOUT, + }; + + write_buf[cmd_offset++] = mode; + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + *status = cd->response_buf[5]; + if (*status) + return -EINVAL; + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pip_calibrate_idacs + * + * SUMMARY: Protected call to pt_hid_output_calibrate_idacs_ by exclusive + * access to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * mode - sense mode to calibrate (0-5) + * *status - pointer to where the command response status is stored + ******************************************************************************/ +static int pt_pip_calibrate_idacs(struct pt_core_data *cd, + u8 mode, u8 *status) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip_calibrate_idacs_(cd, mode, status); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_calibrate_idacs + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to pt_pip_calibrate_idacs + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * mode - sense mode to calibrate (0-5) + * *status - pointer to where the command response status is stored + ******************************************************************************/ +static int _pt_request_pip_calibrate_idacs(struct device *dev, + int protect, u8 mode, u8 *status) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip_calibrate_idacs(cd, mode, status); + + return pt_pip_calibrate_idacs_(cd, mode, status); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_initialize_baselines_ + * + * SUMMARY: Send the PIP "Initialize Baselines" command (0x29) to the DUT + * waiting for the response. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * test_id - bit type flag to allow initialize baseline MUT,BTN,SELG + * each or together with a single command. + * *status - pointer to where the command response status is stored + ******************************************************************************/ +static int pt_hid_output_initialize_baselines_( + struct pt_core_data *cd, u8 test_id, u8 *status) +{ + int rc = 0; + int write_length = 1; + u8 write_buf[1]; + u8 cmd_offset = 0; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_INITIALIZE_BASELINES), + .write_length = write_length, + .write_buf = write_buf, + }; + + write_buf[cmd_offset++] = test_id; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + *status = cd->response_buf[5]; + if (*status) + return -EINVAL; + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_initialize_baselines + * + * SUMMARY: Protected call to pt_hid_output_initialize_baselines_ by exclusive + * access to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * test_id - enumerated ID against which to initialize the baseline + * *status - pointer to where the command response status is stored + ******************************************************************************/ +static int pt_hid_output_initialize_baselines(struct pt_core_data *cd, + u8 test_id, u8 *status) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_initialize_baselines_(cd, test_id, status); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_initialize_baselines + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to + * pt_pip_initialize_baselines + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * test_id - enumerated ID against which to initialize the baseline + * *status - pointer to where the command response status is stored + ******************************************************************************/ +static int _pt_request_pip_initialize_baselines(struct device *dev, + int protect, u8 test_id, u8 *status) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_hid_output_initialize_baselines(cd, test_id, + status); + + return pt_hid_output_initialize_baselines_(cd, test_id, status); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_exec_panel_scan_ + * + * SUMMARY: Sends the PIP "Execute Panel Scan" (0x2A) to the DUT and waits for + * the response + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_hid_output_exec_panel_scan_(struct pt_core_data *cd) +{ + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_EXEC_PANEL_SCAN), + }; + + return pt_pip1_send_output_and_wait_(cd, &hid_output); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_exec_panel_scan + * + * SUMMARY: Protected call to pt_hid_output_exec_panel_scan_ by exclusive + * access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_hid_output_exec_panel_scan(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_exec_panel_scan_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_exec_panel_scan_ + * + * SUMMARY: Send the PIP2 "Execute Panel Scan" (0x21) to the DUT and waits for + * the response + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * scan_type - type of panel scan to perform (PIP2 only) + ******************************************************************************/ +static int pt_pip2_exec_panel_scan_(struct pt_core_data *cd, u8 scan_type) +{ + int rc = 0; + u8 data[2]; + u8 read_buf[10]; + u16 actual_read_len; + + pt_debug(cd->dev, DL_DEBUG, "%s: PIP2 Execute Scan %d\n", + __func__, scan_type); + data[0] = scan_type; + rc = _pt_request_pip2_send_cmd(cd->dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_EXECUTE_SCAN, + data, 1, read_buf, &actual_read_len); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s EXECUTE_SCAN command for type %d failed. rc=%d\n", + __func__, scan_type, rc); + } + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_exec_panel_scan + * + * SUMMARY: Protected call to pt_pip2_exec_panel_scan_ by exclusive + * access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * scan_type - type of panel scan to perform (PIP2 only) + ******************************************************************************/ +static int pt_pip2_exec_panel_scan(struct pt_core_data *cd, u8 scan_type) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip2_exec_panel_scan_(cd, scan_type); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_exec_panel_scan + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to + * pt_pip2_exec_panel_scan or pt_hid_output_exec_panel_scan + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * scan_type - type of panel scan to perform (PIP2 only) + ******************************************************************************/ +static int _pt_request_pip_exec_panel_scan(struct device *dev, + int protect, u8 scan_type) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (cd->sysinfo.ready && IS_PIP_VER_GE(&cd->sysinfo, 1, 12)) { + if (protect) + return pt_pip2_exec_panel_scan(cd, scan_type); + + return pt_pip2_exec_panel_scan_(cd, scan_type); + } + + if (protect) + return pt_hid_output_exec_panel_scan(cd); + + return pt_hid_output_exec_panel_scan_(cd); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_retrieve_panel_scan_ + * + * SUMMARY: Sends the PIP "Retrieve Panel Scan" (0x2B) command to the DUT + * to retrieve the specified data type for a the last successful Execute + * Panel Scan command. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * read_offset - read pointer offset + * read_count - length of data to read + * data_id - enumerated test ID to read selftest results from + * *response - pointer to store the read response status + * *config - pointer to store config data + * *actual_read_len - pointer to store data length actually read + * *read_buf - pointer to the read buffer + ******************************************************************************/ +static int pt_hid_output_retrieve_panel_scan_( + struct pt_core_data *cd, u16 read_offset, u16 read_count, + u8 data_id, u8 *response, u8 *config, u16 *actual_read_len, + u8 *read_buf) +{ + int status; + u8 read_data_id; + int rc = 0; + int write_length = 5; + u8 write_buf[5]; + u8 cmd_offset = 0; + u8 data_elem_size; + int size; + int data_size; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_RETRIEVE_PANEL_SCAN), + .write_length = write_length, + .write_buf = write_buf, + }; + + write_buf[cmd_offset++] = LOW_BYTE(read_offset); + write_buf[cmd_offset++] = HI_BYTE(read_offset); + write_buf[cmd_offset++] = LOW_BYTE(read_count); + write_buf[cmd_offset++] = HI_BYTE(read_count); + write_buf[cmd_offset++] = data_id; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + status = cd->response_buf[5]; + if (status) + return -EINVAL; + + read_data_id = cd->response_buf[6]; + if (read_data_id != data_id) + return -EPROTO; + + size = get_unaligned_le16(&cd->response_buf[0]); + *actual_read_len = get_unaligned_le16(&cd->response_buf[7]); + *config = cd->response_buf[9]; + + data_elem_size = *config & 0x07; + data_size = *actual_read_len * data_elem_size; + + if (read_buf) + memcpy(read_buf, &cd->response_buf[10], data_size); + if (response) + memcpy(response, cd->response_buf, size); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_retrieve_panel_scan + * + * SUMMARY: Protected call to pt_hid_output_retrieve_panel_scan_ by exclusive + * access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * read_offset - read pointer offset + * read_count - length of data to read + * data_id - enumerated test ID to read selftest results from + * *response - pointer to store the read response status + * *config - pointer to store config data + * *actual_read_len - pointer to store data length actually read + * *read_buf - pointer to the read buffer + ******************************************************************************/ +static int pt_hid_output_retrieve_panel_scan( + struct pt_core_data *cd, u16 read_offset, u16 read_count, + u8 data_id, u8 *response, u8 *config, u16 *actual_read_len, + u8 *read_buf) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_retrieve_panel_scan_(cd, read_offset, + read_count, data_id, response, config, + actual_read_len, read_buf); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_retrieve_panel_scan + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to + * pt_hid_output_retrieve_panel_scan + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * read_offset - read pointer offset + * read_count - length of data to read + * data_id - enumerated test ID to read selftest results from + * *response - pointer to store the read response status + * *config - pointer to store config data + * *actual_read_len - pointer to store data length actually read + * *read_buf - pointer to the read buffer + ******************************************************************************/ +static int _pt_request_pip_retrieve_panel_scan(struct device *dev, + int protect, u16 read_offset, u16 read_count, u8 data_id, + u8 *response, u8 *config, u16 *actual_read_len, u8 *read_buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_hid_output_retrieve_panel_scan(cd, + read_offset, read_count, data_id, response, + config, actual_read_len, read_buf); + + return pt_hid_output_retrieve_panel_scan_(cd, + read_offset, read_count, data_id, response, + config, actual_read_len, read_buf); +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_user_cmd + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to + * pt_hid_output_user_cmd + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * read_len - length of data to read + * *read_buf - pointer to store read data + * write_len - length of data to write + * *write_buf - pointer to buffer to write + * *actual_read_len - pointer to store data length actually read + ******************************************************************************/ +static int _pt_request_pip_user_cmd(struct device *dev, + int protect, u16 read_len, u8 *read_buf, u16 write_len, + u8 *write_buf, u16 *actual_read_len) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_hid_output_user_cmd(cd, read_len, read_buf, + write_len, write_buf, actual_read_len); + + return pt_hid_output_user_cmd_(cd, read_len, read_buf, + write_len, write_buf, actual_read_len); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_get_information_ + * + * SUMMARY: Sends the PIP "Get Bootloader Information" (0x38) command to the + * DUT to retrieve bootloader version and chip identification information. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *return_data - pointer to store the return data + *****************************************************************************/ +static int pt_hid_output_bl_get_information_(struct pt_core_data *cd, + u8 *return_data) +{ + int rc; + int data_len; + struct pt_hid_output hid_output = { + CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_GET_INFO), + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + data_len = get_unaligned_le16(&cd->input_buf[6]); + if (!data_len) + return -EPROTO; + + memcpy(return_data, &cd->response_buf[8], data_len); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_get_information + * + * SUMMARY: Protected call to pt_hid_output_bl_get_information_ by exclusive + * access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *return_data - pointer to store the return data + ******************************************************************************/ +static int pt_hid_output_bl_get_information(struct pt_core_data *cd, + u8 *return_data) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_bl_get_information_(cd, return_data); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_bl_get_information + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to + * pt_hid_output_bl_get_information + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * *return_data - pointer to store bl data + ******************************************************************************/ +static int _pt_request_pip_bl_get_information(struct device *dev, + int protect, u8 *return_data) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_hid_output_bl_get_information(cd, return_data); + + return pt_hid_output_bl_get_information_(cd, return_data); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_initiate_bl_ + * + * SUMMARY: Sends the PIP "Initiate Bootload" (0x48) command to the + * DUT to erases the entire TrueTouch application, Configuration Data block, + * and Design Data block in flash and enables the host to execute the Program + * and Verify Row command to bootload the application image and data. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * protect - flag to call protected or non-protected + * key_size - size of key + * *key_buf - pointer to key data to allow operation + * row_size - size of the meta data row + * *metadata_row_buf - pointer to meta data to write + ******************************************************************************/ +static int pt_hid_output_bl_initiate_bl_(struct pt_core_data *cd, + u16 key_size, u8 *key_buf, u16 row_size, u8 *metadata_row_buf) +{ + u16 write_length = key_size + row_size; + u8 *write_buf; + int rc = 0; + struct pt_hid_output hid_output = { + CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_INITIATE_BL), + .write_length = write_length, + .timeout_ms = PT_PIP1_CMD_INITIATE_BL_TIMEOUT, + }; + + write_buf = kzalloc(write_length, GFP_KERNEL); + if (!write_buf) + return -ENOMEM; + + hid_output.write_buf = write_buf; + + if (key_size) + memcpy(write_buf, key_buf, key_size); + + if (row_size) + memcpy(&write_buf[key_size], metadata_row_buf, row_size); + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + + kfree(write_buf); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_initiate_bl + * + * SUMMARY: Protected call to pt_hid_output_bl_initiate_bl_ by exclusive + * access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * key_size - size of key + * *key_buf - pointer to key data to allow operation + * row_size - size of the meta data row + * *metadata_row_buf - pointer to meta data to write + ******************************************************************************/ +static int pt_hid_output_bl_initiate_bl(struct pt_core_data *cd, + u16 key_size, u8 *key_buf, u16 row_size, u8 *metadata_row_buf) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_bl_initiate_bl_(cd, key_size, key_buf, + row_size, metadata_row_buf); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_bl_initiate_bl + * + * SUMMARY: Function pointer included in core_nonhid_cmd struct for external + * calls to the protected or unprotected call to + * pt_hid_output_bl_initiate_bl + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * key_size - size of key + * *key_buf - pointer to key data to allow operation + * row_size - size of the meta data row + * *metadata_row_buf - pointer to meta data to write + ******************************************************************************/ +static int _pt_request_pip_bl_initiate_bl(struct device *dev, + int protect, u16 key_size, u8 *key_buf, u16 row_size, + u8 *metadata_row_buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_hid_output_bl_initiate_bl(cd, key_size, key_buf, + row_size, metadata_row_buf); + + return pt_hid_output_bl_initiate_bl_(cd, key_size, key_buf, + row_size, metadata_row_buf); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_program_and_verify_ + * + * SUMMARY: Sends the PIP "Program and Verify" (0x39) command to upload + * and program a 128-byte row into the flash, and then verifies written data. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * data_len - length of data_buf + * *data_buf - firmware image to program + ******************************************************************************/ +static int pt_hid_output_bl_program_and_verify_( + struct pt_core_data *cd, u16 data_len, u8 *data_buf) +{ + struct pt_hid_output hid_output = { + CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_PROGRAM_AND_VERIFY), + .write_length = data_len, + .write_buf = data_buf, + .timeout_ms = PT_PIP1_CMD_PROGRAM_AND_VERIFY_TIMEOUT, + }; + + return pt_pip1_send_output_and_wait_(cd, &hid_output); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_program_and_verify + * + * SUMMARY: Protected call to pt_hid_output_bl_program_and_verify_ by exclusive + * access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * data_len - length of data_buf + * *data_buf - firmware image to program + ******************************************************************************/ +static int pt_hid_output_bl_program_and_verify( + struct pt_core_data *cd, u16 data_len, u8 *data_buf) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_bl_program_and_verify_(cd, data_len, data_buf); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_bl_program_and_verify + * + * SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules + * to request to have the BL program and verify a FW image + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - boolean to determine to call the protected function + * data_len - length of data_buf + * *data_buf - firmware image to program + ******************************************************************************/ +static int _pt_request_pip_bl_program_and_verify( + struct device *dev, int protect, u16 data_len, u8 *data_buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_hid_output_bl_program_and_verify(cd, data_len, + data_buf); + + return pt_hid_output_bl_program_and_verify_(cd, data_len, + data_buf); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_verify_app_integrity_ + * + * SUMMARY: Sends the PIP "Verify Application Integrity" (0x31) command to + * perform a full verification of the application integrity by calculating the + * CRC of the image in flash and compare it to the expected CRC stored in the + * Metadata row. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *result - pointer to store result + ******************************************************************************/ +static int pt_hid_output_bl_verify_app_integrity_( + struct pt_core_data *cd, u8 *result) +{ + int rc; + struct pt_hid_output hid_output = { + CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY), + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) { + *result = 0; + return rc; + } + + *result = cd->response_buf[8]; + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_verify_app_integrity + * + * SUMMARY: Protected call to pt_hid_output_bl_verify_app_integrity_ by + * exclusive access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *result - pointer to store result + ******************************************************************************/ +static int pt_hid_output_bl_verify_app_integrity( + struct pt_core_data *cd, u8 *result) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_bl_verify_app_integrity_(cd, result); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_bl_verify_app_integrity + * + * SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules + * to request to have the BL verify the application integrity (PIP1.x only) + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - boolean to determine to call the protected function + * *result - pointer to store result + ******************************************************************************/ +static int _pt_request_pip_bl_verify_app_integrity( + struct device *dev, int protect, u8 *result) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_hid_output_bl_verify_app_integrity(cd, result); + + return pt_hid_output_bl_verify_app_integrity_(cd, result); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_launch_app_ + * + * SUMMARY: Sends the PIP "Launch Application" (0x3B) command to launch the + * application from bootloader (PIP1.x only). + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_hid_output_bl_launch_app_(struct pt_core_data *cd) +{ + struct pt_hid_output hid_output = { + CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_LAUNCH_APP), + .reset_expected = 1, + }; + + return pt_pip1_send_output_and_wait_(cd, &hid_output); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_launch_app + * + * SUMMARY: Protected call to pt_hid_output_bl_launch_app_ by exclusive access + * to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_hid_output_bl_launch_app(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_bl_launch_app_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_launch_app + * + * SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules + * to request to have the BL launch the application. (PIP1.x only) + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - boolean to determine to call the protected function + ******************************************************************************/ +static int _pt_request_pip_launch_app(struct device *dev, + int protect) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_hid_output_bl_launch_app(cd); + + return pt_hid_output_bl_launch_app_(cd); +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_get_panel_id_ + * + * SUMMARY: Sends the PIP "Get Panel ID" (0x3E) command to return the Panel ID + * value store in the System Information. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *panel_id - pointer to where the panel ID will be stored + ******************************************************************************/ +static int pt_hid_output_bl_get_panel_id_( + struct pt_core_data *cd, u8 *panel_id) +{ + int rc; + struct pt_hid_output hid_output = { + CREATE_PIP1_BL_CMD(PIP1_BL_CMD_ID_GET_PANEL_ID), + }; + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc == -EPROTO && cd->response_buf[5] == ERROR_COMMAND) { + pt_debug(cd->dev, DL_ERROR, + "%s: Get Panel ID command not supported\n", + __func__); + *panel_id = PANEL_ID_NOT_ENABLED; + return 0; + } else if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on Get Panel ID command\n", __func__); + return rc; + } + + *panel_id = cd->response_buf[8]; + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_hid_output_bl_get_panel_id + * + * SUMMARY: Protected call to pt_hid_output_bl_get_panel_id_ by exclusive access + * to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *panel_id - pointer to where the panel ID will be stored + ******************************************************************************/ +static int pt_hid_output_bl_get_panel_id( + struct pt_core_data *cd, u8 *panel_id) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_hid_output_bl_get_panel_id_(cd, panel_id); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip_bl_get_panel_id + * + * SUMMARY: Function pointer included in core_nonhid_cmds to allow other modules + * to have the BL retrieve the panel ID + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to run in protected mode + * *panel_id - pointer to where the panel ID will be stored + ******************************************************************************/ +static int _pt_request_pip_bl_get_panel_id( + struct device *dev, int protect, u8 *panel_id) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_hid_output_bl_get_panel_id(cd, panel_id); + + return pt_hid_output_bl_get_panel_id_(cd, panel_id); +} + +/******************************************************************************* + * FUNCTION: pt_pip2_get_status_ + * + * SUMMARY: Sends a PIP2 STATUS command to the DUT and stores the data in + * cd status_data. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_pip2_get_status_(struct pt_core_data *cd) +{ + u16 actual_read_len; + u8 read_buf[12]; + u8 status, boot; + int rc = 0; + + rc = _pt_request_pip2_send_cmd(cd->dev, PT_CORE_CMD_UNPROTECTED, + PIP2_CMD_ID_STATUS, NULL, 0, read_buf, &actual_read_len); + if (rc) { + pt_debug(cd->dev, DL_ERROR, "%s: PIP2 STATUS command rc = %d\n", + __func__, rc); + return rc; + } + + pt_pr_buf(cd->dev, DL_DEBUG, read_buf, actual_read_len, + "PIP2 STATUS"); + + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + boot = read_buf[PIP2_RESP_BODY_OFFSET] & 0x01; + + cd->dut_status.fw_system_mode = + read_buf[PIP2_RESP_BODY_OFFSET + 1]; + + if (status == PIP2_RSP_ERR_NONE && boot == 0x00) + cd->dut_status.mode = PT_MODE_BOOTLOADER; + else if (status == PIP2_RSP_ERR_NONE && boot == 0x01) { + cd->dut_status.mode = PT_MODE_OPERATIONAL; + cd->dut_status.protocol_mode = + read_buf[PIP2_RESP_BODY_OFFSET + 2]; + } else + cd->dut_status.mode = PT_MODE_UNKNOWN; + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_get_mode_sysmode_ + * + * SUMMARY: Determine the current mode and system mode of the DUT by use of the + * PIP2 STATUS command. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + * *mode - pointer to store the retrieved mode + * *sys_mode - pointer to store the FW system mode + ******************************************************************************/ +static int pt_pip2_get_mode_sysmode_(struct pt_core_data *cd, + u8 *mode, u8 *sys_mode) +{ + int rc = 0; + + rc = pt_pip2_get_status_(cd); + if (!rc) { + if (sys_mode) + *sys_mode = cd->dut_status.fw_system_mode; + if (mode) + *mode = cd->dut_status.mode; + } else { + if (mode) + *mode = PT_MODE_UNKNOWN; + if (sys_mode) + *sys_mode = FW_SYS_MODE_UNDEFINED; + pt_debug(cd->dev, DL_WARN, + "%s: Mode and sys_mode could not be determined\n", + __func__); + } + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_get_mode_sysmode + * + * SUMMARY: Protected call to pt_pip2_get_mode_sysmode_ by exclusive + * access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + * *mode - pointer to store the retrieved mode + * *sys_mode - pointer to store the FW system mode + ******************************************************************************/ +static int pt_pip2_get_mode_sysmode(struct pt_core_data *cd, + u8 *mode, u8 *sys_mode) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip2_get_mode_sysmode_(cd, mode, sys_mode); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip2_get_mode_sysmode + * + * SUMMARY: Function pointer included in core_commands struct for external + * calls to the protected or unprotected call to + * pt_pip2_get_mode_sysmode + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *mode - pointer to store the retrieved mode + * *sys_mode - pointer to store the FW system mode + ******************************************************************************/ +static int _pt_request_pip2_get_mode_sysmode(struct device *dev, + int protect, u8 *mode, u8 *sys_mode) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_pip2_get_mode_sysmode(cd, mode, sys_mode); + + return pt_pip2_get_mode_sysmode_(cd, mode, sys_mode); +} + +/******************************************************************************* + * FUNCTION: _pt_poll_for_fw_exit_boot_mode + * + * SUMMARY: Verify and or poll for the FW to exit BOOT mode. During the FW BOOT + * mode only the following PIP commands will be serviced, any other PIP + * command the FW will respond with an "Invalid PIP Command" response. + * - Get HID Descriptor (Register 0x0001, no command ID) + * - Reset (Register 0x0005, RESET HID request) + * - Ping (Register 0x0004, Command ID 0x00 + * - Get System Information (Register 0x0004, Command ID 0x02) + * - PIP2 Status (Register 0x0101, Command ID 0x01) + * - PIP2 Version (Register 0x0101, Command ID 0x07) + * This function will loop on the results of the STATUS command until + * the FW reports it is out of BOOT mode. + * + * NOTE: + * - This function will update cd->fw_system_mode + * - The STATUS cmd only supports this functionality for PIP 1.11+ + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * timeout - max time (ms) to wait for FW to exit BOOT mode + * actual_wait - pointer to actual time waited for FW to exit BOOT mode + ******************************************************************************/ +static int _pt_poll_for_fw_exit_boot_mode(struct pt_core_data *cd, int timeout, + int *actual_wait) +{ + int loop = 0; + u8 sys_mode = cd->fw_system_mode; + u8 pause = 10; /* in ms */ + int rc = 0; + int max_loop = (timeout / pause) + 1; /* Add 1 due to int math */ + + if (cd->sysinfo.ready && !IS_PIP_VER_GE(&cd->sysinfo, 1, 11)) { + /* + * For PIP <1.11, no support for polling wait so do a hard + * coded wait and assume the FW is out of BOOT. Added 1 to + * timeout to make it clear in kmsg if non polling was done. + */ + *actual_wait = PT_FW_EXIT_BOOT_MODE_TIMEOUT + 1; + pt_debug(cd->dev, DL_ERROR, + "%s: PIP %d.%d no support for ext STATUS, sleep %d\n", + __func__, + cd->sysinfo.ttdata.pip_ver_major, + cd->sysinfo.ttdata.pip_ver_minor, *actual_wait); + msleep(*actual_wait); + sys_mode = FW_SYS_MODE_SCANNING; + } + + if (sys_mode == FW_SYS_MODE_BOOT) { + while (!rc && loop <= max_loop && + (sys_mode == FW_SYS_MODE_BOOT)) { + loop++; + usleep_range(9000, pause * 1000); + rc = pt_pip2_get_mode_sysmode_(cd, NULL, &sys_mode); + pt_debug(cd->dev, DL_DEBUG, + "%s: FW in BOOT mode-sleep %dms, sys_mode=%d\n", + __func__, loop * pause, sys_mode); + } + *actual_wait = (int)(loop * pause); + pt_debug(cd->dev, DL_WARN, + "%s: FW exited BOOT mode in %dms, sys_mode=%d\n", + __func__, *actual_wait, sys_mode); + + if (rc) + sys_mode = FW_SYS_MODE_UNDEFINED; + else if (sys_mode == FW_SYS_MODE_BOOT || + sys_mode == FW_SYS_MODE_UNDEFINED) + rc = -EBUSY; + } + + mutex_lock(&cd->system_lock); + cd->fw_system_mode = sys_mode; + mutex_unlock(&cd->system_lock); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_poll_for_fw_exit_boot_mode + * + * SUMMARY: Protected call to _pt_poll_for_fw_exit_boot_mode by exclusive + * access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * timeout - max time (ms) to wait for FW to exit BOOT mode + * actual_wait - pointer to actual time waited for FW to exit BOOT mode + ******************************************************************************/ +static int pt_poll_for_fw_exit_boot_mode(struct pt_core_data *cd, int timeout, + int *actual_wait) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = _pt_poll_for_fw_exit_boot_mode(cd, timeout, actual_wait); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_get_fw_sys_mode + * + * SUMMARY: Determine the FW system mode. For PIP 1.11+ the + * PIP2 STATUS command is used to directly query the FW system mode. For older + * PIP versions, there is no direct PIP commamnd that will directly provide this + * information but any PIP command above 0x1F requires scanning to be disabled + * before it will be operational. If scanning was not disabled before sending + * these PIP commands the FW will respond with a 6 byte error response. So to + * safely determine the scanning state, a PIP message that will not affect the + * operation of the FW was chosen: + * "Verify Data Block CRC (ID 0x20)" is sent and if a 6 byte error code is + * received scanning is active. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *sys_mode - pointer to FW System mode + * *mode - pointer to mode (BL/FW) + ******************************************************************************/ +static int _pt_get_fw_sys_mode(struct pt_core_data *cd, u8 *sys_mode, u8 *mode) +{ + int write_length = 1; + int report_length; + int rc = 0; + u8 tmp_sys_mode = FW_SYS_MODE_UNDEFINED; + u8 tmp_mode = PT_MODE_UNKNOWN; + u8 param[1] = { PT_TCH_PARM_EBID }; + struct pt_hid_output hid_output = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC), + .write_length = write_length, + .write_buf = param, + .novalidate = true, + }; + + /* AFter PIP1.11 the preferred method is using STATUS cmd */ + if (IS_PIP_VER_GE(&cd->sysinfo, 1, 11)) { + rc = pt_pip2_get_mode_sysmode_(cd, &tmp_mode, &tmp_sys_mode); + pt_debug(cd->dev, DL_DEBUG, "%s: tmp_sys_mode=%d tmp_mode=%d\n", + __func__, tmp_sys_mode, tmp_mode); + if (!rc) { + if (tmp_mode != PT_MODE_OPERATIONAL) + tmp_sys_mode = FW_SYS_MODE_UNDEFINED; + } + goto exit; + } + + /* Older systems use PIP1 CONFIG_BLOCK_CRC to best determine sys_mode */ + if (cd->mode != PT_MODE_OPERATIONAL) { + tmp_mode = cd->mode; + goto exit; + } + + rc = pt_pip1_send_output_and_wait_(cd, &hid_output); + if (rc) + goto exit; + + report_length = (cd->response_buf[1] << 8) | (cd->response_buf[0]); + if ((report_length == 0x06) && + ((cd->response_buf[4] & PIP1_RESP_COMMAND_ID_MASK) == 0x00) && + (cd->response_buf[5] == PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC)) { + tmp_mode = PIP2_STATUS_APP_EXEC; + tmp_sys_mode = FW_SYS_MODE_SCANNING; + } else if ((report_length == 0x0A) && + ((cd->response_buf[4] & PIP1_RESP_COMMAND_ID_MASK) == + PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC)) { + tmp_mode = PIP2_STATUS_APP_EXEC; + tmp_sys_mode = FW_SYS_MODE_TEST; + } + +exit: + if (mode) + *mode = tmp_mode; + if (sys_mode) + *sys_mode = tmp_sys_mode; + pt_debug(cd->dev, DL_INFO, "%s: Return Mode=%d sys_mode=%d\n", + __func__, tmp_mode, tmp_sys_mode); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_get_fw_sys_mode + * + * SUMMARY: Protected call to _pt_get_fw_sys_mode() to determine if FW scanning + * is active or not. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *sys_mode - pointer to fw system mode + * *mode - pointer to mode + ******************************************************************************/ +static int pt_get_fw_sys_mode(struct pt_core_data *cd, u8 *sys_mode, u8 *mode) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = _pt_get_fw_sys_mode(cd, sys_mode, mode); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_get_fw_sys_mode + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to request to get scan state + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * *sys_mode - pointer to FW system mode + * *mode - pointer to mode + ******************************************************************************/ +static int _pt_request_get_fw_sys_mode(struct device *dev, int protect, + u8 *sys_mode, u8 *mode) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return pt_get_fw_sys_mode(cd, sys_mode, mode); + + return _pt_get_fw_sys_mode(cd, sys_mode, mode); +} + +/* Default hid descriptor to provide basic register map */ +const struct pt_hid_desc hid_desc_default = { + 230, /* hid_desc_len */ + HID_APP_REPORT_ID, /* packet_id */ + 0x00, /* reserved_byte */ + 0x0100, /* bcd_version */ + 0x00EC, /* report_desc_len */ + 0x0002, /* report_desc_register */ + 0x0003, /* input_register */ + 0x00FE, /* max_input_len */ + 0x0004, /* output_register */ + 0x00FE, /* max_output_len */ + 0x0005, /* command_register */ + 0x0006, /* data_register */ + 0x04B4, /* vendor_id */ + 0xC101, /* product_id */ + 0x0100, /* version_id */ + {0x00, 0x00, 0x00, 0x00} /* reserved[4] */ +}; + +/******************************************************************************* + * FUNCTION: pt_init_hid_descriptor + * + * SUMMARY: Setup default values for HID descriptor structure + * + * + * PARAMETERS: + * *desc - pointer to the HID descriptor data read back from DUT + ******************************************************************************/ +static inline void pt_init_hid_descriptor(struct pt_hid_desc *desc) +{ + memcpy(desc, &hid_desc_default, sizeof(hid_desc_default)); +} + +/******************************************************************************* + * FUNCTION: pt_get_hid_descriptor_ + * + * SUMMARY: Send the get HID descriptor command to the DUT and load the response + * into the HID descriptor structure + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *desc - pointer to the HID descriptor data read back from DUT + ******************************************************************************/ +static int pt_get_hid_descriptor_(struct pt_core_data *cd, + struct pt_hid_desc *desc) +{ + struct device *dev = cd->dev; + struct pt_hid_cmd hid_cmd = { + .descriptor = cd->hid_core.hid_desc_register, + .read_length = 0, + }; + int rc = 0; + u16 hid_len; + + /* + * During startup the HID descriptor is required for all future + * processing. If IRQ is already asserted due to an early touch report + * the report must be cleared before sending command. + */ + pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN); + + rc = pt_hid_send_command(cd, &hid_cmd); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: failed to get HID descriptor, rc=%d\n", + __func__, rc); + goto exit; + } + + hid_len = get_unaligned_le16(&cd->response_buf[0]); + pt_pr_buf(cd->dev, DL_DEBUG, cd->response_buf, hid_len, "<<< HIDDesc"); + + /* + * HID doesn't have packet_id and reserve_byte in struct struct + * pt_hid_desc and assign fixed value to packet_id. + */ + if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) { + if ((hid_len + 2) != sizeof(*desc)) { + pt_debug(dev, DL_ERROR, "%s: Unsupported HID length: %X\n", + __func__, hid_len); + rc = -ENODEV; + goto exit; + } + /* Copy length filed */ + memcpy((u8 *)desc, &cd->response_buf[0], 2); + /* Assign fixed value to packet_id */ + desc->packet_id = HID_APP_REPORT_ID; + /* Skip 2 bytes in desc and Copy to left fields */ + memcpy((u8 *)desc + 4, &cd->response_buf[2], hid_len - 2); + } else { + if (hid_len != sizeof(*desc)) { + pt_debug(dev, DL_ERROR, "%s: Unsupported HID length: %X\n", + __func__, hid_len); + rc = -ENODEV; + goto exit; + } + + /* Load the HID descriptor including all registers */ + memcpy((u8 *)desc, cd->response_buf, hid_len); + } + + /* Check HID descriptor length and version */ + pt_debug(dev, DL_INFO, "%s: HID len:%X HID ver:%X\n", __func__, + le16_to_cpu(desc->hid_desc_len), + le16_to_cpu(desc->bcd_version)); + + cd->hid_core.hid_report_desc_len = + le16_to_cpu(desc->report_desc_len); + + pt_debug(dev, DL_INFO, "%s: report descriptor len:%d\n", __func__, + cd->hid_core.hid_report_desc_len); + + if (le16_to_cpu(desc->bcd_version) != HID_VERSION) { + pt_debug(dev, DL_ERROR, "%s: Unsupported HID version\n", + __func__); + rc = -ENODEV; + goto exit; + } + +#ifdef TTHE_TUNER_SUPPORT + tthe_print(cd, cd->response_buf, hid_len, "HIDDesc="); +#endif + +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_get_hid_descriptor + * + * SUMMARY: Protected call to pt_get_hid_descriptor_() + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *desc - pointer to the HID descriptor data read back from DUT + ******************************************************************************/ +static int pt_get_hid_descriptor(struct pt_core_data *cd, + struct pt_hid_desc *desc) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_get_hid_descriptor_(cd, desc); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_get_version_ + * + * SUMMARY: Sends a PIP2 VERSION command to the DUT and stores the data in + * cd-ttdata + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_pip2_get_version_(struct pt_core_data *cd) +{ + int rc = 0; + int status; + u8 read_buf[64]; + u16 actual_read_len; + + rc = _pt_request_pip2_send_cmd(cd->dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_VERSION, + NULL, 0, read_buf, &actual_read_len); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error Sending PIP2 VERSION Cmd rc=%d\n", + __func__, rc); + return rc; + } + + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + if (status == 0) { + /* Parse the PIP2 VERSION response into ttdata */ + pt_pip2_ver_load_ttdata(cd, actual_read_len); + } else { + pt_debug(cd->dev, DL_ERROR, + "%s: Error in PIP2 VERSION Cmd status=%d\n", + __func__, status); + return status; + } + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_get_version + * + * SUMMARY: Protected call to pt_pip2_get_version_ by exclusive + * access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_pip2_get_version(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip2_get_version_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + + +/******************************************************************************* + * FUNCTION: _pt_request_active_pip_protocol + * + * SUMMARY: Get active PIP protocol version using the PIP2 version command. + * Function will return PIP version of BL or application based on + * when it's called. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to run in protected mode + * *pip_version_major - pointer to store PIP major version + * *pip_version_minor - pointer to store PIP minor version + ******************************************************************************/ +int _pt_request_active_pip_protocol(struct device *dev, int protect, + u8 *pip_version_major, u8 *pip_version_minor) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_ttdata *ttdata = &cd->sysinfo.ttdata; + int rc = 0; + struct pt_hid_output sys_info = { + CREATE_PIP1_FW_CMD(PIP1_CMD_ID_GET_SYSINFO), + .timeout_ms = PT_PIP1_CMD_GET_SYSINFO_TIMEOUT, + }; + + pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN); + + /* Skip PIP2 command if DUT generation is confirmed */ + if (cd->active_dut_generation == DUT_PIP1_ONLY) + goto skip_pip2_command; + + rc = pt_pip2_get_version_(cd); + if (!rc) { + *pip_version_major = ttdata->pip_ver_major; + *pip_version_minor = ttdata->pip_ver_minor; + pt_debug(dev, DL_INFO, + "%s: pip_version = %d.%d\n", __func__, + *pip_version_major, *pip_version_minor); + } else { + /* + * Legacy products do not support the pip2 protocol to get + * pip version. However, they do support the "get sysinfo" + * command to get pip version from FW, but the bootloader + * does not support it. This function will try "get sysinfo" + * command if the pip2 command failed but this cmd could also + * fail if DUT is stuck in bootloader mode. + */ + pt_debug(dev, DL_INFO, + "%s: PIP2 no response rc = %d, try legacy cmd\n", + __func__, rc); + +skip_pip2_command: + rc = pt_pip1_send_output_and_wait_(cd, &sys_info); + if (!rc) { + *pip_version_minor = + cd->response_buf[PIP1_SYSINFO_TTDATA_OFFSET + 1]; + *pip_version_major = + cd->response_buf[PIP1_SYSINFO_TTDATA_OFFSET]; + + pt_debug(dev, DL_INFO, + "%s: pip_version = %d.%d\n", __func__, + *pip_version_major, *pip_version_minor); + } else { + *pip_version_major = 0; + *pip_version_minor = 0; + pt_debug(dev, DL_ERROR, + "%s: pip_version Not Detected\n", __func__); + } + } + + return rc; +} +EXPORT_SYMBOL_GPL(_pt_request_active_pip_protocol); + +/******************************************************************************* + * FUNCTION: _pt_detect_dut_generation + * + * SUMMARY: Determine the generation of device that we are communicating with: + * DUT_PIP1_ONLY (Gen5 or Gen6) + * DUT_PIP2_CAPABLE (TC33xx or TT7xxx) + * The HID_DESC command is supported in Gen5/6 BL and FW as well as + * TT/TC FW. The packet ID in the descriptor, however, is unique when + * coming form the BL or the FW: + * Packet_ID in BL = HID_BL_REPORT_ID (0xFF) + * Packet_ID in FW = HID_APP_REPORT_ID (0xF7) + * This function will return a modified status if it detects the DUT + * is in the BL. In the case of a Gen5/6 BL, which also sends out a FW + * reset sentinel, the status is "corrected" from a FW to BL sentinel. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *status - pointer to status bitmask + * *dut_gen - pointer to store the dut_generation + * *mode - pointer to store the PT_MODE + ******************************************************************************/ +static int _pt_detect_dut_generation(struct device *dev, + u32 *status, u8 *dut_gen, enum pt_mode *mode) +{ + int rc = 0; + u8 dut_gen_tmp = DUT_UNKNOWN; + u8 mode_tmp = PT_MODE_UNKNOWN; + u8 attempt = 1; + u32 status_tmp = STARTUP_STATUS_START; + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_hid_desc hid_desc; + + /* protocol_mode must be known before dut gen */ + rc = pt_pip2_get_status_(cd); + if (rc) { + /* PIP prot assumed if error */ + cd->dut_status.protocol_mode = PT_PROTOCOL_MODE_PIP; + } + + memset(&hid_desc, 0, sizeof(hid_desc)); + rc = pt_get_hid_descriptor_(cd, &hid_desc); + while (rc && attempt < 3) { + attempt++; + usleep_range(2000, 5000); + rc = pt_get_hid_descriptor_(cd, &hid_desc); + } + + if (!rc && hid_desc.packet_id == HID_BL_REPORT_ID) { + dut_gen_tmp = DUT_PIP1_ONLY; /* Gen5/6 BL */ + mode_tmp = PT_MODE_BOOTLOADER; + status_tmp = STARTUP_STATUS_BL_RESET_SENTINEL; + } else if (!rc && hid_desc.packet_id == HID_APP_REPORT_ID) { + rc = pt_pip2_get_version_(cd); + if (!rc) + dut_gen_tmp = DUT_PIP2_CAPABLE; /* TT/TC FW */ + else + dut_gen_tmp = DUT_PIP1_ONLY; /* Gen5/6 FW */ + mode_tmp = PT_MODE_OPERATIONAL; + status_tmp = STARTUP_STATUS_FW_RESET_SENTINEL; + rc = 0; /* To return success instead of error code */ + } else if (rc) { + rc = pt_pip2_get_version_(cd); + if (!rc) { + dut_gen_tmp = DUT_PIP2_CAPABLE; /* TT/TC BL */ + mode_tmp = PT_MODE_BOOTLOADER; + status_tmp = STARTUP_STATUS_BL_RESET_SENTINEL; + } + } + + mutex_lock(&cd->system_lock); + if (dut_gen) + *dut_gen = dut_gen_tmp; + if (mode) + *mode = mode_tmp; + if (status) + *status = status_tmp; + mutex_unlock(&cd->system_lock); + +#ifdef TTDL_DIAGNOSTICS + pt_debug(cd->dev, DL_INFO, "%s: Generation=%d Mode=%d\n", + __func__, dut_gen_tmp, mode_tmp); +#endif /* TTDL_DIAGNOSTICS */ + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_dut_generation + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to get current dut generation. + * + * NOTE: This function WILL NOT try to determine dut generation. + * + * RETURN: + * The current dut generation. + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int _pt_request_dut_generation(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + return cd->active_dut_generation; +} + +#define HW_VERSION_LEN_MAX 13 +/******************************************************************************* + * FUNCTION: _legacy_generate_hw_version + * + * SUMMARY: Format chip information from struct ttdata (maintained by PIP1 + * SYSINFO command) or struct bl_info (maintained by PIP1 BL INFORMATION + * command) to the hw_version. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hw_version - pointer to store the hardware version + ******************************************************************************/ +static int _legacy_generate_hw_version(struct pt_core_data *cd, + char *hw_version) +{ + struct pt_ttdata *ttdata = &cd->sysinfo.ttdata; + + if (cd->sysinfo.ready) { + scnprintf(hw_version, HW_VERSION_LEN_MAX, "%04X.FFFF.%02X", + ttdata->jtag_id_h, cd->pid_for_loader); + return 0; + } else if (cd->bl_info.ready) { + scnprintf(hw_version, HW_VERSION_LEN_MAX, "%04X.FFFF.%02X", + cd->bl_info.chip_id, cd->pid_for_loader); + return 0; + } else { + snprintf(hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF"); + pt_debug(cd->dev, DL_ERROR, + "%s: SYSINFO and BL_INFO are not ready\n", __func__); + return -ENODATA; + } +} + +/******************************************************************************* + * FUNCTION: _pip2_generate_hw_version + * + * SUMMARY: Format chip information from struct ttdata (maintained by PIP2 + * VERSION command) to the hw_version. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hw_version - pointer to store the hardware version + ******************************************************************************/ +static int _pip2_generate_hw_version(struct pt_core_data *cd, char *hw_version) +{ + struct pt_ttdata *ttdata = &cd->sysinfo.ttdata; + + if (cd->app_pip_ver_ready | cd->bl_pip_ver_ready) { + snprintf(hw_version, HW_VERSION_LEN_MAX, "%04X.%04X.%02X", + ttdata->chip_id, ttdata->chip_rev, cd->pid_for_loader); + return 0; + } else { + snprintf(hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF"); + pt_debug(cd->dev, DL_ERROR, + "%s: PIP Version are not ready\n", __func__); + return -ENODATA; + } +} + +/******************************************************************************* + * FUNCTION: pt_generate_hw_version + * + * SUMMARY: Wraaper function for both legacy and TT/TC products generate the + * hw_version from static data. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *hw_version - pointer to store the hardware version + ******************************************************************************/ +static int pt_generate_hw_version(struct pt_core_data *cd, char *hw_version) +{ + int rc = 0; + + if (!hw_version) + return -ENOMEM; + + if (cd->active_dut_generation == DUT_PIP1_ONLY) + rc = _legacy_generate_hw_version(cd, hw_version); + else if (cd->active_dut_generation == DUT_PIP2_CAPABLE) + rc = _pip2_generate_hw_version(cd, hw_version); + else { + snprintf(hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF"); + rc = -ENODATA; + } + + return rc; +} +/******************************************************************************* + * SUMMARY: Attempt to retrieve the HW version of the connected DUT + * + * NOTE: The calling function must ensure to free *hw_version + * + * RETURN: + * 0 = success + * !0 = Failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *hw_version - pointer to where the hw_version string will be stored + ******************************************************************************/ +static int _pt_request_hw_version(struct device *dev, char *hw_version) +{ + int rc = 0; + u16 actual_read_len; + u16 pip_ver; + u8 rd_buf[256]; + u8 status; + u8 index = PIP2_RESP_STATUS_OFFSET; + u8 return_data[8]; + u8 panel_id; + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_ttdata *ttdata = &cd->sysinfo.ttdata; + + if (!hw_version) + return -ENOMEM; + + if (!cd->hw_detected) { + /* No HW detected */ + rc = -ENODEV; + pt_debug(dev, DL_ERROR, "%s: no hardware is detected!\n", + __func__); + goto exit_error; + } + + /* For Parade TC or TT parts */ + if (cd->active_dut_generation == DUT_PIP2_CAPABLE) { + rc = _pt_request_pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_VERSION, + NULL, 0, rd_buf, &actual_read_len); + + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Failed to send PIP2 VERSION cmd\n", + __func__); + goto exit_error; + } + + status = rd_buf[index]; + if (status == 0) { + pip_ver = 256 * rd_buf[index + 2] + rd_buf[index + 1]; + + /* + * BL PIP 2.02 and greater the version fields are + * swapped + */ + if (pip_ver >= 0x0202) { + snprintf(hw_version, HW_VERSION_LEN_MAX, + "%02X%02X.%02X%02X.FF", + rd_buf[index + 10], rd_buf[index + 9], + rd_buf[index + 8], rd_buf[index + 7]); + } else { + snprintf(hw_version, HW_VERSION_LEN_MAX, + "%02X%02X.%02X%02X.FF", + rd_buf[index + 8], rd_buf[index + 7], + rd_buf[index + 10], rd_buf[index + 9]); + } + return STATUS_SUCCESS; + } else { + rc = status; + pt_debug(dev, DL_WARN, + "%s: PIP2 VERSION cmd response error\n", + __func__); + } + } else if (cd->active_dut_generation == DUT_PIP1_ONLY) { + /* + * For Parade/Cypress legacy parts the RevID and FamilyID are + * hard coded to FFFF + */ + if (cd->mode == PT_MODE_OPERATIONAL) { + rc = pt_hid_output_get_sysinfo(cd); + if (!rc) { + panel_id = + cd->sysinfo.sensing_conf_data.panel_id; + } else { + panel_id = PANEL_ID_NOT_ENABLED; + } + /* In FW - simply retrieve from ttdata struct */ + snprintf(hw_version, HW_VERSION_LEN_MAX, + "%04X.FFFF.%02X", + ttdata->jtag_id_h, + panel_id); + return STATUS_SUCCESS; + } else { + /* + * Return the stored value if PT_PANEL_ID_BY_BL + * is not supported while other feature is. + */ + if (cd->panel_id_support & PT_PANEL_ID_BY_BL) { + rc = pt_hid_output_bl_get_information( + cd, return_data); + if (!rc) { + rc = pt_hid_output_bl_get_panel_id( + cd, &panel_id); + } + } else + panel_id = cd->pid_for_loader; + if (!rc) { + snprintf(hw_version, + HW_VERSION_LEN_MAX, + "%02X%02X.FFFF.%02X", + return_data[3], return_data[2], + panel_id); + return STATUS_SUCCESS; + } + } + } else { + /* Unknown generation */ + rc = -ENODEV; + pt_debug(dev, DL_ERROR, "%s: generation is unknown!\n", + __func__); + } + +exit_error: + snprintf(hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF"); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_start_wd_timer + * + * SUMMARY: Starts the TTDL watchdog timer if the timer interval is > 0 + * + * RETURN: void + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static void pt_start_wd_timer(struct pt_core_data *cd) +{ + if (cd->watchdog_interval < 100) { + pt_debug(cd->dev, DL_ERROR, + "%s: WARNING: Invalid watchdog interval: %d\n", + __func__, cd->watchdog_interval); + return; + } + + if (cd->watchdog_force_stop) { + pt_debug(cd->dev, DL_INFO, + "%s: TTDL WD Forced stop\n", __func__); + return; + } + + mod_timer(&cd->watchdog_timer, jiffies + + msecs_to_jiffies(cd->watchdog_interval)); + cd->watchdog_enabled = 1; + pt_debug(cd->dev, DL_INFO, "%s: TTDL WD Started\n", __func__); +} + +/******************************************************************************* + * FUNCTION: pt_stop_wd_timer + * + * SUMMARY: Stops the TTDL watchdog timer if the timer interval is > 0 + * + * RETURN: void + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static void pt_stop_wd_timer(struct pt_core_data *cd) +{ + if (!cd->watchdog_interval) + return; + + /* + * Ensure we wait until the watchdog timer + * running on a different CPU finishes + */ + del_timer_sync(&cd->watchdog_timer); + cancel_work_sync(&cd->watchdog_work); + del_timer_sync(&cd->watchdog_timer); + cd->watchdog_enabled = 0; + pt_debug(cd->dev, DL_INFO, "%s: TTDL WD Stopped\n", __func__); +} + +/******************************************************************************* + * FUNCTION: pt_hw_soft_reset + * + * SUMMARY: Sends a PIP reset command to the DUT. Disable/re-enable the + * TTDL watchdog around the reset to ensure the WD doesn't happen to + * schedule an enum if it fires when the DUT is being reset. + * This can cause a double reset. + * + * NOTE: The WD MUST be stopped/restarted by the calling Function. Having + * the WD active could cause this function to fail! + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data struct + * protect - flag to call protected or non-protected + ******************************************************************************/ +static int pt_hw_soft_reset(struct pt_core_data *cd, int protect) +{ + int rc = 0; + + mutex_lock(&cd->system_lock); + cd->startup_status = STARTUP_STATUS_START; + pt_debug(cd->dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__); + mutex_unlock(&cd->system_lock); + if (protect) + rc = pt_hid_cmd_reset(cd); + else + rc = pt_hid_cmd_reset_(cd); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: FAILED to execute SOFT reset\n", __func__); + return rc; + } + pt_debug(cd->dev, DL_INFO, "%s: SOFT reset successful\n", + __func__); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_hw_hard_reset + * + * SUMMARY: Calls the platform xres function if it exists to perform a hard + * reset on the DUT by toggling the XRES gpio. Disable/re-enable the + * TTDL watchdog around the reset to ensure the WD doesn't happen to + * schedule an enum if it fires when the DUT is being reset. + * This can cause a double reset. + * + * NOTE: The WD MUST be stopped/restarted by the calling Function. Having + * the WD active could cause this function to fail! + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data struct + ******************************************************************************/ +static int pt_hw_hard_reset(struct pt_core_data *cd) +{ + if (cd->cpdata->xres) { + cd->startup_status = STARTUP_STATUS_START; + pt_debug(cd->dev, DL_DEBUG, "%s: Startup Status Reset\n", + __func__); + cd->cpdata->xres(cd->cpdata, cd->dev); + pt_debug(cd->dev, DL_WARN, "%s: executed HARD reset\n", + __func__); + return 0; + } + pt_debug(cd->dev, DL_ERROR, + "%s: FAILED to execute HARD reset\n", __func__); + + return -ENODEV; +} + +/******************************************************************************* + * FUNCTION: pt_dut_reset + * + * SUMMARY: Attempts to reset the DUT by a hard reset and if that fails a + * soft reset. + * + * NOTE: The WD MUST be stopped/restarted by the calling Function. Having + * the WD active could cause this function to fail! + * NOTE: "protect" flag is only used for soft reset. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + * protect - flag to call protected or non-protected + ******************************************************************************/ +static int pt_dut_reset(struct pt_core_data *cd, int protect) +{ + int rc = 0; + + pt_debug(cd->dev, DL_INFO, "%s: reset hw...\n", __func__); + mutex_lock(&cd->system_lock); + cd->hid_reset_cmd_state = 1; + rc = pt_hw_hard_reset(cd); + mutex_unlock(&cd->system_lock); + + if (rc == -ENODEV) { + mutex_lock(&cd->system_lock); + cd->hid_reset_cmd_state = 0; + mutex_unlock(&cd->system_lock); + pt_debug(cd->dev, DL_ERROR, + "%s: Hard reset failed, try soft reset\n", __func__); + rc = pt_hw_soft_reset(cd, protect); + } + + if (rc) + pt_debug(cd->dev, DL_ERROR, "%s: %s dev='%s' r=%d\n", + __func__, "Fail hw reset", dev_name(cd->dev), rc); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_dut_reset_and_wait + * + * SUMMARY: Wrapper function for pt_dut_reset that waits for the reset to + * complete + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int pt_dut_reset_and_wait(struct pt_core_data *cd) +{ + int rc = 0; + int t; + + rc = pt_dut_reset(cd, PT_CORE_CMD_UNPROTECTED); + if (rc < 0) + goto exit; + + t = wait_event_timeout(cd->wait_q, + (cd->hid_reset_cmd_state == 0), + msecs_to_jiffies(PT_HID_CMD_DEFAULT_TIMEOUT)); + if (IS_TMO(t)) { +#ifdef TTDL_DIAGNOSTICS + cd->bus_transmit_error_count++; + pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS); +#endif /* TTDL_DIAGNOSTICS */ + pt_debug(cd->dev, DL_ERROR, "%s: reset timed out\n", + __func__); + rc = -ETIME; + goto exit; + } + +exit: + return rc; +} + +/* + * touch default parameters (from report descriptor) to resolve protocol for + * touch report + */ +const struct pt_tch_abs_params tch_hdr_default[PT_TCH_NUM_HDR] = { + /* byte offset, size, min, max, bit offset, report */ + {0x00, 0x02, 0x00, 0x10000, 0x00, 0x01}, /* SCAN TIME */ + {0x02, 0x01, 0x00, 0x20, 0x00, 0x01}, /* NUMBER OF RECORDS */ + {0x02, 0x01, 0x00, 0x02, 0x05, 0x01}, /* LARGE OBJECT */ + {0x03, 0x01, 0x00, 0x08, 0x00, 0x01}, /* NOISE EFFECT */ + {0x03, 0x01, 0x00, 0x04, 0x06, 0x01}, /* REPORT_COUNTER */ +}; + +/* + * button default parameters (from report descriptor) to resolve protocol for + * button report + */ +const struct pt_tch_abs_params tch_abs_default[PT_TCH_NUM_ABS] = { + /* byte offset, size, min, max, bit offset, report */ + {0x02, 0x02, 0x00, 0x10000, 0x00, 0x01}, /* X */ + {0x04, 0x02, 0x00, 0x10000, 0x00, 0x01}, /* Y */ + {0x06, 0x01, 0x00, 0x100, 0x00, 0x01}, /* P (Z) */ + {0x01, 0x01, 0x00, 0x20, 0x00, 0x01}, /* TOUCH ID */ + {0x01, 0x01, 0x00, 0x04, 0x05, 0x01}, /* EVENT ID */ + {0x00, 0x01, 0x00, 0x08, 0x00, 0x01}, /* OBJECT ID */ + {0x01, 0x01, 0x00, 0x02, 0x07, 0x01}, /* LIFTOFF */ + {0x07, 0x01, 0x00, 0x100, 0x00, 0x01}, /* TOUCH_MAJOR */ + {0x08, 0x01, 0x00, 0x100, 0x00, 0x01}, /* TOUCH_MINOR */ + {0x09, 0x01, 0x00, 0x100, 0x00, 0x01}, /* ORIENTATION */ +}; + +/******************************************************************************* + * FUNCTION: pt_init_pip_report_fields + * + * SUMMARY: Setup default values for touch/button report parsing. + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static void pt_init_pip_report_fields(struct pt_core_data *cd) +{ + struct pt_sysinfo *si = &cd->sysinfo; + + memcpy(si->tch_hdr, tch_hdr_default, sizeof(tch_hdr_default)); + memcpy(si->tch_abs, tch_abs_default, sizeof(tch_abs_default)); + + si->desc.tch_report_id = PT_PIP_TOUCH_REPORT_ID; + si->desc.tch_record_size = TOUCH_REPORT_SIZE; + si->desc.tch_header_size = TOUCH_INPUT_HEADER_SIZE; + si->desc.btn_report_id = PT_PIP_CAPSENSE_BTN_REPORT_ID; + si->desc.pen_report_id = PT_HID_PEN_REPORT_ID; + si->desc.max_touch_num = MAX_TOUCH_NUM; + + cd->features.easywake = 1; + cd->features.noise_metric = 1; + cd->features.tracking_heatmap = 1; + cd->features.sensor_data = 1; +} + +/******************************************************************************* + * FUNCTION: pt_get_hid_report_ + * + * SUMMARY: Get or create report. Must be called with cd->hid_report_lock + * acquired. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + * *index - pointer to report index + * report_type - type of hid report + * report_id - id of hid report + * create - true: will create a new report + * false: will not create a new report + ******************************************************************************/ +static int pt_get_hid_report_(struct pt_core_data *cd, u8 *index, + u8 report_type, u8 report_id, bool create) +{ + struct pt_hid_report *report = NULL; + int i; + int rc = 0; + + /* Look for created reports */ + for (i = 0; i < cd->num_hid_reports; i++) { + if (cd->hid_reports[i]->type == report_type && + cd->hid_reports[i]->id == report_id) { + *index = i; + goto exit; + } + } + + if (create && cd->num_hid_reports >= PT_HID_MAX_REPORTS) { + pt_debug(cd->dev, DL_WARN, + "%s: num_hid_reports=%d max=%d\n", __func__, + cd->num_hid_reports, PT_HID_MAX_REPORTS); + rc = -EINVAL; + } else if (create && cd->num_hid_reports < PT_HID_MAX_REPORTS) { + /* Create a new report */ + report = kzalloc(sizeof(struct pt_hid_report), + GFP_KERNEL); + if (!report) + rc = -ENOMEM; + else { + report->type = report_type; + report->id = report_id; + *index = cd->num_hid_reports; + cd->hid_reports[cd->num_hid_reports++] = report; + } + } + +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_free_hid_reports_ + * + * SUMMARY: Free HID report. Must be called with cd->hid_report_lock acquired. + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static void pt_free_hid_reports_(struct pt_core_data *cd) +{ + struct pt_hid_report *report; + int i, j; + + for (i = 0; i < cd->num_hid_reports; i++) { + report = cd->hid_reports[i]; + for (j = 0; j < report->num_fields; j++) + kfree(report->fields[j]); + kfree(report); + cd->hid_reports[i] = NULL; + } + + cd->num_hid_reports = 0; +} + +/******************************************************************************* + * FUNCTION: pt_free_hid_reports + * + * SUMMARY: Protected call to pt_free_hid_reports_() by a mutex lock. + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static void pt_free_hid_reports(struct pt_core_data *cd) +{ + mutex_lock(&cd->hid_report_lock); + pt_free_hid_reports_(cd); + mutex_unlock(&cd->hid_report_lock); +} + +/******************************************************************************* + * FUNCTION: pt_create_hid_field_ + * + * SUMMARY: Create field for HID report.Must be called with cd->hid_report_lock + * acquired. + * + * RETURN: + * pointer to hid field structure + * + * PARAMETERS: + * *report - pointer to hid report structure + ******************************************************************************/ +static struct pt_hid_field *pt_create_hid_field_( + struct pt_hid_report *report) +{ + struct pt_hid_field *field; + + if (!report) + return NULL; + + if (report->num_fields == PT_HID_MAX_FIELDS) + return NULL; + + field = kzalloc(sizeof(struct pt_hid_field), GFP_KERNEL); + if (!field) + return NULL; + + field->report = report; + + report->fields[report->num_fields++] = field; + + return field; +} + +/******************************************************************************* + * FUNCTION: get_hid_item_data + * + * SUMMARY: Get hid item data according to the item size. + * + * RETURN: + * 0 = no data + * !0 = data + * + * PARAMETERS: + * *data - pointer to item data + * item_size - the size of the item + ******************************************************************************/ +static inline int get_hid_item_data(u8 *data, int item_size) +{ + if (item_size == 1) + return (int)*data; + else if (item_size == 2) + return (int)get_unaligned_le16(data); + else if (item_size == 4) + return (int)get_unaligned_le32(data); + return 0; +} + +/******************************************************************************* + * FUNCTION: parse_report_descriptor + * + * SUMMARY: Parse report descriptor. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + * *report_desc - pointer to report descriptor structure + * len - length of the data buffer to be parsed + ******************************************************************************/ +static int parse_report_descriptor(struct pt_core_data *cd, + u8 *report_desc, size_t len) +{ + struct pt_hid_report *report; + struct pt_hid_field *field; + u8 *buf = report_desc; + u8 *end = buf + len; + int rc = 0; + int offset = 0; + int i, j; + u8 report_type; + u32 up_usage; + /* Global items */ + u8 report_id = 0; + u16 usage_page = 0; + int report_count = 0; + int report_size = 0; + int logical_min = 0; + int logical_max = 0; + /* Local items */ + u16 usage = 0; + /* Main items - Collection stack */ + u32 collection_usages[PT_HID_MAX_NESTED_COLLECTIONS] = {0}; + u8 collection_types[PT_HID_MAX_NESTED_COLLECTIONS] = {0}; + u32 usages_pen[PT_HID_MAX_CONTINUOUS_USAGES] = {0}; + /* First collection for header, second for report */ + int logical_collection_count = 0; + int app_collection_count = 0; + int collection_nest = 0; + int usage_cnt = 0; + u8 report_index = 0; + + pt_debug(cd->dev, DL_DEBUG, "%s: Report descriptor length: %u\n", + __func__, (u32)len); + + mutex_lock(&cd->hid_report_lock); + pt_free_hid_reports_(cd); + + while (buf < end) { + int item_type; + int item_size; + int item_tag; + u8 *data; + + /* Get Item */ + item_size = HID_GET_ITEM_SIZE(buf[0]); + if (item_size == 3) + item_size = 4; + item_type = HID_GET_ITEM_TYPE(buf[0]); + item_tag = HID_GET_ITEM_TAG(buf[0]); + + data = ++buf; + buf += item_size; + + /* Process current item */ + switch (item_type) { + case HID_ITEM_TYPE_GLOBAL: /* 1 */ + switch (item_tag) { + case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: /* 0 */ + if (item_size == 0 || item_size == 4) { + rc = -EINVAL; + goto exit; + } + usage_page = (u16)get_hid_item_data(data, + item_size); + break; + case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: /* 1 */ + if (item_size == 0) { + rc = -EINVAL; + goto exit; + } + logical_min = get_hid_item_data(data, + item_size); + break; + case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: /* 2 */ + if (item_size == 0) { + rc = -EINVAL; + goto exit; + } + logical_max = get_hid_item_data(data, + item_size); + break; + case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: /* 3 */ + pt_debug(cd->dev, DL_INFO, + "%s: TAG Ignored - Physical Min\n", + __func__); + break; + case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: /* 4 */ + pt_debug(cd->dev, DL_INFO, + "%s: TAG Ignored - Physical Max\n", + __func__); + break; + case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: /* 5 */ + pt_debug(cd->dev, DL_INFO, + "%s: TAG Ignored - Unit Exponent\n", + __func__); + break; + case HID_GLOBAL_ITEM_TAG_UNIT: /* 6 */ + pt_debug(cd->dev, DL_INFO, + "%s: TAG Ignored - Unit\n", + __func__); + break; + case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: /* 7 */ + if (item_size == 0) { + rc = -EINVAL; + goto exit; + } + report_size = get_hid_item_data(data, + item_size); + break; + case HID_GLOBAL_ITEM_TAG_REPORT_ID: /* 8 */ + if (item_size != 1) { + rc = -EINVAL; + goto exit; + } + report_id = get_hid_item_data(data, item_size); + offset = 0; + logical_collection_count = 0; + break; + case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: /* 9 */ + if (item_size == 0) { + rc = -EINVAL; + goto exit; + } + report_count = get_hid_item_data(data, + item_size); + break; + case HID_GLOBAL_ITEM_TAG_PUSH: /* A */ + pt_debug(cd->dev, DL_INFO, + "%s: TAG Ignored - Push\n", + __func__); + break; + case HID_GLOBAL_ITEM_TAG_POP: /* B */ + pt_debug(cd->dev, DL_INFO, + "%s: TAG Ignored - Pop\n", + __func__); + break; + default: + pt_debug(cd->dev, DL_INFO, + "%s: Unrecognized Global tag 0x%X\n", + __func__, item_tag); + } + break; + case HID_ITEM_TYPE_LOCAL: /* 2 */ + switch (item_tag) { + case HID_LOCAL_ITEM_TAG_USAGE: + if (item_size == 0 || item_size == 4) { + rc = -EINVAL; + goto exit; + } + usage = (u16)get_hid_item_data(data, + item_size); + if (report_id == PT_HID_PEN_REPORT_ID) { + usages_pen[usage_cnt++] = + usage_page << 16 | usage; + } + break; + case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: + if (item_size == 0) { + rc = -EINVAL; + goto exit; + } + /* usage_min = */ + get_hid_item_data(data, item_size); + break; + case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: + if (item_size == 0) { + rc = -EINVAL; + goto exit; + } + /* usage_max = */ + get_hid_item_data(data, item_size); + break; + default: + pt_debug(cd->dev, DL_INFO, + "%s: Unrecognized Local tag %d\n", + __func__, item_tag); + } + break; + case HID_ITEM_TYPE_MAIN: /* 0 */ + switch (item_tag) { + case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: + usage_cnt = 0; + + if (item_size != 1) { + rc = -EINVAL; + goto exit; + } + if (PT_HID_MAX_NESTED_COLLECTIONS == + collection_nest) { + rc = -EINVAL; + goto exit; + } + + up_usage = usage_page << 16 | usage; + + /* Update collection stack */ + collection_usages[collection_nest] = up_usage; + collection_types[collection_nest] = + get_hid_item_data(data, item_size); + + if (collection_types[collection_nest] == + HID_COLLECTION_LOGICAL) { + logical_collection_count++; + app_collection_count = 0; + } + if (collection_types[collection_nest] == + HID_COLLECTION_APPLICATION) + app_collection_count++; + + collection_nest++; + break; + case HID_MAIN_ITEM_TAG_END_COLLECTION: + if (item_size != 0) { + rc = -EINVAL; + goto exit; + } + if (--collection_nest < 0) { + rc = -EINVAL; + goto exit; + } + break; + case HID_MAIN_ITEM_TAG_INPUT: + report_type = HID_INPUT_REPORT; + goto continue_main_item; + case HID_MAIN_ITEM_TAG_OUTPUT: + report_type = HID_OUTPUT_REPORT; + goto continue_main_item; + case HID_MAIN_ITEM_TAG_FEATURE: + report_type = HID_FEATURE_REPORT; +continue_main_item: + if (item_size != 1) { + pt_debug(cd->dev, DL_WARN, + "%s: %s=%d\n", + __func__, + "item_size", item_size); + rc = -EINVAL; + goto exit; + } + + up_usage = usage_page << 16 | usage; + + /* Get or create report */ + rc = pt_get_hid_report_(cd, &report_index, + report_type, report_id, true); + if (rc) { + pt_debug(cd->dev, DL_WARN, + "%s: %s rc=%d\n", + __func__, + "get_hid_report failed", rc); + goto exit; + } else + report = cd->hid_reports[report_index]; + + if (!report->usage_page && collection_nest > 0) + report->usage_page = + collection_usages + [collection_nest - 1]; + + if (report->id == PT_HID_PEN_REPORT_ID + && report_count > 1) + goto continue_pen_usages; + + /* Create field */ + field = pt_create_hid_field_(report); + if (!field) { + pt_debug(cd->dev, DL_WARN, + "%s: %s\n", + __func__, + "create field failed"); + rc = -ENOMEM; + goto exit; + } + + field->report_count = report_count; + field->report_size = report_size; + field->size = report_count + * report_size; + field->offset = offset; + field->data_type = + get_hid_item_data(data, + item_size); + field->logical_min = logical_min; + field->logical_max = logical_max; + field->usage_page = up_usage; + usages_pen[0] = 0; + for (j = 0; j < collection_nest; j++) { + field->collection_usage_pages + [collection_types[j]] = + collection_usages[j]; + } + + goto exit_main_item; +continue_pen_usages: + for (i = 0; i < report_count; i++) { + /* Create field */ + field = pt_create_hid_field_( + report); + if (!field) { + pt_debug(cd->dev, DL_WARN, + "%s: Pen - %s\n", + __func__, + "create field failed"); + rc = -ENOMEM; + goto exit; + } + + field->report_size = report_size; + field->size = report_count * + report_size; + field->report_count = 1; + field->offset = offset + i*report_size; + field->data_type = + get_hid_item_data(data, + item_size); + field->logical_min = logical_min; + field->logical_max = logical_max; + field->usage_page = usages_pen[i]; + usages_pen[i] = 0; + for (j = 0; j < collection_nest; j++) { + field->collection_usage_pages + [collection_types[j]] = + collection_usages[j]; + } + } + +exit_main_item: + /* Update report's header or record size */ + if (app_collection_count == 1) { + report->header_size += field->size; + } else if (logical_collection_count == 1) { + field->record_field = true; + field->offset -= report->header_size; + /* Set record field index */ + if (report->record_field_index == 0) + report->record_field_index = + report->num_fields - 1; + report->record_size += field->size; + } + + report->size += field->size; + report->log_collection_num = + logical_collection_count; + + offset += field->size; + + usage_cnt = 0; + break; + default: + pt_debug(cd->dev, DL_INFO, + "%s: Unrecognized Main tag %d\n", + __func__, item_tag); + } + + /* Reset all local items */ + usage = 0; + break; + } + } + + if (buf != end) { + pt_debug(cd->dev, DL_ERROR, + "%s: Report descriptor length invalid\n", + __func__); + rc = -EINVAL; + goto exit; + } + + if (collection_nest) { + pt_debug(cd->dev, DL_ERROR, + "%s: Unbalanced collection items (%d)\n", + __func__, collection_nest); + rc = -EINVAL; + goto exit; + } + +exit: + if (rc) + pt_free_hid_reports_(cd); + mutex_unlock(&cd->hid_report_lock); + return rc; +} + +/******************************************************************************* + * FUNCTION: find_report_desc_field + * + * SUMMARY: Find the corresponding field from report according to the usage page + * and collection usage page. + * + * RETURN: + * pointer to hid field structure + * + * PARAMETERS: + * *cd - pointer to core data structure + * usage_page - hid usage page + * collection_usage_page - hid collection usage page + * collection_type - hid collection type + ******************************************************************************/ +static struct pt_hid_field *find_report_desc_field( + struct pt_core_data *cd, u32 usage_page, + u32 collection_usage_page, u8 collection_type) +{ + struct pt_hid_report *report = NULL; + struct pt_hid_field *field = NULL; + int i; + int j; + u32 field_cup; + u32 field_up; + + for (i = 0; i < cd->num_hid_reports; i++) { + report = cd->hid_reports[i]; + for (j = 0; j < report->num_fields; j++) { + field = report->fields[j]; + field_cup = field->collection_usage_pages + [collection_type]; + field_up = field->usage_page; + if (field_cup == collection_usage_page + && field_up == usage_page) { + return field; + } + } + } + + return NULL; +} + +/******************************************************************************* + * FUNCTION: fill_tch_abs + * + * SUMMARY: Fill touch abs with hid field. + * + * PARAMETERS: + * *cd - pointer to core data structure + * *field - pointer to hid field structure + ******************************************************************************/ +static void fill_tch_abs(struct pt_tch_abs_params *tch_abs, + struct pt_hid_field *field) +{ + tch_abs->ofs = field->offset / 8; + tch_abs->size = field->report_size / 8; + if (field->report_size % 8) + tch_abs->size += 1; + tch_abs->min = 0; + tch_abs->max = 1 << field->report_size; + tch_abs->bofs = field->offset - (tch_abs->ofs << 3); + tch_abs->logical_max = field->logical_max; +} + +/******************************************************************************* + * FUNCTION: find_report_desc + * + * SUMMARY: Find out the corresponding report based on the usage page. + * + * RETURN: + * pointer to hid report structure + * + * PARAMETERS: + * *cd - pointer to core data structure + * id - report id + * usage_page - hid usage page + ******************************************************************************/ +static struct pt_hid_report *find_report_desc(struct pt_core_data *cd, + u8 id, u32 usage_page) +{ + struct pt_hid_report *report = NULL; + int i; + + for (i = 0; i < cd->num_hid_reports; i++) { + if (cd->hid_reports[i]->usage_page == usage_page && + cd->hid_reports[i]->id == id) { + report = cd->hid_reports[i]; + break; + } + } + + return report; +} + +/******************************************************************************* + * FUNCTION: setup_pen_report_from_report_desc + * + * SUMMARY: Setup values for pen report according to report descriptor. + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static void setup_pen_report_from_report_desc( + struct pt_core_data *cd) +{ + struct pt_sysinfo *si = &cd->sysinfo; + struct pt_hid_report *report; + struct pt_hid_field *field; + int i; + u32 pen_collection_usage_page = HID_PT_PEN_COL_USAGE_PG; + u8 id = PT_HID_PEN_REPORT_ID; + + /* + * Search each pen abs field. If found, fill the values into the + * pen struct.If not, mark this pen field as invalid (report = 0). + */ + for (i = PT_PEN_X; i < PT_PEN_NUM_ABS; i++) { + field = find_report_desc_field(cd, + pt_pen_abs_field_map[i], + pen_collection_usage_page, + HID_COLLECTION_PHYSICAL); + if (field) { + pt_debug(cd->dev, DL_DEBUG, + " Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n", + field, field->report_count, field->report_size, + field->offset, field->data_type, + field->logical_min, field->logical_max, + field->usage_page); + fill_tch_abs(&si->pen_abs[i], field); + si->pen_abs[i].report = 2; + pt_debug(cd->dev, DL_DEBUG, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d", + pt_pen_abs_string[i], + (u32)si->pen_abs[i].ofs, + (u32)si->pen_abs[i].size, + (u32)si->pen_abs[i].min, + (u32)si->pen_abs[i].max, + (u32)si->pen_abs[i].bofs, + si->pen_abs[i].report); + + } else { + si->pen_abs[i].report = 0; + pt_debug(cd->dev, DL_DEBUG, "%s: report:%d\n", + pt_pen_abs_string[i], si->pen_abs[i].report); + } + } + + report = find_report_desc(cd, id, pen_collection_usage_page); + if (report) + si->desc.pen_report_id = report->id; + else + si->desc.pen_report_id = PT_HID_PEN_REPORT_ID; +} + +/******************************************************************************* + * FUNCTION: setup_finger_report_from_report_desc + * + * SUMMARY: Setup values for finger report according to report descriptor. + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static void setup_finger_report_from_report_desc( + struct pt_core_data *cd) +{ + struct pt_sysinfo *si = &cd->sysinfo; + struct pt_hid_report *report; + struct pt_hid_field *field; + u32 tch_collection_usage_page = HID_PT_TCH_COL_USAGE_PG; + u8 id = PT_HID_FINGER_REPORT_ID; + int i; + + /* + * Search each touch abs field. If found, fill the values into the + * abs struct. If not, mark this abs field as invalid (report = 0). + */ + for (i = PT_TCH_X; i < PT_TCH_NUM_ABS; i++) { + field = find_report_desc_field(cd, + pt_tch_abs_field_map[i], + tch_collection_usage_page, + HID_COLLECTION_APPLICATION); + if (field) { + pt_debug(cd->dev, DL_DEBUG, + " Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n", + field, field->report_count, field->report_size, + field->offset, field->data_type, + field->logical_min, field->logical_max, + field->usage_page); + fill_tch_abs(&si->tch_abs[i], field); + si->tch_abs[i].report = 1; + pt_debug(cd->dev, DL_DEBUG, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d", + pt_tch_abs_string[i], + (u32)si->tch_abs[i].ofs, + (u32)si->tch_abs[i].size, + (u32)si->tch_abs[i].min, + (u32)si->tch_abs[i].max, + (u32)si->tch_abs[i].bofs, + si->tch_abs[i].report); + } else { + si->tch_abs[i].report = 0; + } + } + + /* + * Search each touch header field. If found, fill the values into + * the header struct. If not, mark this header field as invalid + * (report = 0). + */ + for (i = PT_TCH_TIME; i < PT_TCH_NUM_HDR; i++) { + field = find_report_desc_field(cd, + pt_tch_hdr_field_map[i], + tch_collection_usage_page, + HID_COLLECTION_APPLICATION); + if (field) { + pt_debug(cd->dev, DL_DEBUG, + " Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n", + field, field->report_count, field->report_size, + field->offset, field->data_type, + field->logical_min, field->logical_max, + field->usage_page); + fill_tch_abs(&si->tch_hdr[i], field); + si->tch_hdr[i].report = 1; + pt_debug(cd->dev, DL_DEBUG, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d", + pt_tch_hdr_string[i], + (u32)si->tch_hdr[i].ofs, + (u32)si->tch_hdr[i].size, + (u32)si->tch_hdr[i].min, + (u32)si->tch_hdr[i].max, + (u32)si->tch_hdr[i].bofs, + si->tch_hdr[i].report); + } else { + si->tch_hdr[i].report = 0; + } + } + + si->desc.max_touch_num = si->tch_hdr[PT_TCH_NUM].max; + + report = find_report_desc(cd, id, tch_collection_usage_page); + if (report) { + si->desc.tch_report_id = report->id; + /* First, report->record_size and report->header_size are based + * on 'BIT', they need to be divided by 8 to get tch_record_size + * and tch_header_size, which are based on 'BYTE'. + * Second, tch_header_size needs to add 3 bytes: 2 bytes length, + * 1 byte report id. + */ + si->desc.tch_record_size = report->record_size / 8; + si->desc.tch_header_size = (report->header_size / 8) + 3; + si->desc.max_tch_per_packet = report->log_collection_num; + } else { + si->desc.tch_report_id = PT_HID_FINGER_REPORT_ID; + si->desc.tch_record_size = TOUCH_REPORT_SIZE; + si->desc.tch_header_size = TOUCH_INPUT_HEADER_SIZE; + si->desc.max_tch_per_packet = MAX_TOUCH_NUM; + } + + pt_debug(cd->dev, DL_DEBUG, + "%s: tch_record_size:%d, tch_header_size:%d, max_touch_num:%d\n", + __func__, + si->desc.tch_record_size, + si->desc.tch_header_size, + si->desc.max_touch_num); +} + +/******************************************************************************* + * FUNCTION: publish_report_desc + * + * SUMMARY: If TTHE_TUNER_SUPPORT is defined print the report descriptor data + * into the tthe_tuner sysfs node under the label "HIDRptDesc". + * + * PARAMETERS: + * *cd - pointer to core data + * len - Report descriptor length + ******************************************************************************/ +static void publish_report_desc(struct pt_core_data *cd, u16 len) +{ + int max_bytes = 300; + int pr_bytes = 0; + int size = len; + +#ifdef TTHE_TUNER_SUPPORT + tthe_print(cd, cd->response_buf, size, + "HIDRptDesc="); +#endif + + while (size > max_bytes) { + pt_pr_buf(cd->dev, DL_DEBUG, + cd->response_buf + pr_bytes, + max_bytes, "<<< HIDRptDesc"); + pr_bytes += max_bytes; + size -= max_bytes; + } + if (size > 0) + pt_pr_buf(cd->dev, DL_DEBUG, + cd->response_buf + pr_bytes, + size, "<<< HIDRptDesc"); +} + +/******************************************************************************* + * FUNCTION: pt_get_report_descriptor_ + * + * SUMMARY: Get and parse report descriptor. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_get_report_descriptor_(struct pt_core_data *cd) +{ + struct device *dev = cd->dev; + struct pt_hid_cmd hid_cmd = { + .descriptor = cd->hid_desc.report_desc_register, + .read_length = cd->hid_core.hid_report_desc_len, + }; + int rc; + int t; + u8 *desc; + u16 desc_len; + + rc = pt_hid_send_command(cd, &hid_cmd); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: failed to get HID report descriptor length, rc=%d\n", + __func__, rc); + goto exit; + } + + desc = cd->response_buf; + desc_len = cd->hid_core.hid_report_desc_len; + + /* Remove length field and report id to prepare parse work */ + if (cd->dut_status.protocol_mode != PT_PROTOCOL_MODE_HID) { + desc = cd->response_buf + 3; + desc_len -= 3; + } + + publish_report_desc(cd, cd->hid_core.hid_report_desc_len); + + rc = parse_report_descriptor(cd, desc, desc_len); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error parsing report descriptor rc=%d\n", + __func__, rc); + } + + pt_debug(cd->dev, DL_INFO, "%s: %d reports found in descriptor\n", + __func__, cd->num_hid_reports); + + for (t = 0; t < cd->num_hid_reports; t++) { + struct pt_hid_report *report = cd->hid_reports[t]; + int j; + + pt_debug(cd->dev, DL_DEBUG, + "Report %d: type:%d id:%02X size:%d fields:%d rec_fld_index:%d hdr_sz:%d rec_sz:%d usage_page:%08X\n", + t, report->type, report->id, + report->size, report->num_fields, + report->record_field_index, report->header_size, + report->record_size, report->usage_page); + + if (report->id == PT_HID_FINGER_REPORT_ID) + pt_debug(cd->dev, DL_INFO, "%s: logical collection number: %d\n", + __func__, report->log_collection_num); + + for (j = 0; j < report->num_fields; j++) { + struct pt_hid_field *field = report->fields[j]; + + pt_debug(cd->dev, DL_DEBUG, + " Field %d: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n", + j, field->report_count, field->report_size, + field->offset, field->data_type, + field->logical_min, field->logical_max, + field->usage_page); + + pt_debug(cd->dev, DL_DEBUG, " Collections Phys:%08X App:%08X Log:%08X\n", + field->collection_usage_pages + [HID_COLLECTION_PHYSICAL], + field->collection_usage_pages + [HID_COLLECTION_APPLICATION], + field->collection_usage_pages + [HID_COLLECTION_LOGICAL]); + } + } + + setup_pen_report_from_report_desc(cd); + setup_finger_report_from_report_desc(cd); + + /* Free it for now */ + pt_free_hid_reports_(cd); + + pt_debug(cd->dev, DL_INFO, "%s: %d reports found in descriptor\n", + __func__, cd->num_hid_reports); + +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_get_report_descriptor + * + * SUMMARY: Protected call to pt_get_report_descriptor_() + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_get_report_descriptor(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_get_report_descriptor_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_get_mode + * + * SUMMARY: Determine the current mode from the contents of a HID descriptor + * message + * + * RETURN: Enum of the current mode + * + * PARAMETERS: + * *cd - pointer to the Core Data structure + * protect - run command in protected mode + * *mode - pointer to store the retrieved mode + ******************************************************************************/ +static int pt_get_mode(struct pt_core_data *cd, struct pt_hid_desc *desc) +{ + if (desc->packet_id == HID_APP_REPORT_ID) + return PT_MODE_OPERATIONAL; + else if (desc->packet_id == HID_BL_REPORT_ID) + return PT_MODE_BOOTLOADER; + + return PT_MODE_UNKNOWN; +} + +/******************************************************************************* + * FUNCTION: _pt_request_get_mode + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to determine the current mode of the DUT by use of the Get HID + * Descriptor command. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - run command in protected mode + * *mode - pointer to store the retrieved mode + ******************************************************************************/ +static int _pt_request_get_mode(struct device *dev, int protect, u8 *mode) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_hid_desc hid_desc; + int rc = 0; + + memset(&hid_desc, 0, sizeof(hid_desc)); + if (protect) + rc = pt_get_hid_descriptor(cd, &hid_desc); + else + rc = pt_get_hid_descriptor_(cd, &hid_desc); + + if (rc) + *mode = PT_MODE_UNKNOWN; + else + *mode = pt_get_mode(cd, &hid_desc); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_queue_enum_ + * + * SUMMARY: Queues a TTDL enum by scheduling work with the pt_enum_with_dut() + * function. It won't try to add/delete sysfs node or modules. + * + * RETURN: void + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static void pt_queue_enum_(struct pt_core_data *cd) +{ + if (cd->startup_state == STARTUP_NONE) { + cd->startup_state = STARTUP_QUEUED; + +#ifdef TTDL_DIAGNOSTICS + if (!cd->bridge_mode) + schedule_work(&cd->enum_work); + else + cd->startup_state = STARTUP_NONE; +#else + schedule_work(&cd->enum_work); +#endif + pt_debug(cd->dev, DL_INFO, + "%s: enum_work queued\n", __func__); + } else { + pt_debug(cd->dev, DL_WARN, + "%s: Enum not queued - startup_state = %d\n", + __func__, cd->startup_state); + } +} + +/******************************************************************************* + * FUNCTION: pt_queue_enum + * + * SUMMARY: Queues a TTDL enum within a mutex lock + * + * RETURN: void + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static void pt_queue_enum(struct pt_core_data *cd) +{ + mutex_lock(&cd->system_lock); + pt_queue_enum_(cd); + mutex_unlock(&cd->system_lock); +} + +static void remove_sysfs_and_modules(struct device *dev); +/******************************************************************************* + * FUNCTION: pt_queue_restart + * + * SUMMARY: Queues a TTDL restart within a mutex lock + * + * RETURN: void + * + * PARAMETERS: + * *cd - pointer to core data + * remove_sysfs_module - True: remove all DUT relative sysfs nodes and modules + * False: will not perform remove action + ******************************************************************************/ +static void pt_queue_restart(struct pt_core_data *cd) +{ + mutex_lock(&cd->system_lock); + if (cd->startup_state == STARTUP_NONE) { + cd->startup_state = STARTUP_QUEUED; + + schedule_work(&cd->ttdl_restart_work); + pt_debug(cd->dev, DL_INFO, + "%s: pt_ttdl_restart queued\n", __func__); + } else { + pt_debug(cd->dev, DL_INFO, "%s: startup_state = %d\n", + __func__, cd->startup_state); + } + mutex_unlock(&cd->system_lock); +} + +/******************************************************************************* + * FUNCTION: call_atten_cb + * + * SUMMARY: Iterate over attention list call the function that registered. + * + * RETURN: void + * + * PARAMETERS: + * *cd - pointer to core data + * type - type of attention list + * mode - condition for execution + ******************************************************************************/ +static void call_atten_cb(struct pt_core_data *cd, + enum pt_atten_type type, int mode) +{ + struct atten_node *atten, *atten_n; + + pt_debug(cd->dev, DL_DEBUG, "%s: check list type=%d mode=%d\n", + __func__, type, mode); + spin_lock(&cd->spinlock); + list_for_each_entry_safe(atten, atten_n, + &cd->atten_list[type], node) { + if (!mode || atten->mode & mode) { + spin_unlock(&cd->spinlock); + pt_debug(cd->dev, DL_DEBUG, + "%s: attention for '%s'", + __func__, dev_name(atten->dev)); + atten->func(atten->dev); + spin_lock(&cd->spinlock); + } + } + spin_unlock(&cd->spinlock); +} + +/******************************************************************************* + * FUNCTION: start_fw_upgrade + * + * SUMMARY: Calling "PT_ATTEN_LOADER" attention list that loader registered to + * start firmware upgrade. + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *data - pointer to core data + ******************************************************************************/ +static int start_fw_upgrade(void *data) +{ + struct pt_core_data *cd = (struct pt_core_data *)data; + + call_atten_cb(cd, PT_ATTEN_LOADER, 0); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_put_device_into_easy_wakeup_ + * + * SUMMARY: Call the enter_easywake_state function and set the device into easy + * wake up state. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_put_device_into_easy_wakeup_(struct pt_core_data *cd) +{ + int rc = 0; + u8 status = 0; + + mutex_lock(&cd->system_lock); + cd->wait_until_wake = 0; + mutex_unlock(&cd->system_lock); + + rc = pt_hid_output_enter_easywake_state_(cd, + cd->easy_wakeup_gesture, &status); + if (rc || status == 0) + return -EBUSY; + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_put_device_into_deep_sleep_ + * + * SUMMARY: Call the set_power function and set the DUT to deep sleep + * + * RETURN: + * 0 = success + * !0 = error + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_put_device_into_deep_sleep_(struct pt_core_data *cd) +{ + int rc = 0; + + rc = pt_hid_cmd_set_power_(cd, HID_POWER_SLEEP); + if (rc) + rc = -EBUSY; + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_put_device_into_deep_standby_ + * + * SUMMARY: Call the set_power function and set the DUT to Deep Standby + * + * RETURN: + * 0 = success + * !0 = error + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_put_device_into_deep_standby_(struct pt_core_data *cd) +{ + int rc = 0; + + rc = pt_hid_cmd_set_power_(cd, HID_POWER_STANDBY); + if (rc) + rc = -EBUSY; + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_core_poweroff_device_ + * + * SUMMARY: Disable IRQ and HW power down the device. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_core_poweroff_device_(struct pt_core_data *cd) +{ + int rc; + + if (cd->irq_enabled) { + cd->irq_enabled = false; + disable_irq_nosync(cd->irq); + } + + rc = cd->cpdata->power(cd->cpdata, 0, cd->dev, 0); + if (rc < 0) + pt_debug(cd->dev, DL_ERROR, "%s: HW Power down fails r=%d\n", + __func__, rc); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_core_sleep_ + * + * SUMMARY: Suspend the device with power off or deep sleep based on the + * configuration in the core platform data structure. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_core_sleep_(struct pt_core_data *cd) +{ + int rc = 0; + + /* + * Do nothing if system already sleeping or in progress of + * entering sleep. Proceed if awake or waking. + */ + if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_SLEEPING) + goto exit; + + mutex_lock(&cd->system_lock); + cd->sleep_state = SS_SLEEPING; + mutex_unlock(&cd->system_lock); + + /* Ensure watchdog and startup works stopped */ + pt_stop_wd_timer(cd); + cancel_work_sync(&cd->enum_work); + pt_stop_wd_timer(cd); + + if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) + rc = pt_put_device_into_easy_wakeup_(cd); + else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) + rc = pt_core_poweroff_device_(cd); + else if (cd->cpdata->flags & PT_CORE_FLAG_DEEP_STANDBY) + rc = pt_put_device_into_deep_standby_(cd); + else + rc = pt_put_device_into_deep_sleep_(cd); + + mutex_lock(&cd->system_lock); + if (rc == 0) { + cd->sleep_state = SS_SLEEP_ON; + } else { + cd->sleep_state = SS_SLEEP_OFF; + pt_start_wd_timer(cd); + } + mutex_unlock(&cd->system_lock); + +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_core_sleep + * + * SUMMARY: Protected call to pt_core_sleep_ by exclusive access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_core_sleep(struct pt_core_data *cd) +{ + int rc = 0; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_core_sleep_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + else + pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n", + __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_wakeup_host + * + * SUMMARY: Check wake up report and call the PT_ATTEN_WAKE attention list. + * + * NOTE: TSG5 EasyWake and TSG6 EasyWake use different protocol. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_wakeup_host(struct pt_core_data *cd) +{ +#ifndef EASYWAKE_TSG6 + /* TSG5 EasyWake */ + int rc = 0; + int event_id; + int size = get_unaligned_le16(&cd->input_buf[0]); + + /* Validate report */ + if (size != 4 || cd->input_buf[2] != 4) + rc = -EINVAL; + + event_id = cd->input_buf[3]; + + pt_debug(cd->dev, DL_INFO, "%s: e=%d, rc=%d\n", + __func__, event_id, rc); + + if (rc) { + pt_core_sleep_(cd); + goto exit; + } + + /* attention WAKE */ + call_atten_cb(cd, PT_ATTEN_WAKE, 0); +exit: + return rc; +#else + /* TSG6 FW1.3 EasyWake */ + int rc = 0; + int i = 0; + int report_length; + + /* Validate report */ + if (cd->input_buf[2] != PT_PIP_WAKEUP_REPORT_ID) { + rc = -EINVAL; + pt_core_sleep_(cd); + goto exit; + } + + /* Get gesture id and gesture data length */ + cd->gesture_id = cd->input_buf[3]; + report_length = (cd->input_buf[1] << 8) | (cd->input_buf[0]); + cd->gesture_data_length = report_length - 4; + + pt_debug(cd->dev, DL_INFO, + "%s: gesture_id = %d, gesture_data_length = %d\n", + __func__, cd->gesture_id, cd->gesture_data_length); + + for (i = 0; i < cd->gesture_data_length; i++) + cd->gesture_data[i] = cd->input_buf[4 + i]; + + /* attention WAKE */ + call_atten_cb(cd, PT_ATTEN_WAKE, 0); +exit: + return rc; +#endif +} + +/******************************************************************************* + * FUNCTION: pt_get_touch_field + * + * SUMMARY: Function to calculate touch fields. The field refers to each element + * in the input report, such as "Number of Records", "X-axis". This function + * can calculate the value of element based on the bit offset, size and the + * max value of the element. + * + * PARAMETERS: + * *dev - pointer to device structure + * *field - pointer to field calculation result + * size - size in bytes + * max - max value of result + * *data - pointer to input data to be parsed + * bofs - bit offset + ******************************************************************************/ +void pt_get_touch_field(struct device *dev, + int *field, int size, int max, u8 *data, int bofs) +{ + int nbyte; + int next; + + for (nbyte = 0, *field = 0, next = 0; nbyte < size; nbyte++) { + pt_debug(dev, DL_DEBUG, + "%s: *field=%02X(%d) size=%d max=%08X data=%p data[%d]=%02X(%d) bofs=%d\n", + __func__, *field, *field, size, max, data, next, + data[next], data[next], bofs); + *field = *field + ((data[next] >> bofs) << (nbyte * 8)); + next++; + } + + if (max > 0) + *field &= max - 1; + + pt_debug(dev, DL_DEBUG, + "%s: *field=%02X(%d) size=%d max=%08X data=%p data[%d]=%02X(%d)\n", + __func__, *field, *field, size, max, data, next, + data[next], data[next]); +} + +/******************************************************************************* + * FUNCTION: move_tracking_heatmap_data + * + * SUMMARY: Move the valid tracking heatmap data from the input buffer into the + * system information structure, xy_mode and xy_data. + * - If TTHE_TUNER_SUPPORT is defined print the raw sensor data into + * the tthe_tuner sysfs node under the label "THM" + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *si - pointer to the system information structure + ******************************************************************************/ +static int move_tracking_heatmap_data(struct pt_core_data *cd, + struct pt_sysinfo *si) +{ +#ifdef TTHE_TUNER_SUPPORT + int size = get_unaligned_le16(&cd->input_buf[0]); + + if (size) + tthe_print(cd, cd->input_buf, size, "THM="); +#endif + memcpy(si->xy_mode, cd->input_buf, SENSOR_HEADER_SIZE); + return 0; +} + +/******************************************************************************* + * FUNCTION: move_sensor_data + * + * SUMMARY: Move the valid sensor data from the input buffer into the system + * information structure, xy_mode and xy_data. + * - If TTHE_TUNER_SUPPORT is defined print the raw sensor data into + * the tthe_tuner sysfs node under the label "sensor_monitor" + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *si - pointer to the system information structure + ******************************************************************************/ +static int move_sensor_data(struct pt_core_data *cd, + struct pt_sysinfo *si) +{ +#ifdef TTHE_TUNER_SUPPORT + int size = get_unaligned_le16(&cd->input_buf[0]); + + if (size) + tthe_print(cd, cd->input_buf, size, "sensor_monitor="); +#endif + memcpy(si->xy_mode, cd->input_buf, SENSOR_HEADER_SIZE); + return 0; +} + +/******************************************************************************* + * FUNCTION: move_button_data + * + * SUMMARY: Move the valid button data from the input buffer into the system + * information structure, xy_mode and xy_data. + * - If TTHE_TUNER_SUPPORT is defined print the raw button data into + * the tthe_tuner sysfs node under the label "OpModeData" + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *si - pointer to the system information structure + ******************************************************************************/ +static int move_button_data(struct pt_core_data *cd, + struct pt_sysinfo *si) +{ +#ifdef TTHE_TUNER_SUPPORT + int size = get_unaligned_le16(&cd->input_buf[0]); + + if (size) + tthe_print(cd, cd->input_buf, size, "OpModeData="); +#endif + memcpy(si->xy_mode, cd->input_buf, BTN_INPUT_HEADER_SIZE); + pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_mode, BTN_INPUT_HEADER_SIZE, + "xy_mode"); + + memcpy(si->xy_data, &cd->input_buf[BTN_INPUT_HEADER_SIZE], + BTN_REPORT_SIZE); + pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_data, BTN_REPORT_SIZE, + "xy_data"); + return 0; +} + +/******************************************************************************* + * FUNCTION: move_touch_data_pip + * + * SUMMARY: Move the valid touch data from the input buffer into the system + * information structure, xy_mode and xy_data. + * - If TTHE_TUNER_SUPPORT is defined print the raw touch data into + * the tthe_tuner sysfs node under the label "OpModeData" + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *si - pointer to the system information structure + ******************************************************************************/ +static int move_touch_data_pip(struct pt_core_data *cd, struct pt_sysinfo *si) +{ + int max_tch = si->sensing_conf_data.max_tch; + int num_cur_tch = 0; + int length; + int actual_tch_num; + struct pt_tch_abs_params *tch = &si->tch_hdr[PT_TCH_NUM]; + + int size = get_unaligned_le16(&cd->input_buf[0]); +#ifdef TTHE_TUNER_SUPPORT + if (size > 0) + tthe_print(cd, cd->input_buf, size, "OpModeData="); +#endif + + memcpy(si->xy_mode, cd->input_buf, si->desc.tch_header_size); + pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_mode, + si->desc.tch_header_size, "xy_mode"); + + pt_get_touch_field(cd->dev, &num_cur_tch, tch->size, + tch->max, si->xy_mode + 3 + tch->ofs, tch->bofs); + if (unlikely(num_cur_tch > max_tch)) + num_cur_tch = max_tch; + + actual_tch_num = (size - si->desc.tch_header_size) + / si->desc.tch_record_size; + + if (actual_tch_num < num_cur_tch) { + pt_debug(cd->dev, DL_ERROR, + "%s: ATM - Malformed touch packet. actual_tch_num=%d, num_cur_tch=%d\n", + __func__, actual_tch_num, num_cur_tch); + num_cur_tch = actual_tch_num; + } + + length = num_cur_tch * si->desc.tch_record_size; + + memcpy(si->xy_data, &cd->input_buf[si->desc.tch_header_size], length); + pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_data, length, "xy_data"); + return 0; +} + +/******************************************************************************* + * FUNCTION: move_touch_data_hid + * + * SUMMARY: Move the valid touch data from the input buffer into the system + * information structure, xy_mode and xy_data. + * - If TTHE_TUNER_SUPPORT is defined print the raw touch data into + * the tthe_tuner sysfs node under the label "HID-USB", "HID-I2C" or + * "OpModeData" + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *si - pointer to the system information structure + ******************************************************************************/ +static int move_touch_data_hid(struct pt_core_data *cd, + struct pt_sysinfo *si) +{ + int max_tch = si->sensing_conf_data.max_tch; + int num_cur_tch = 0; + u16 hdr_sz; + u16 rec_sz; + int max_tch_per_packet; + static u8 remain_tch; + static u8 packet_no; + static u8 input_sz; + int length; + int rc = 0; + struct pt_tch_abs_params *tch = &si->tch_hdr[PT_TCH_NUM]; + int size = get_unaligned_le16(&cd->input_buf[0]); + + hdr_sz = si->desc.tch_header_size; + rec_sz = si->desc.tch_record_size; + max_tch_per_packet = si->desc.max_tch_per_packet; + +#ifdef TTHE_TUNER_SUPPORT + length = hdr_sz + rec_sz * max_tch_per_packet; + pt_debug(cd->dev, DL_DEBUG, + "%s: touch report size=%d, len=%d, record=%d, max_tch=%d\n", + __func__, size, length, rec_sz, max_tch_per_packet); + /* + * HID over USB does not require the two byte length field, so + * this should print from input_buf[2] but to keep both finger + * and pen reports the same the length is included + */ + if (cd->tthe_hid_usb_format == PT_TTHE_TUNER_FORMAT_HID_USB) + tthe_print(cd, &(cd->input_buf[2]), length - 2, + "HID-USB="); + else if (cd->tthe_hid_usb_format == + PT_TTHE_TUNER_FORMAT_HID_I2C) + tthe_print(cd, &(cd->input_buf[0]), length, + "HID-I2C="); +#endif + + memcpy(si->xy_mode, cd->input_buf, hdr_sz); + pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_mode, + hdr_sz, "xy_mode"); + + /* + * The fifth parameter points to the location of "Number of Records": + * Pointer "xy_mode" points to start address of touch report header. + * The byte offset of "Number of Records" is 2 (tch->ofs, retrieved + * from report descriptor). Then the pointer "xy_mode + 3 (skip the + * two bytes length and 1 byte report ID) + tch->ofs" points to the + * location of "Number of Records". + */ + pt_get_touch_field(cd->dev, &num_cur_tch, tch->size, + tch->max, si->xy_mode + 3 + tch->ofs, tch->bofs); + + if (unlikely(num_cur_tch > max_tch)) + num_cur_tch = max_tch; + + /* According to Hybrid Mode touch report defined by Microsoft, + * if the touch number exceeds the max touch per packet (defined + * in report descriptor), the touch packet will be broken up into + * to multiple reports. Timestamp will be consistent across all + * touches in a single frame. The "Number of records" will have the + * total in the first report and be 0 in all subsequent reports that + * belong to the same frame. + */ + pt_debug(cd->dev, DL_INFO, "%s: max_tch=%d, packet_no=%d, num_cur_tch=%d\n", + __func__, max_tch, packet_no, num_cur_tch); + + if (packet_no == 0) { + input_sz = hdr_sz + num_cur_tch * rec_sz; + memset(cd->touch_buf, 0, sizeof(cd->touch_buf)); + memcpy(cd->touch_buf, cd->input_buf, input_sz); + if (num_cur_tch > max_tch_per_packet) { + remain_tch = num_cur_tch - max_tch_per_packet; + packet_no++; + rc = -EINVAL; + goto exit; + } + } else { + if (remain_tch <= max_tch_per_packet) { + memcpy(&cd->touch_buf[hdr_sz + + max_tch_per_packet * packet_no * rec_sz], + &(cd->input_buf[hdr_sz]), + rec_sz * remain_tch); + remain_tch = 0; + packet_no = 0; + rc = 0; + } else { + memcpy(&cd->touch_buf[hdr_sz + + max_tch_per_packet * packet_no * rec_sz], + &(cd->input_buf[hdr_sz]), + rec_sz * max_tch_per_packet); + remain_tch -= max_tch_per_packet; + packet_no++; + rc = -EINVAL; + goto exit; + } + } + +#ifdef TTHE_TUNER_SUPPORT + /* Update pip packet length */ + cd->touch_buf[0] = input_sz & 0xff; + cd->touch_buf[1] = (input_sz & 0xff00) >> 8; + + /* + * For PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP and + * PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP mode, all touches + * should be combined into a single row for the tthe_tuner node. + */ + if (cd->tthe_hid_usb_format == + PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP || + cd->tthe_hid_usb_format == + PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP) + tthe_print(cd, cd->touch_buf, input_sz, + "OpModeData="); +#endif + + memcpy(si->xy_data, &cd->touch_buf[hdr_sz], input_sz - hdr_sz); + pt_pr_buf(cd->dev, DL_INFO, + (u8 *)si->xy_data, input_sz - hdr_sz, "xy_data"); + + input_sz = 0; + +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: move_hid_pen_data + * + * SUMMARY: TODO Move the valid pen data from the input buffer into the system + * information structure, xy_mode and xy_data. + * - If TTHE_TUNER_SUPPORT is defined print the raw pen data into + * the tthe_tuner sysfs node under the label "HID" starting with the + * report ID. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *si - pointer to the system information structure + ******************************************************************************/ +static int move_hid_pen_data(struct pt_core_data *cd, struct pt_sysinfo *si) +{ + int size = get_unaligned_le16(&cd->input_buf[0]); + +#ifdef TTHE_TUNER_SUPPORT + enum pt_pen_abs abs; + struct pt_pen pen; + int packet_len = 17; + int report_id = 1; + static int report_counter; + int event_id = 2; + int touch_id = 0; + int i = 0; + + if (size) { + /* + * HID over USB does not require the two byte length field, so + * this should print from input_buf[2] but to keep both finger + * and pen reports the same the length is included + */ + if (cd->tthe_hid_usb_format == PT_TTHE_TUNER_FORMAT_HID_USB) + tthe_print(cd, &(cd->input_buf[2]), size - 2, + "HID-USB="); + else if (cd->tthe_hid_usb_format == + PT_TTHE_TUNER_FORMAT_HID_I2C || + cd->tthe_hid_usb_format == + PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP) + tthe_print(cd, &(cd->input_buf[0]), size, + "HID-I2C="); + else if (cd->tthe_hid_usb_format == + PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP) { + memset(cd->touch_buf, 0, sizeof(cd->touch_buf)); + + for (abs = PT_PEN_X; abs < PT_PEN_NUM_ABS; abs++) { + if (!si->pen_abs[abs].report) + continue; + pt_get_touch_field(cd->dev, &pen.abs[abs], + si->pen_abs[abs].size, + si->pen_abs[abs].max, + cd->input_buf + 3 + + si->pen_abs[abs].ofs, + si->pen_abs[abs].bofs); + pt_debug(cd->dev, DL_DEBUG, "%s: get %s=%04X(%d)\n", + __func__, pt_pen_abs_string[abs], + pen.abs[abs], pen.abs[abs]); + } + /* pip packet length: 17 */ + cd->touch_buf[i++] = packet_len & 0xff; + cd->touch_buf[i++] = (packet_len & 0xff00) >> 8; + /* pip finger report id: 1*/ + cd->touch_buf[i++] = report_id; + /* Timestamp: 0 */ + cd->touch_buf[i++] = 0; + cd->touch_buf[i++] = 0; + /* LO: 0; Number of Records: 1*/ + cd->touch_buf[i++] = 1; + /* Report Counter:[0-3]; Noise Effects:0 */ + cd->touch_buf[i++] = (report_counter & 0x03) << 6; + /* Touch Type: Stylus (2)*/ + cd->touch_buf[i++] = PT_OBJ_STYLUS; + /* Tip: pen tip switch; Event ID: 2; Touch ID: 0 */ + cd->touch_buf[i++] = + ((pen.abs[PT_PEN_TS] & 0x01) << 7) | + ((event_id & 0x03) << 5) | + (touch_id & 0x1f); + /* X: Pen X */ + cd->touch_buf[i++] = pen.abs[PT_PEN_X] & 0xff; + cd->touch_buf[i++] = (pen.abs[PT_PEN_X] & 0xff00) >> 8; + /* Y: Pen Y */ + cd->touch_buf[i++] = pen.abs[PT_PEN_Y] & 0xff; + cd->touch_buf[i++] = (pen.abs[PT_PEN_Y] & 0xff00) >> 8; + /* Pressure: Pen pressure drop the least 4 bits */ + cd->touch_buf[i++] = (pen.abs[PT_PEN_P] & 0xff0) >> 4; + /* Major: Pen Tilt_X*/ + cd->touch_buf[i++] = pen.abs[PT_PEN_X_TILT] & 0xff; + /* Minor: Pen Tilt_Y*/ + cd->touch_buf[i++] = pen.abs[PT_PEN_Y_TILT] & 0xff; + /* Orientation: Btn2, Btn1*/ + cd->touch_buf[i++] = + (pen.abs[PT_PEN_BS] & 0x01) | + ((pen.abs[PT_PEN_2ND_BS] & 0x01) << 1); + + if (++report_counter > 3) + report_counter = 0; + + tthe_print(cd, cd->touch_buf, packet_len, + "OpModeData="); + } + } +#endif + memcpy(si->xy_data, cd->input_buf, size); + pt_pr_buf(cd->dev, DL_INFO, (u8 *)&(cd->input_buf[0]), size, "HID Pen"); + return 0; +} + +/******************************************************************************* + * FUNCTION: parse_touch_input + * + * SUMMARY: Parse the touch report and take action based on the touch + * report_id. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * size - size of touch record + ******************************************************************************/ +static int parse_touch_input(struct pt_core_data *cd, int size) +{ + struct pt_sysinfo *si = &cd->sysinfo; + int report_id = cd->input_buf[2]; + int rc = -EINVAL; + + pt_debug(cd->dev, DL_DEBUG, "%s: Received touch report\n", + __func__); + if (!si->ready) { + pt_debug(cd->dev, DL_ERROR, + "%s: Need system information to parse touches\n", + __func__); + return 0; + } + + if (!si->xy_mode || !si->xy_data) + return rc; + + if (report_id == PT_PIP_TOUCH_REPORT_ID || + report_id == PT_HID_VS_FINGER_REPORT_ID) { + if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_PIP) + rc = move_touch_data_pip(cd, si); + else { + rc = move_touch_data_hid(cd, si); + if (rc) { + pt_debug(cd->dev, DL_INFO, + "%s: Skip report for the first touch packet\n", + __func__); + return 0; + } + } + } else if ((report_id == PT_HID_PEN_REPORT_ID || + report_id == PT_HID_VS_PEN_REPORT_ID) && + cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) + rc = move_hid_pen_data(cd, si); + else if (report_id == PT_PIP_CAPSENSE_BTN_REPORT_ID) + rc = move_button_data(cd, si); + else if (report_id == PT_PIP_SENSOR_DATA_REPORT_ID) + rc = move_sensor_data(cd, si); + else if (report_id == PT_PIP_TRACKING_HEATMAP_REPORT_ID) + rc = move_tracking_heatmap_data(cd, si); + + if (rc) + return rc; + + /* attention IRQ */ + call_atten_cb(cd, PT_ATTEN_IRQ, cd->mode); + + return 0; +} + +/******************************************************************************* + * FUNCTION: parse_command_input + * + * SUMMARY: Move the response data from the input buffer to the response buffer + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * size - size of response data + ******************************************************************************/ +static int parse_command_input(struct pt_core_data *cd, int size) +{ + pt_debug(cd->dev, DL_DEBUG, "%s: Received cmd interrupt\n", + __func__); + + memcpy(cd->response_buf, cd->input_buf, size); +#if defined(TTHE_TUNER_SUPPORT) && defined(TTDL_DIAGNOSTICS) + if (size && cd->show_tt_data) { + if (cd->pip2_prot_active) + tthe_print(cd, cd->input_buf, size, "TT_DATA_PIP2="); + else + tthe_print(cd, cd->input_buf, size, "TT_DATA="); + } +#endif + + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = 0; + mutex_unlock(&cd->system_lock); + wake_up(&cd->wait_q); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_allow_enumeration + * + * SUMMARY: Determine if an enumeration or fully re-probe should perform when + * FW sentinel is seen. + * + * RETURN: + * true = allow enumeration or fully re-probe + * false = skip enumeration and fully re-probe + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static inline bool pt_allow_enumeration(struct pt_core_data *cd) +{ + if ((cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) && + (!cd->hid_reset_cmd_state) && + (cd->core_probe_complete) && + (cd->hid_cmd_state != PIP1_CMD_ID_START_BOOTLOADER + 1) && + (cd->hid_cmd_state != PIP1_BL_CMD_ID_LAUNCH_APP + 1) && + (cd->mode == PT_MODE_OPERATIONAL)) { + return true; + } + + if ((!cd->hid_reset_cmd_state) && + (cd->core_probe_complete) && + (cd->hid_cmd_state != PIP1_CMD_ID_START_BOOTLOADER + 1) && + (cd->hid_cmd_state != PIP1_BL_CMD_ID_LAUNCH_APP + 1) && + (cd->active_dut_generation != DUT_PIP1_ONLY)) { + return true; + } + + pt_debug(cd->dev, DL_INFO, + "%s: Dissallow - %s=%d %s=%d %s=0x%02X %s=%d\n", + __func__, + "hid_reset_cmd_state(0)", cd->hid_reset_cmd_state, + "core_probe_complete(1)", cd->core_probe_complete, + "hid_cmd_state(Not 0x02 or 0x39)", cd->hid_cmd_state, + "active_dut_gen(0,2)", cd->active_dut_generation); + return false; +} + +/******************************************************************************* + * FUNCTION: pt_is_touch_report + * + * SUMMARY: Determine if a report ID should be treated as a touch report + * + * RETURN: + * true = report ID is a touch report + * false = report ID is not a touch report + * + * PARAMETERS: + * id - Report ID + ******************************************************************************/ +static bool pt_is_touch_report(int id) +{ + if (id == PT_PIP_TOUCH_REPORT_ID || + id == PT_HID_PEN_REPORT_ID || + id == PT_HID_VS_FINGER_REPORT_ID || + id == PT_HID_VS_PEN_REPORT_ID || + id == PT_PIP_CAPSENSE_BTN_REPORT_ID || + id == PT_PIP_SENSOR_DATA_REPORT_ID || + id == PT_PIP_TRACKING_HEATMAP_REPORT_ID) + return true; + else + return false; +} + +/******************************************************************************* + * FUNCTION: pt_parse_input + * + * SUMMARY: Parse the input data read from DUT due to IRQ. Handle data based + * on if its a response to a command or asynchronous touch data / reset + * sentinel. PIP2.x messages have additional error checking that is + * parsed (SEQ match from cmd to rsp, CRC valid). + * Look for special packets based on unique lengths: + * 0 bytes - APP(FW) reset sentinel or Gen5/6 BL sentinel + * 2 bytes - Empty buffer (PIP 1.7 and earlier) + * 11 bytes - possible PIP2.x reset sentinel (TAG and SEQ must = 0) + * 0xFFXX - Empty buffer (PIP 1.7+) + * Queue a startup after any asynchronous FW reset sentinel is seen, unless + * the initial probe has not yet been done. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_parse_input(struct pt_core_data *cd) +{ + int report_id = 0; + int cmd_id; + int is_command = 0; + int size; + int print_size; + bool touch_report = true; + unsigned short calc_crc; + unsigned short resp_crc; + + cd->fw_sys_mode_in_standby_state = false; + size = get_unaligned_le16(&cd->input_buf[0]); + print_size = size; + pt_debug(cd->dev, DL_DEBUG, "<<< %s: IRQ Triggered, read len [%d]\n", + __func__, print_size); + if (print_size <= PT_MAX_INPUT) + pt_pr_buf(cd->dev, DL_DEBUG, cd->input_buf, print_size, + "<<< Read buf"); + + if (size == 0 || + (size == 11 && + (cd->input_buf[PIP2_RESP_SEQUENCE_OFFSET] & + PIP2_RESP_SEQUENCE_MASK) == 0 && + (cd->input_buf[PIP2_RESP_REPORT_ID_OFFSET] & + PIP2_CMD_COMMAND_ID_MASK) == + PIP2_CMD_ID_STATUS)) { + touch_report = false; + cd->hw_detected = true; + cd->bl_pip_ver_ready = false; + cd->app_pip_ver_ready = false; + if (size == 0) { + mutex_lock(&cd->system_lock); + cd->pip2_prot_active = false; + if (cd->active_dut_generation == DUT_PIP1_ONLY) { + /* + * For Gen5/6 this sentinel could be from + * the BL or FW. Attempt to set the correct + * mode based on the previous PIP command. + */ + if (cd->hid_cmd_state == + PIP1_BL_CMD_ID_LAUNCH_APP + 1) { + cd->mode = PT_MODE_OPERATIONAL; + cd->startup_status = + STARTUP_STATUS_FW_RESET_SENTINEL; + } else if (cd->hid_cmd_state == + PIP1_CMD_ID_START_BOOTLOADER + 1 || + cd->hid_reset_cmd_state) { + cd->mode = PT_MODE_BOOTLOADER; + cd->startup_status = + STARTUP_STATUS_BL_RESET_SENTINEL; + } else { + cd->mode = PT_MODE_UNKNOWN; + cd->startup_status = + STARTUP_STATUS_START; + } + cd->fw_system_mode = FW_SYS_MODE_UNDEFINED; + + pt_debug(cd->dev, DL_INFO, + "%s: ATM Gen5/6 %s sentinel received\n", + __func__, + (cd->mode == PT_MODE_OPERATIONAL ? + "FW" : + (cd->mode == PT_MODE_BOOTLOADER ? + "BL" : "Unknown"))); + } else { + cd->mode = PT_MODE_OPERATIONAL; + cd->fw_system_mode = FW_SYS_MODE_BOOT; + cd->startup_status = + STARTUP_STATUS_FW_RESET_SENTINEL; + pt_debug(cd->dev, DL_INFO, + "%s: ATM PT/TT FW sentinel received\n", + __func__); + } + mutex_unlock(&cd->system_lock); + + if (pt_allow_enumeration(cd)) { + if (cd->active_dut_generation == DUT_UNKNOWN) { + pt_debug(cd->dev, DL_INFO, + "%s: Queue Restart\n", __func__); + pt_queue_restart(cd); + } else { + pt_debug(cd->dev, DL_INFO, + "%s: Queue Enum\n", __func__); + pt_queue_enum(cd); + } + } else { + pt_debug(cd->dev, DL_INFO, + "%s: Sentinel - No Queued Action\n", + __func__); + } + + } else { + /* Sentinel must be from TT/TC BL */ + mutex_lock(&cd->system_lock); + cd->pip2_prot_active = true; + cd->startup_status = STARTUP_STATUS_BL_RESET_SENTINEL; + cd->mode = PT_MODE_BOOTLOADER; + cd->sysinfo.ready = false; + mutex_unlock(&cd->system_lock); + pt_debug(cd->dev, DL_INFO, + "%s: BL Reset sentinel received\n", __func__); + if (cd->flashless_dut && + cd->flashless_auto_bl == PT_ALLOW_AUTO_BL) { + pt_debug(cd->dev, DL_INFO, + "%s: BL to RAM for flashless DUT\n", + __func__); + kthread_run(start_fw_upgrade, cd, "pt_loader"); + } + } + mutex_lock(&cd->system_lock); + memcpy(cd->response_buf, cd->input_buf, 2); + if (!cd->hid_reset_cmd_state && !cd->hid_cmd_state) { + mutex_unlock(&cd->system_lock); + pt_debug(cd->dev, DL_WARN, + "%s: Device Initiated Reset\n", __func__); + wake_up(&cd->wait_q); + return 0; + } + + cd->hid_reset_cmd_state = 0; + if (cd->hid_cmd_state == PIP1_CMD_ID_START_BOOTLOADER + 1 || + cd->hid_cmd_state == PIP1_BL_CMD_ID_LAUNCH_APP + 1 || + cd->hid_cmd_state == PIP1_CMD_ID_USER_CMD + 1) + cd->hid_cmd_state = 0; + wake_up(&cd->wait_q); + mutex_unlock(&cd->system_lock); + return 0; + } else if (size == 2 || size >= PT_PIP_1P7_EMPTY_BUF) { + /* + * This debug message below is used by PBATS to calculate the + * time from the last lift off IRQ to when FW exits LFT mode. + */ + touch_report = false; + pt_debug(cd->dev, DL_WARN, + "%s: DUT - Empty buffer detected\n", __func__); + return 0; + } else if (size > PT_MAX_INPUT) { + pt_debug(cd->dev, DL_ERROR, + "%s: DUT - Unexpected len field in active bus data!\n", + __func__); + return -EINVAL; + } + + if (cd->pip2_prot_active) { + pt_debug(cd->dev, DL_DEBUG, + "%s: Decode PIP2.x Response\n", __func__); + + /* PIP2 does not have a report id, hard code it */ + report_id = 0x00; + cmd_id = cd->input_buf[PIP2_RESP_COMMAND_ID_OFFSET]; + + calc_crc = crc_ccitt_calculate(cd->input_buf, size - 2); + resp_crc = cd->input_buf[size - 2] << 8; + resp_crc |= cd->input_buf[size - 1]; + + if ((cd->pip2_cmd_tag_seq != + (cd->input_buf[PIP2_RESP_SEQUENCE_OFFSET] & 0x0F)) && + (resp_crc != calc_crc) && + ((cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET] + == PT_PIP_TOUCH_REPORT_ID) || + (cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET] + == PT_PIP_CAPSENSE_BTN_REPORT_ID))) { + report_id = cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET]; + cmd_id = cd->input_buf[PIP1_RESP_COMMAND_ID_OFFSET]; + pt_debug(cd->dev, DL_INFO, + "%s: %s %d %s\n", __func__, + "Received PIP1 report id =", + report_id, + "when expecting a PIP2 report"); + } else { + is_command = 1; + touch_report = false; + } + } else { + report_id = cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET]; + cmd_id = cd->input_buf[PIP1_RESP_COMMAND_ID_OFFSET]; + } + +#ifdef TTDL_DIAGNOSTICS + pt_debug(cd->dev, DL_DEBUG, + "%s: pip2 = %d report_id: 0x%02X, cmd_code: 0x%02X\n", + __func__, cd->pip2_prot_active, report_id, + (cmd_id & PIP2_CMD_COMMAND_ID_MASK)); +#endif /* TTDL_DIAGNOSTICS */ + + if (report_id == PT_PIP_WAKEUP_REPORT_ID) { + pt_wakeup_host(cd); +#ifdef TTHE_TUNER_SUPPORT + tthe_print(cd, cd->input_buf, size, "TT_WAKEUP="); +#endif + return 0; + } + + mod_timer_pending(&cd->watchdog_timer, jiffies + + msecs_to_jiffies(cd->watchdog_interval)); + + /* + * If it is PIP2 response, the report_id has been set to 0, + * so it will not be parsed as a touch packet. + */ + if (!pt_is_touch_report(report_id)) { + is_command = 1; + touch_report = false; + } + + if (unlikely(is_command)) { + parse_command_input(cd, size); + return 0; + } + + if (touch_report) + parse_touch_input(cd, size); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_read_input + * + * SUMMARY: Reads incoming data off of the active bus + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_read_input(struct pt_core_data *cd) +{ + struct device *dev = cd->dev; + int rc = 0; + int t; + int retry = PT_BUS_READ_INPUT_RETRY_COUNT; + + /* + * Workaround for easywake failure + * Interrupt for easywake, wait for bus controller to wake + */ + mutex_lock(&cd->system_lock); + if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) { + if (cd->sleep_state == SS_SLEEP_ON) { + mutex_unlock(&cd->system_lock); + if (!dev->power.is_suspended) + goto read; + t = wait_event_timeout(cd->wait_q, + (cd->wait_until_wake == 1), + msecs_to_jiffies(2000)); +#ifdef TTDL_DIAGNOSTICS + if (IS_TMO(t)) { + cd->bus_transmit_error_count++; + pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS); + pt_debug(dev, DL_ERROR, + "%s: !!!I2C Transmission Error %d\n", + __func__, + cd->bus_transmit_error_count); + } +#else + if (IS_TMO(t)) + pt_queue_enum(cd); +#endif /* TTDL_DIAGNOSTICS */ + goto read; + } + } + mutex_unlock(&cd->system_lock); + +read: + memset(cd->input_buf, 0, sizeof(cd->input_buf)); + /* Try reading up to 'retry' times */ + while (retry-- != 0) { + rc = pt_adap_read_default_nosize(cd, cd->input_buf, + PT_MAX_INPUT); + if (!rc) { + pt_debug(dev, DL_DEBUG, + "%s: Read input successfully\n", __func__); + goto read_exit; + } + usleep_range(1000, 2000); + } + pt_debug(dev, DL_ERROR, + "%s: Error getting report, rc=%d\n", __func__, rc); + +read_exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_irq + * + * SUMMARY: Process all detected interrupts + * + * RETURN: + * IRQ_HANDLED - Finished processing the interrupt + * + * PARAMETERS: + * irq - IRQ number + * *handle - pointer to core data struct + ******************************************************************************/ +irqreturn_t pt_irq(int irq, void *handle) +{ + struct pt_core_data *cd = handle; + int rc = 0; + +#ifdef TTDL_PTVIRTDUT_SUPPORT + if (!cd->route_bus_virt_dut) { + if (!pt_check_irq_asserted(cd)) + return IRQ_HANDLED; + } +#else + if (!pt_check_irq_asserted(cd)) + return IRQ_HANDLED; +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + + rc = pt_read_input(cd); +#ifdef TTDL_DIAGNOSTICS + cd->irq_count++; + + /* Used to calculate T-Refresh */ + if (cd->t_refresh_active) { + if (cd->t_refresh_count == 0) { + cd->t_refresh_time = jiffies; + cd->t_refresh_count++; + } else if (cd->t_refresh_count < cd->t_refresh_total) { + cd->t_refresh_count++; + } else { + cd->t_refresh_active = 0; + cd->t_refresh_time = + jiffies_to_msecs(jiffies - cd->t_refresh_time); + } + } +#endif /* TTDL_DIAGNOSTICS */ + + if (!rc) + pt_parse_input(cd); + + return IRQ_HANDLED; +} + +#ifdef PT_AUX_BRIDGE_ENABLED +/******************************************************************************* + * FUNCTION: pt_trigger_ttdl_irq + * + * SUMMARY: API for other kernel drivers to artificially trigger a TTDL IRQ + * + * IMPROVE - dpmux should likely call pt_get_core_data once and make a + * copy of cd so that the function only needs to be called once. + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: none + * + ******************************************************************************/ +int pt_trigger_ttdl_irq(void) +{ + struct pt_core_data *cd; + char *core_name = PT_DEFAULT_CORE_ID; + + cd = pt_get_core_data(core_name); + if (!cd) { + pr_err("%s: No Device\n", __func__); + return -ENODEV; + } + pt_irq(cd->irq, (void *)cd); + return 0; +} +EXPORT_SYMBOL_GPL(pt_trigger_ttdl_irq); +#endif + +/******************************************************************************* + * FUNCTION: _pt_subscribe_attention + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to subscribe themselves into the TTDL attention list + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * type - attention type enum + * *id - ID of the module calling this function + * *func - callback function pointer to be called when notified + * mode - attention mode + ******************************************************************************/ +int _pt_subscribe_attention(struct device *dev, + enum pt_atten_type type, char *id, int (*func)(struct device *), + int mode) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct atten_node *atten, *atten_new; + + atten_new = kzalloc(sizeof(*atten_new), GFP_KERNEL); + if (!atten_new) + return -ENOMEM; + + pt_debug(cd->dev, DL_INFO, "%s from '%s'\n", __func__, + dev_name(cd->dev)); + + spin_lock(&cd->spinlock); + list_for_each_entry(atten, &cd->atten_list[type], node) { + if (atten->id == id && atten->mode == mode) { + spin_unlock(&cd->spinlock); + kfree(atten_new); + pt_debug(cd->dev, DL_INFO, "%s: %s=%p %s=%d\n", + __func__, + "already subscribed attention", + dev, "mode", mode); + + return 0; + } + } + + atten_new->id = id; + atten_new->dev = dev; + atten_new->mode = mode; + atten_new->func = func; + + list_add(&atten_new->node, &cd->atten_list[type]); + spin_unlock(&cd->spinlock); + + return 0; +} + +/******************************************************************************* + * FUNCTION: _pt_unsubscribe_attention + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to unsubscribe themselves from the TTDL attention list + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * type - attention type enum + * *id - ID of the module calling this function + * *func - function pointer + * mode - attention mode + ******************************************************************************/ +int _pt_unsubscribe_attention(struct device *dev, + enum pt_atten_type type, char *id, int (*func)(struct device *), + int mode) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct atten_node *atten, *atten_n; + + spin_lock(&cd->spinlock); + list_for_each_entry_safe(atten, atten_n, &cd->atten_list[type], node) { + if (atten->id == id && atten->mode == mode) { + list_del(&atten->node); + spin_unlock(&cd->spinlock); + kfree(atten); + pt_debug(cd->dev, DL_DEBUG, "%s: %s=%p %s=%d\n", + __func__, + "unsub for atten->dev", atten->dev, + "atten->mode", atten->mode); + return 0; + } + } + spin_unlock(&cd->spinlock); + + return -ENODEV; +} + +/******************************************************************************* + * FUNCTION: _pt_request_exclusive + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to request exclusive access + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * timeout_ms - timeout to wait for exclusive access + ******************************************************************************/ +static int _pt_request_exclusive(struct device *dev, + int timeout_ms) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + return request_exclusive(cd, (void *)dev, timeout_ms); +} + +/******************************************************************************* + * FUNCTION: _pt_release_exclusive + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to release exclusive access + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int _pt_release_exclusive(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + return release_exclusive(cd, (void *)dev); +} + +/******************************************************************************* + * FUNCTION: _pt_request_reset + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to request the DUT to be reset. Function returns err if refused or + * timeout occurs (Note: core uses fixed timeout period). + * + * NOTE: Function blocks until ISR occurs. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + ******************************************************************************/ +static int _pt_request_reset(struct device *dev, int protect) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int rc; + + rc = pt_dut_reset(cd, protect); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, "%s: Error on h/w reset r=%d\n", + __func__, rc); + } + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_enum + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to request TTDL to queue an enum. This function will return err + * if refused; if no error then enum has completed and system is in + * normal operation mode. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * wait - boolean to determine if to wait for startup event + ******************************************************************************/ +static int _pt_request_enum(struct device *dev, bool wait) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + pt_queue_enum(cd); + + if (wait) + wait_event_timeout(cd->wait_q, + cd->startup_state == STARTUP_NONE, + msecs_to_jiffies(PT_REQUEST_ENUM_TIMEOUT)); + + return 0; +} + +/******************************************************************************* + * FUNCTION: _pt_request_sysinfo + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to request the pointer to the system information structure. This + * function will return NULL if sysinfo has not been acquired from the + * DUT yet. + * + * RETURN: Pointer to the system information struct + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +struct pt_sysinfo *_pt_request_sysinfo(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int rc = 0; + + /* Attempt to get sysinfo if not ready and panel_id is from XY pin */ + if ((cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) && + !cd->sysinfo.ready) { + rc = pt_hid_output_get_sysinfo_(cd); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error getting sysinfo rc=%d\n", + __func__, rc); + } + } + + if (cd->sysinfo.ready) + return &cd->sysinfo; + + return NULL; +} + +/******************************************************************************* + * FUNCTION: _pt_request_loader_pdata + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to request the pointer to the loader platform data + * + * RETURN: Pointer to the loader platform data struct + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static struct pt_loader_platform_data *_pt_request_loader_pdata( + struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + return cd->pdata->loader_pdata; +} + +/******************************************************************************* + * FUNCTION: _pt_request_start_wd + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to request to start the TTDL watchdog + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int _pt_request_start_wd(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + pt_start_wd_timer(cd); + return 0; +} + +/******************************************************************************* + * FUNCTION: _pt_request_stop_wd + * + * SUMMARY: Function pointer included in core_cmds to allow other modules + * to request to stop the TTDL watchdog + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int _pt_request_stop_wd(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + pt_stop_wd_timer(cd); + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_launch_app + * + * SUMMARY: Sends the PIP2 EXECUTE command to launch the APP and then wait for + * the FW reset sentinel to indicate the function succeeded. + * + * NOTE: Calling this function when the DUT is in Application mode WILL result + * in a timeout delay and with the DUT being reset with an XRES. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + * protect - flag to call protected or non-protected + ******************************************************************************/ +static int pt_pip2_launch_app(struct device *dev, int protect) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u16 actual_read_len; + u16 tmp_startup_status = cd->startup_status; + u8 read_buf[12]; + u8 status; + int time = 0; + int rc = 0; + + mutex_lock(&cd->system_lock); + cd->startup_status = STARTUP_STATUS_START; + pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__); + mutex_unlock(&cd->system_lock); + + rc = _pt_request_pip2_send_cmd(dev, protect, + PIP2_CMD_ID_EXECUTE, NULL, 0, read_buf, + &actual_read_len); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: PIP2 EXECUTE cmd failed rc = %d\n", + __func__, rc); + } else { + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + + /* Test for no or invalid image in FLASH, no point to reset */ + if (status == PIP2_RSP_ERR_INVALID_IMAGE) { + rc = status; + goto exit; + } + + /* Any other boot failure */ + if (status != 0) { + pt_debug(dev, DL_ERROR, + "%s: FW did not EXECUTE, status = %d\n", + __func__, status); + rc = status; + } + } + + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Failed to launch APP, XRES DUT rc = %d\n", + __func__, rc); + goto exit; + } + + while ((cd->startup_status == STARTUP_STATUS_START) && time < 240) { + msleep(20); + pt_debug(cd->dev, DL_INFO, "%s: wait for %d for enum=0x%04X\n", + __func__, time, cd->startup_status); + time += 20; + } + if (cd->startup_status == STARTUP_STATUS_START) { + pt_debug(cd->dev, DL_WARN, + "%s: TMO waiting for FW reset sentinel\n", __func__); + rc = -ETIME; + } + +exit: + if (cd->startup_status == STARTUP_STATUS_START) { + /* Reset to original state because we could be stuck in BL */ + mutex_lock(&cd->system_lock); + cd->startup_status = tmp_startup_status; + mutex_unlock(&cd->system_lock); + } + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip2_launch_app + * + * SUMMARY: Calls pt_pip2_launch_app() when configured to. A small delay is + * inserted to ensure the reset has allowed the BL reset sentinel to be + * consumed. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + ******************************************************************************/ +static int _pt_request_pip2_launch_app(struct device *dev, int protect) +{ + return pt_pip2_launch_app(dev, protect); +} + +/******************************************************************************* + * FUNCTION: _pt_request_wait_for_enum_state + * + * SUMMARY: Loops for up to timeout waiting for the startup_status to reach + * the state passed in or STARTUP_STATUS_COMPLETE whichever comes first + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data structure + * timeout - timeout for how long to wait + * state - enum state to wait for + ******************************************************************************/ +static int _pt_request_wait_for_enum_state(struct device *dev, int timeout, + int state) +{ + int rc = 0; + int t; + struct pt_core_data *cd = dev_get_drvdata(dev); + + t = wait_event_timeout(cd->wait_q, + (cd->startup_status & state) || (cd->startup_status & 0x0100), + msecs_to_jiffies(timeout)); + if (IS_TMO(t)) { + pt_debug(cd->dev, DL_ERROR, + "%s: TMO waiting for enum state 0x%04X in %dms\n", + __func__, state, timeout); + pt_debug(cd->dev, DL_WARN, + "%s: enum state reached 0x%04X\n", + __func__, cd->startup_status); + rc = -ETIME; + } else if (cd->startup_status & state) { + pt_debug(cd->dev, DL_INFO, + "%s: Enum state reached: enum=0x%04X in %dms\n", + __func__, cd->startup_status, + (t == 1) ? timeout : (timeout - jiffies_to_msecs(t))); + } else { + if (t == 1) { + pt_debug( + cd->dev, DL_ERROR, + "%s: TMO waiting for enum state 0x%04X in %dms\n", + __func__, state, timeout); + rc = -ETIME; + } else { + pt_debug( + cd->dev, DL_ERROR, + "%s: Enum state 0x%04X not reached in %dms\n", + __func__, state, timeout - jiffies_to_msecs(t)); + rc = -EINVAL; + } + pt_debug(cd->dev, DL_INFO, "%s: enum state reached 0x%04X\n", + __func__, cd->startup_status); + } + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_core_wake_device_from_deep_sleep_ + * + * SUMMARY: Call the set_power function and set the DUT to wake up from + * deep sleep. + * + * RETURN: + * 0 = success + * !0 = error + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_core_wake_device_from_deep_sleep_( + struct pt_core_data *cd) +{ + int rc; + + rc = pt_hid_cmd_set_power_(cd, HID_POWER_ON); + if (rc) + rc = -EAGAIN; + + /* Prevent failure on sequential wake/sleep requests from OS */ + msleep(20); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_core_wake_device_from_easy_wake_ + * + * SUMMARY: Wake up device from Easy-Wake state. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_core_wake_device_from_easy_wake_(struct pt_core_data *cd) +{ + mutex_lock(&cd->system_lock); + cd->wait_until_wake = 1; + mutex_unlock(&cd->system_lock); + wake_up(&cd->wait_q); + msleep(20); + + return pt_core_wake_device_from_deep_sleep_(cd); +} + +/******************************************************************************* + * FUNCTION: pt_restore_parameters_ + * + * SUMMARY: This function sends all RAM parameters stored in the linked list + * back to the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer the core data structure + ******************************************************************************/ +static int pt_restore_parameters_(struct pt_core_data *cd) +{ + struct param_node *param; + int rc = 0; + + if (!(cd->cpdata->flags & PT_CORE_FLAG_RESTORE_PARAMETERS)) + goto exit; + + spin_lock(&cd->spinlock); + list_for_each_entry(param, &cd->param_list, node) { + spin_unlock(&cd->spinlock); + pt_debug(cd->dev, DL_INFO, "%s: Parameter id:%d value:%d\n", + __func__, param->id, param->value); + rc = pt_pip_set_param_(cd, param->id, + param->value, param->size); + if (rc) + goto exit; + spin_lock(&cd->spinlock); + } + spin_unlock(&cd->spinlock); +exit: + return rc; +} + + +/******************************************************************************* + * FUNCTION: pt_pip2_exit_bl_ + * + * SUMMARY: Attempt to exit the BL and run the application, taking into account + * a DUT that may not have flash and will need FW to be loaded into RAM + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer the core data structure + * *status_str - pointer to optional status string buffer + * buf_size - size of status_str buffer + ******************************************************************************/ +int pt_pip2_exit_bl_(struct pt_core_data *cd, u8 *status_str, int buf_size) +{ + int rc; + int wait_time = 0; + u8 mode = PT_MODE_UNKNOWN; + bool load_status_str = false; + + /* + * Below function has protective call to ensure no enum is still on + * going, while this kind of protection should be applied widely in + * future (TODO). + */ + rc = pt_pip2_get_mode_sysmode(cd, &mode, NULL); + + if (status_str && buf_size <= 50) + load_status_str = true; + + if (mode == PT_MODE_BOOTLOADER) { + if (cd->flashless_dut == 1) { + rc = pt_hw_hard_reset(cd); + } else { + rc = pt_pip2_launch_app(cd->dev, + PT_CORE_CMD_UNPROTECTED); + if (rc == PIP2_RSP_ERR_INVALID_IMAGE) { + pt_debug(cd->dev, DL_ERROR, "%s: %s = %d\n", + __func__, "Invalid image in FLASH rc", rc); + } else if (rc) { + pt_debug(cd->dev, DL_ERROR, "%s: %s = %d\n", + __func__, "Failed to launch app rc", rc); + } + } + + if (!rc) { + if (cd->flashless_dut == 1) { + /* Wait for BL to complete before enum */ + rc = _pt_request_wait_for_enum_state(cd->dev, + 4000, STARTUP_STATUS_FW_RESET_SENTINEL); + if (rc && load_status_str) { + strlcpy(status_str, + "No FW sentinel after BL", + sizeof(*status_str)*PT_STATUS_STR_LEN); + goto exit; + } + } + + /* + * If the host wants to interact with the FW or do a + * forced calibration, the FW must be out of BOOT mode + * and the system information must have been retrieved. + * Reaching the FW_OUT_OF_BOOT state guarantees both. + * If, however, the enumeration does not reach this + * point, the DUT may still be in APP mode so test + * for all conditions. + */ + rc = _pt_request_wait_for_enum_state(cd->dev, 4500, + STARTUP_STATUS_FW_OUT_OF_BOOT); + if (!rc || cd->startup_status >= + STARTUP_STATUS_FW_RESET_SENTINEL) { + mutex_lock(&cd->system_lock); + cd->mode = PT_MODE_OPERATIONAL; + mutex_unlock(&cd->system_lock); + } + if (rc) { + pt_debug(cd->dev, DL_WARN, "%s: %s: 0x%04X\n", + __func__, "Failed to enum with DUT", + cd->startup_status); + if (load_status_str && !(cd->startup_status & + STARTUP_STATUS_FW_OUT_OF_BOOT)) { + strlcpy(status_str, + "FW Stuck in Boot mode", + sizeof(*status_str)*PT_STATUS_STR_LEN); + goto exit; + } + } + + /* + * The coming FW sentinel could wake up the event + * queue, which has chance to be taken by next command + * wrongly. Following delay is a workaround to avoid + * this issue for most situations. + */ + msleep(20); + + pt_start_wd_timer(cd); + } + if (load_status_str) { + if (rc == PIP2_RSP_ERR_INVALID_IMAGE) + strlcpy(status_str, + "Failed - Invalid image in FLASH", + sizeof(*status_str)*PT_STATUS_STR_LEN); + else if (!rc) + strlcpy(status_str, + "Entered APP from BL mode", sizeof(*status_str)*PT_STATUS_STR_LEN); + else + strlcpy(status_str, + "Failed to enter APP from BL mode", + sizeof(*status_str)*PT_STATUS_STR_LEN); + } + } else if (mode == PT_MODE_OPERATIONAL) { + mutex_lock(&cd->system_lock); + cd->mode = mode; + mutex_unlock(&cd->system_lock); + rc = pt_poll_for_fw_exit_boot_mode(cd, 1500, &wait_time); + if (load_status_str) { + if (!rc) + strlcpy(status_str, + "Already in APP mode", + sizeof(*status_str)*PT_STATUS_STR_LEN); + else + strlcpy(status_str, + "Already in APP mode - FW stuck in Boot mode", + sizeof(*status_str)*PT_STATUS_STR_LEN); + } + } else if (rc || mode == PT_MODE_UNKNOWN) { + mutex_lock(&cd->system_lock); + cd->mode = mode; + mutex_unlock(&cd->system_lock); + if (load_status_str) + strlcpy(status_str, "Failed to determine active mode", + sizeof(*status_str)*PT_STATUS_STR_LEN); + } + +exit: + if (!rc) + pt_start_wd_timer(cd); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_exit_bl + * + * SUMMARY: Wrapper function for _pt_pip2_exit_bl that guarantees exclusive + * access. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer the core data structure + * *status_str - pointer to optional status string buffer + * buf_size - size of status_str buffer + ******************************************************************************/ +int pt_pip2_exit_bl(struct pt_core_data *cd, u8 *status_str, int buf_size) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", __func__, + cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_pip2_exit_bl_(cd, status_str, buf_size); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n", + __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _fast_startup + * + * SUMMARY: Perform fast startup after resume device by power on/off stratergy. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer the core data structure + ******************************************************************************/ +static int _fast_startup(struct pt_core_data *cd) +{ + int retry = PT_CORE_STARTUP_RETRY_COUNT; + int rc = 0; + u8 mode = PT_MODE_UNKNOWN; + struct pt_hid_desc hid_desc; + int wait_time = 0; + + memset(&hid_desc, 0, sizeof(hid_desc)); + +reset: + if (retry != PT_CORE_STARTUP_RETRY_COUNT) + pt_debug(cd->dev, DL_INFO, "%s: Retry %d\n", __func__, + PT_CORE_STARTUP_RETRY_COUNT - retry); + + if (cd->active_dut_generation == DUT_PIP1_ONLY) { + pt_debug(cd->dev, DL_INFO, "%s: PIP1 Enumeration start\n", + __func__); + pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN); + + rc = pt_get_hid_descriptor_(cd, &hid_desc); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on getting HID descriptor r=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + cd->mode = pt_get_mode(cd, &hid_desc); + + if (cd->mode == PT_MODE_BOOTLOADER) { + pt_debug(cd->dev, DL_INFO, "%s: Bootloader mode\n", + __func__); + rc = pt_hid_output_bl_launch_app_(cd); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on launch app r=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + rc = pt_get_hid_descriptor_(cd, &hid_desc); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on getting HID descriptor r=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + cd->mode = pt_get_mode(cd, &hid_desc); + if (cd->mode == PT_MODE_BOOTLOADER) { + if (retry--) + goto reset; + goto exit; + } + } + + cd->startup_status |= STARTUP_STATUS_GET_DESC; + cd->startup_status |= STARTUP_STATUS_FW_OUT_OF_BOOT; + } else { + pt_debug(cd->dev, DL_INFO, "%s: PIP2 Enumeration start\n", + __func__); + + if (retry == PT_CORE_STARTUP_RETRY_COUNT) { + /* Wait for any sentinel before first try */ + rc = _pt_request_wait_for_enum_state( + cd->dev, 150, + STARTUP_STATUS_BL_RESET_SENTINEL | + STARTUP_STATUS_FW_RESET_SENTINEL); + if (rc) + pt_debug(cd->dev, DL_ERROR, + "%s: No Sentinel detected rc = %d\n", + __func__, rc); + } else + pt_flush_bus_if_irq_asserted(cd, + PT_FLUSH_BUS_BASED_ON_LEN); + + rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Get mode failed, mode unknown\n", + __func__); + mode = PT_MODE_UNKNOWN; + } + cd->mode = mode; + + if (cd->mode == PT_MODE_BOOTLOADER) { + pt_debug(cd->dev, DL_INFO, "%s: Bootloader mode\n", + __func__); + rc = pt_pip2_exit_bl_(cd, NULL, 0); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s Failed to exit bootloader\n", + __func__); + msleep(50); + rc = -ENODEV; + if (retry--) + goto reset; + goto exit; + } else { + pt_debug(cd->dev, DL_INFO, + "%s: Exit bootloader successfully\n", + __func__); + } + + if (cd->mode != PT_MODE_OPERATIONAL) { + pt_debug(cd->dev, DL_WARN, + "%s: restore mode failure mode = %d\n", + __func__, cd->mode); + if (retry--) + goto reset; + goto exit; + } + } + cd->startup_status |= STARTUP_STATUS_GET_DESC; + } + + /* FW cannot handle most PIP cmds when it is still in BOOT mode */ + rc = _pt_poll_for_fw_exit_boot_mode(cd, 500, &wait_time); + if (!rc) { + cd->startup_status |= STARTUP_STATUS_FW_OUT_OF_BOOT; + pt_debug(cd->dev, DL_WARN, + "%s: Exit FW BOOT Mode after %dms\n", + __func__, wait_time); + } else { + pt_debug(cd->dev, DL_WARN, + "%s: FW stuck in BOOT Mode after %dms\n", + __func__, wait_time); + goto exit; + } + + if (!cd->sysinfo.ready) { + rc = pt_hid_output_get_sysinfo_(cd); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on getting sysinfo r=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + } + cd->startup_status |= STARTUP_STATUS_GET_SYS_INFO; + + rc = pt_restore_parameters_(cd); + if (rc) + pt_debug(cd->dev, DL_ERROR, + "%s: failed to restore parameters rc=%d\n", + __func__, rc); + else + cd->startup_status |= STARTUP_STATUS_RESTORE_PARM; + +exit: + cd->startup_status |= STARTUP_STATUS_COMPLETE; + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_core_poweron_device_ + * + * SUMMARY: Power on device, enable IRQ, and then perform a fast startup. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_core_poweron_device_(struct pt_core_data *cd) +{ + struct device *dev = cd->dev; + int rc = 0; + + /* + * After power on action, the chip can general FW sentinel. It can + * trigger an enumeration without hid_reset_cmd_state flag. Since the + * _fast_startup() can perform a quick enumeration too, here doesn't + * need another enumeration. + */ + mutex_lock(&cd->system_lock); + cd->startup_status = STARTUP_STATUS_START; + cd->hid_reset_cmd_state = 1; + mutex_unlock(&cd->system_lock); + + rc = cd->cpdata->power(cd->cpdata, 1, dev, 0); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: HW Power up fails r=%d\n", + __func__, rc); + goto exit; + } + + if (!cd->irq_enabled) { + cd->irq_enabled = true; + enable_irq(cd->irq); + } + + /* TBD: following function doesn't update startup_status */ + rc = _fast_startup(cd); +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_core_wake_device_from_deep_standby_ + * + * SUMMARY: Reset device, and then trigger a full enumeration. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_core_wake_device_from_deep_standby_(struct pt_core_data *cd) +{ + int rc; + + rc = pt_dut_reset_and_wait(cd); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, "%s: Error on h/w reset r=%d\n", + __func__, rc); + goto exit; + } + + rc = _fast_startup(cd); +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_core_wake_ + * + * SUMMARY: Resume the device with a power on or wake from deep sleep based on + * the configuration in the core platform data structure. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_core_wake_(struct pt_core_data *cd) +{ + int rc = 0; + + /* + * Do nothing if system already awake or in progress of waking. + * Proceed if sleeping or entering sleep state. + */ + if (cd->sleep_state == SS_SLEEP_OFF || cd->sleep_state == SS_WAKING) + goto exit; + + mutex_lock(&cd->system_lock); + cd->sleep_state = SS_WAKING; + mutex_unlock(&cd->system_lock); + + if (!(cd->cpdata->flags & PT_CORE_FLAG_SKIP_RESUME)) { + if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) + rc = pt_core_wake_device_from_easy_wake_(cd); + else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) + rc = pt_core_poweron_device_(cd); + else if (cd->cpdata->flags & PT_CORE_FLAG_DEEP_STANDBY) + rc = pt_core_wake_device_from_deep_standby_(cd); + else /* Default action to exit DeepSleep */ + rc = pt_core_wake_device_from_deep_sleep_(cd); + } + + mutex_lock(&cd->system_lock); + if (rc == 0) + cd->sleep_state = SS_SLEEP_OFF; + else + cd->sleep_state = SS_SLEEP_ON; + mutex_unlock(&cd->system_lock); + + pt_start_wd_timer(cd); + +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_core_wake + * + * SUMMARY: Protected call to pt_core_wake_ by exclusive access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_core_wake(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_core_wake_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n", + __func__); + else + pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n", + __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_get_ic_crc_ + * + * SUMMARY: This function retrieves the config block CRC + * + * NOTE: The post condition of calling this function will be that the DUT will + * be in SCANNINING mode if no failures occur + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer the core data structure + * ebid - enumerated block ID + ******************************************************************************/ +static int pt_get_ic_crc_(struct pt_core_data *cd, u8 ebid) +{ + struct pt_sysinfo *si = &cd->sysinfo; + int rc = 0; + u8 status; + u16 calculated_crc = 0; + u16 stored_crc = 0; + + rc = pt_pip_suspend_scanning_(cd); + if (rc) + goto error; + + rc = pt_pip_verify_config_block_crc_(cd, ebid, &status, + &calculated_crc, &stored_crc); + if (rc) + goto exit; + + if (status) { + rc = -EINVAL; + goto exit; + } + + si->ttconfig.crc = stored_crc; + +exit: + pt_pip_resume_scanning_(cd); +error: + pt_debug(cd->dev, DL_INFO, + "%s: CRC: ebid:%d, calc:0x%04X, stored:0x%04X, rc=%d\n", + __func__, ebid, calculated_crc, stored_crc, rc); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_read_gpio + * + * SUMMARY: Sends a PIP2 READ_GPIO command to the DUT and stores the 32 gpio + * bits into the passed in variable + * + * NOTE: PIP2 READ_GPIO command is only supported in bootloader + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *status - pointer to where the command response status is stored + * *gpio - pointer to device attributes structure + ******************************************************************************/ +static int pt_pip2_read_gpio(struct device *dev, u8 *status, u32 *gpio) +{ + int rc = 0; + u16 actual_read_len; + u8 read_buf[12]; + u8 tmp_status = 0; + u8 index = PIP2_RESP_STATUS_OFFSET; + + memset(read_buf, 0, ARRAY_SIZE(read_buf)); + rc = _pt_request_pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_READ_GPIO, + NULL, 0, read_buf, &actual_read_len); + + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Failed to send PIP2 READ_GPIO cmd\n", __func__); + rc = -ECOMM; + } else { + tmp_status = read_buf[index]; + } + + if (status) + *status = tmp_status; + + if (!rc && gpio && (tmp_status == 0)) { + *gpio = ((read_buf[index + 4] << 24) | + (read_buf[index + 3] << 16) | + (read_buf[index + 2] << 8) | + (read_buf[index + 1])); + } + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_get_panel_id_by_gpio + * + * SUMMARY: Wrapper function to call pt_pip2_read_gpio() to get panel ID + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer the core data structure + * *pid - pointer to store panel ID + ******************************************************************************/ +static int _pt_pip2_get_panel_id_by_gpio(struct pt_core_data *cd, u8 *pid) +{ + u32 gpio_value = 0; + u8 status = 0; + u8 panel_id = PANEL_ID_NOT_ENABLED; + int rc = 0; + + if (!pid) + return -ENOMEM; + + rc = pt_pip2_read_gpio(cd->dev, &status, &gpio_value); + if (!rc) { + if (status == 0) { + panel_id = (gpio_value & PT_PANEL_ID_BITMASK) >> + PT_PANEL_ID_SHIFT; + pt_debug(cd->dev, DL_INFO, "%s: %s=0x%02X %s=0x%08X\n", + __func__, + "BL mode PID", panel_id, "gpio", gpio_value); + *pid = panel_id; + } else { + pt_debug(cd->dev, DL_ERROR, "%s: %s=%d\n", + __func__, + "BL read gpio failed status", status); + } + } else { + pt_debug(cd->dev, DL_ERROR, "%s: %s=%d\n", + __func__, + "BL read gpio failed status", status); + } + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_enum_with_dut_ + * + * SUMMARY: This function does the full enumeration of the DUT with TTDL. + * The core data (cd) startup_status will store, as a bitmask, each + * state of the enumeration process. The startup will be attempted + * PT_CORE_STARTUP_RETRY_COUNT times before giving up. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer the core data structure + * reset - Flag to reset the DUT before attempting to enumerate + * *status - poionter to store the enum status bitmask flags + ******************************************************************************/ +static int pt_enum_with_dut_(struct pt_core_data *cd, bool reset, + u32 *enum_status) +{ + int try = 1; + int rc = 0; + int wait_time = 0; + bool detected = false; + u8 return_data[8]; + u8 mode = PT_MODE_UNKNOWN; + u8 protocol_mode = PT_PROTOCOL_MODE_PIP; + u8 pid = PANEL_ID_NOT_ENABLED; + u8 sys_mode = FW_SYS_MODE_UNDEFINED; + struct pt_hid_desc hid_desc; + + memset(&hid_desc, 0, sizeof(hid_desc)); +#ifdef TTHE_TUNER_SUPPORT + tthe_print(cd, NULL, 0, "enter startup"); +#endif + pt_debug(cd->dev, DL_INFO, "%s: Start enum... 0x%04X, reset=%d\n", + __func__, cd->startup_status, reset); + pt_stop_wd_timer(cd); + +reset: + if (try > 1) + pt_debug(cd->dev, DL_WARN, "%s: DUT Enum Attempt %d\n", + __func__, try); + pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN); + + if (cd->active_dut_generation == DUT_PIP1_ONLY) { + pt_debug(cd->dev, DL_INFO, + "%s: PIP1 Enumeration start\n", __func__); + + /* Only reset the DUT after the first try */ + if (reset || (try > 1)) { + /* + * Reset hardware only for Legacy parts. Skip for TT/TC + * parts because if the FW image was loaded directly + * to SRAM issueing a reset ill wipe out what was just + * loaded. + */ + rc = pt_dut_reset_and_wait(cd); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on h/w reset r=%d\n", + __func__, rc); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + /* sleep to allow FW to be launched if available */ + msleep(120); + } + + rc = pt_get_hid_descriptor_(cd, &hid_desc); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error getting HID Descriptor r=%d\n", + __func__, rc); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + rc = -EIO; + goto exit; + } + detected = true; + cd->mode = pt_get_mode(cd, &hid_desc); + + /* + * Most systems do not use an XY pin as the panel_id and so + * the BL is used to retrieve the panel_id, however for + * systems that do use an XY pin, the panel_id MUST be + * retrieved from the system information when running FW + * (done below) and so this section of code is skipped. + * Entering the BL here is only needed on XY_PIN systems. + */ + if (cd->panel_id_support & PT_PANEL_ID_BY_BL) { + if (cd->mode == PT_MODE_OPERATIONAL) { + rc = pt_pip_start_bootloader_(cd); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on start bootloader r=%d\n", + __func__, rc); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + cd->mode = PT_MODE_BOOTLOADER; + pt_debug(cd->dev, DL_INFO, + "%s: Bootloader mode\n", __func__); + } + + rc = pt_hid_output_bl_get_information_(cd, return_data); + if (!rc) { + cd->bl_info.ready = true; + cd->bl_info.chip_id = + get_unaligned_le16(&return_data[2]); + pt_debug(cd->dev, DL_INFO, "%s: chip ID %04X\n", + __func__, cd->bl_info.chip_id); + } else { + pt_debug(cd->dev, DL_ERROR, + "%s: failed to get chip ID, r=%d\n", + __func__, rc); + } + + rc = pt_hid_output_bl_get_panel_id_(cd, &pid); + mutex_lock(&cd->system_lock); + if (!rc) { + cd->pid_for_loader = pid; + pt_debug(cd->dev, DL_INFO, + "%s: Panel ID: 0x%02X\n", + __func__, cd->pid_for_loader); + } else { + cd->pid_for_loader = PANEL_ID_NOT_ENABLED; + pt_debug(cd->dev, DL_WARN, + "%s: Read Failed, disable Panel ID: 0x%02X\n", + __func__, cd->pid_for_loader); + } + mutex_unlock(&cd->system_lock); + } + + + /* Exit BL due to XY_PIN case or any other cause to be in BL */ + if (cd->mode == PT_MODE_BOOTLOADER) { + rc = pt_hid_output_bl_launch_app_(cd); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on launch app r=%d\n", + __func__, rc); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + rc = -ENODEV; + goto exit; + } + + /* sleep to allow FW to be launched if available */ + msleep(120); + + /* Ensure the DUT is now in Application mode */ + rc = pt_get_hid_descriptor_(cd, &hid_desc); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error getting HID Desc r=%d\n", + __func__, rc); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + rc = -EIO; + goto exit; + } + cd->mode = pt_get_mode(cd, &hid_desc); + if (cd->mode == PT_MODE_BOOTLOADER) { + pt_debug(cd->dev, DL_WARN, + "%s: Error confiming exit BL\n", + __func__); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + rc = -ENODEV; + goto exit; + } + } + pt_debug(cd->dev, DL_INFO, "%s: Operational mode\n", __func__); + cd->mode = PT_MODE_OPERATIONAL; + *enum_status |= STARTUP_STATUS_GET_DESC; + *enum_status |= STARTUP_STATUS_FW_OUT_OF_BOOT; + } else { + /* Generation is PIP2 Capable */ + pt_debug(cd->dev, DL_INFO, + "%s: PIP2 Enumeration start\n", __func__); + rc = pt_pip2_get_status_(cd); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Get mode failed, mode unknown\n", + __func__); + mode = PT_MODE_UNKNOWN; + } else { + mode = cd->dut_status.mode; + detected = true; + } + + cd->mode = mode; + + switch (cd->mode) { + case PT_MODE_OPERATIONAL: + pt_debug(cd->dev, DL_INFO, + "%s: Operational mode\n", __func__); + protocol_mode = cd->dut_status.protocol_mode; + if (cd->app_pip_ver_ready == false) { + rc = pt_pip2_get_version_(cd); + if (!rc) + cd->app_pip_ver_ready = true; + else { + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + } + break; + case PT_MODE_BOOTLOADER: + pt_debug(cd->dev, DL_INFO, + "%s: Bootloader mode\n", __func__); + if (cd->panel_id_support & PT_PANEL_ID_BY_BL) { + rc = _pt_pip2_get_panel_id_by_gpio(cd, &pid); + mutex_lock(&cd->system_lock); + if (!rc) { + cd->pid_for_loader = pid; + pt_debug(cd->dev, DL_INFO, + "%s: Panel ID: 0x%02X\n", + __func__, cd->pid_for_loader); + } else { + cd->pid_for_loader = + PANEL_ID_NOT_ENABLED; + pt_debug(cd->dev, DL_WARN, + "%s: Read Failed, disable Panel ID: 0x%02X\n", + __func__, cd->pid_for_loader); + } + mutex_unlock(&cd->system_lock); + } + + if (cd->bl_pip_ver_ready == false) { + rc = pt_pip2_get_version_(cd); + if (!rc) + cd->bl_pip_ver_ready = true; + else { + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + } + + /* + * Launch app command will fail in flashless mode. + * Skip launch app command here to save time for + * enumeration flow. + */ + if (cd->flashless_dut) + goto exit; + + /* + * pt_pip2_launch_app() is needed here instead of + * pt_pip2_exit_bl() because exit_bl will cause several + * hw_resets to occur and the auto BL on a flashless + * DUT will fail. + */ + rc = pt_pip2_launch_app(cd->dev, + PT_CORE_CMD_UNPROTECTED); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on launch app r=%d\n", + __func__, rc); + msleep(50); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + + /* + * IRQ thread can be delayed if the serial log print is + * enabled. It causes next command to get wrong response + * Here the delay is to ensure pt_parse_input() to be + * finished. + */ + msleep(60); + /* Check and update the mode */ + rc = pt_pip2_get_status_(cd); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Get mode failed, mode unknown\n", + __func__); + mode = PT_MODE_UNKNOWN; + } else + mode = cd->dut_status.mode; + cd->mode = mode; + if (cd->mode == PT_MODE_OPERATIONAL) { + pt_debug(cd->dev, DL_INFO, + "%s: Launched to Operational mode\n", + __func__); + protocol_mode = cd->dut_status.protocol_mode; + } else if (cd->mode == PT_MODE_BOOTLOADER) { + pt_debug(cd->dev, DL_ERROR, + "%s: Launch failed, Bootloader mode\n", + __func__); + goto exit; + } else if (cd->mode == PT_MODE_UNKNOWN) { + pt_debug(cd->dev, DL_ERROR, + "%s: Launch failed, Unknown mode\n", + __func__); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + + if (cd->app_pip_ver_ready == false) { + rc = pt_pip2_get_version_(cd); + if (!rc) + cd->app_pip_ver_ready = true; + else { + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + } + break; + default: + pt_debug(cd->dev, DL_ERROR, + "%s: Unknown mode\n", __func__); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) { + rc = pt_get_hid_descriptor_(cd, &hid_desc); + if (!rc) + *enum_status |= STARTUP_STATUS_GET_DESC; + } else { + *enum_status |= STARTUP_STATUS_GET_DESC; + } + } + + pt_init_pip_report_fields(cd); + if (protocol_mode == PT_PROTOCOL_MODE_HID) { + rc = pt_get_report_descriptor_(cd); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on getting report descriptor r=%d\n", + __func__, rc); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + } + + *enum_status |= STARTUP_STATUS_GET_RPT_DESC; + + if (!cd->features.easywake) + cd->easy_wakeup_gesture = PT_CORE_EWG_NONE; + + pt_debug(cd->dev, DL_INFO, "%s: Reading sysinfo\n", __func__); + rc = pt_hid_output_get_sysinfo_(cd); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on getting sysinfo r=%d\n", __func__, rc); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + goto exit; + } + *enum_status |= STARTUP_STATUS_GET_SYS_INFO; + + /* FW cannot handle most PIP cmds when it is still in BOOT mode */ + rc = _pt_poll_for_fw_exit_boot_mode(cd, 10000, &wait_time); + if (!rc) { + *enum_status |= STARTUP_STATUS_FW_OUT_OF_BOOT; + pt_debug(cd->dev, DL_WARN, + "%s: Exit FW BOOT Mode after %dms\n", + __func__, wait_time); + } else { + pt_debug(cd->dev, DL_WARN, + "%s: FW stuck in BOOT Mode after %dms\n", + __func__, wait_time); + goto exit; + } + + pt_debug(cd->dev, DL_INFO, "%s pt Prot Version: %d.%d\n", + __func__, + cd->sysinfo.ttdata.pip_ver_major, + cd->sysinfo.ttdata.pip_ver_minor); + + rc = pt_get_ic_crc_(cd, PT_TCH_PARM_EBID); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: DUT Config block CRC failure rc=%d\n", + __func__, rc); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + } else { + _pt_get_fw_sys_mode(cd, &sys_mode, NULL); + if (sys_mode != FW_SYS_MODE_SCANNING) { + pt_debug(cd->dev, DL_ERROR, + "%s: scan state: %d, retry: %d\n", + __func__, sys_mode, try); + if (try++ < PT_CORE_STARTUP_RETRY_COUNT) + goto reset; + } else + *enum_status |= STARTUP_STATUS_GET_CFG_CRC; + } + + rc = pt_restore_parameters_(cd); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: Failed to restore parameters rc=%d\n", + __func__, rc); + } else + *enum_status |= STARTUP_STATUS_RESTORE_PARM; + + call_atten_cb(cd, PT_ATTEN_STARTUP, 0); + cd->watchdog_interval = PT_WATCHDOG_TIMEOUT; + cd->startup_retry_count = 0; + +exit: + /* Generate the HW Version of the connected DUT and store in cd */ + pt_generate_hw_version(cd, cd->hw_version); + pt_debug(cd->dev, DL_WARN, "%s: HW Version: %s\n", __func__, + cd->hw_version); + + pt_start_wd_timer(cd); + + if (!detected) + rc = -ENODEV; + +#ifdef TTHE_TUNER_SUPPORT + tthe_print(cd, NULL, 0, "exit startup"); +#endif + + pt_debug(cd->dev, DL_WARN, + "%s: Completed Enumeration rc=%d On Attempt %d\n", + __func__, rc, try); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_enum_with_dut + * + * SUMMARY: This is the safe function wrapper for pt_enum_with_dut_() by + * requesting exclusive access around the call. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer the core data structure + * reset - Flag to reset the DUT before attempting to enumerate + * *status - pointer to store the enum status bitmask flags + ******************************************************************************/ +static int pt_enum_with_dut(struct pt_core_data *cd, bool reset, u32 *status) +{ + int rc = 0; + + mutex_lock(&cd->system_lock); + cd->startup_state = STARTUP_RUNNING; + mutex_unlock(&cd->system_lock); + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + goto exit; + } + + rc = pt_enum_with_dut_(cd, reset, status); + + if (release_exclusive(cd, cd->dev) < 0) + /* Don't return fail code, mode is already changed. */ + pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n", + __func__); + else + pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n", + __func__); + +exit: + mutex_lock(&cd->system_lock); + /* Clear startup state for any tasks waiting for startup completion */ + cd->startup_state = STARTUP_NONE; + mutex_unlock(&cd->system_lock); + + /* Set STATUS_COMPLETE bit to indicate the status is ready to be read */ + *status |= STARTUP_STATUS_COMPLETE; + + /* Wake the waiters for end of startup */ + wake_up(&cd->wait_q); + + return rc; +} + +#ifndef TTDL_KERNEL_SUBMISSION +static int add_sysfs_interfaces(struct device *dev); +static void remove_sysfs_interfaces(struct device *dev); +static void remove_sysfs_and_modules(struct device *dev); +#endif /*!TTDL_KERNEL_SUBMISSION */ +static void pt_release_modules(struct pt_core_data *cd); +static void pt_probe_modules(struct pt_core_data *cd); +/******************************************************************************* + * FUNCTION: _pt_ttdl_restart + * + * SUMMARY: Restarts TTDL enumeration with the DUT and re-probes all modules + * + * NOTE: The function DOSE NOT remove sysfs and modules. Trying to create + * existing sysfs nodes will produce an error. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to core device + ******************************************************************************/ +static int _pt_ttdl_restart(struct device *dev) +{ + int rc = 0; + struct pt_core_data *cd = dev_get_drvdata(dev); +#ifdef CONFIG_TOUCHSCREEN_PARADE_I2C + struct i2c_client *client = + (struct i2c_client *)container_of(dev, struct i2c_client, dev); +#endif + + /* + * Make sure the device is awake, pt_mt_release function will + * cause pm sleep function and lead to deadlock. + */ + pm_runtime_get_sync(dev); + /* Use ttdl_restart_lock to avoid reentry */ + mutex_lock(&cd->ttdl_restart_lock); + + remove_sysfs_and_modules(cd->dev); + +#ifdef CONFIG_TOUCHSCREEN_PARADE_I2C + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pt_debug(dev, DL_ERROR, + "%s I2C functionality not Supported\n", __func__); + rc = -EIO; + goto ttdl_no_error; + } +#endif + + if (cd->active_dut_generation == DUT_UNKNOWN) { + rc = _pt_detect_dut_generation(cd->dev, + &cd->startup_status, &cd->active_dut_generation, + &cd->mode); + if ((cd->active_dut_generation == DUT_UNKNOWN) || (rc)) { + pt_debug(dev, DL_ERROR, + "%s: Error, Unknown DUT Generation rc=%d\n", + __func__, rc); + } + } + + rc = add_sysfs_interfaces(cd->dev); + if (rc < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: Error, failed sysfs nodes rc=%d\n", + __func__, rc); + + if (!(cd->startup_status & STARTUP_STATUS_BL_RESET_SENTINEL)) { + pt_debug(dev, DL_INFO, "%s: Call pt_enum_with_dut\n", __func__); + rc = pt_enum_with_dut(cd, true, &cd->startup_status); + if (rc < 0) + pt_debug(dev, DL_ERROR, + "%s: Error, Failed to Enumerate\n", __func__); + } + + rc = pt_mt_probe(dev); + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: Error, fail mt probe\n", __func__); + } + + rc = pt_btn_probe(dev); + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: Error, fail btn probe\n", __func__); + } + + pt_probe_modules(cd); + + pt_debug(cd->dev, DL_WARN, + "%s: Well Done! TTDL Restart Completed\n", __func__); + rc = 0; + +#ifdef CONFIG_TOUCHSCREEN_PARADE_I2C +ttdl_no_error: +#endif + mutex_unlock(&cd->ttdl_restart_lock); + + mutex_lock(&cd->system_lock); + cd->startup_status |= STARTUP_STATUS_COMPLETE; + cd->startup_state = STARTUP_NONE; + mutex_unlock(&cd->system_lock); + + pm_runtime_put(dev); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_restart_work_function + * + * SUMMARY: This is the wrapper function placed in a work queue to call + * _pt_ttdl_restart() + * + * RETURN: none + * + * PARAMETERS: + * *work - pointer to the work_struct + ******************************************************************************/ +static void pt_restart_work_function(struct work_struct *work) +{ + struct pt_core_data *cd = container_of(work, + struct pt_core_data, ttdl_restart_work); + int rc = 0; + + rc = _pt_ttdl_restart(cd->dev); + if (rc < 0) + pt_debug(cd->dev, DL_ERROR, "%s: Fail queued startup r=%d\n", + __func__, rc); +} + +/******************************************************************************* + * FUNCTION: pt_enum_work_function + * + * SUMMARY: This is the wrapper function placed in a work queue to call + * pt_enum_with_dut() + * + * RETURN: none + * + * PARAMETERS: + * *work - pointer to the work_struct + ******************************************************************************/ +static void pt_enum_work_function(struct work_struct *work) +{ + struct pt_core_data *cd = container_of(work, + struct pt_core_data, enum_work); + int rc; + + rc = pt_enum_with_dut(cd, false, &cd->startup_status); + if (rc < 0) + pt_debug(cd->dev, DL_ERROR, "%s: Fail queued startup r=%d\n", + __func__, rc); +} + +#if (KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE) +#define KERNEL_VER_GT_3_19 +#endif + +#if defined(CONFIG_PM_RUNTIME) || defined(KERNEL_VER_GT_3_19) +/* CONFIG_PM_RUNTIME option is removed in 3.19.0 */ +#if defined(CONFIG_PM_SLEEP) +/******************************************************************************* + * FUNCTION: pt_core_rt_suspend + * + * SUMMARY: Wrapper function with PM Runtime stratergy to call pt_core_sleep. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to core device + ******************************************************************************/ +static int pt_core_rt_suspend(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int rc = 0; + + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) + return 0; + + rc = pt_core_sleep(cd); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: Error on sleep\n", __func__); + return -EAGAIN; + } + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_core_rt_resume + * + * SUMMARY: Wrapper function with PM Runtime stratergy to call pt_core_wake. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to core device + ******************************************************************************/ +static int pt_core_rt_resume(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int rc = 0; + + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) + return 0; + + rc = pt_core_wake(cd); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: Error on wake\n", __func__); + return -EAGAIN; + } + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ +#endif /* CONFIG_PM_RUNTIME || LINUX_VERSION_CODE */ + +#if defined(CONFIG_PM_SLEEP) +/******************************************************************************* + * FUNCTION: pt_core_suspend_ + * + * SUMMARY: Wrapper function with device suspend/resume stratergy to call + * pt_core_sleep. This function may disable IRQ during sleep state. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to core device + ******************************************************************************/ +static int pt_core_suspend_(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + pt_core_sleep(cd); + + if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) + return 0; + + /* Required to prevent interrupts before bus awake */ + disable_irq(cd->irq); + cd->irq_disabled = 1; + + if (device_may_wakeup(dev)) { + pt_debug(dev, DL_WARN, "%s Device MAY wakeup\n", + __func__); + if (!enable_irq_wake(cd->irq)) + cd->irq_wake = 1; + } else { + pt_debug(dev, DL_WARN, "%s Device MAY NOT wakeup\n", + __func__); + } + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_core_suspend + * + * SUMMARY: Wrapper function of pt_core_suspend_() to help avoid TP from being + * woke up or put to sleep based on Kernel power state even when the display + * is off based on the check of TTDL core platform flag. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to core device + ******************************************************************************/ +static int pt_core_suspend(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) + return 0; + + return pt_core_suspend_(dev); +} + +/******************************************************************************* + * FUNCTION: pt_core_resume_ + * + * SUMMARY: Wrapper function with device suspend/resume stratergy to call + * pt_core_wake. This function may enable IRQ before wake up. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to core device + ******************************************************************************/ +static int pt_core_resume_(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) + goto exit; + + /* + * Bus pm does not call suspend if device runtime suspended + * This flag is covers that case + */ + if (cd->irq_disabled) { + enable_irq(cd->irq); + cd->irq_disabled = 0; + } + + if (device_may_wakeup(dev)) { + pt_debug(dev, DL_WARN, "%s Device MAY wakeup\n", + __func__); + if (cd->irq_wake) { + disable_irq_wake(cd->irq); + cd->irq_wake = 0; + } + } else { + pt_debug(dev, DL_WARN, "%s Device MAY NOT wakeup\n", + __func__); + } + +exit: + pt_core_wake(cd); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_core_resume + * + * SUMMARY: Wrapper function of pt_core_resume_() to avoid TP to be waken/slept + * along with kernel power state even the display is off based on the check of + * TTDL core platform flag. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to core device + ******************************************************************************/ +static int pt_core_resume(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) + return 0; + + return pt_core_resume_(dev); +} +#endif + +#ifdef NEED_SUSPEND_NOTIFIER +/******************************************************************************* + * FUNCTION: pt_pm_notifier + * + * SUMMARY: This function is registered to notifier chain and will perform + * suspend operation if match event PM_SUSPEND_PREPARE. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *nb - pointer to notifier_block structure + * action - notifier event type + * *data - void pointer + ******************************************************************************/ +static int pt_pm_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct pt_core_data *cd = container_of(nb, + struct pt_core_data, pm_notifier); + + if (action == PM_SUSPEND_PREPARE) { + pt_debug(cd->dev, DL_INFO, "%s: Suspend prepare\n", + __func__); + + /* + * If PM runtime is not suspended, either call runtime + * PM suspend callback or wait until it finishes + */ + if (!pm_runtime_suspended(cd->dev)) + pm_runtime_suspend(cd->dev); + + (void) pt_core_suspend(cd->dev); + } + + return NOTIFY_DONE; +} +#endif + +const struct dev_pm_ops pt_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pt_core_suspend, pt_core_resume) + SET_RUNTIME_PM_OPS(pt_core_rt_suspend, pt_core_rt_resume, + NULL) +}; +EXPORT_SYMBOL_GPL(pt_pm_ops); + +/******************************************************************************* + * FUNCTION: _pt_request_pip2_enter_bl + * + * SUMMARY: Force the DUT to enter the BL by resetting the DUT by use of the + * XRES pin or a soft reset. + * + * NOTE: The WD MUST be stopped/restarted by the calling Function. Having + * the WD active could cause this function to fail! + * NOTE: If start_mode is passed in as PT_MODE_IGNORE, this function + * will not try to determine the current mode but will proceed with + * resetting the DUT and entering the BL. + * + * NOTE: The definition of result code: + * PT_ENTER_BL_PASS (0) + * PT_ENTER_BL_ERROR (1) + * PT_ENTER_BL_RESET_FAIL (2) + * PT_ENTER_BL_HID_START_BL_FAIL (3) + * PT_ENTER_BL_CONFIRM_FAIL (4) + * PT_ENTER_BL_GET_FLASH_INFO_FAIL (5) + * + * RETURNS: + * 0 = success + * !0 = failure + * + * + * PARAMETERS: + * *dev - pointer to device structure + * *start_mode - pointer to the mode the DUT was in when this function + * starts + * *result - pointer to store the result when to enter BL + ******************************************************************************/ +int _pt_request_pip2_enter_bl(struct device *dev, u8 *start_mode, int *result) +{ + int rc = 0; + int t; + int tmp_result = PT_ENTER_BL_ERROR; + int flash_info_retry = 2; + u8 mode = PT_MODE_UNKNOWN; + u8 sys_mode = FW_SYS_MODE_UNDEFINED; + u8 read_buf[32]; + u16 actual_read_len; + struct pt_core_data *cd = dev_get_drvdata(dev); + u8 host_mode_cmd[4] = {0xA5, 0xA5, 0xA5, 0xA5}; + u8 time = 0; + u8 saved_flashless_auto_bl_mode = cd->flashless_auto_bl; + + if (cd->watchdog_enabled) { + pt_debug(dev, DL_WARN, + "%s: Watchdog must be stopped before entering BL\n", + __func__); + goto exit; + } + + cancel_work_sync(&cd->enum_work); + cancel_work_sync(&cd->watchdog_work); + + /* if undefined assume operational/test to bypass all checks */ + if (*start_mode == PT_MODE_IGNORE) { + mode = PT_MODE_OPERATIONAL; + sys_mode = FW_SYS_MODE_TEST; + pt_debug(dev, DL_INFO, "%s: Assume Mode = %d", __func__, mode); + } else if (*start_mode == PT_MODE_UNKNOWN) { + rc = pt_pip2_get_mode_sysmode_(cd, &mode, &sys_mode); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Get mode failed, mode unknown\n", + __func__); + } + *start_mode = mode; + pt_debug(dev, DL_INFO, "%s: Get Mode = %d", __func__, mode); + } else if (*start_mode == PT_MODE_OPERATIONAL) { + /* Assume SCANNIING mode to avoid doing an extra get_mode */ + sys_mode = FW_SYS_MODE_SCANNING; + } + +_retry: + /* For Flashless DUTs - Suppress auto BL on next BL sentinel */ + pt_debug(dev, DL_INFO, "%s: Flashless Auto_BL - SUPPRESS\n", __func__); + cd->flashless_auto_bl = PT_SUPPRESS_AUTO_BL; + + switch (mode) { + case PT_MODE_UNKNOWN: + /* + * When the mode could not be determined the DUT could be + * in App mode running corrupted FW or FW that is not + * responding to the mode request, assume no communication + * and do a hard reset + */ + mutex_lock(&cd->system_lock); + cd->startup_status = STARTUP_STATUS_START; + pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__); + mutex_unlock(&cd->system_lock); + + rc = pt_dut_reset(cd, PT_CORE_CMD_UNPROTECTED); + if (rc) { + tmp_result = PT_ENTER_BL_RESET_FAIL; + goto exit; + } + break; + + case PT_MODE_OPERATIONAL: + if (sys_mode == FW_SYS_MODE_SCANNING) { + pt_debug(dev, DL_INFO, "%s: Suspend Scanning\n", + __func__); + rc = pt_pip_suspend_scanning_(cd); + if (rc) { + /* + * Print to log but don't exit, the FW could be + * running but be hung or fail to respond to + * this request + */ + pt_debug(dev, DL_ERROR, + "%s Suspend Scan Failed\n", __func__); + } + /* sleep to allow the suspend scan to be processed */ + usleep_range(1000, 2000); + } + + mutex_lock(&cd->system_lock); + cd->startup_status = STARTUP_STATUS_START; + pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__); + mutex_unlock(&cd->system_lock); + + /* Reset device to enter the BL */ + rc = pt_dut_reset(cd, PT_CORE_CMD_UNPROTECTED); + if (rc) { + tmp_result = PT_ENTER_BL_RESET_FAIL; + goto exit; + } + break; + + case PT_MODE_BOOTLOADER: + /* Do nothing as we are already in the BL */ + tmp_result = PT_ENTER_BL_PASS; + goto exit; + + default: + /* Should NEVER get here */ + tmp_result = PT_ENTER_BL_ERROR; + pt_debug(dev, DL_ERROR, "%s: Unknown mode code\n", __func__); + goto exit; + } + + if (!cd->flashless_dut && + (mode == PT_MODE_UNKNOWN || mode == PT_MODE_OPERATIONAL)) { + /* + * Sending the special "Host Mode" command will instruct the + * BL to not execute the FW it has loaded into RAM. + * The command must be sent within a 40ms window from releasing + * the XRES pin. If the messages is sent too early it will NAK, + * so keep sending it every 2ms until it is accepted by the BL. + * A no-flash DUT does not require this command as there is no + * FW for the BL to load and execute. + */ + usleep_range(4000, 6000); + pt_debug(cd->dev, DL_INFO, + ">>> %s: Write Buffer Size[%d] Stay in BL\n", + __func__, (int)sizeof(host_mode_cmd)); + pt_pr_buf(cd->dev, DL_DEBUG, host_mode_cmd, + (int)sizeof(host_mode_cmd), ">>> User CMD"); + rc = 1; + while (rc && time < 34) { + rc = pt_adap_write_read_specific(cd, 4, + host_mode_cmd, NULL, 0); + usleep_range(1800, 2000); + time += 2; + } + + /* Sleep to allow the BL to come up */ + usleep_range(1000, 2000); + } + + /* + * To avoid the case that next PIP command can be confused by BL/FW + * sentinel's "wakeup" event, chekcing hid_reset_cmd_state which is + * followed by "wakeup event" function can lower the failure rate. + */ + t = wait_event_timeout(cd->wait_q, + ((cd->startup_status != STARTUP_STATUS_START) + && (cd->hid_reset_cmd_state == 0)), + msecs_to_jiffies(300)); + if (IS_TMO(t)) { + pt_debug(cd->dev, DL_ERROR, + "%s: TMO waiting for BL sentinel\n", __func__); + } + + /* Check if device is now in BL mode */ + rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL); + pt_debug(dev, DL_INFO, "%s: Mode=%d, Status=0x%04X\n", __func__, mode, + cd->startup_status); + if (!rc && mode == PT_MODE_BOOTLOADER) { + pt_debug(dev, DL_INFO, "%s In bootloader mode now\n", __func__); + mutex_lock(&cd->system_lock); + cd->pip2_prot_active = true; + cd->mode = PT_MODE_BOOTLOADER; + mutex_unlock(&cd->system_lock); + tmp_result = PT_ENTER_BL_PASS; + } else { + /* + * If the device doesn't enter BL mode as expected and rc is + * tested pass by above function pt_pip2_get_mode_sysmode_(), + * the function should return an error code to indicate this + * failure PT_ENTER_BL_CONFIRM_FAIL. + */ + if (!rc) + rc = -EINVAL; + tmp_result = PT_ENTER_BL_CONFIRM_FAIL; + mutex_lock(&cd->system_lock); + cd->mode = mode; + mutex_unlock(&cd->system_lock); + pt_debug(dev, DL_ERROR, + "%s ERROR: Not in BL as expected", __func__); + } + +exit: + if (!cd->flashless_dut && (tmp_result == PT_ENTER_BL_PASS)) { + /* Check to get (buffered) flash information */ + rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED, + PIP2_CMD_ID_FLASH_INFO, NULL, 0, + read_buf, &actual_read_len); + if (!rc) { + if (read_buf[PIP2_RESP_BODY_OFFSET] == 0) { + pt_debug( + dev, DL_WARN, + "%s Unavailable Manufacturer ID: 0x%02x\n", + __func__, + read_buf[PIP2_RESP_BODY_OFFSET]); + /* + * If the BL was unable to cache the correct + * values when entering the first time due to + * the Flash part not having been powered up + * long enough, re-enter the BL to trigger the + * BL to re-attempt to cache the values. + */ + if (flash_info_retry-- > 0) { + mode = PT_MODE_UNKNOWN; + pt_debug(dev, DL_WARN, + "%s Assume mode to UNKNOWN to enter BL again, retry=%d\n", + __func__, flash_info_retry); + goto _retry; + } else { + pt_debug(dev, DL_WARN, + "%s Manufacturer ID Unknown\n", + __func__); + tmp_result = PT_ENTER_BL_PASS; + } + } + } else { + tmp_result = PT_ENTER_BL_GET_FLASH_INFO_FAIL; + pt_debug( + dev, DL_ERROR, + "%s: Failed to send PIP2 READ_FLASH_INFO cmd\n", + __func__); + } + } + + pt_debug(dev, DL_INFO, "%s: Flashless Auto_BL - %s\n", __func__, + saved_flashless_auto_bl_mode == PT_ALLOW_AUTO_BL ? "ALLOW" : + "SUPPRESS"); + cd->flashless_auto_bl = saved_flashless_auto_bl_mode; + if (result) + *result = tmp_result; + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_file_open + * + * SUMMARY: Using the BL PIP2 commands open a file and return the file handle + * + * NOTE: The DUT must be in BL mode for this command to work + * + * RETURNS: + * <0 = Error + * >0 = file handle opened + * + * PARAMETERS: + * *dev - pointer to device structure + * file_no - PIP2 file number to open + ******************************************************************************/ +int _pt_pip2_file_open(struct device *dev, u8 file_no) +{ + int rc = 0; + u16 status; + u16 actual_read_len; + u8 file_handle; + u8 data[2]; + u8 read_buf[10]; + u8 expected_len = pt_pip2_get_cmd_response_len(PIP2_CMD_ID_FILE_OPEN); + + pt_debug(dev, DL_DEBUG, "%s: OPEN file %d\n", __func__, file_no); + data[0] = file_no; + rc = _pt_request_pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_OPEN, + data, 1, read_buf, &actual_read_len); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: FILE_OPEN timeout for file=%d\n", + __func__, file_no); + return -PIP2_RSP_ERR_NOT_OPEN; + } + + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + if (rc || ((status != PIP2_RSP_ERR_NONE) && + (status != PIP2_RSP_ERR_ALREADY_OPEN))) { + pt_debug(dev, DL_ERROR, + "%s: %s 0x%02X for file=%d\n", + __func__, "FILE_OPEN failure:", status, file_no); + return -status; + } else if (actual_read_len == expected_len) { + /* File_open returned a file handle */ + file_handle = read_buf[PIP2_RESP_BODY_OFFSET]; + if (file_handle != file_no) { + pt_debug(dev, DL_ERROR, + "%s: %s 0x%02X file=%d returned handle=%d\n", + __func__, "FILE_OPEN failure:", + status, file_no, file_handle); + return -status; + } + } else { + return -status; + } + + return file_handle; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_file_close + * + * SUMMARY: Using the BL PIP2 commands close a file + * + * NOTE: The DUT must be in BL mode for this command to work + * + * RETURNS: + * <0 = Error + * >0 = file handle closed + * + * PARAMETERS: + * *dev - pointer to device structure + * file_handle - handle to the file to be closed + ******************************************************************************/ +int _pt_pip2_file_close(struct device *dev, u8 file_handle) +{ + int ret = 0; + u16 status; + u16 actual_read_len; + u8 data[2]; + u8 read_buf[32]; + + pt_debug(dev, DL_DEBUG, "%s: CLOSE file %d\n", __func__, file_handle); + data[0] = file_handle; + ret = _pt_request_pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_CLOSE, + data, 1, read_buf, &actual_read_len); + if (ret) { + pt_debug(dev, DL_ERROR, + "%s ROM BL FILE_CLOSE timeout for file = %d\n", + __func__, file_handle); + return -ETIME; + } + + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + if (status != 0x00) { + pt_debug(dev, DL_ERROR, + "%s ROM BL FILE_CLOSE failure: 0x%02X for file = %d\n", + __func__, status, file_handle); + return -status; + } + return file_handle; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_file_erase_by_file_no + * + * SUMMARY: Using the BL PIP2 commands erase a file by file number only. + * + * NOTE: The DUT must be in BL mode for this command to work + * NOTE: Some FLASH parts can time out while erasing one or more sectors, + * one retry is attempted for each sector in a file. + * + * RETURNS: + * <0 = Error + * >0 = file handle closed + * + * PARAMETERS: + * *dev - pointer to device structure + * file_handle - handle to the file to be erased + * *status - PIP2 erase status code + ******************************************************************************/ +int _pt_pip2_file_erase_by_file_no(struct device *dev, u8 file_handle, + int *status) +{ + int ret = 0; + int max_retry = PT_PIP2_MAX_FILE_SIZE/PT_PIP2_FILE_SECTOR_SIZE; + int retry = 1; + u16 actual_read_len; + u8 data[2]; + u8 read_buf[10]; + struct pt_core_data *cd = dev_get_drvdata(dev); + + pt_debug(dev, DL_DEBUG, "%s: ERASE file %d\n", __func__, file_handle); + data[0] = file_handle; + data[1] = PIP2_FILE_IOCTL_CODE_ERASE_FILE; + *status = PIP2_RSP_ERR_TIMEOUT; + + /* Increase waiting time for large file erase */ + mutex_lock(&cd->system_lock); + cd->pip_cmd_timeout = PT_PIP2_CMD_FILE_ERASE_TIMEOUT; + mutex_unlock(&cd->system_lock); + while (*status == PIP2_RSP_ERR_TIMEOUT && retry < max_retry) { + ret = _pt_request_pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL, + data, 2, read_buf, &actual_read_len); + if (ret) + break; + *status = read_buf[PIP2_RESP_STATUS_OFFSET]; + if (*status == PIP2_RSP_ERR_TIMEOUT) { +#ifdef TTDL_DIAGNOSTICS + cd->file_erase_timeout_count++; +#endif + pt_debug(dev, DL_WARN, + "%s: ERASE timeout %d for file = %d\n", + __func__, retry, file_handle); + } + retry++; + } + mutex_lock(&cd->system_lock); + cd->pip_cmd_timeout = cd->pip_cmd_timeout_default; + mutex_unlock(&cd->system_lock); + if (ret) { + pt_debug(dev, DL_ERROR, + "%s ROM FILE_ERASE cmd failure: %d for file = %d\n", + __func__, ret, file_handle); + return -EIO; + } + + if (*status != 0x00) { + pt_debug(dev, DL_ERROR, + "%s ROM FILE_ERASE failure: 0x%02X for file = %d\n", + __func__, *status, file_handle); + return -EIO; + } + return file_handle; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_file_erase_by_file_sector + * + * SUMMARY: Using the BL PIP2 commands erase a file sector by sector. + * + * NOTE: The DUT must be in BL mode for this command to work + * NOTE: Some FLASH parts can time out while erasing one or more sectors, + * one retry is attempted for each sector in a file. + * + * RETURNS: + * <0 = Error + * >0 = file handle closed + * + * PARAMETERS: + * *dev - pointer to device structure + * file_handle - handle to the file to be erased + * file_sector - Number of sectors to erase + * *status - PIP2 erase status code + ******************************************************************************/ +int _pt_pip2_file_erase_by_file_sector(struct device *dev, u8 file_handle, + u16 file_sector, int *status) +{ + int ret = 0, index = 0; + int max_retry = 3; + int retry = 0; + int tmp_status = PIP2_RSP_ERR_NONE; + u16 sector_to_erase = 1; + u16 actual_read_len; + u8 data[6]; + u8 read_buf[10]; + struct pt_core_data *cd = dev_get_drvdata(dev); + + pt_debug(dev, DL_DEBUG, "%s: ERASE file=%d, sector=%d\n", __func__, + file_handle, file_sector); + data[0] = file_handle; + data[1] = PIP2_FILE_IOCTL_CODE_ERASE_FILE; + /* Set how many sectors to erase*/ + data[4] = LOW_BYTE(sector_to_erase); + data[5] = HI_BYTE(sector_to_erase); + + /* Increase waiting time for large file erase */ + mutex_lock(&cd->system_lock); + cd->pip_cmd_timeout = PT_PIP2_CMD_FILE_SECTOR_ERASE_TIMEOUT; + mutex_unlock(&cd->system_lock); + for (retry = 0; retry < max_retry; retry++) { + for (index = 0; index < file_sector; index++) { + /* Initialize status code */ + tmp_status = PIP2_RSP_ERR_NONE; + /* Set which sector starts to erase */ + data[2] = LOW_BYTE(index); + data[3] = HI_BYTE(index); + ret = _pt_request_pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL, + data, 6, read_buf, &actual_read_len); + + tmp_status = read_buf[PIP2_RESP_STATUS_OFFSET]; + if (ret || tmp_status != PIP2_RSP_ERR_NONE) + break; + } + /* + * Only retry if response doesn't arrive or the status code is + * PIP2_RSP_ERR_TIMEOUT. + */ + if ((ret != -ETIME) || (tmp_status != PIP2_RSP_ERR_TIMEOUT)) + break; + else { +#ifdef TTDL_DIAGNOSTICS + cd->file_erase_timeout_count++; +#endif + pt_debug(dev, DL_WARN, + "%s: ERASE timeout %d for file=%d, sector=%d\n", + __func__, retry, file_handle, index); + } + } + + mutex_lock(&cd->system_lock); + *status = tmp_status; + cd->pip_cmd_timeout = cd->pip_cmd_timeout_default; + mutex_unlock(&cd->system_lock); + if (ret) { + pt_debug(dev, DL_ERROR, + "%s ROM FILE_ERASE cmd failure: %d for file=%d, sector=%d\n", + __func__, ret, file_handle, index); + return -EIO; + } + + if (*status != 0x00) { + pt_debug(dev, DL_ERROR, + "%s ROM FILE_ERASE failure: 0x%02X for file=%d, sector=%d\n", + __func__, *status, file_handle, index); + return -EIO; + } + return file_handle; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_file_erase + * + * SUMMARY: Wrapper function to call _pt_pip2_file_erase_by_file_sector() and + * _pt_pip2_file_erase_by_file_no() by checking file_sector. + * + * NOTE: If the file_sector is 0, it will erase the entire file. + * + * RETURNS: + * <0 = Error + * >0 = file handle closed + * + * PARAMETERS: + * *dev - pointer to device structure + * file_handle - handle to the file to be erased + * file_sector - Number of sectors to erase + * *status - PIP2 erase status code + ******************************************************************************/ +int _pt_pip2_file_erase(struct device *dev, u8 file_handle, u16 file_sector, + int *status) +{ + if (file_sector) + return _pt_pip2_file_erase_by_file_sector(dev, file_handle, + file_sector, status); + else + return _pt_pip2_file_erase_by_file_no(dev, file_handle, status); +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_file_read + * + * SUMMARY: Using the BL PIP2 commands read n bytes from a already opened file + * + * NOTE: The DUT must be in BL mode for this command to work + * + * RETURNS: + * <0 = Error + * >0 = number of bytes read + * + * PARAMETERS: + * *dev - pointer to device structure + * file_handle - File handle to read from + * num_bytes - number of bytes to read + ******************************************************************************/ +int _pt_pip2_file_read(struct device *dev, u8 file_handle, u16 num_bytes, + u8 *read_buf) +{ + int ret = 0; + u16 status; + u16 actual_read_len; + u8 data[3]; + + data[0] = file_handle; + data[1] = (num_bytes & 0x00FF); + data[2] = (num_bytes & 0xFF00) >> 8; + ret = _pt_request_pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_READ, + data, 3, read_buf, &actual_read_len); + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + if (ret || ((status != 0x00) && (status != 0x03))) { + pt_debug(dev, DL_ERROR, + "%s File open failure with error code = %d\n", + __func__, status); + return -1; + } + ret = num_bytes; + + return ret; +} + +/******************************************************************************* + * FUNCTION: _pt_read_us_file + * + * SUMMARY: Open a user space file and read 'size' bytes into buf. If size = 0 + * then read the entire file. + * NOTE: The file size must be less than PT_PIP2_MAX_FILE_SIZE + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *file_path - pointer to the file path + * *buf - pointer to the buffer to store the file contents + * *size - pointer to the size of the file + ******************************************************************************/ +int _pt_read_us_file(struct device *dev, u8 *file_path, u8 *buf, int *size) +{ + struct file *filp = NULL; + struct inode *inode = NULL; + unsigned int file_len = 0; + unsigned int read_len = 0; + mm_segment_t oldfs; + int rc = 0; + + if (file_path == NULL || buf == NULL) { + pt_debug(dev, DL_ERROR, "%s: path || buf is NULL.\n", __func__); + return -EINVAL; + } + + pt_debug(dev, DL_WARN, "%s: path = %s\n", __func__, file_path); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + filp = filp_open(file_path, O_RDONLY, 0400); + if (IS_ERR(filp)) { + pt_debug(dev, DL_WARN, "%s: Failed to open %s\n", __func__, + file_path); + rc = -ENOENT; + goto err; + } + + if (filp->f_op == NULL) { + pt_debug(dev, DL_ERROR, "%s: File Operation Method Error\n", + __func__); + rc = -EINVAL; + goto exit; + } + inode = filp->f_path.dentry->d_inode; + if (inode == NULL) { + pt_debug(dev, DL_ERROR, "%s: Get inode from filp failed\n", + __func__); + rc = -EINVAL; + goto exit; + } + file_len = i_size_read(inode->i_mapping->host); + if (file_len == 0) { + pt_debug(dev, DL_ERROR, "%s: file size error,file_len = %d\n", + __func__, file_len); + rc = -EINVAL; + goto exit; + } + + if (*size == 0) + read_len = file_len; + else + read_len = *size; + + if (read_len > PT_PIP2_MAX_FILE_SIZE) { + pt_debug(dev, DL_ERROR, "%s: file size ( %d ) exception.\n", + __func__, read_len); + rc = -EINVAL; + goto exit; + } + filp->private_data = inode->i_private; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)) + if (kernel_read(filp, buf, read_len, &(filp->f_pos)) != read_len) { +#else + if (vfs_read(filp, buf, read_len, &(filp->f_pos)) != read_len) { +#endif + pt_debug(dev, DL_ERROR, "%s: file read error.\n", __func__); + rc = -EINVAL; + goto exit; + } + *size = read_len; + +exit: + if (filp_close(filp, NULL) != 0) + pt_debug(dev, DL_ERROR, "%s: file close error.\n", __func__); +err: + set_fs(oldfs); + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_request_pip2_bin_hdr + * + * SUMMARY: Read the stored bin file header from Flash or the User Space file + * in the case of a flashless DUT, and parse the contents + * + * RETURNS: + * 0 = Success + * !0 = Error condition + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +int _pt_request_pip2_bin_hdr(struct device *dev, struct pt_bin_file_hdr *hdr) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u8 file_handle; + u8 read_buf[255]; + u8 hdr_len = 0; + u8 i; + int bytes_read; + int read_size; + int ret = 0; + int rc = 0; + bool load_hdr_struct = false; + + if (cd->flashless_dut) { + read_size = sizeof(read_buf); + rc = _pt_read_us_file(dev, cd->pip2_us_file_path, + read_buf, &read_size); + if (rc) { + ret = rc; + pt_debug(dev, DL_WARN, + "%s Failed to read fw image from US, rc=%d\n", + __func__, rc); + goto exit; + } + load_hdr_struct = true; + hdr_len = read_buf[0]; + i = 0; + } else { + if (cd->mode != PT_MODE_BOOTLOADER) { + ret = -EPERM; + goto exit; + } + + /* Open the bin file in Flash */ + pt_debug(dev, DL_INFO, "%s Open File 1\n", __func__); + file_handle = _pt_pip2_file_open(dev, PIP2_FW_FILE); + if (file_handle != PIP2_FW_FILE) { + ret = -ENOENT; + pt_debug(dev, DL_ERROR, + "%s Failed to open bin file\n", __func__); + goto exit; + } + + /* Read the header length from the file */ + pt_debug(dev, DL_INFO, "%s Read length of header\n", __func__); + read_size = 1; + bytes_read = _pt_pip2_file_read(dev, file_handle, read_size, + read_buf); + if (bytes_read != read_size) { + ret = -EX_ERR_FREAD; + pt_debug(dev, DL_ERROR, + "%s Failed to bin file header len\n", __func__); + goto exit_close_file; + } + hdr_len = read_buf[PIP2_RESP_BODY_OFFSET]; + if (hdr_len == 0xFF) { + ret = -EX_ERR_FLEN; + pt_debug(dev, DL_ERROR, + "%s Bin header len is invalid\n", __func__); + goto exit_close_file; + } + + /* Read the rest of the header from the bin file */ + pt_debug(dev, DL_INFO, "%s Read bin file header\n", __func__); + memset(read_buf, 0, sizeof(read_buf)); + bytes_read = _pt_pip2_file_read(dev, file_handle, hdr_len, + read_buf); + if (bytes_read != hdr_len) { + ret = -EX_ERR_FREAD; + pt_debug(dev, DL_ERROR, + "%s Failed to read bin file\n", __func__); + goto exit_close_file; + } + load_hdr_struct = true; + +exit_close_file: + /* Close the file */ + if (file_handle != _pt_pip2_file_close(dev, file_handle)) { + ret = -EX_ERR_FCLOSE; + pt_debug(dev, DL_ERROR, + "%s Failed to close bin file\n", __func__); + } + + /* + * The length was already read so subtract 1 to make the rest of + * the offsets match the spec + */ + i = PIP2_RESP_BODY_OFFSET - 1; + } + + if (load_hdr_struct) { + hdr->length = hdr_len; + hdr->ttpid = (read_buf[i+1] << 8) | read_buf[i+2]; + hdr->fw_major = (read_buf[i+3]); + hdr->fw_minor = (read_buf[i+4]); + hdr->fw_crc = (read_buf[i+5] << 24) | + (read_buf[i+6] << 16) | + (read_buf[i+7] << 8) | + (read_buf[i+8]); + hdr->fw_rev_ctrl = (read_buf[i+9] << 24) | + (read_buf[i+10] << 16) | + (read_buf[i+11] << 8) | + (read_buf[i+12]); + hdr->si_rev = (read_buf[i+14] << 8) | (read_buf[i+13]); + hdr->si_id = (read_buf[i+16] << 8) | (read_buf[i+15]); + hdr->config_ver = (read_buf[i+17] << 8) | (read_buf[i+18]); + if (hdr_len >= 22) { + hdr->hex_file_size = (read_buf[i+19] << 24) | + (read_buf[i+20] << 16) | + (read_buf[i+21] << 8) | + (read_buf[i+22]); + } else { + hdr->hex_file_size = 0; + } + } + +exit: + return ret; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_file_get_stats + * + * SUMMARY: Using the BL PIP2 commands get file information from an already + * opened file + * + * NOTE: The DUT must be in BL mode for this command to work + * + * RETURNS: + * !0 = Error + * 0 = Success + * + * PARAMETERS: + * *dev - pointer to device structure + * file_handle - File handle to read from + * *address - pointer to store address of file + * *file_size _ pointer to store size of file + ******************************************************************************/ +int _pt_pip2_file_get_stats(struct device *dev, u8 file_handle, u32 *address, + u32 *file_size) +{ + int ret = 1; + u16 status; + u16 actual_read_len; + u8 data[2]; + u8 read_buf[16]; + + data[0] = file_handle; + data[1] = PIP2_FILE_IOCTL_CODE_FILE_STATS; + + ret = _pt_request_pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL, + data, 2, read_buf, &actual_read_len); + + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + if (ret || (status != 0x00)) { + pt_debug(dev, DL_ERROR, + "%s ROM FILE_STATS failure: 0x%02X for file = %d, ret = %d\n", + __func__, status, file_handle, ret); + ret = -EIO; + goto exit; + } + + pt_debug(dev, DL_DEBUG, + "%s --- FILE %d Information ---\n", __func__, file_handle); + + if (address) { + *address = read_buf[PIP2_RESP_BODY_OFFSET] + + (read_buf[PIP2_RESP_BODY_OFFSET + 1] << 8) + + (read_buf[PIP2_RESP_BODY_OFFSET + 2] << 16) + + (read_buf[PIP2_RESP_BODY_OFFSET + 3] << 24); + pt_debug(dev, DL_DEBUG, "%s Address: 0x%08x\n", + __func__, *address); + } + + if (file_size) { + *file_size = read_buf[PIP2_RESP_BODY_OFFSET + 4] + + (read_buf[PIP2_RESP_BODY_OFFSET + 5] << 8) + + (read_buf[PIP2_RESP_BODY_OFFSET + 6] << 16) + + (read_buf[PIP2_RESP_BODY_OFFSET + 7] << 24); + pt_debug(dev, DL_DEBUG, "%s Size : 0x%08x\n", + __func__, *file_size); + } + +exit: + return ret; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_file_seek_offset + * + * SUMMARY: Using the BL PIP2 commands seek read/write offset for an already + * opened file + * + * NOTE: The DUT must be in BL mode for this command to work + * NOTE: File open/erase command can reset the offset + * + * RETURNS: + * !0 = Error + * 0 = Success + * + * PARAMETERS: + * *dev - pointer to device structure + * file_handle - File handle to read from + * read_offset - read offset of file + * write_offset - write offset of file + ******************************************************************************/ +int _pt_pip2_file_seek_offset(struct device *dev, u8 file_handle, + u32 read_offset, u32 write_offset) +{ + int ret = 1; + u16 status; + u16 actual_read_len; + u8 data[10]; + u8 read_buf[16]; + + data[0] = file_handle; + data[1] = PIP2_FILE_IOCTL_CODE_SEEK_POINTER; + data[2] = 0xff & read_offset; + data[3] = 0xff & (read_offset >> 8); + data[4] = 0xff & (read_offset >> 16); + data[5] = 0xff & (read_offset >> 24); + data[6] = 0xff & write_offset; + data[7] = 0xff & (write_offset >> 8); + data[8] = 0xff & (write_offset >> 16); + data[9] = 0xff & (write_offset >> 24); + + ret = _pt_request_pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL, + data, 10, read_buf, &actual_read_len); + + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + if (ret || (status != 0x00)) { + pt_debug(dev, DL_ERROR, + "%s ROM FILE_SEEK failure: 0x%02X for file = %d, ret = %d\n", + __func__, status, ret, file_handle); + ret = -EIO; + } + return ret; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_file_crc + * + * SUMMARY: Using the BL PIP2 commands to calculate CRC for a file or portion of + * the file. + * + * NOTE: The DUT must be in BL mode for this command to work + * NOTE: This command only can be used for BL version 1.8 or greater. + * BL version 1.8 added this change according to PGV-173. + * + * RETURNS: + * !0 = Error + * 0 = Success + * + * PARAMETERS: + * *dev - pointer to device structure + * file_handle - File handle to read from + * offset - start offset for CRC calculation + * length - number of bytes to calculate CRC over + * read_buf - pointer to the read buffer + ******************************************************************************/ +int _pt_pip2_file_crc(struct device *dev, u8 file_handle, + u32 offset, u32 length, u8 *read_buf) +{ + int rc = 1; + u16 actual_read_len; + u8 data[10]; + + data[0] = file_handle; + data[1] = PIP2_FILE_IOCTL_CODE_FILE_CRC; + data[2] = 0xff & offset; + data[3] = 0xff & (offset >> 8); + data[4] = 0xff & (offset >> 16); + data[5] = 0xff & (offset >> 24); + data[6] = 0xff & length; + data[7] = 0xff & (length >> 8); + data[8] = 0xff & (length >> 16); + data[9] = 0xff & (length >> 24); + + rc = _pt_request_pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL, + data, 10, read_buf, &actual_read_len); + + if (rc) + pt_debug(dev, DL_ERROR, + "%s Return FILE_CRC failure, rc = %d\n", + __func__, rc); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_ping_test + * + * SUMMARY: BIST type test that uses the PIP2 PING command and ramps up the + * optional payload from 0 bytes to max_bytes and ensures the PIP2 + * response payload matches what was sent. + * The max payload size is 247: + * (255 - 2 byte reg address - 4 byte header - 2 byte CRC) + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +int pt_pip2_ping_test(struct device *dev, int max_bytes, int *last_packet_size) +{ + u16 actual_read_len; + u8 *read_buf = NULL; + u8 *data = NULL; + int data_offset = PIP2_RESP_STATUS_OFFSET; + int i = 1; + int j = 0; + int rc = 0; + + read_buf = kzalloc(PT_MAX_PIP2_MSG_SIZE, GFP_KERNEL); + if (!read_buf) + goto ping_test_exit; + + data = kzalloc(PT_MAX_PIP2_MSG_SIZE, GFP_KERNEL); + if (!data) + goto ping_test_exit; + + /* Load data payload with an array of walking 1's */ + for (i = 0; i < 255; i++) + data[i] = 0x01 << (i % 8); + + /* Send 'max_bytes' PING cmds using 'i' bytes as payload for each */ + for (i = 0; i <= max_bytes; i++) { + rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED, + PIP2_CMD_ID_PING, data, i, read_buf, + &actual_read_len); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: PING failed with %d byte payload\n", + __func__, i); + break; + } + /* Verify data returned matches data sent */ + for (j = 0; j < i; j++) { + if (read_buf[data_offset + j] != data[j]) { + pt_debug(dev, DL_DEBUG, + "%s: PING packet size %d: sent[%d]=0x%02X recv[%d]=0x%02X\n", + __func__, i, j, data[j], j, + read_buf[data_offset + j]); + goto ping_test_exit; + } + } + } + +ping_test_exit: + *last_packet_size = i - 1; + kfree(read_buf); + kfree(data); + return rc; +} + +#ifndef TTDL_KERNEL_SUBMISSION +/******************************************************************************* + * FUNCTION: _pt_ic_parse_input_hex + * + * SUMMARY: Parse a char data array as space delimited hex values into + * an int array. + * + * NOTE: _pt_ic_parse_input() function may have similar work while the type of + * buffer to store data is "u32". This function is still needed by the + * "command" sysfs node because the buffer type to store data is "u8". + * + * RETURN: Length of parsed data + * + * PARAMETERS: + * *dev - pointer to device structure + * *buf - pointer to buffer that holds the input array to parse + * buf_size - size of buf + * *ic_buf - pointer to array to store parsed data + * ic_buf_size - max size of ic_buf + ******************************************************************************/ +static int _pt_ic_parse_input_hex(struct device *dev, const char *buf, + size_t buf_size, u8 *ic_buf, size_t ic_buf_size) +{ + const char *pbuf = buf; + unsigned long value; + char scan_buf[PT_MAX_PIP2_MSG_SIZE + 1]; + u32 i = 0; + u32 j; + int last = 0; + int ret; + + pt_debug(dev, DL_DEBUG, + "%s: pbuf=%p buf=%p size=%zu %s=%d buf=%s\n", + __func__, pbuf, buf, buf_size, "scan buf size", + PT_MAX_PIP2_MSG_SIZE, buf); + + while (pbuf <= (buf + buf_size)) { + if (i >= PT_MAX_PIP2_MSG_SIZE) { + pt_debug(dev, DL_ERROR, "%s: %s size=%d max=%d\n", + __func__, "Max cmd size exceeded", i, + PT_MAX_PIP2_MSG_SIZE); + return -EINVAL; + } + if (i >= ic_buf_size) { + pt_debug(dev, DL_ERROR, "%s: %s size=%d buf_size=%zu\n", + __func__, "Buffer size exceeded", i, + ic_buf_size); + return -EINVAL; + } + while (((*pbuf == ' ') || (*pbuf == ',')) + && (pbuf < (buf + buf_size))) { + last = *pbuf; + pbuf++; + } + + if (pbuf >= (buf + buf_size)) + break; + + memset(scan_buf, 0, PT_MAX_PIP2_MSG_SIZE); + if ((last == ',') && (*pbuf == ',')) { + pt_debug(dev, DL_ERROR, "%s: %s \",,\" not allowed.\n", + __func__, "Invalid data format."); + return -EINVAL; + } + for (j = 0; j < (PT_MAX_PIP2_MSG_SIZE - 1) + && (pbuf < (buf + buf_size)) + && (*pbuf != ' ') + && (*pbuf != ','); j++) { + last = *pbuf; + scan_buf[j] = *pbuf++; + } + ret = kstrtoul(scan_buf, 16, &value); + if (ret < 0) { + pt_debug(dev, DL_ERROR, + "%s: %s '%s' %s%s i=%d r=%d\n", __func__, + "Invalid data format. ", scan_buf, + "Use \"0xHH,...,0xHH\"", " instead.", + i, ret); + return ret; + } + + ic_buf[i] = value; + pt_debug(dev, DL_DEBUG, "%s: item = %d, value = 0x%02lx", + __func__, i, value); + i++; + } + + return i; +} + +/******************************************************************************* + * FUNCTION: _pt_ic_parse_input + * + * SUMMARY: Parse user sysfs input data as a space or comma delimited list of + * hex values or dec values into an int array with the following rules: + * 1) Hex values must have a "0x" prefix for each element or the first element + * only + * 2) Dec values do not have any prefix + * 3) It is not allowed to have a mix of dec and hex values in the user input + * string + * + * RETURN: Number of values parsed + * + * PARAMETERS: + * *dev - pointer to device structure + * *buf - pointer to buffer that holds the input array to parse + * buf_size - size of buf + * *out_buf - pointer to array to store parsed data + * out_buf_size - max size of buffer to be stored + ******************************************************************************/ +static int _pt_ic_parse_input(struct device *dev, + const char *buf, size_t buf_size, + u32 *out_buf, size_t out_buf_size) +{ + const char *pbuf = buf; + unsigned long value; + char scan_buf[PT_MAX_PIP2_MSG_SIZE + 1]; + u32 i = 0; + u32 j; + int last = 0; + int ret = 0; + u8 str_base = 0; + + pt_debug(dev, DL_DEBUG, + "%s: in_buf_size=%zu out_buf_size=%zu %s=%d buf=%s\n", + __func__, buf_size, out_buf_size, "scan buf size", + PT_MAX_PIP2_MSG_SIZE, buf); + + while (pbuf <= (buf + buf_size)) { + if (i >= PT_MAX_PIP2_MSG_SIZE) { + pt_debug(dev, DL_ERROR, "%s: %s size=%d max=%d\n", + __func__, "Max cmd size exceeded", i, + PT_MAX_PIP2_MSG_SIZE); + ret = -EINVAL; + goto error; + } + if (i >= out_buf_size) { + pt_debug(dev, DL_ERROR, "%s: %s size=%d buf_size=%zu\n", + __func__, "Buffer size exceeded", i, + out_buf_size); + ret = -EINVAL; + goto error; + } + while (((*pbuf == ' ') || (*pbuf == ',')) + && (pbuf < (buf + buf_size))) { + last = *pbuf; + pbuf++; + } + + if (pbuf >= (buf + buf_size)) + break; + + memset(scan_buf, 0, PT_MAX_PIP2_MSG_SIZE); + if ((last == ',') && (*pbuf == ',')) { + pt_debug(dev, DL_ERROR, "%s: %s \",,\" not allowed.\n", + __func__, "Invalid data format."); + ret = -EINVAL; + goto error; + } + for (j = 0; j < (PT_MAX_PIP2_MSG_SIZE - 1) + && (pbuf < (buf + buf_size)) + && (*pbuf != ' ') + && (*pbuf != ','); j++) { + last = *pbuf; + scan_buf[j] = *pbuf++; + } + + if (i == 0) { + if ((strncmp(scan_buf, "0x", 2) == 0) || + (strncmp(scan_buf, "0X", 2) == 0)) + str_base = 16; + else + str_base = 10; + } else { + if (((strncmp(scan_buf, "0x", 2) == 0) || + (strncmp(scan_buf, "0X", 2) == 0)) && + (str_base == 10)) { + pt_debug(dev, DL_ERROR, + "%s: Decimal and Heximal data mixed\n", + __func__); + ret = -EINVAL; + goto error; + } + } + ret = kstrtoul(scan_buf, str_base, &value); + if (ret < 0) { + pt_debug(dev, DL_ERROR, + "%s: %s '%s' %s%s i=%d r=%d\n", __func__, + "Invalid data format. ", scan_buf, + "Use \"0xHH,...,0xHH\" or \"DD DD DD ... DD\"", + " instead.", i, ret); + goto error; + } + + out_buf[i] = value; + pt_debug(dev, DL_DEBUG, "%s: item = %d, value = 0x%02lx(%lu)", + __func__, i, value, value); + i++; + } + ret = i; +error: + return ret; +} +#endif /* !TTDL_KERNEL_SUBMISSION */ + +#ifdef TTHE_TUNER_SUPPORT +/******************************************************************************* + * FUNCTION: tthe_debugfs_open + * + * SUMMARY: Open method for tthe_tuner debugfs node. On some hosts the size of + * PT_MAX_PRBUF_SIZE (equal to PAGE_SIZE) is not large enough to handle + * printing a large number of fingers and sensor data without overflowing + * the buffer. tthe_tuner needs ~4K and so the buffer is sized to some + * even multiple of PAGE_SIZE + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *inode - file inode number + * *filp - file pointer to debugfs file + ******************************************************************************/ +static int tthe_debugfs_open(struct inode *inode, struct file *filp) +{ + struct pt_core_data *cd = inode->i_private; + u32 buf_size = PT_MAX_PRBUF_SIZE; + + filp->private_data = inode->i_private; + + if (cd->tthe_buf) + return -EBUSY; + + while (buf_size < 4096) + buf_size = buf_size << 1; + + pt_debug(cd->dev, DL_INFO, "%s:PT_MAX_BRBUF_SIZE=%d buf_size=%d\n", + __func__, (int)PT_MAX_PRBUF_SIZE, (int)buf_size); + + cd->tthe_buf_size = buf_size; + cd->tthe_buf = kzalloc(cd->tthe_buf_size, GFP_KERNEL); + if (!cd->tthe_buf) + return -ENOMEM; + + return 0; +} + +/******************************************************************************* + * FUNCTION: tthe_debugfs_close + * + * SUMMARY: Close method for tthe_tuner debugfs node. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *inode - file inode number + * *filp - file pointer to debugfs file + ******************************************************************************/ +static int tthe_debugfs_close(struct inode *inode, struct file *filp) +{ + struct pt_core_data *cd = filp->private_data; + + filp->private_data = NULL; + + kfree(cd->tthe_buf); + cd->tthe_buf = NULL; + + return 0; +} + +/******************************************************************************* + * FUNCTION: tthe_debugfs_store + * + * SUMMARY: Write method for tthe_tuner debugfs node. This function allows + * user configuration of some output values via a bitmask. + * 0x01 = HID USB vs I2C output + * 0xFE = Reserved + * + * RETURN: Size of debugfs data write + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t tthe_debugfs_store(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct pt_core_data *cd = filp->private_data; + ssize_t length; + u32 input_data[2]; + u8 tmp_buf[4]; /* large enough for 1 32bit value */ + int rc = 0; + + mutex_lock(&cd->tthe_lock); + + /* copy data from user space */ + rc = simple_write_to_buffer(tmp_buf, sizeof(tmp_buf), + ppos, buf, count); + if (rc < 0) + goto exit; + + length = _pt_ic_parse_input(cd->dev, tmp_buf, count, + input_data, ARRAY_SIZE(input_data)); + + if (length == 1) { + mutex_lock(&(cd->system_lock)); + cd->tthe_hid_usb_format = input_data[0]; + if (input_data[0] == PT_TTHE_TUNER_FORMAT_HID_USB) + pt_debug(cd->dev, DL_INFO, + "%s: Enable tthe_tuner HID-USB format\n", + __func__); + else if (input_data[0] == PT_TTHE_TUNER_FORMAT_HID_I2C) + pt_debug(cd->dev, DL_INFO, + "%s: Enable tthe_tuner HID-I2C format\n", + __func__); + else if (input_data[0] == + PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP) + pt_debug(cd->dev, DL_INFO, + "%s: Enable tthe_tuner HID-FINGER-TO-PIP format\n", + __func__); + else if (input_data[0] == + PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP) + pt_debug(cd->dev, DL_INFO, + "%s: Enable tthe_tuner HID_FINGER_AND_PEN_TO_PIP format\n", + __func__); + else { + rc = -EINVAL; + pt_debug(cd->dev, DL_ERROR, + "%s: Invalid parameter: %d\n", + __func__, input_data[0]); + } + mutex_unlock(&(cd->system_lock)); + } else { + rc = -EINVAL; + pt_debug(cd->dev, DL_ERROR, + "%s: Invalid number of parameters\n", __func__); + } + +exit: + mutex_unlock(&cd->tthe_lock); + if (rc) + return rc; + else + return count; +} + +/******************************************************************************* + * FUNCTION: tthe_debugfs_read + * + * SUMMARY: Read method for tthe_tuner debugfs node. This function prints + * tthe_buf to user buffer. + * + * RETURN: Size of debugfs data print + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t tthe_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct pt_core_data *cd = filp->private_data; + int size; + int ret; + static int partial_read; + + wait_event_interruptible(cd->wait_q, + cd->tthe_buf_len != 0 || cd->tthe_exit); + mutex_lock(&cd->tthe_lock); + if (cd->tthe_exit) { + mutex_unlock(&cd->tthe_lock); + return 0; + } + if (count > cd->tthe_buf_len) + size = cd->tthe_buf_len; + else + size = count; + if (!size) { + mutex_unlock(&cd->tthe_lock); + return 0; + } + + if (partial_read) { + ret = copy_to_user(buf, cd->tthe_buf + partial_read, size); + partial_read = 0; + } else { + ret = copy_to_user(buf, cd->tthe_buf, size); + } + + /* + * When size >= tthe_buf_len setting partial_read will cause NULL + * characters to be printed in the output. + */ + if (size == count && size < cd->tthe_buf_len) + partial_read = count; + + if (ret == size) { + mutex_unlock(&cd->tthe_lock); + return -EFAULT; + } + size -= ret; + cd->tthe_buf_len -= size; + mutex_unlock(&cd->tthe_lock); + *ppos += size; + return size; +} + +static const struct file_operations tthe_debugfs_fops = { + .open = tthe_debugfs_open, + .release = tthe_debugfs_close, + .read = tthe_debugfs_read, + .write = tthe_debugfs_store, +}; +#endif + +static struct pt_core_nonhid_cmd _pt_core_nonhid_cmd = { + .start_bl = _pt_request_pip_start_bl, + .suspend_scanning = _pt_request_pip_suspend_scanning, + .resume_scanning = _pt_request_pip_resume_scanning, + .get_param = _pt_request_pip_get_param, + .set_param = _pt_request_pip_set_param, + .verify_cfg_block_crc = _pt_request_pip_verify_config_block_crc, + .get_config_row_size = _pt_request_pip_get_config_row_size, + .get_data_structure = _pt_request_pip_get_data_structure, + .run_selftest = _pt_request_pip_run_selftest, + .get_selftest_result = _pt_request_pip_get_selftest_result, + .load_self_test_param = _pt_request_pip_load_self_test_param, + .calibrate_idacs = _pt_request_pip_calibrate_idacs, + .calibrate_ext = _pt_request_pip_calibrate_ext, + .initialize_baselines = _pt_request_pip_initialize_baselines, + .exec_panel_scan = _pt_request_pip_exec_panel_scan, + .retrieve_panel_scan = _pt_request_pip_retrieve_panel_scan, + .read_data_block = _pt_request_pip_read_data_block, + .write_data_block = _pt_request_pip_write_data_block, + .user_cmd = _pt_request_pip_user_cmd, + .get_bl_info = _pt_request_pip_bl_get_information, + .initiate_bl = _pt_request_pip_bl_initiate_bl, + .launch_app = _pt_request_pip_launch_app, + .prog_and_verify = _pt_request_pip_bl_program_and_verify, + .verify_app_integrity = _pt_request_pip_bl_verify_app_integrity, + .get_panel_id = _pt_request_pip_bl_get_panel_id, + .pip2_send_cmd = _pt_request_pip2_send_cmd, + .pip2_send_cmd_no_int = _pt_pip2_send_cmd_no_int, + .pip2_file_open = _pt_pip2_file_open, + .pip2_file_close = _pt_pip2_file_close, + .pip2_file_erase = _pt_pip2_file_erase, + .read_us_file = _pt_read_us_file, + .manage_cal_data = _pt_manage_local_cal_data, + .calc_crc = crc_ccitt_calculate, +#ifdef TTDL_DIAGNOSTICS + .pip2_file_read = _pt_pip2_file_read, + .pip2_file_seek_offset = _pt_pip2_file_seek_offset, + .pip2_file_get_stats = _pt_pip2_file_get_stats, + .pip2_file_crc = _pt_pip2_file_crc, +#endif +}; + +static struct pt_core_commands _pt_core_commands = { + .subscribe_attention = _pt_subscribe_attention, + .unsubscribe_attention = _pt_unsubscribe_attention, + .request_exclusive = _pt_request_exclusive, + .release_exclusive = _pt_release_exclusive, + .request_reset = _pt_request_reset, + .request_pip2_launch_app = _pt_request_pip2_launch_app, + .request_enum = _pt_request_enum, + .request_sysinfo = _pt_request_sysinfo, + .request_loader_pdata = _pt_request_loader_pdata, + .request_stop_wd = _pt_request_stop_wd, + .request_start_wd = _pt_request_start_wd, + .request_get_mode = _pt_request_get_mode, + .request_active_pip_prot = _pt_request_active_pip_protocol, + .request_pip2_get_mode_sysmode = _pt_request_pip2_get_mode_sysmode, + .request_pip2_enter_bl = _pt_request_pip2_enter_bl, + .request_pip2_bin_hdr = _pt_request_pip2_bin_hdr, + .request_dut_generation = _pt_request_dut_generation, + .request_hw_version = _pt_request_hw_version, +#ifndef TTDL_KERNEL_SUBMISSION + .parse_sysfs_input = _pt_ic_parse_input, +#endif +#ifdef TTHE_TUNER_SUPPORT + .request_tthe_print = _pt_request_tthe_print, +#endif +#ifdef TTDL_DIAGNOSTICS + .request_toggle_err_gpio = _pt_request_toggle_err_gpio, +#endif + .nonhid_cmd = &_pt_core_nonhid_cmd, + .request_get_fw_mode = _pt_request_get_fw_sys_mode, +}; + +struct pt_core_commands *pt_get_commands(void) +{ + return &_pt_core_commands; +} +EXPORT_SYMBOL_GPL(pt_get_commands); + +static DEFINE_MUTEX(core_list_lock); +static LIST_HEAD(core_list); +static DEFINE_MUTEX(module_list_lock); +static LIST_HEAD(module_list); +static int core_number; + +/******************************************************************************* + * FUNCTION: pt_probe_module + * + * SUMMARY: Add the module pointer to module_node and call the probe pointer. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + * *module - pointer to module structure + ******************************************************************************/ +static int pt_probe_module(struct pt_core_data *cd, + struct pt_module *module) +{ + struct module_node *module_node; + int rc = 0; + + module_node = kzalloc(sizeof(*module_node), GFP_KERNEL); + if (!module_node) + return -ENOMEM; + + module_node->module = module; + + mutex_lock(&cd->module_list_lock); + list_add(&module_node->node, &cd->module_list); + mutex_unlock(&cd->module_list_lock); + + rc = module->probe(cd->dev, &module_node->data); + if (rc) { + /* + * Remove from the list when probe fails + * in order not to call release + */ + mutex_lock(&cd->module_list_lock); + list_del(&module_node->node); + mutex_unlock(&cd->module_list_lock); + kfree(module_node); + goto exit; + } + +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_release_module + * + * SUMMARY: Call the release pointer and remove the module pointer from + * module_list. + * + * PARAMETERS: + * *cd - pointer to core data + * *module - pointer to module structure + ******************************************************************************/ +static void pt_release_module(struct pt_core_data *cd, + struct pt_module *module) +{ + struct module_node *m, *m_n; + + mutex_lock(&cd->module_list_lock); + list_for_each_entry_safe(m, m_n, &cd->module_list, node) + if (m->module == module) { + module->release(cd->dev, m->data); + list_del(&m->node); + kfree(m); + break; + } + mutex_unlock(&cd->module_list_lock); +} + +/******************************************************************************* + * FUNCTION: pt_probe_modules + * + * SUMMARY: Iterate module_list and probe each module. + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static void pt_probe_modules(struct pt_core_data *cd) +{ + struct pt_module *m; + int rc = 0; + + mutex_lock(&module_list_lock); + list_for_each_entry(m, &module_list, node) { + pt_debug(cd->dev, DL_WARN, "%s: Probe module %s\n", + __func__, m->name); + rc = pt_probe_module(cd, m); + if (rc) + pt_debug(cd->dev, DL_ERROR, + "%s: Probe fails for module %s\n", + __func__, m->name); + } + mutex_unlock(&module_list_lock); +} + +/******************************************************************************* + * FUNCTION: pt_release_modules + * + * SUMMARY: Iterate module_list and remove each module. + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static void pt_release_modules(struct pt_core_data *cd) +{ + struct pt_module *m; + + mutex_lock(&module_list_lock); + list_for_each_entry(m, &module_list, node) + pt_release_module(cd, m); + mutex_unlock(&module_list_lock); +} + +/******************************************************************************* + * FUNCTION: pt_get_core_data + * + * SUMMARY: Iterate core_list and get core data. + * + * RETURN: + * pointer to core data or null pointer if fail + * + * PARAMETERS: + * *id - pointer to core id + ******************************************************************************/ +struct pt_core_data *pt_get_core_data(char *id) +{ + struct pt_core_data *d; + + list_for_each_entry(d, &core_list, node) + if (!strncmp(d->core_id, id, 20)) + return d; + return NULL; +} +EXPORT_SYMBOL_GPL(pt_get_core_data); + +/******************************************************************************* + * FUNCTION: pt_add_core + * + * SUMMARY: Add core data to the core_list. + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static void pt_add_core(struct device *dev) +{ + struct pt_core_data *d; + struct pt_core_data *cd = dev_get_drvdata(dev); + + mutex_lock(&core_list_lock); + list_for_each_entry(d, &core_list, node) + if (d->dev == dev) + goto unlock; + + list_add(&cd->node, &core_list); +unlock: + mutex_unlock(&core_list_lock); +} + +/******************************************************************************* + * FUNCTION: pt_del_core + * + * SUMMARY: Remove core data from the core_list. + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static void pt_del_core(struct device *dev) +{ + struct pt_core_data *d, *d_n; + + mutex_lock(&core_list_lock); + list_for_each_entry_safe(d, d_n, &core_list, node) + if (d->dev == dev) { + list_del(&d->node); + goto unlock; + } +unlock: + mutex_unlock(&core_list_lock); +} + +/******************************************************************************* + * FUNCTION: pt_register_module + * + * SUMMARY: Register the module to module_list and probe the module for each + * core. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *module - pointer to module structure + ******************************************************************************/ +int pt_register_module(struct pt_module *module) +{ + struct pt_module *m; + struct pt_core_data *cd; + + int rc = 0; + + if (!module || !module->probe || !module->release) + return -EINVAL; + + mutex_lock(&module_list_lock); + list_for_each_entry(m, &module_list, node) + if (m == module) { + rc = -EEXIST; + goto unlock; + } + + list_add(&module->node, &module_list); + + /* Probe the module for each core */ + mutex_lock(&core_list_lock); + list_for_each_entry(cd, &core_list, node) + pt_probe_module(cd, module); + mutex_unlock(&core_list_lock); + +unlock: + mutex_unlock(&module_list_lock); + return rc; +} +EXPORT_SYMBOL_GPL(pt_register_module); + +/******************************************************************************* + * FUNCTION: pt_unregister_module + * + * SUMMARY: Release the module for each core and remove the module from + * module_list. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *module - pointer to module structure + ******************************************************************************/ +void pt_unregister_module(struct pt_module *module) +{ + struct pt_module *m, *m_n; + struct pt_core_data *cd; + + if (!module) + return; + + mutex_lock(&module_list_lock); + + /* Release the module for each core */ + mutex_lock(&core_list_lock); + list_for_each_entry(cd, &core_list, node) + pt_release_module(cd, module); + mutex_unlock(&core_list_lock); + + list_for_each_entry_safe(m, m_n, &module_list, node) + if (m == module) { + list_del(&m->node); + break; + } + + mutex_unlock(&module_list_lock); +} +EXPORT_SYMBOL_GPL(pt_unregister_module); + +/******************************************************************************* + * FUNCTION: pt_get_module_data + * + * SUMMARY: Get module data from module_node by module_list. + * + * RETURN: + * pointer to module data + * + * PARAMETERS: + * *dev - pointer to device structure + * *module - pointer to module structure + ******************************************************************************/ +void *pt_get_module_data(struct device *dev, struct pt_module *module) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct module_node *m; + void *data = NULL; + + mutex_lock(&cd->module_list_lock); + list_for_each_entry(m, &cd->module_list, node) + if (m->module == module) { + data = m->data; + break; + } + mutex_unlock(&cd->module_list_lock); + + return data; +} +EXPORT_SYMBOL(pt_get_module_data); + +#ifdef CONFIG_HAS_EARLYSUSPEND +/******************************************************************************* + * FUNCTION: pt_early_suspend + * + * SUMMARY: Android PM architecture function that will call "PT_ATTEN_SUSPEND" + * attention list. + * + * PARAMETERS: + * *h - pointer to early_suspend structure + ******************************************************************************/ +static void pt_early_suspend(struct early_suspend *h) +{ + struct pt_core_data *cd = + container_of(h, struct pt_core_data, es); + + call_atten_cb(cd, PT_ATTEN_SUSPEND, 0); +} + +/******************************************************************************* + * FUNCTION: pt_late_resume + * + * SUMMARY: Android PM architecture function that will call "PT_ATTEN_RESUME" + * attention list. + * + * PARAMETERS: + * *h - pointer to early_suspend structure + ******************************************************************************/ +static void pt_late_resume(struct early_suspend *h) +{ + struct pt_core_data *cd = + container_of(h, struct pt_core_data, es); + + call_atten_cb(cd, PT_ATTEN_RESUME, 0); +} + +/******************************************************************************* + * FUNCTION: pt_setup_early_suspend + * + * SUMMARY: Register early/suspend function to the system. + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static void pt_setup_early_suspend(struct pt_core_data *cd) +{ + cd->es.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + cd->es.suspend = pt_early_suspend; + cd->es.resume = pt_late_resume; + + register_early_suspend(&cd->es); +} +#elif defined(CONFIG_FB) +/******************************************************************************* + * FUNCTION: fb_notifier_callback + * + * SUMMARY: Call back function for FrameBuffer notifier to allow to call + * resume/suspend attention list. + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *self - pointer to notifier_block structure + * event - event type of fb notifier + * *data - pointer to fb_event structure + ******************************************************************************/ +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct pt_core_data *cd = + container_of(self, struct pt_core_data, fb_notifier); + struct fb_event *evdata = data; + int *blank; + + if (event != FB_EVENT_BLANK || !evdata) + goto exit; + + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) { + pt_debug(cd->dev, DL_INFO, "%s: UNBLANK!\n", __func__); + if (cd->fb_state != FB_ON) { + call_atten_cb(cd, PT_ATTEN_RESUME, 0); +#if defined(CONFIG_PM_SLEEP) + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) + pt_core_resume_(cd->dev); +#endif + cd->fb_state = FB_ON; + } + } else if (*blank == FB_BLANK_POWERDOWN) { + pt_debug(cd->dev, DL_INFO, "%s: POWERDOWN!\n", __func__); + if (cd->fb_state != FB_OFF) { +#if defined(CONFIG_PM_SLEEP) + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) + pt_core_suspend_(cd->dev); +#endif + call_atten_cb(cd, PT_ATTEN_SUSPEND, 0); + cd->fb_state = FB_OFF; + } + } + +exit: + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_setup_fb_notifier + * + * SUMMARY: Set up call back function into fb notifier. + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static void pt_setup_fb_notifier(struct pt_core_data *cd) +{ + int rc = 0; + + cd->fb_state = FB_ON; + + cd->fb_notifier.notifier_call = fb_notifier_callback; + + rc = fb_register_client(&cd->fb_notifier); + if (rc) + pt_debug(cd->dev, DL_ERROR, + "Unable to register fb_notifier: %d\n", rc); +} +#endif + +/******************************************************************************* + * FUNCTION: pt_watchdog_work + * + * SUMMARY: This is where the watchdog work is done except if the DUT is + * sleeping then this function simply returns. If the DUT is awake the + * first thing is to ensure the IRQ is not stuck asserted meaning that + * somehow a response is waiting on the DUT that has not been read. If + * this occurs the message is simply consumed. If or once the IRQ is + * cleared, a PIP PING message is sent to the DUT and if the response + * is received the watchdog succeeds and exits, if no response is seen + * a startup is queued unless the maximum number of startups have already + * been attempted, in that case a BL is attempted. + * + * NOTE: pt_stop_wd_timer() cannot be called within the context of this + * work thread + * + * RETURN: void + * + * PARAMETERS: + * *work - pointer to a work structure for the watchdog work queue + ******************************************************************************/ +static void pt_watchdog_work(struct work_struct *work) +{ + int rc = 0; + struct pt_core_data *cd = container_of(work, + struct pt_core_data, watchdog_work); + + /* + * if found the current sleep_state is SS_SLEEPING + * then no need to request_exclusive, directly return + */ + if (cd->sleep_state == SS_SLEEPING) + return; + +#ifdef TTDL_DIAGNOSTICS + cd->watchdog_count++; +#endif /* TTDL_DIAGNOSTICS */ + + /* + * The first WD interval was extended to allow DDI to come up. + * If the WD interval is not the default then adjust timer to the + * current setting. The user can override value with drv_debug sysfs. + */ + if (cd->watchdog_interval != PT_WATCHDOG_TIMEOUT) { + mod_timer_pending(&cd->watchdog_timer, jiffies + + msecs_to_jiffies(cd->watchdog_interval)); + } + + if (pt_check_irq_asserted(cd)) { +#ifdef TTDL_DIAGNOSTICS + cd->watchdog_irq_stuck_count++; + pt_toggle_err_gpio(cd, PT_ERR_GPIO_IRQ_STUCK); +#endif /* TTDL_DIAGNOSTICS */ + pt_debug(cd->dev, DL_WARN, + "%s: TTDL WD found IRQ asserted, attempt to clear\n", + __func__); + pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL); + } + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + goto queue_startup; + } + + rc = pt_pip_null_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + +queue_startup: + if (rc) { +#ifdef TTDL_DIAGNOSTICS + cd->watchdog_failed_access_count++; + pt_toggle_err_gpio(cd, PT_ERR_GPIO_EXCLUSIVE_ACCESS); +#endif /* TTDL_DIAGNOSTICS */ + pt_debug(cd->dev, DL_ERROR, + "%s: failed to access device in WD, retry count=%d\n", + __func__, cd->startup_retry_count); + + /* Already tried FW upgrade because of watchdog but failed */ + if (cd->startup_retry_count > PT_WATCHDOG_RETRY_COUNT) + return; + + if (cd->startup_retry_count++ < PT_WATCHDOG_RETRY_COUNT) { + /* + * Any wrapper function that trys to disable the + * WD killing this worker cannot be called here. + */ + rc = request_exclusive(cd, cd->dev, + PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + goto exit; + } + + cd->hw_detected = false; + cd->startup_status = STARTUP_STATUS_START; + pt_debug(cd->dev, DL_DEBUG, + "%s: Startup Status Reset\n", __func__); + rc = pt_dut_reset_and_wait(cd); + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", + __func__); + if (!rc) { + cd->hw_detected = true; + if (!cd->flashless_dut) + pt_queue_enum(cd); + } +#ifdef TTDL_DIAGNOSTICS + cd->wd_xres_count++; + pt_debug(cd->dev, DL_WARN, + "%s: Comm Failed - DUT reset [#%d]\n", + __func__, cd->wd_xres_count); +#endif /* TTDL_DIAGNOSTICS */ + } else { + /* + * After trying PT_WATCHDOG_RETRY_COUNT times to + * reset the part to regain communications, try to BL + */ + pt_debug(cd->dev, DL_WARN, + "%s: WD DUT access failure, Start FW Upgrade\n", + __func__); +#ifdef TTDL_DIAGNOSTICS + /* + * When diagnostics is enabled allow TTDL to keep + * trying to find the DUT. This allows the DUT to be + * hot swap-able while the host stays running. In + * production this may not be wanted as a customer + * may have several touch drivers and any driver + * that doesn't match the current DUT should give + * up trying and give up using the bus. + */ + pt_debug(cd->dev, DL_INFO, + "%s: Resetting startup_retry_count\n", + __func__); + cd->startup_retry_count = 0; +#endif /* TTDL_DIAGNOSTICS */ + /* + * Since fw may be broken,reset sysinfo ready flag + * to let upgrade function work. + */ + mutex_lock(&cd->system_lock); + cd->sysinfo.ready = false; + mutex_unlock(&cd->system_lock); + + if (cd->active_dut_generation == DUT_UNKNOWN) { + pt_debug(cd->dev, DL_WARN, + "%s: Queue Restart\n", __func__); + pt_queue_restart(cd); + } else + kthread_run(start_fw_upgrade, cd, "pt_loader"); + } + } else { + cd->hw_detected = true; + if (cd->startup_status <= (STARTUP_STATUS_FW_RESET_SENTINEL | + STARTUP_STATUS_BL_RESET_SENTINEL)) { + pt_debug(cd->dev, DL_WARN, + "%s: HW detected but not enumerated\n", + __func__); + pt_queue_enum(cd); + } + } + +exit: + pt_start_wd_timer(cd); +} + +#if (KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE) +/******************************************************************************* + * FUNCTION: pt_watchdog_timer + * + * SUMMARY: The function that is called when the WD timer expires. If the + * watchdog work is not already busy schedule the watchdog work queue. + * + * RETURN: void + * + * PARAMETERS: + * handle - Handle to the watchdog timer + ******************************************************************************/ +static void pt_watchdog_timer(unsigned long handle) +{ + struct pt_core_data *cd = (struct pt_core_data *)handle; + + if (!cd) + return; + + pt_debug(cd->dev, DL_DEBUG, "%s: Watchdog timer triggered\n", + __func__); + + if (!work_pending(&cd->watchdog_work)) + schedule_work(&cd->watchdog_work); +} +#else +/******************************************************************************* + * FUNCTION: pt_watchdog_timer + * + * SUMMARY: The function that is called when the WD timer expires. If the + * watchdog work is not already busy schedule the watchdog work queue. + * + * RETURN: void + * + * PARAMETERS: + * *t - Pointer to timer list + ******************************************************************************/ +static void pt_watchdog_timer(struct timer_list *t) +{ + struct pt_core_data *cd = from_timer(cd, t, watchdog_timer); + + if (!cd) + return; + + pt_debug(cd->dev, DL_DEBUG, "%s: Watchdog timer triggered\n", + __func__); + + if (!work_pending(&cd->watchdog_work)) + schedule_work(&cd->watchdog_work); +} +#endif + +#ifdef PT_PTSBC_SUPPORT +/* Required to support the Parade Techologies Development Platform */ +static int pt_probe_complete(struct pt_core_data *cd); + +/******************************************************************************* + * FUNCTION: pt_probe_work + * + * SUMMARY: For the PtSBC the probe functionality is split into two functions; + * pt_probe() and pt_probe_complete() which is called from here. + * This function is scheduled as a "work" task in order to launch after + * I2C/SPI is up. + * + * RETURN: Void + * + * PARAMETERS: + * *work - pointer to work structure + ******************************************************************************/ +static void pt_probe_work(struct work_struct *work) +{ + struct pt_core_data *cd = + container_of(work, struct pt_core_data, + probe_work); + int rc; + + rc = pt_probe_complete(cd); + + if (rc < 0) + pr_err("%s: Probe_complete returns rc=%d\n", __func__, rc); + else + pt_debug(cd->dev, DL_INFO, + "%s: Probe_complete returns rc=%d\n", __func__, rc); +} + +/******************************************************************************* + * FUNCTION: pt_probe_timer + * + * SUMMARY: For the PtSBC the probe functionality is split into two functions; + * pt_probe() and pt_probe_complete(). This timer shedules the + * probe_work function. + * + * RETURN: Void + * + * PARAMETERS: + * handle - pointer to the core data + ******************************************************************************/ +static void pt_probe_timer(unsigned long handle) +{ + struct pt_core_data *cd = (struct pt_core_data *)handle; + + if (!cd) + return; + + pt_debug(cd->dev, DL_INFO, "%s: Watchdog timer triggered\n", + __func__); + + if (!work_pending(&cd->probe_work)) + schedule_work(&cd->probe_work); +} +#endif /* --- End PT_PTSBC_SUPPORT --- */ + + + +/******************************************************************************* + * Core sysfs show and store functions + ******************************************************************************/ + +/******************************************************************************* + * FUNCTION: pt_hw_version_show + * + * SUMMARY: Gets the HW version for either PIP1.x or PIP2.x DUTS + * Output data format: [SiliconID].[RevID FamilyID].[PanelID] + * + * RETURN: size of data written to sysfs node + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes structure + * *buf - pointer to print output buffer + ******************************************************************************/ +static ssize_t pt_hw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + _pt_request_hw_version(dev, cd->hw_version); + return snprintf(buf, PT_MAX_PRBUF_SIZE, "%s\n", cd->hw_version); +} +static DEVICE_ATTR(hw_version, 0444, pt_hw_version_show, NULL); + +/******************************************************************************* + * FUNCTION: pt_drv_version_show + * + * SUMMARY: Show method for the drv_version sysfs node that will show the + * TTDL version information + * + * RETURN: Char buffer with printed TTDL version information + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_drv_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PT_MAX_PRBUF_SIZE, + "Driver: %s\nVersion: %s\nDate: %s\n", + pt_driver_core_name, pt_driver_core_version, + pt_driver_core_date); +} +static DEVICE_ATTR(drv_version, 0444, pt_drv_version_show, NULL); +static DEVICE_ATTR(drv_ver, 0444, pt_drv_version_show, NULL); + +/******************************************************************************* + * FUNCTION: pt_fw_version_show + * + * SUMMARY: Show method for the fw_version sysfs node that will + * show the firmware, bootloader and PIP version information + * + * RETURN: Size of printed buffer + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_ttdata *ttdata; + int rc = 0; + + if (cd->mode == PT_MODE_OPERATIONAL) + rc = pt_hid_output_get_sysinfo_(cd); + + pt_debug(cd->dev, DL_INFO, "%s: mode = %d sysinfo.ready = %d\n", + __func__, cd->mode, cd->sysinfo.ready); + + if (cd->sysinfo.ready) + ttdata = &cd->sysinfo.ttdata; + else + rc = -ENODATA; + + if (!rc) { + return scnprintf(buf, strlen(buf), + "Status: %d\n" + "FW : %d.%d.%d\n" + "Config: %d\n" + "BL : %d.%d\n" + "PIP : %d.%d\n", + rc, + ttdata->fw_ver_major, ttdata->fw_ver_minor, + ttdata->revctrl, + ttdata->fw_ver_conf, + ttdata->bl_ver_major, ttdata->bl_ver_minor, + ttdata->pip_ver_major, ttdata->pip_ver_minor); + } else { + return scnprintf(buf, strlen(buf), + "Status: %d\n" + "FW : n/a\n" + "Config: n/a\n" + "BL : n/a\n" + "PIP : n/a\n", + rc); + } +} +static DEVICE_ATTR(fw_version, 0444, pt_fw_version_show, NULL); + +/******************************************************************************* + * FUNCTION: pt_sysinfo_show + * + * SUMMARY: Show method for the sysinfo sysfs node that will + * show all the information from get system information command. + * + * RETURN: Size of printed buffer + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_sysinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_sysinfo *si; + struct pt_ttdata *ttdata = NULL; + struct pt_sensing_conf_data *scd = NULL; + int rc = 0; + + if (cd->mode == PT_MODE_OPERATIONAL) { + rc = pt_hid_output_get_sysinfo_(cd); + if (cd->sysinfo.ready) { + si = &cd->sysinfo; + ttdata = &si->ttdata; + scd = &si->sensing_conf_data; + } else + rc = -ENODATA; + } else + rc = -EPERM; + + pt_debug(cd->dev, DL_INFO, "%s: mode = %d sysinfo.ready = %d\n", + __func__, cd->mode, cd->sysinfo.ready); + + if (!rc && ttdata && scd) { + return scnprintf(buf, strlen(buf), + "Status: %d\n" + "pip_ver_major: 0x%02X\n" + "pip_ver_minor: 0x%02X\n" + "fw_pid : 0x%04X\n" + "fw_ver_major : 0x%02X\n" + "fw_ver_minor : 0x%02X\n" + "revctrl : 0x%08X\n" + "fw_ver_conf : 0x%04X\n" + "bl_ver_major : 0x%02X\n" + "bl_ver_minor : 0x%02X\n" + "jtag_id_h : 0x%04X\n" + "jtag_id_l : 0x%04X\n" + "mfg_id[0] : 0x%02X\n" + "mfg_id[1] : 0x%02X\n" + "mfg_id[2] : 0x%02X\n" + "mfg_id[3] : 0x%02X\n" + "mfg_id[4] : 0x%02X\n" + "mfg_id[5] : 0x%02X\n" + "mfg_id[6] : 0x%02X\n" + "mfg_id[7] : 0x%02X\n" + "post_code : 0x%04X\n" + "electrodes_x : 0x%02X\n" + "electrodes_y : 0x%02X\n" + "len_x : 0x%04X\n" + "len_y : 0x%04X\n" + "res_x : 0x%04X\n" + "res_y : 0x%04X\n" + "max_z : 0x%04X\n" + "origin_x : 0x%02X\n" + "origin_y : 0x%02X\n" + "panel_id : 0x%02X\n" + "btn : 0x%02X\n" + "scan_mode : 0x%02X\n" + "max_num_of_tch_per_refresh_cycle: 0x%02X\n", + rc, + ttdata->pip_ver_major, + ttdata->pip_ver_minor, + ttdata->fw_pid, + ttdata->fw_ver_major, + ttdata->fw_ver_minor, + ttdata->revctrl, + ttdata->fw_ver_conf, + ttdata->bl_ver_major, + ttdata->bl_ver_minor, + ttdata->jtag_id_h, + ttdata->jtag_id_l, + ttdata->mfg_id[0], + ttdata->mfg_id[1], + ttdata->mfg_id[2], + ttdata->mfg_id[3], + ttdata->mfg_id[4], + ttdata->mfg_id[5], + ttdata->mfg_id[6], + ttdata->mfg_id[7], + ttdata->post_code, + scd->electrodes_x, + scd->electrodes_y, + scd->len_x, + scd->len_y, + scd->res_x, + scd->res_y, + scd->max_z, + scd->origin_x, + scd->origin_y, + scd->panel_id, + scd->btn, + scd->scan_mode, + scd->max_tch); + } else { + return scnprintf(buf, strlen(buf), + "Status: %d\n", + rc); + } +} +static DEVICE_ATTR(sysinfo, 0444, pt_sysinfo_show, NULL); + +#ifndef TTDL_KERNEL_SUBMISSION +/******************************************************************************* + * FUNCTION: pt_hw_reset_show + * + * SUMMARY: The show method for the hw_reset sysfs node that does a hw reset + * by toggling the XRES line and then calls the startup function to + * allow TTDL to re-enumerate the DUT. + * The printed value reflects the status of the full reset/enum. + * + * PARAMETERS: + * *dev - pointer to Device structure + * *attr - pointer to the device attribute structure + * *buf - pointer to buffer to print + ******************************************************************************/ +static ssize_t pt_hw_reset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int rc = 0; + int time = 0; + u8 reset_status = 0; + int t; + struct pt_hid_desc hid_desc; + + memset(&hid_desc, 0, sizeof(hid_desc)); + + /* Only allow DUT reset if no active BL in progress */ + mutex_lock(&cd->firmware_class_lock); + + mutex_lock(&cd->system_lock); + cd->startup_state = STARTUP_NONE; + mutex_unlock(&(cd->system_lock)); + + pt_stop_wd_timer(cd); + + /* ensure no left over exclusive access is still locked */ + release_exclusive(cd, cd->dev); + + rc = pt_dut_reset(cd, PT_CORE_CMD_PROTECTED); + if (rc) { + mutex_unlock(&cd->firmware_class_lock); + pt_debug(cd->dev, DL_ERROR, + "%s: HW reset failed rc = %d\n", __func__, rc); + goto exit_hw_reset; + } + reset_status |= 0x01 << 0; + + if (cd->flashless_dut) { + mutex_unlock(&cd->firmware_class_lock); + + t = wait_event_timeout(cd->wait_q, (cd->fw_updating == true), + msecs_to_jiffies(200)); + if (IS_TMO(t)) { + pt_debug(dev, DL_WARN, + "%s: Timeout waiting for FW update", + __func__); + rc = -ETIME; + goto exit_hw_reset; + } else { + pt_debug(dev, DL_INFO, + "%s: ----- Wait FW Loading ----", + __func__); + rc = _pt_request_wait_for_enum_state( + dev, 4000, STARTUP_STATUS_FW_RESET_SENTINEL); + if (rc) { + pt_debug(cd->dev, DL_ERROR, + "%s: No FW Sentinel detected rc = %d\n", + __func__, rc); + goto exit_hw_reset; + } + reset_status |= 0x01 << 1; + } + } else { + /* Wait for any sentinel */ + rc = _pt_request_wait_for_enum_state(dev, 250, + STARTUP_STATUS_BL_RESET_SENTINEL | + STARTUP_STATUS_FW_RESET_SENTINEL); + if (rc) { + mutex_unlock(&cd->firmware_class_lock); + pt_debug(cd->dev, DL_ERROR, + "%s: No Sentinel detected rc = %d\n", + __func__, rc); + goto exit_hw_reset; + } + + /* sleep needed to ensure no cmd is sent while DUT will NAK */ + msleep(30); + + if (cd->active_dut_generation == DUT_PIP1_ONLY) { + rc = pt_get_hid_descriptor_(cd, &hid_desc); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on getting HID descriptor r=%d\n", + __func__, rc); + goto exit_hw_reset; + } + + cd->mode = pt_get_mode(cd, &hid_desc); + if (cd->mode == PT_MODE_BOOTLOADER) + rc = pt_hid_output_bl_launch_app_(cd); + } else { + if (cd->mode == PT_MODE_BOOTLOADER) + rc = pt_pip2_launch_app(dev, + PT_CORE_CMD_UNPROTECTED); + } + + if (rc) { + mutex_unlock(&cd->firmware_class_lock); + pt_debug(cd->dev, DL_ERROR, + "%s: PIP launch app failed rc = %d\n", + __func__, rc); + goto exit_hw_reset; + } + mutex_unlock(&cd->firmware_class_lock); + + reset_status |= 0x01 << 1; + msleep(20); + if ((cd->active_dut_generation == DUT_UNKNOWN) || + (cd->mode != PT_MODE_OPERATIONAL)) + pt_queue_restart(cd); + else + pt_queue_enum(cd); + } + + while (!(cd->startup_status & STARTUP_STATUS_COMPLETE) && time < 2000) { + msleep(50); + pt_debug(cd->dev, DL_INFO, + "%s: wait %dms for 0x%04X, current enum=0x%04X\n", + __func__, time, STARTUP_STATUS_COMPLETE, + cd->startup_status); + time += 50; + } + if (!(cd->startup_status & STARTUP_STATUS_COMPLETE)) { + rc = -ETIME; + goto exit_hw_reset; + } + + pt_debug(cd->dev, DL_INFO, "%s: HW Reset complete. enum=0x%04X\n", + __func__, cd->startup_status); + reset_status |= 0x01 << 2; + pt_start_wd_timer(cd); + +exit_hw_reset: + return scnprintf(buf, strlen(buf), + "Status: %d\n" + "Reset Status: 0x%02X\n", rc, reset_status); +} +static DEVICE_ATTR(hw_reset, 0444, pt_hw_reset_show, NULL); + +/******************************************************************************* + * FUNCTION: pt_pip2_cmd_rsp_store + * + * SUMMARY: This is the store method for the raw PIP2 cmd/rsp sysfs node. Any + * raw PIP2 command echo'd to this node will be sent directly to the DUT. + * Command byte order: + * Byte [0] - PIP2 command ID + * Byte [1-n] - PIP2 command payload + * + * RETURN: Size of passed in buffer + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_pip2_cmd_rsp_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u16 actual_read_len; + u8 input_data[PT_MAX_PIP2_MSG_SIZE + 1]; + u8 read_buf[PT_MAX_PIP2_MSG_SIZE + 1]; + u8 pip2_cmd_id = 0x00; + u8 *pip2_cmd_data = NULL; + int data_len = 0; + int length; + int rc = 0; + + /* clear shared data */ + mutex_lock(&cd->sysfs_lock); + cd->raw_cmd_status = 0; + cd->cmd_rsp_buf_len = 0; + memset(cd->cmd_rsp_buf, 0, sizeof(cd->cmd_rsp_buf)); + mutex_unlock(&cd->sysfs_lock); + + length = _pt_ic_parse_input_hex(dev, buf, size, + input_data, PT_MAX_PIP2_MSG_SIZE); + if (length <= 0 || length > PT_MAX_PIP2_MSG_SIZE) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto exit; + } + + /* Send PIP2 command if enough data was provided */ + if (length >= 1) { + pip2_cmd_id = input_data[0]; + pip2_cmd_data = &input_data[1]; + data_len = length - 1; + rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_PROTECTED, + pip2_cmd_id, pip2_cmd_data, data_len, + read_buf, &actual_read_len); + cd->raw_cmd_status = rc; + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: PIP2 cmd 0x%02x failed rc = %d\n", + __func__, pip2_cmd_id, rc); + goto exit; + } else { + cd->cmd_rsp_buf_len = actual_read_len; + memcpy(cd->cmd_rsp_buf, read_buf, actual_read_len); + pt_debug(dev, DL_WARN, + "%s: PIP2 actual_read_len = %d\n", + __func__, actual_read_len); + } + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, + "%s: Insufficient data provided for PIP2 cmd\n", + __func__); + } + +exit: + if (rc) + return rc; + return size; +} +/******************************************************************************* + * FUNCTION: pt_pip2_cmd_rsp_show + * + * SUMMARY: The show method for the raw pip2_cmd_rsp sysfs node. Any PIP2 + * response generated after using the store method of the pip2_cmd_rsp + * sysfs node, are available to be read here. + * + * PARAMETERS: + * *dev - pointer to Device structure + * *attr - pointer to the device attribute structure + * *buf - pointer to buffer to print + ******************************************************************************/ +static ssize_t pt_pip2_cmd_rsp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int i; + ssize_t data_len; + int index; + + mutex_lock(&cd->sysfs_lock); + index = scnprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n", cd->raw_cmd_status); + if (cd->raw_cmd_status) + goto error; + + /* Remove the CRC from the length of the response */ + data_len = cd->cmd_rsp_buf_len - 2; + + /* Start printing from the data payload */ + for (i = PIP1_RESP_COMMAND_ID_OFFSET; i < data_len; i++) + index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index, + "%02X ", cd->cmd_rsp_buf[i]); + + if (data_len >= PIP1_RESP_COMMAND_ID_OFFSET) { + index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index, + "\n(%zd bytes)\n", + data_len - PIP1_RESP_COMMAND_ID_OFFSET); + } else { + index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index, + "\n(%zd bytes)\n", 0); + } + +error: + mutex_unlock(&cd->sysfs_lock); + return index; +} +static DEVICE_ATTR(pip2_cmd_rsp, 0644, pt_pip2_cmd_rsp_show, + pt_pip2_cmd_rsp_store); + +/******************************************************************************* + * FUNCTION: pt_command_store + * + * SUMMARY: This is the store method for the raw PIP command sysfs node. Any + * raw PIP command echo'd to this node will be sent directly to the DUT. + * TTDL will not parse the command. + * + * RETURN: Size of passed in buffer + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_command_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); +#ifdef TTDL_PTVIRTDUT_SUPPORT + struct pt_hid_output hid_output; +#endif + unsigned short crc; + u16 actual_read_len; + u8 input_data[PT_MAX_PIP2_MSG_SIZE + 1]; + int length; + int len_field; + int rc = 0; + + mutex_lock(&cd->sysfs_lock); + cd->cmd_rsp_buf_len = 0; + memset(cd->cmd_rsp_buf, 0, sizeof(cd->cmd_rsp_buf)); + mutex_unlock(&cd->sysfs_lock); + + length = _pt_ic_parse_input_hex(dev, buf, size, + input_data, PT_MAX_PIP2_MSG_SIZE); + if (length <= 0 || length > PT_MAX_PIP2_MSG_SIZE) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto pt_command_store_exit; + } + + /* Get HID Desc */ + if (length == 2 && input_data[0] == 0x01 && input_data[1] == 0x00) { + pm_runtime_get_sync(dev); + rc = pt_get_hid_descriptor(cd, &cd->hid_desc); + mutex_lock(&cd->sysfs_lock); + if (!rc) { + cd->cmd_rsp_buf_len = + get_unaligned_le16(&cd->response_buf[0]); + memcpy(cd->cmd_rsp_buf, cd->response_buf, + cd->cmd_rsp_buf_len); + } + cd->raw_cmd_status = rc; + mutex_unlock(&cd->sysfs_lock); + pm_runtime_put(dev); + goto pt_command_store_exit; + } + + /* Get Report Desc */ + if (length == 2 && input_data[0] == 0x02 && input_data[1] == 0x00) { + pm_runtime_get_sync(dev); + rc = pt_get_report_descriptor(cd); + mutex_lock(&cd->sysfs_lock); + if (!rc) { + cd->cmd_rsp_buf_len = cd->hid_core.hid_report_desc_len; + memcpy(cd->cmd_rsp_buf, cd->response_buf, + cd->cmd_rsp_buf_len); + } + cd->raw_cmd_status = rc; + mutex_unlock(&cd->sysfs_lock); + pm_runtime_put(dev); + goto pt_command_store_exit; + } + + /* PIP2 messages begin with 01 01 */ + if (length >= 2 && input_data[0] == 0x01 && input_data[1] == 0x01) { + cd->pip2_prot_active = 1; + /* Override next seq tag with what was sent */ + cd->pip2_cmd_tag_seq = input_data[4] & 0x0F; + /* For PIP2 cmd if length does not include crc, add it */ + len_field = (input_data[3] << 8) | input_data[2]; + if (len_field == length && length <= 254) { + crc = crc_ccitt_calculate(&input_data[2], + length - 2); + pt_debug(dev, DL_WARN, "%s: len=%d crc=0x%02X\n", + __func__, length, crc); + input_data[length] = (crc & 0xFF00) >> 8; + input_data[length + 1] = crc & 0x00FF; + length = length + 2; + } + } + + /* write PIP command to log */ + pt_pr_buf(dev, DL_INFO, input_data, length, "command_buf"); + + pm_runtime_get_sync(dev); + +#ifdef TTDL_PTVIRTDUT_SUPPORT + /* Special case for handling Virtual DUT exit */ + if (length >= 3 && + input_data[0] == 0xFF && + input_data[1] == 0xFF && + input_data[2] == 0x00) { + hid_output.length = length, + hid_output.write_buf = input_data, + rc = pt_hid_send_output_user_(cd, &hid_output); + pm_runtime_put(dev); + goto pt_command_store_exit; + } +#endif + rc = pt_hid_output_user_cmd(cd, PT_MAX_INPUT, cd->cmd_rsp_buf, + length, input_data, &actual_read_len); + pm_runtime_put(dev); + + mutex_lock(&cd->sysfs_lock); + cd->raw_cmd_status = rc; + if (rc) { + cd->cmd_rsp_buf_len = 0; + pt_debug(dev, DL_ERROR, "%s: Failed to send command: %s\n", + __func__, buf); + } else { + cd->cmd_rsp_buf_len = actual_read_len; + } + cd->pip2_prot_active = 0; + mutex_unlock(&cd->sysfs_lock); + +pt_command_store_exit: + if (rc) + return rc; + return size; +} +static DEVICE_ATTR(command, 0220, NULL, pt_command_store); + +/******************************************************************************* + * FUNCTION: pt_response_show + * + * SUMMARY: The show method for the raw PIP response sysfs node. Any PIP + * response generated after using the pt_command_store sysfs node, are + * available to be read here. + * + * PARAMETERS: + * *filp - pointer to file structure + * *kobj - pointer to kobject structure + * *bin_attr - pointer to bin_attribute structure + * buf - pointer to cmd input buffer + * offset - offset index to store input buffer + * count - size of data in buffer + ******************************************************************************/ +static ssize_t pt_response_show(struct file *filp, + struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t offset, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pt_core_data *cd = dev_get_drvdata(dev); + static int pr_left; + static int pr_index; + static u8 *pr_buf; + int i = 0; + int index = 0; + int rc = 0; + int print_len = 0; + + if (pr_left) { + if (count < pr_left) { + index = count; + memcpy(buf, pr_buf + pr_index, index); + pr_left -= count; + pr_index += count; + } else { + index = pr_left; + memcpy(buf, pr_buf + pr_index, index); + pr_left = 0; + pr_index = 0; + kfree(pr_buf); + } + return index; + } + + if (offset == 0) { + mutex_lock(&cd->sysfs_lock); + if (cd->raw_cmd_status) { + index = scnprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n", cd->raw_cmd_status); + mutex_unlock(&cd->sysfs_lock); + return index; + } + + /* + * Allocate memory according to the cost, each byte need 5 + * character, and extra 100 bytes for the header and tail. + */ + if (cd->cmd_rsp_buf_len > PT_MAX_INPUT) { + rc = -EINVAL; + goto exit; + } + print_len = cd->cmd_rsp_buf_len * 5 + 100; + pr_buf = kzalloc(print_len, GFP_KERNEL); + if (!pr_buf) { + rc = -ENOMEM; + goto exit; + } + + /* Format all data to pr_buf */ + index = scnprintf(pr_buf, print_len - index, "Status: %d\n", + cd->raw_cmd_status); + for (i = 0; i < cd->cmd_rsp_buf_len; i++) + index += scnprintf(pr_buf + index, + print_len - index, "0x%02X\n", + cd->cmd_rsp_buf[i]); + index += scnprintf(pr_buf + index, print_len - index, + "(%zd bytes)\n", cd->cmd_rsp_buf_len); + + mutex_unlock(&cd->sysfs_lock); + + if (count < index) { + memcpy(buf, pr_buf, count); + pr_left = index - count; + pr_index = count; + index = count; + } else { + memcpy(buf, pr_buf, index); + pr_left = 0; + pr_index = 0; + kfree(pr_buf); + } + return index; + } + +exit: + mutex_unlock(&cd->sysfs_lock); + return rc; +} + +static struct bin_attribute bin_attr_pt_response = { + .attr = { + .name = "response", + .mode = (0444), + }, + .read = pt_response_show, +}; +/******************************************************************************* + * FUNCTION: pt_dut_debug_show + * + * SUMMARY: Show method for the dut_debug sysfs node. Shows what parameters + * are available for the store method. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_dut_debug_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + + ret = scnprintf(buf, strlen(buf), + "Status: 0\n" + "dut_debug sends the following commands to the DUT:\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + , + PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY, "", "BL Verify APP", + PT_DUT_DBG_HID_RESET, "", "HID Reset", + PT_DUT_DBG_HID_SET_POWER_ON, "", "HID SET_POWER ON", + PT_DUT_DBG_HID_SET_POWER_SLEEP, "", "HID SET_POWER SLEEP", + PT_DUT_DBG_HID_SET_POWER_STANDBY, "", "HID SET_POWER STANDBY", + PIP1_BL_CMD_ID_GET_INFO, "", "BL Get Info", + PIP1_BL_CMD_ID_PROGRAM_AND_VERIFY, "", "BL Program & Verify", + PIP1_BL_CMD_ID_LAUNCH_APP, "", "BL Launch APP", + PIP1_BL_CMD_ID_INITIATE_BL, "", "BL Initiate BL", + PT_DUT_DBG_PIP_SOFT_RESET, "", "PIP Soft Reset", + PT_DUT_DBG_RESET, "", "Toggle the TP_XRES GPIO", + PT_DUT_DBG_PIP_NULL, "", "PIP NULL (PING)", + PT_DUT_DBG_PIP_ENTER_BL, "", "PIP enter BL", + PT_DUT_DBG_HID_SYSINFO, "", "HID system info", + PT_DUT_DBG_PIP_SUSPEND_SCAN, "", "Suspend Scan", + PT_DUT_DBG_PIP_RESUME_SCAN, "", "Resume Scan", + PT_DUT_DBG_HID_DESC, "", "Get HID Desc", + PT_DUT_DBG_REPORT_DESC, "", "Get HID Report Desc" + ); + + return ret; +} +/******************************************************************************* + * FUNCTION: pt_drv_debug_show + * + * SUMMARY: Show method for the drv_debug sysfs node. Shows what parameters + * are available for the store method. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_drv_debug_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + + ret = scnprintf(buf, strlen(buf), + "Status: 0\n" + "drv_debug supports the following values:\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s - %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" +#ifdef TTDL_DIAGNOSTICS + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" +#endif /* TTDL_DIAGNOSTICS */ + "%d %s \t- %s\n" +#ifdef TTDL_DIAGNOSTICS + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" + "%d %s \t- %s\n" +#endif /* TTDL_DIAGNOSTICS */ + , + PT_DRV_DBG_SUSPEND, " ", "Suspend TTDL responding to INT", + PT_DRV_DBG_RESUME, " ", "Resume TTDL responding to INT", + PT_DRV_DBG_STOP_WD, " ", "Stop TTDL WD", + PT_DRV_DBG_START_WD, " ", "Start TTDL WD", + PT_DRV_DBG_TTHE_TUNER_EXIT, " ", "Exit TTHE Tuner Logging", + PT_DRV_DBG_TTHE_BUF_CLEAN, " ", "Clear TTHE Tuner buffer", + PT_DRV_DBG_CLEAR_PARM_LIST, " ", "Clear RAM Param list", + PT_DRV_DBG_FORCE_BUS_READ, " ", "Force bus read", + PT_DRV_DBG_CLEAR_CAL_DATA, " ", "Clear CAL Cache", + PT_DRV_DBG_REPORT_LEVEL, "[0|1|2|3|4]", "Set TTDL Debug Level", + PT_DRV_DBG_WATCHDOG_INTERVAL, "[n] ", "TTDL WD Interval in ms", + PT_DRV_DBG_SHOW_TIMESTAMP, "[0|1]", "Show Timestamps" +#ifdef TTDL_DIAGNOSTICS + , PT_DRV_DBG_SETUP_PWR, "[0|1]", "Power DUT up/down", + PT_DRV_DBG_GET_PUT_SYNC, "[0|1]", "Get/Put Linux Sleep", + PT_DRV_DBG_SET_TT_DATA, "[0|1]", "Display TT_DATA" +#endif /* TTDL_DIAGNOSTICS */ + , PT_DRV_DBG_SET_GENERATION, "[0|1|2]", "Set DUT generation" +#ifdef TTDL_DIAGNOSTICS + , PT_DRV_DBG_SET_BRIDGE_MODE, "[0|1]", "On/Off Bridge Mode", + PT_DRV_DBG_SET_I2C_ADDRESS, "[0-127]", "I2C DUT Address", + PT_DRV_DBG_SET_FLASHLESS_DUT, "[0|1]", "Flashless DUT yes/no", + PT_DRV_DBG_SET_FORCE_SEQ, "[8-15]", "Force PIP2 Sequence #", + PT_DRV_DBG_BL_WITH_NO_INT, "[0|1]", "BL with no INT", + PT_DRV_DBG_CAL_CACHE_IN_HOST, "[0|1]", "CAL Cache in host", + PT_DRV_DBG_NUM_DEVICES, "[1-255]", "Number of Devices", + PT_DRV_DBG_PIP_TIMEOUT, "[100-7000]", "PIP Resp Timeout (ms)" +#endif /* TTDL_DIAGNOSTICS */ + ); + + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_drv_debug_store + * + * SUMMARY: Currently the store method for both sysfs nodes: drv_debug and + * dut_debug. Drv_debug will contain all functionality that can be run + * without a DUT preset and is available anytime TTDL is running. + * Dut_debug requires a DUT to be available and will only be created after + * a DUT has been detected. + * This function will eventually be split into two but until the overlap + * has been depricated this function contains all commands that can be + * used for TTDL/DUT debugging status and control. + * All commands require at least one value to be passed in *buf with some + * requiring two. + * + * RETURN: Size of passed in buffer + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_drv_debug_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + unsigned long value; + int rc = 0; + u8 return_data[8]; + static u8 wd_disabled; + u32 input_data[3]; + int length; +#ifdef TTDL_DIAGNOSTICS + struct i2c_client *client = to_i2c_client(dev); + unsigned short crc = 0; + u16 cal_size; +#endif + + input_data[0] = 0; + input_data[1] = 0; + + /* Maximmum input is two elements */ + length = _pt_ic_parse_input(dev, buf, size, + input_data, ARRAY_SIZE(input_data)); + if (length < 1 || length > 2) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto pt_drv_debug_store_exit; + } + value = input_data[0]; + + if (length == 1) { + pt_debug(dev, DL_DEBUG, + "%s: Debug Cmd Received (id=%d)\n", + __func__, input_data[0]); + } else if (length == 2) { + pt_debug(dev, DL_DEBUG, + "%s: Debug Cmd Received (id=%d, data=%d)\n", + __func__, input_data[0], input_data[1]); + } else { + pt_debug(dev, DL_DEBUG, + "%s: Invalid arguments received\n", __func__); + rc = -EINVAL; + goto pt_drv_debug_store_exit; + } + + /* Start watchdog timer command */ + if (value == PT_DRV_DBG_START_WD) { + pt_debug(dev, DL_INFO, "%s: Cmd: Start Watchdog\n", __func__); + wd_disabled = 0; + cd->watchdog_force_stop = false; + pt_start_wd_timer(cd); + goto pt_drv_debug_store_exit; + } + + /* Stop watchdog timer temporarily */ + pt_stop_wd_timer(cd); + + if (value == PT_DRV_DBG_STOP_WD) { + pt_debug(dev, DL_INFO, "%s: Cmd: Stop Watchdog\n", __func__); + wd_disabled = 1; + cd->watchdog_force_stop = true; + goto pt_drv_debug_store_exit; + } + + switch (value) { + case PT_DRV_DBG_SUSPEND: /* 4 */ + pt_debug(dev, DL_INFO, "%s: TTDL: Core Sleep\n", __func__); + wd_disabled = 1; + rc = pt_core_sleep(cd); + if (rc) + pt_debug(dev, DL_ERROR, "%s: Suspend failed rc=%d\n", + __func__, rc); + else + pt_debug(dev, DL_INFO, "%s: Suspend succeeded\n", + __func__); + break; + + case PT_DRV_DBG_RESUME: /* 5 */ + pt_debug(dev, DL_INFO, "%s: TTDL: Wake\n", __func__); + rc = pt_core_wake(cd); + if (rc) + pt_debug(dev, DL_ERROR, "%s: Resume failed rc=%d\n", + __func__, rc); + else + pt_debug(dev, DL_INFO, "%s: Resume succeeded\n", + __func__); + break; + case PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY: /* BL - 49 */ + pt_debug(dev, DL_INFO, "%s: Cmd: verify app integ\n", __func__); + pt_hid_output_bl_verify_app_integrity(cd, &return_data[0]); + break; + case PT_DUT_DBG_HID_RESET: /* 50 */ + pt_debug(dev, DL_INFO, "%s: Cmd: hid_reset\n", __func__); + pt_hid_cmd_reset(cd); + break; + case PT_DUT_DBG_HID_SET_POWER_ON: /* 53 */ + pt_debug(dev, DL_INFO, "%s: Cmd: hid_set_power_on\n", __func__); + pt_hid_cmd_set_power(cd, HID_POWER_ON); + wd_disabled = 0; + break; + case PT_DUT_DBG_HID_SET_POWER_SLEEP: /* 54 */ + pt_debug(dev, DL_INFO, "%s: Cmd: hid_set_power_off\n", + __func__); + wd_disabled = 1; + pt_hid_cmd_set_power(cd, HID_POWER_SLEEP); + break; + case PT_DUT_DBG_HID_SET_POWER_STANDBY: /* 55 */ + pt_debug(dev, DL_INFO, "%s: Cmd: hid_set_power_standby\n", + __func__); + wd_disabled = 1; + pt_hid_cmd_set_power(cd, HID_POWER_STANDBY); + break; + case PIP1_BL_CMD_ID_GET_INFO: /* BL - 56 */ + pt_debug(dev, DL_INFO, "%s: Cmd: bl get info\n", __func__); + pt_hid_output_bl_get_information(cd, return_data); + break; + case PIP1_BL_CMD_ID_PROGRAM_AND_VERIFY: /* BL - 57 */ + pt_debug(dev, DL_INFO, "%s: Cmd: program and verify\n", + __func__); + pt_hid_output_bl_program_and_verify(cd, 0, NULL); + break; + case PIP1_BL_CMD_ID_LAUNCH_APP: /* BL - 59 */ + pt_debug(dev, DL_INFO, "%s: Cmd: launch app\n", __func__); + pt_hid_output_bl_launch_app(cd); + break; + case PIP1_BL_CMD_ID_INITIATE_BL: /* BL - 72 */ + pt_debug(dev, DL_INFO, "%s: Cmd: initiate bl\n", __func__); + pt_hid_output_bl_initiate_bl(cd, 0, NULL, 0, NULL); + break; + case PT_DUT_DBG_PIP_SOFT_RESET: /* 97 */ + pt_debug(dev, DL_INFO, "%s: Cmd: Soft Reset\n", __func__); + rc = pt_hw_soft_reset(cd, PT_CORE_CMD_PROTECTED); + break; + case PT_DUT_DBG_RESET: /* 98 */ + pt_debug(dev, DL_INFO, "%s: Cmd: Hard Reset\n", __func__); + rc = pt_hw_hard_reset(cd); + break; + case PT_DUT_DBG_PIP_NULL: /* 100 */ + pt_debug(dev, DL_INFO, "%s: Cmd: Ping (null)\n", __func__); + pt_pip_null(cd); + break; + case PT_DUT_DBG_PIP_ENTER_BL: /* 101 */ + pt_debug(dev, DL_INFO, "%s: Cmd: start_bootloader\n", __func__); + rc = pt_pip_start_bootloader(cd); + if (!rc) { + cd->startup_status = STARTUP_STATUS_BL_RESET_SENTINEL; + cd->mode = PT_MODE_BOOTLOADER; + } + break; + case PT_DUT_DBG_HID_SYSINFO: /* 102 */ + pt_debug(dev, DL_INFO, "%s: Cmd: get_sysinfo\n", __func__); + pt_hid_output_get_sysinfo(cd); + break; + case PT_DUT_DBG_PIP_SUSPEND_SCAN: /* 103 */ + pt_debug(dev, DL_INFO, "%s: Cmd: suspend_scanning\n", __func__); + pt_pip_suspend_scanning(cd); + break; + case PT_DUT_DBG_PIP_RESUME_SCAN: /* 104 */ + pt_debug(dev, DL_INFO, "%s: Cmd: resume_scanning\n", __func__); + pt_pip_resume_scanning(cd); + break; +#ifdef TTHE_TUNER_SUPPORT + case PT_DRV_DBG_TTHE_TUNER_EXIT: /* 107 */ + cd->tthe_exit = 1; + wake_up(&cd->wait_q); + kfree(cd->tthe_buf); + cd->tthe_buf = NULL; + cd->tthe_exit = 0; + break; + case PT_DRV_DBG_TTHE_BUF_CLEAN: /* 108 */ + if (cd->tthe_buf) + memset(cd->tthe_buf, 0, PT_MAX_PRBUF_SIZE); + else + pt_debug(dev, DL_INFO, "%s : tthe_buf not existed\n", + __func__); + break; +#endif +#ifdef TTDL_DIAGNOSTICS + case PT_DUT_DBG_HID_DESC: /* 109 */ + pt_debug(dev, DL_INFO, "%s: Cmd: get_hid_desc\n", __func__); + pt_get_hid_descriptor(cd, &cd->hid_desc); + break; + case PT_DRV_DBG_CLEAR_PARM_LIST: /* 110 */ + pt_debug(dev, DL_INFO, "%s: TTDL: Clear Parameter List\n", + __func__); + pt_erase_parameter_list(cd); + break; + case PT_DRV_DBG_FORCE_BUS_READ: /* 111 */ + rc = pt_read_input(cd); + if (!rc) + pt_parse_input(cd); + break; + case PT_DRV_DBG_CLEAR_CAL_DATA: /* 112 */ + rc = _pt_manage_local_cal_data(dev, PT_CAL_DATA_CLEAR, + &cal_size, &crc); + if (rc) + pt_debug(dev, DL_ERROR, + "%s: CAL Data clear failed rc=%d\n", + __func__, rc); + else + pt_debug(dev, DL_INFO, + "%s: CAL Cleared, Chip ID=0x%04X size=%d\n", + __func__, crc, size); + break; + case PT_DUT_DBG_REPORT_DESC: /* 113 */ + rc = pt_get_report_descriptor(cd); + if (rc != 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: Error on getting report descriptor r=%d\n", + __func__, rc); + } + break; + case PT_DRV_DBG_REPORT_LEVEL: /* 200 */ + mutex_lock(&cd->system_lock); + if (input_data[1] >= 0 && input_data[1] < DL_MAX) { + cd->debug_level = input_data[1]; + pt_debug(dev, DL_INFO, "%s: Set debug_level: %d\n", + __func__, cd->debug_level); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Invalid debug_level: %d\n", + __func__, input_data[1]); + } + mutex_unlock(&(cd->system_lock)); + break; +#endif + case PT_DRV_DBG_WATCHDOG_INTERVAL: /* 201 */ + mutex_lock(&cd->system_lock); + if (input_data[1] > 100) { + cd->watchdog_interval = input_data[1]; + pt_debug(dev, DL_INFO, + "%s: Set watchdog_ interval to: %d\n", + __func__, cd->watchdog_interval); + pt_start_wd_timer(cd); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, + "%s: Invalid watchdog interval: %d\n", + __func__, input_data[1]); + } + mutex_unlock(&(cd->system_lock)); + break; +#ifdef TTDL_DIAGNOSTICS + case PT_DRV_DBG_SHOW_TIMESTAMP: /* 202 */ + mutex_lock(&cd->system_lock); + if (input_data[1] == 0) { + cd->show_timestamp = 0; + pt_debug(dev, DL_INFO, "%s: Disable show_timestamp\n", + __func__); + } else if (input_data[1] == 1) { + cd->show_timestamp = 1; + pt_debug(dev, DL_INFO, "%s: Enable show_timestamp\n", + __func__); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, + "%s: Invalid parameter: %d\n", + __func__, input_data[1]); + } + mutex_unlock(&(cd->system_lock)); + break; + case PT_DRV_DBG_SETUP_PWR: /* 205 */ + if (input_data[1] == 0) { + cd->cpdata->setup_power(cd->cpdata, + PT_MT_POWER_OFF, cd->dev); + pt_debug(dev, DL_INFO, + "%s: Initiate Power Off\n", __func__); + } else if (input_data[1] == 1) { + cd->cpdata->setup_power(cd->cpdata, + PT_MT_POWER_ON, cd->dev); + pt_debug(dev, DL_INFO, + "%s: Initiate Power On\n", __func__); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, + "%s: Invalid parameter: %d\n", + __func__, input_data[1]); + } + break; + case PT_DRV_DBG_GET_PUT_SYNC: /* 206 */ + if (input_data[1] == 0) { + pm_runtime_put(dev); + pt_debug(dev, DL_WARN, + "%s: Force call pm_runtime_put()\n", __func__); + } else if (input_data[1] == 1) { + pm_runtime_get_sync(dev); + pt_debug(dev, DL_WARN, + "%s: Force call pm_runtime_get_sync()\n", + __func__); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, + "%s: WARNING: Invalid parameter: %d\n", + __func__, input_data[1]); + } + break; + case PT_DRV_DBG_SET_TT_DATA: /* 208 */ + mutex_lock(&cd->system_lock); + if (input_data[1] == 0) { + cd->show_tt_data = false; + pt_debug(dev, DL_INFO, + "%s: Disable TT_DATA\n", __func__); + } else if (input_data[1] == 1) { + cd->show_tt_data = true; + pt_debug(dev, DL_INFO, + "%s: Enable TT_DATA\n", __func__); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, + "%s: Invalid parameter: %d\n", + __func__, input_data[1]); + } + mutex_unlock(&(cd->system_lock)); + break; + case PT_DRV_DBG_SET_GENERATION: /* 210 */ + if (input_data[1] == cd->active_dut_generation) { + mutex_lock(&cd->system_lock); + cd->set_dut_generation = true; + mutex_unlock(&(cd->system_lock)); + } else { + mutex_lock(&cd->system_lock); + if (input_data[1] == 0) { + cd->active_dut_generation = DUT_UNKNOWN; + cd->set_dut_generation = false; + } else if (input_data[1] == 1) { + cd->active_dut_generation = DUT_PIP1_ONLY; + cd->set_dut_generation = true; + } else if (input_data[1] == 2) { + cd->active_dut_generation = DUT_PIP2_CAPABLE; + cd->set_dut_generation = true; + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, + "%s: Invalid parameter: %d\n", + __func__, input_data[1]); + break; + } + cd->startup_status = STARTUP_STATUS_START; + pt_debug(cd->dev, DL_DEBUG, + "%s: Startup Status Reset\n", __func__); + mutex_unlock(&(cd->system_lock)); + + pt_debug(dev, DL_INFO, + "%s: Active DUT Generation Set to: %d\n", + __func__, cd->active_dut_generation); + + /* Changing DUT generations full restart needed */ + pt_queue_restart(cd); + } + break; + case PT_DRV_DBG_SET_BRIDGE_MODE: /* 211 */ + mutex_lock(&cd->system_lock); + if (input_data[1] == 0) { + cd->bridge_mode = false; + pt_debug(dev, DL_INFO, + "%s: Disable Bridge Mode\n", __func__); + } else if (input_data[1] == 1) { + cd->bridge_mode = true; + pt_debug(dev, DL_INFO, + "%s: Enable Bridge Mode\n", __func__); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n", + __func__, input_data[1]); + } + mutex_unlock(&(cd->system_lock)); + break; + case PT_DRV_DBG_SET_I2C_ADDRESS: /* 212 */ + mutex_lock(&cd->system_lock); + /* Only a 7bit address is valid */ + if (input_data[1] >= 0 && input_data[1] <= 0x7F) { + client->addr = input_data[1]; + pt_debug(dev, DL_INFO, + "%s: Set I2C Address: 0x%2X\n", + __func__, client->addr); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Invalid I2C Address %d\n", + __func__, input_data[1]); + client->addr = 0x24; + } + mutex_unlock(&(cd->system_lock)); + break; + case PT_DRV_DBG_SET_FLASHLESS_DUT: /* 213 */ + mutex_lock(&cd->system_lock); + if (input_data[1] == 0) { + cd->flashless_dut = 0; + cd->flashless_auto_bl = PT_SUPPRESS_AUTO_BL; + pt_debug(dev, DL_INFO, "%s: Disable FLAHLESS DUT\n", + __func__); + } else if (input_data[1] == 1) { + cd->flashless_dut = 1; + cd->flashless_auto_bl = PT_ALLOW_AUTO_BL; + pt_debug(dev, DL_INFO, "%s: Enable FLAHLESS DUT\n", + __func__); + } + mutex_unlock(&(cd->system_lock)); + break; + case PT_DRV_DBG_SET_FORCE_SEQ: /* 214 */ + mutex_lock(&cd->system_lock); + if (input_data[1] >= 0x8 && input_data[1] <= 0xF) { + cd->force_pip2_seq = input_data[1]; + cd->pip2_cmd_tag_seq = input_data[1]; + pt_debug(dev, DL_INFO, + "%s: Force PIP2 Seq to: 0x%02X\n", + __func__, input_data[1]); + } else { + cd->force_pip2_seq = 0; + pt_debug(dev, DL_INFO, + "%s: Clear Forced PIP2 Seq\n", __func__); + } + mutex_unlock(&(cd->system_lock)); + break; + case PT_DRV_DBG_BL_WITH_NO_INT: /* 215 */ + mutex_lock(&cd->system_lock); + if (input_data[1] == 0) { + cd->bl_with_no_int = 0; + pt_debug(dev, DL_INFO, "%s: BL using IRQ\n", __func__); + } else if (input_data[1] == 1) { + cd->bl_with_no_int = 1; + pt_debug(dev, DL_INFO, "%s: BL using Polling\n", + __func__); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n", + __func__, input_data[1]); + } + mutex_unlock(&(cd->system_lock)); + break; + case PT_DRV_DBG_CAL_CACHE_IN_HOST: /* 216 */ + mutex_lock(&cd->system_lock); + if (input_data[1] == 0) { + cd->cal_cache_in_host = PT_FEATURE_DISABLE; + pt_debug(dev, DL_INFO, + "%s: Disable Calibration cache in host\n", + __func__); + } else if (input_data[1] == 1) { + cd->cal_cache_in_host = PT_FEATURE_ENABLE; + pt_debug(dev, DL_INFO, + "%s: Enable Calibration cache in host\n", + __func__); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n", + __func__, input_data[1]); + } + mutex_unlock(&(cd->system_lock)); + break; + case PT_DRV_DBG_NUM_DEVICES: /* 217 */ + mutex_lock(&cd->system_lock); + if (input_data[1] == 0) { + /* 0 is not supported */ + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n", + __func__, input_data[1]); + } else if (input_data[1] == 1) { + cd->num_devices = input_data[1]; + cd->ttdl_bist_select = 0x07; + pt_debug(dev, DL_INFO, + "%s: Multi-chip support Disabled\n", __func__); + } else if (input_data[1] > 1 && + input_data[1] <= PT_MAX_DEVICES) { + cd->num_devices = input_data[1]; + cd->ttdl_bist_select = 0x3F; + pt_debug(dev, DL_INFO, + "%s: Multi-chip support Enabled with %d DUTs\n", + __func__, cd->num_devices); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n", + __func__, input_data[1]); + } + mutex_unlock(&(cd->system_lock)); + break; + case PT_DRV_DBG_SET_PANEL_ID_TYPE: /* 218 */ + mutex_lock(&cd->system_lock); + if (input_data[1] <= 0x07) { + cd->panel_id_support = input_data[1]; + pt_debug(dev, DL_INFO, + "%s: ATM - Set panel_id_support to %d\n", + __func__, cd->panel_id_support); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, + "%s: ATM - Invalid parameter: %d\n", + __func__, input_data[1]); + } + mutex_unlock(&(cd->system_lock)); + break; + case PT_DRV_DBG_PIP_TIMEOUT: /* 219 */ + mutex_lock(&cd->system_lock); + if (input_data[1] >= 100 && input_data[1] <= 7000) { + /* + * The timeout is changed for some cases so the + * pip_cmd_timeout_default is used to retore back to + * what the user requested as the new timeout. + */ + cd->pip_cmd_timeout_default = input_data[1]; + cd->pip_cmd_timeout = input_data[1]; + pt_debug(dev, DL_INFO, + "%s: ATM - PIP Timeout = %d\n", __func__, + cd->pip_cmd_timeout_default); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, + "%s: ATM - Invalid parameter: %d\n", + __func__, input_data[1]); + } + mutex_unlock(&(cd->system_lock)); + break; + case PT_DRV_DBG_CORE_PLATFORM_FLAG: /* 220 */ + mutex_lock(&cd->system_lock); + if (cd->cpdata) { + cd->cpdata->flags = input_data[1]; + pt_debug(dev, DL_INFO, + "%s: ATM - Set core platform flag to 0x%02X\n", + __func__, input_data[1]); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: No platform data\n", + __func__); + } + mutex_unlock(&cd->system_lock); + break; +#ifdef TTDL_PTVIRTDUT_SUPPORT + case PT_DRV_DBG_SET_HW_DETECT: /* 298 */ + mutex_lock(&cd->system_lock); + if (input_data[1]) + cd->hw_detect_enabled = true; + else + cd->hw_detect_enabled = false; + pt_debug(dev, DL_INFO, + "%s: Set hw_detect_enabled to %d\n", + __func__, cd->hw_detect_enabled); + mutex_unlock(&(cd->system_lock)); + break; + case PT_DRV_DBG_VIRTUAL_I2C_DUT: /* 299 */ + if (input_data[1] == 0) { + /* Cancel all work threads when disabling */ + pt_debug(dev, DL_WARN, "%s: Canceling Work", __func__); +#ifdef PT_PTSBC_SUPPORT + cancel_work_sync(&cd->irq_work); + cancel_work_sync(&cd->probe_work); +#endif + cancel_work_sync(&cd->ttdl_restart_work); + cancel_work_sync(&cd->enum_work); + pt_stop_wd_timer(cd); + call_atten_cb(cd, PT_ATTEN_CANCEL_LOADER, 0); + + mutex_lock(&cd->system_lock); + cd->route_bus_virt_dut = 0; + mutex_unlock(&(cd->system_lock)); + pt_debug(dev, DL_WARN, "%s: Enable Virtual DUT mode\n", + __func__); + } else if (input_data[1] == 1) { + mutex_lock(&cd->system_lock); + cd->route_bus_virt_dut = 1; + mutex_unlock(&(cd->system_lock)); + pt_debug(dev, DL_WARN, "%s: Enable Virtual DUT mode\n", + __func__); + } else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n", + __func__, input_data[1]); + } + break; +#endif /* TTDL_PTVIRTDUT_SUPPORT */ +#endif /* TTDL_DIAGNOSTICS */ + default: + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__); + } + + /* Enable watchdog timer */ + if (!wd_disabled) + pt_start_wd_timer(cd); + +pt_drv_debug_store_exit: + if (rc) + return rc; + return size; +} +static DEVICE_ATTR(drv_debug, 0644, pt_drv_debug_show, + pt_drv_debug_store); + +/******************************************************************************* + * FUNCTION: pt_sleep_status_show + * + * SUMMARY: Show method for the sleep_status sysfs node that will show the + * sleep status as either ON or OFF + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_sleep_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + ssize_t ret; + + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_ON) + ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "off\n"); + else + ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "on\n"); + mutex_unlock(&cd->system_lock); + + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_panel_id_show + * + * SUMMARY: Show method for the panel_id sysfs node that will show the + * detected panel ID from the DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_panel_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + ssize_t ret; + u8 pid = PANEL_ID_NOT_ENABLED; + int rc = 0; + + if (cd->active_dut_generation == DUT_PIP1_ONLY) { + /* + * The DUT should report the same panel ID from both the BL and + * the FW unless the panel_id feature is set to only + * PT_PANEL_ID_BY_SYS_INFO, in which case the BL is not able + * to retrieve the panel_id. + */ + if (cd->mode == PT_MODE_BOOTLOADER) { + /* + * Return the stored value if PT_PANEL_ID_BY_BL + * is not supported while other feature exits. + */ + if (cd->panel_id_support & PT_PANEL_ID_BY_BL) { + rc = pt_hid_output_bl_get_panel_id_(cd, &pid); + if (rc) { + pt_debug(dev, DL_WARN, "%s: %s %s\n", + "Failed to retrieve Panel ID. ", + "Using cached value\n", + __func__); + } + } + } else if (cd->mode == PT_MODE_OPERATIONAL) { + if (cd->panel_id_support & + (PT_PANEL_ID_BY_BL | PT_PANEL_ID_BY_SYS_INFO)) { + /* For all systems sysinfo has the panel_id */ + rc = pt_hid_output_get_sysinfo(cd); + if (!rc) + pid = + cd->sysinfo.sensing_conf_data.panel_id; + pt_debug(dev, DL_WARN, + "%s: Gen6 FW mode rc=%d PID=0x%02X\n", + __func__, rc, pid); + } + } else { + pt_debug(dev, DL_WARN, "%s: Active mode unknown\n", + __func__); + rc = -EPERM; + } + } else if (cd->active_dut_generation == DUT_PIP2_CAPABLE) { + if (cd->mode == PT_MODE_BOOTLOADER) { + if (cd->panel_id_support & PT_PANEL_ID_BY_BL) { + rc = _pt_pip2_get_panel_id_by_gpio(cd, &pid); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: BL get panel ID failed rc=%d\n", + __func__, rc); + } + } + } else if (cd->mode == PT_MODE_OPERATIONAL) { + if (cd->panel_id_support & + (PT_PANEL_ID_BY_BL | PT_PANEL_ID_BY_SYS_INFO)) { + rc = pt_hid_output_get_sysinfo(cd); + if (!rc) + pid = + cd->sysinfo.sensing_conf_data.panel_id; + pt_debug(dev, DL_WARN, + "%s: TT/TC FW mode rc=%d PID=0x%02X\n", + __func__, rc, pid); + } + } else { + pt_debug(dev, DL_WARN, "%s: Active mode unknown\n", + __func__); + rc = -EPERM; + } + } else { + pt_debug(dev, DL_WARN, "%s: Dut generation is unknown\n", + __func__); + rc = -EPERM; + } + + ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n0x%02X\n", + rc, pid); + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_get_param_store + * + * SUMMARY: Store method for the get_param sysfs node. Stores what parameter + * ID to retrieve with the show method. + * + * NOTE: This sysfs node is only available after a DUT has been enumerated + * + * RETURN: Size of passed in buffer if successful + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + * size - size of buf + ******************************************************************************/ +static ssize_t pt_get_param_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u32 input_data[2]; + int length; + int rc = 0; + + /* Maximum input of one value */ + length = _pt_ic_parse_input(dev, buf, size, input_data, + ARRAY_SIZE(input_data)); + if (length != 1) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto exit; + } + + mutex_lock(&cd->system_lock); + cd->get_param_id = input_data[0]; + mutex_unlock(&(cd->system_lock)); + +exit: + if (rc) + return rc; + return size; +} + +/******************************************************************************* + * FUNCTION: pt_get_param_show + * + * SUMMARY: Show method for the get_param sysfs node. Retrieves the + * parameter data from the DUT based on the ID stored in the core + * data variable "get_param_id". If the ID is invalid, the DUT cannot + * communicate or some other error occures, an error status is returned + * with no value following. + * Output is in the form: + * Status: x + * 0xyyyyyyyy + * The 32bit data will only follow the status code if the status == 0 + * + * NOTE: This sysfs node is only available after a DUT has been enumerated + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_get_param_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + ssize_t ret = 0; + int status; + u32 value = 0; + + status = pt_pip_get_param(cd, cd->get_param_id, &value); + if (status) { + pt_debug(dev, DL_ERROR, "%s: %s Failed, status = %d\n", + __func__, "pt_get_param", status); + ret = scnprintf(buf, strlen(buf), + "%s %d\n", + "Status:", status); + } else { + pt_debug(dev, DL_DEBUG, "%s: Param [%d] = 0x%04X\n", + __func__, cd->get_param_id, value); + ret = scnprintf(buf, strlen(buf), + "Status: %d\n" + "0x%04X\n", + status, value); + } + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_ttdl_restart_show + * + * SUMMARY: Show method for ttdl_restart sysfs node. This node releases all + * probed modules, calls startup() and then re-probes modules. + * + * RETURN: size of data written to sysfs node + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes structure + * *buf - pointer to print output buffer + ******************************************************************************/ +static ssize_t pt_ttdl_restart_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int t; + int rc = 0; + + mutex_lock(&cd->system_lock); + cd->startup_state = STARTUP_NONE; + mutex_unlock(&(cd->system_lock)); + + /* ensure no left over exclusive access is still locked */ + release_exclusive(cd, cd->dev); + + pt_queue_restart(cd); + + t = wait_event_timeout(cd->wait_q, + (cd->startup_status >= STARTUP_STATUS_COMPLETE), + msecs_to_jiffies(PT_REQUEST_ENUM_TIMEOUT)); + if (IS_TMO(t)) { + pt_debug(cd->dev, DL_ERROR, + "%s: TMO waiting for FW sentinel\n", __func__); + rc = -ETIME; + } + + return scnprintf(buf, strlen(buf), + "Status: %d\n" + "Enum Status: 0x%04X\n", rc, cd->startup_status); +} +static DEVICE_ATTR(ttdl_restart, 0444, pt_ttdl_restart_show, NULL); + +/******************************************************************************* + * FUNCTION: pt_pip2_gpio_read_show + * + * SUMMARY: Sends a PIP2 READ_GPIO command to the DUT and prints the + * contents of the response to the passed in output buffer. + * + * RETURN: size of data written to sysfs node + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes structure + * *buf - pointer to print output buffer + ******************************************************************************/ +static ssize_t pt_pip2_gpio_read_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u8 status = 0; + u32 gpio_value = 0; + int rc = 0; + + /* This functionality is only available in the BL */ + if (cd->mode == PT_MODE_BOOTLOADER) + rc = pt_pip2_read_gpio(dev, &status, &gpio_value); + else + rc = -EPERM; + + if (!rc) { + if (status == 0) + return snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n" + "DUT GPIO Reg: 0x%08X\n", + rc, gpio_value); + else + return snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n" + "DUT GPIO Reg: n/a\n", + status); + } else + return snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n" + "DUT GPIO Reg: n/a\n", + rc); +} + +/******************************************************************************* + * FUNCTION: pt_pip2_version_show + * + * SUMMARY: Sends a PIP2 VERSION command to the DUT and prints the + * contents of the response to the passed in output buffer. + * + * RETURN: size of data written to sysfs node + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes structure + * *buf - pointer to print output buffer + ******************************************************************************/ +static ssize_t pt_pip2_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rc = 0; + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_ttdata *ttdata = &cd->sysinfo.ttdata; + + rc = pt_pip2_get_version(cd); + if (!rc) { + return snprintf(buf, PT_MAX_PRBUF_SIZE, + "PIP VERSION : %02X.%02X\n" + "BL VERSION : %02X.%02X\n" + "FW VERSION : %02X.%02X\n" + "SILICON ID : %04X.%04X\n" + "UID : 0x%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n", + ttdata->pip_ver_major, ttdata->pip_ver_minor, + ttdata->bl_ver_major, ttdata->bl_ver_minor, + ttdata->fw_ver_major, ttdata->fw_ver_minor, + ttdata->chip_id, ttdata->chip_rev, + ttdata->uid[0], ttdata->uid[1], + ttdata->uid[2], ttdata->uid[3], + ttdata->uid[4], ttdata->uid[5], + ttdata->uid[6], ttdata->uid[7], + ttdata->uid[8], ttdata->uid[9], + ttdata->uid[10], ttdata->uid[11]); + } else { + pt_debug(dev, DL_ERROR, + "%s: Failed to retriev PIP2 VERSION data\n", __func__); + + return snprintf(buf, PT_MAX_PRBUF_SIZE, + "PIP VERSION : -\n" + "BL VERSION : -\n" + "FW VERSION : -\n" + "SILICON ID : -\n" + "UID : -\n"); + } +} + +#ifdef TTDL_DIAGNOSTICS +/******************************************************************************* + * FUNCTION: pt_ttdl_status_show + * + * SUMMARY: Show method for the ttdl_status sysfs node. Displays TTDL internal + * variable states and GPIO levels. Additional information printed when + * TTDL_DIAGNOSTICS is enabled. + * + * NOTE: All counters will be reset to 0 when this function is called. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_ttdl_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_platform_data *pdata = dev_get_platdata(dev); + struct i2c_client *client = to_i2c_client(dev); + ssize_t ret; + u16 cal_size = 0; + unsigned short crc = 0; + + if (cd->cal_cache_in_host) + _pt_manage_local_cal_data(dev, + PT_CAL_DATA_INFO, &cal_size, &crc); + ret = scnprintf(buf, strlen(buf), + "%s: 0x%04X\n" + "%s: %d\n" + "%s: %s\n" + "%s: 0x%02X\n" + "%s: %s\n" + "%s: %s\n" + "%s: %d\n" + "%s: %s %s\n" + "%s: %s\n" + "%s: %s\n" + "%s: %s\n" + "%s: %s\n" + "%s: %s\n" + "%s: %s\n" + "%s: %d\n" + "%s: %s\n" + "%s: %s\n" + "%s: %d\n" +#ifdef TTDL_DIAGNOSTICS + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %s\n" + "%s: %d\n" + "%s: 0x%04X\n" +#endif /* TTDL_DIAGNOSTICS */ + , + "Startup Status ", cd->startup_status, + "TTDL Debug Level ", cd->debug_level, + "Active Bus Module ", + cd->bus_ops->bustype == BUS_I2C ? "I2C" : "SPI", + "I2C Address ", + cd->bus_ops->bustype == BUS_I2C ? client->addr : 0, + "Exclusive Access Lock ", cd->exclusive_dev ? "Set":"Free", + "HW Detected ", + cd->hw_detected ? "True" : "False", + "Number of Devices ", cd->num_devices, + "DUT Generation ", + cd->active_dut_generation ? + (cd->active_dut_generation == DUT_PIP2_CAPABLE ? + "PT TC/TT" : "Gen5/6") : "Unknown", + cd->active_dut_generation ? + (cd->set_dut_generation == true ? + "(Set)" : "(Detected)") : "", + "Protocol Mode ", + cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID ? + "Hybrid HID" : "PIP", + "Mode ", + cd->mode ? (cd->mode == PT_MODE_OPERATIONAL ? + "Operational" : "BL") : "Unknown", + "Flashless Mode ", + cd->flashless_dut == 1 ? "Yes" : "No", + "Suppress No-Flash Auto BL ", + cd->flashless_auto_bl == PT_SUPPRESS_AUTO_BL ? + "Yes" : "No", + "GPIO state - IRQ ", + cd->cpdata->irq_stat ? + (cd->cpdata->irq_stat(cd->cpdata, dev) ? + "High" : "Low") : "not defined", + "GPIO state - TP_XRES ", + pdata->core_pdata->rst_gpio ? + (gpio_get_value(pdata->core_pdata->rst_gpio) ? + "High" : "Low") : "not defined", + "Error GPIO trigger type ", cd->err_gpio_type, + "WD - Manual Force Stop ", + cd->watchdog_force_stop ? "True" : "False", + "WD - Enabled ", + cd->watchdog_enabled ? "True" : "False", + "WD - Interval (ms) ", cd->watchdog_interval +#ifdef TTDL_DIAGNOSTICS + , + "WD - Triggered Count ", cd->watchdog_count, + "WD - IRQ Stuck low count ", cd->watchdog_irq_stuck_count, + "WD - Device Access Errors ", cd->watchdog_failed_access_count, + "WD - XRES Count ", cd->wd_xres_count, + "Startup Retry Count ", cd->startup_retry_count, + "IRQ Triggered Count ", cd->irq_count, + "BL Packet Retry Count ", cd->bl_retry_packet_count, + "PIP2 CRC Error Count ", cd->pip2_crc_error_count, + "Bus Transmit Error Count ", cd->bus_transmit_error_count, + "File Erase Timeout Count ", cd->file_erase_timeout_count, + "RAM Parm Restore Count ", pt_count_parameter_list(cd), + "Calibration Cache on host ", + cd->cal_cache_in_host == PT_FEATURE_ENABLE ? + "Yes" : "No", + "Calibration Cache size ", cal_size, + "Calibration Cache chip ID ", crc +#endif /* TTDL_DIAGNOSTICS */ + ); + +#ifdef TTDL_DIAGNOSTICS + /* Reset all diagnostic counters */ + cd->watchdog_count = 0; + cd->watchdog_irq_stuck_count = 0; + cd->watchdog_failed_access_count = 0; + cd->wd_xres_count = 0; + cd->irq_count = 0; + cd->bl_retry_packet_count = 0; + cd->pip2_crc_error_count = 0; + cd->bus_transmit_error_count = 0; +#endif + + return ret; +} +static DEVICE_ATTR(ttdl_status, 0444, pt_ttdl_status_show, NULL); + +/******************************************************************************* + * FUNCTION: pt_pip2_enter_bl_show + * + * SUMMARY: Show method for the pip2_enter_bl sysfs node that will force + * the DUT into the BL and show the success or failure of entering the BL + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_pip2_enter_bl_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + int rc = 0; + int result = 0; + u8 mode = PT_MODE_UNKNOWN; + struct pt_core_data *cd = dev_get_drvdata(dev); + bool current_bridge_mode = cd->bridge_mode; + + /* Turn off the TTDL WD before enter bootloader */ + pt_stop_wd_timer(cd); + + /* Ensure NO enumeration work is queued or will be queued */ + cancel_work_sync(&cd->enum_work); + mutex_lock(&cd->system_lock); + cd->bridge_mode = true; + mutex_unlock(&cd->system_lock); + + /* set mode to operational to avoid any extra PIP traffic */ + rc = _pt_request_pip2_enter_bl(dev, &mode, &result); + switch (result) { + case PT_ENTER_BL_PASS: + ret = snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\nEntered BL\n", PT_ENTER_BL_PASS); + break; + case PT_ENTER_BL_ERROR: + ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n", + rc, + " Unknown Error"); + break; + case PT_ENTER_BL_RESET_FAIL: + ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n", + rc, + " Soft Reset Failed"); + break; + case PT_ENTER_BL_HID_START_BL_FAIL: + ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n", + rc, + " PIP Start BL Cmd Failed"); + break; + case PT_ENTER_BL_CONFIRM_FAIL: + ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n", + rc, + " Error confirming DUT entered BL"); + break; + default: + ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n", + rc, " Unknown Error"); + break; + }; + + /* Restore state of allowing enumeration work to be queued again */ + mutex_lock(&cd->system_lock); + cd->bridge_mode = current_bridge_mode; + mutex_unlock(&cd->system_lock); + + return ret; +} +static DEVICE_ATTR(pip2_enter_bl, 0444, pt_pip2_enter_bl_show, NULL); + +#define PT_STATUS_STR_LEN (50) +/******************************************************************************* + * FUNCTION: pt_pip2_exit_bl_show + * + * SUMMARY: Show method for the pip2_exit_bl sysfs node that will attempt to + * launch the APP and put the DUT Application mode + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_pip2_exit_bl_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + ssize_t ret = 0; + int rc = 0; + u8 status_str[PT_STATUS_STR_LEN]; + + rc = pt_pip2_exit_bl_(cd, status_str, PT_STATUS_STR_LEN); + /* + * Perform enum if startup_status doesn't reach to + * STARTUP_STATUS_FW_OUT_OF_BOOT. + */ + if (!rc && (!(cd->startup_status & STARTUP_STATUS_FW_OUT_OF_BOOT))) { + rc = pt_enum_with_dut(cd, false, &cd->startup_status); + if (!(cd->startup_status & STARTUP_STATUS_FW_OUT_OF_BOOT)) { + strlcpy(status_str, + "Already in APP mode - FW stuck in Boot mode", sizeof(status_str)); + } + } + ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n", + rc, status_str); + return ret; +} +static DEVICE_ATTR(pip2_exit_bl, 0444, pt_pip2_exit_bl_show, NULL); +#endif + +#ifdef EASYWAKE_TSG6 +/******************************************************************************* + * FUNCTION: pt_easy_wakeup_gesture_show + * + * SUMMARY: Show method for the easy_wakeup_gesture sysfs node that will show + * current easy wakeup gesture + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_easy_wakeup_gesture_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + ssize_t ret; + + mutex_lock(&cd->system_lock); + ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "0x%02X\n", + cd->easy_wakeup_gesture); + mutex_unlock(&cd->system_lock); + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_easy_wakeup_gesture_store + * + * SUMMARY: The store method for the easy_wakeup_gesture sysfs node that + * allows the wake gesture to be set to a custom value. + * + * RETURN: Size of passed in buffer is success + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_easy_wakeup_gesture_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u32 input_data[2]; + int length; + int rc = 0; + + /* Maximum input of one value */ + length = _pt_ic_parse_input(dev, buf, size, input_data, + ARRAY_SIZE(input_data)); + if (length != 1) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto exit; + } + + pt_debug(dev, DL_INFO, "%s: features.easywake = 0x%02X\n", + __func__, cd->features.easywake); + + if (!cd->features.easywake || input_data[0] > 0xFF) { + rc = -EINVAL; + goto exit; + } + + + pm_runtime_get_sync(dev); + mutex_lock(&cd->system_lock); + + if (cd->sysinfo.ready && IS_PIP_VER_GE(&cd->sysinfo, 1, 2)) { + cd->easy_wakeup_gesture = (u8)input_data[0]; + pt_debug(dev, DL_INFO, + "%s: Updated easy_wakeup_gesture = 0x%02X\n", + __func__, cd->easy_wakeup_gesture); + } else + rc = -ENODEV; + + mutex_unlock(&cd->system_lock); + pm_runtime_put(dev); + +exit: + if (rc) + return rc; + return size; +} + +/******************************************************************************* + * FUNCTION: pt_easy_wakeup_gesture_id_show + * + * SUMMARY: Show method for the easy_wakeup_gesture_id sysfs node that will + * show the TSG6 easywake gesture ID + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_easy_wakeup_gesture_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + ssize_t ret; + + mutex_lock(&cd->system_lock); + ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 0\n0x%02X\n", + cd->gesture_id); + mutex_unlock(&cd->system_lock); + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_easy_wakeup_gesture_data_show + * + * SUMMARY: Show method for the easy_wakeup_gesture_data sysfs node that will + * show the TSG6 easywake gesture data in the following format: + * x1(LSB),x1(MSB), y1(LSB),y1(MSB), x2(LSB),x2(MSB), y2(LSB),y2(MSB),... + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_easy_wakeup_gesture_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + ssize_t ret = 0; + int i; + + mutex_lock(&cd->system_lock); + + ret += snprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, "Status: %d\n", 0); + for (i = 0; i < cd->gesture_data_length; i++) + ret += snprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, + "0x%02X\n", cd->gesture_data[i]); + + ret += snprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, + "(%d bytes)\n", cd->gesture_data_length); + + mutex_unlock(&cd->system_lock); + return ret; +} +#endif /* EASYWAKE_TSG6 */ + +#ifdef TTDL_DIAGNOSTICS +/******************************************************************************* + * FUNCTION: pt_err_gpio_show + * + * SUMMARY: Show method for the err_gpio sysfs node that will show if + * setting up the gpio was successful + * + * RETURN: Char buffer with printed GPIO creation state + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_err_gpio_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + return scnprintf(buf, strlen(buf), "Status: 0\n" + "Err GPIO (%d) : %s\n" + "Err GPIO trig type: %d\n", + cd->err_gpio, + (cd->err_gpio ? (gpio_get_value(cd->err_gpio) ? + "HIGH" : "low") : "not defined"), + cd->err_gpio_type); +} + +/******************************************************************************* + * FUNCTION: pt_err_gpio_store + * + * SUMMARY: The store method for the err_gpio sysfs node that allows any + * available host GPIO to be used to trigger when TTDL detects a PIP + * command/response timeout. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_err_gpio_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + unsigned long gpio; + int rc = 0; + u32 input_data[3]; + int length; + u8 err_type; + + input_data[0] = 0; + input_data[1] = 0; + + /* Maximmum input is two elements */ + length = _pt_ic_parse_input(dev, buf, size, + input_data, ARRAY_SIZE(input_data)); + if (length < 1 || length > 2) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto exit; + } + + gpio = input_data[0]; + err_type = (u8)input_data[1]; + if (err_type < 0 || err_type > PT_ERR_GPIO_MAX_TYPE) { + rc = -EINVAL; + goto exit; + } + + mutex_lock(&cd->system_lock); + gpio_free(gpio); + rc = gpio_request(gpio, NULL); + if (rc) { + pt_debug(dev, DL_ERROR, "error requesting gpio %lu\n", gpio); + rc = -ENODEV; + } else { + cd->err_gpio = gpio; + cd->err_gpio_type = err_type; + gpio_direction_output(gpio, 0); + } + mutex_unlock(&cd->system_lock); + +exit: + if (rc) + return rc; + return size; +} +static DEVICE_ATTR(err_gpio, 0644, pt_err_gpio_show, + pt_err_gpio_store); + +/******************************************************************************* + * FUNCTION: pt_drv_irq_show + * + * SUMMARY: Show method for the drv_irq sysfs node that will show if the + * TTDL interrupt is enabled/disabled + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + ssize_t ret = 0; + + mutex_lock(&cd->system_lock); + ret += snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", 0); + if (cd->irq_enabled) + ret += snprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, + "Driver interrupt: ENABLED\n"); + else + ret += snprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, + "Driver interrupt: DISABLED\n"); + mutex_unlock(&cd->system_lock); + + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_drv_irq_store + * + * SUMMARY: The store method for the drv_irq sysfs node that allows the TTDL + * IRQ to be enabled/disabled. + * + * RETURN: Size of passed in buffer + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u32 input_data[2]; + int length; + int rc = 0; + + /* Maximum input of one value */ + length = _pt_ic_parse_input(dev, buf, size, input_data, + ARRAY_SIZE(input_data)); + if (length != 1) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto exit; + } + + mutex_lock(&cd->system_lock); + switch (input_data[0]) { + case 0: + if (cd->irq_enabled) { + cd->irq_enabled = false; + /* Disable IRQ has no return value to check */ + disable_irq_nosync(cd->irq); + pt_debug(dev, DL_INFO, + "%s: Driver IRQ now disabled\n", + __func__); + } else + pt_debug(dev, DL_INFO, + "%s: Driver IRQ already disabled\n", + __func__); + break; + + case 1: + if (cd->irq_enabled == false) { + cd->irq_enabled = true; + enable_irq(cd->irq); + pt_debug(dev, DL_INFO, + "%s: Driver IRQ now enabled\n", + __func__); + } else + pt_debug(dev, DL_INFO, + "%s: Driver IRQ already enabled\n", + __func__); + break; + + default: + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__); + } + mutex_unlock(&(cd->system_lock)); + +exit: + if (rc) + return rc; + return size; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_bin_hdr_show + * + * SUMMARY: Show method for the pip2_bin_hdr sysfs node that will read + * the bin file header from flash and show each field + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_pip2_bin_hdr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct pt_bin_file_hdr hdr = {0}; + int rc; + + rc = _pt_request_pip2_bin_hdr(dev, &hdr); + + ret = scnprintf(buf, strlen(buf), + "%s: %d\n" + "%s: %d\n" + "%s: 0x%04X\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: 0x%08X\n" + "%s: 0x%04X\n" + "%s: 0x%04X\n" + "%s: %d\n" + "%s: %d\n", + "Status", rc, + "Header Length ", hdr.length, + "TTPID ", hdr.ttpid, + "FW Major Ver ", hdr.fw_major, + "FW Minor Ver ", hdr.fw_minor, + "FW Rev Control ", hdr.fw_rev_ctrl, + "FW CRC ", hdr.fw_crc, + "Silicon Rev ", hdr.si_rev, + "Silicon ID ", hdr.si_id, + "Config Ver ", hdr.config_ver, + "HEX File Size ", hdr.hex_file_size + ); + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_platform_data_show + * + * SUMMARY: Show method for the platform_data sysfs node that will show the + * active platform data including: GPIOs, Vendor and Product IDs, + * Virtual Key coordinates, Core/MT/Loader flags, Level trigger delay, + * HID registers, and Easy wake gesture + * + * RETURN: Size of printed data + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_platform_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_platform_data *pdata = dev_get_platdata(dev); + struct pt_core_data *cd = dev_get_drvdata(dev); + ssize_t ret; + + ret = scnprintf(buf, strlen(buf), + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %s\n" + "%s: %s\n" + "%s: %d\n", + "Status", 0, + "Interrupt GPIO ", pdata->core_pdata->irq_gpio, + "Interrupt GPIO Value ", + pdata->core_pdata->irq_gpio ? + gpio_get_value(pdata->core_pdata->irq_gpio) : 0, + "Reset GPIO ", pdata->core_pdata->rst_gpio, + "Reset GPIO Value ", + pdata->core_pdata->rst_gpio ? + gpio_get_value(pdata->core_pdata->rst_gpio) : 0, + "DDI Reset GPIO ", pdata->core_pdata->ddi_rst_gpio, + "DDI Reset GPIO Value ", + pdata->core_pdata->ddi_rst_gpio ? + gpio_get_value(pdata->core_pdata->ddi_rst_gpio) : 0, + "VDDI GPIO ", pdata->core_pdata->vddi_gpio, + "VDDI GPIO Value ", + pdata->core_pdata->vddi_gpio ? + gpio_get_value(pdata->core_pdata->vddi_gpio) : 0, + "VCC GPIO ", pdata->core_pdata->vcc_gpio, + "VCC GPIO Value ", + pdata->core_pdata->vcc_gpio ? + gpio_get_value(pdata->core_pdata->vcc_gpio) : 0, + "AVDD GPIO ", pdata->core_pdata->avdd_gpio, + "AVDD GPIO Value ", + pdata->core_pdata->avdd_gpio ? + gpio_get_value(pdata->core_pdata->avdd_gpio) : 0, + "AVEE GPIO ", pdata->core_pdata->avee_gpio, + "AVEE GPIO Value ", + pdata->core_pdata->avee_gpio ? + gpio_get_value(pdata->core_pdata->avee_gpio) : 0, + "Vendor ID ", pdata->core_pdata->vendor_id, + "Product ID ", pdata->core_pdata->product_id, + "Vkeys x ", pdata->mt_pdata->vkeys_x, + "Vkeys y ", pdata->mt_pdata->vkeys_y, + "Core data flags ", pdata->core_pdata->flags, + "MT data flags ", pdata->mt_pdata->flags, + "Loader data flags ", pdata->loader_pdata->flags, + "Level trigger delay (us) ", + pdata->core_pdata->level_irq_udelay, + "HID Descriptor Register ", + pdata->core_pdata->hid_desc_register, + "HID Output Register ", + cd->hid_desc.output_register, + "HID Command Register ", + cd->hid_desc.command_register, + "Easy wakeup gesture ", + pdata->core_pdata->easy_wakeup_gesture, + "Config DUT generation ", + pdata->core_pdata->config_dut_generation ? + (pdata->core_pdata->config_dut_generation == + CONFIG_DUT_PIP2_CAPABLE ? + "PT TC/TT" : "Gen5/6") : "Auto", + "Watchdog Force Stop ", + pdata->core_pdata->watchdog_force_stop ? + "True" : "False", + "Panel ID Support ", + pdata->core_pdata->panel_id_support); + return ret; +} + +#define PT_ERR_STR_SIZE 64 +/******************************************************************************* + * FUNCTION: pt_bist_bus_test + * + * SUMMARY: Tests the connectivity of the active bus pins: + * I2C - SDA and SCL + * SPI - MOSI, MISO, CLK + * + * Disable TTDL interrupts, send a PIP cmd and then manually read the + * bus. If any data is read we know the I2C/SPI pins are connected + * + * RETURN: + * 0 = Success + * !0 = Failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *net_toggled - pointer to where to store if bus toggled + * *err_str - pointer to error string buffer + ******************************************************************************/ +static int pt_bist_bus_test(struct device *dev, u8 *net_toggled, u8 *err_str) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u8 ver_cmd[8] = {0x01, 0x01, 0x06, 0x00, 0x0E, 0x07, 0xF0, 0xB1}; + u8 *read_buf = NULL; + int bytes_read = 0; + int rc = 0; + + read_buf = kzalloc(PT_MAX_PIP1_MSG_SIZE, GFP_KERNEL); + if (read_buf == NULL) { + rc = -ENOMEM; + goto exit; + } + + bytes_read = pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL); + + pt_debug(dev, DL_INFO, "%s: TTDL Core Suspend\n", __func__); + disable_irq(cd->irq); + mutex_lock(&cd->system_lock); + cd->irq_disabled = true; + mutex_unlock(&cd->system_lock); + + /* + * Sleep >4ms to allow any pending TTDL IRQ to finish. Without this + * the disable_irq_nosync() could cause the IRQ to get stuck asserted + */ + usleep_range(5000, 6000); + + pt_debug(cd->dev, DL_INFO, ">>> %s: Write Buffer Size[%d] VERSION\n", + __func__, (int)sizeof(ver_cmd)); + pt_pr_buf(cd->dev, DL_DEBUG, ver_cmd, (int)sizeof(ver_cmd), + ">>> User CMD"); + rc = pt_adap_write_read_specific(cd, sizeof(ver_cmd), ver_cmd, NULL, 0); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: BUS Test - Failed to send VER cmd\n", __func__); + *net_toggled = 0; + strlcpy(err_str, + "- Write failed, bus open or shorted or DUT in reset", PT_ERR_STR_SIZE); + goto exit_enable_irq; + } + usleep_range(4000, 5000); + bytes_read = pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, read_buf); + pt_debug(dev, DL_INFO, "%s: BUS Test - %d bytes forced read\n", + __func__, bytes_read); + if (bytes_read == 0) { + *net_toggled = 0; + pt_debug(dev, DL_INFO, "%s: BUS Read Failed, 0 bytes read\n", + __func__); + strlcpy(err_str, + "- Bus open, shorted or DUT in reset", PT_ERR_STR_SIZE); + rc = -EIO; + goto exit_enable_irq; + } else { + if (cd->bus_ops->bustype == BUS_I2C) + *net_toggled = 1; + else { + if ((bytes_read > 3) && + (read_buf[3] & PIP2_CMD_COMMAND_ID_MASK) == + PIP2_CMD_ID_VERSION) + *net_toggled = 1; + else { + *net_toggled = 0; + pt_debug(dev, DL_INFO, + "%s: BUS Read Failed, %d bytes read\n", + __func__, bytes_read); + strlcpy(err_str, + "- Bus open, shorted or DUT in reset", PT_ERR_STR_SIZE); + } + } + } + +exit_enable_irq: + enable_irq(cd->irq); + usleep_range(5000, 6000); + mutex_lock(&cd->system_lock); + cd->irq_disabled = false; + mutex_unlock(&cd->system_lock); + pt_debug(dev, DL_INFO, "%s: TTDL Core Resumed\n", __func__); + +exit: + kfree(read_buf); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_bist_irq_test + * + * SUMMARY: Tests the connectivity of the IRQ net + * + * This test will ensure there is a good connection between the host + * and the DUT on the irq pin. First determine if the IRQ is stuck + * asserted and if so keep reading messages off of the bus until + * it de-asserts. Possible outcomes: + * - IRQ was already de-asserted: Send a PIP command and if an + * interrupt is generated the test passes. + * - IRQ was asserted: Reading off the bus de-assertes the IRQ, + * test passes. + * - IRQ stays asserted: After reading the bus multiple times + * the IRQ stays asserted. Likely open or shorted to GND + * + * RETURN: + * 0 = Success + * !0 = Failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *bus_toggled - pointer to where to store if bus toggled + * *irq_toggled - pointer to where to store if IRQ toggled + * *xres_toggled - pointer to where to store if XRES toggled + * *err_str - pointer to error string buffer + ******************************************************************************/ +static int pt_bist_irq_test(struct device *dev, + u8 *bus_toggled, u8 *irq_toggled, u8 *xres_toggled, u8 *err_str) +{ +#ifdef TTDL_PTVIRTDUT_SUPPORT + u8 release_irq[3] = {0xFF, 0xFF, 0x03}; +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + struct pt_core_data *cd = dev_get_drvdata(dev); + u8 *read_buf = NULL; + u8 mode = PT_MODE_UNKNOWN; + u16 actual_read_len; + int bytes_read = 0; + int count = 0; + int rc = 0; + + read_buf = kzalloc(PT_MAX_PIP1_MSG_SIZE, GFP_KERNEL); + if (read_buf == NULL) { + rc = -ENOMEM; + goto exit; + } + + /* Clear IRQ triggered count, and re-evaluate at the end of test */ + cd->irq_count = 0; + + /* + * Check if IRQ is stuck asserted, if so try a non forced flush of + * the bus based on the 2 byte initial length read. Try up to 5x. + */ + while (pt_check_irq_asserted(cd) && count < 5) { + count++; + bytes_read += pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL); + } +#ifdef TTDL_PTVIRTDUT_SUPPORT + if (pt_check_irq_asserted(cd) && cd->route_bus_virt_dut) { + /* Force virtual DUT to release IRQ */ + pt_pr_buf(cd->dev, DL_DEBUG, release_irq, + (int)sizeof(release_irq), ">>> User CMD"); + pt_adap_write_read_specific(cd, 3, release_irq, NULL, 0); + } +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + + if (count > 1 && count < 5 && bytes_read > 0) { + /* + * IRQ was stuck but data was successfully read from the + * bus releasing the IRQ line. + */ + pt_debug(dev, DL_INFO, "%s: count=%d bytes_read=%d\n", + __func__, count, bytes_read); + *bus_toggled = 1; + *irq_toggled = 1; + goto exit; + } + + if (count == 5 && bytes_read == 0) { + /* + * Looped 5x and read nothing off the bus yet the IRQ is still + * asserted. Possible conditions: + * - IRQ open circuit + * - IRQ shorted to GND + * - I2C/SPI bus is disconnected + * - FW holding the pin low + * Try entering the BL to see if communication works there. + */ + mode = PT_MODE_IGNORE; + rc = _pt_request_pip2_enter_bl(dev, &mode, NULL); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s Failed to enter BL\n", __func__); + strlcpy(err_str, + "- likely shorted to GND or FW holding it.", PT_ERR_STR_SIZE); + *irq_toggled = 0; + goto exit; + } + /* + * If original mode was operational and we successfully + * entered the BL, then the XRES net must have toggled + */ + if (mode == PT_MODE_OPERATIONAL) + *xres_toggled = 1; + + rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_PROTECTED, + PIP2_CMD_ID_VERSION, NULL, 0, read_buf, + &actual_read_len); + if (rc) { + /* + * Could not communicate to DUT in BL mode. Save the + * error string, slim chance but the XRES test below may + * show the IRQ is actually working. + */ + strlcpy(err_str, "- likely shorted to GND.", PT_ERR_STR_SIZE); + pt_debug(dev, DL_ERROR, + "%s: %s, count=%d bytes_read=%d\n", + __func__, err_str, count, bytes_read); + *irq_toggled = 0; + rc = pt_pip2_exit_bl_(cd, NULL, 0); + goto exit; + } else { + *bus_toggled = 1; + *irq_toggled = 1; + goto exit; + } + } + if (pt_check_irq_asserted(cd)) { + strlcpy(err_str, "- likely shorted to GND", PT_ERR_STR_SIZE); + rc = -EIO; + *irq_toggled = 0; + goto exit; + } + + /* Try sending a PIP command to see if we get a response */ + rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_PROTECTED, + PIP2_CMD_ID_VERSION, NULL, 0, read_buf, &actual_read_len); + if (rc) { + /* + * Potential IRQ issue, no communication in App mode, attempt + * the same command in the BL + */ + mode = PT_MODE_IGNORE; + rc = _pt_request_pip2_enter_bl(dev, &mode, NULL); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s Failed to enter BL\n", __func__); + *irq_toggled = 0; + strlcpy(err_str, "- likely open or shorted to VDDI.", PT_ERR_STR_SIZE); + goto exit; + } + /* + * If original mode was operational and we successfully + * entered the BL, this will be useful info for the tp_xres + * test below. + */ + if (mode == PT_MODE_OPERATIONAL) + *xres_toggled = 1; + + rc = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_PROTECTED, + PIP2_CMD_ID_VERSION, NULL, 0, read_buf, + &actual_read_len); + if (rc) { + /* + * Could not communicate in FW mode or BL mode. Save the + * error string, slim chance but the XRES test below may + * show the IRQ is actually working. + */ + strlcpy(err_str, "- likely open or shorted to VDDI.", PT_ERR_STR_SIZE); + pt_debug(dev, DL_ERROR, + "%s: request_active_pip_prot failed\n", + __func__); + *irq_toggled = 0; + goto exit; + } + } + + if (cd->irq_count > 0) { + pt_debug(dev, DL_INFO, "%s: irq_count=%d\n", __func__, + cd->irq_count); + *bus_toggled = 1; + *irq_toggled = 1; + goto exit; + } + +exit: + kfree(read_buf); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_bist_xres_test + * + * SUMMARY: Tests the connectivity of the TP_XRES net + * + * This test will ensure there is a good connection between the host + * and the DUT on the tp_xres pin. The pin will be toggled to + * generate a TP reset which will cause the DUT to output a reset + * sentinel. If the reset sentinel is seen the test passes. If it is + * not seen the test will attempt to send a soft reset to simply gain + * some additional information on the failure: + * - soft reset fails to send: XRES and IRQ likely open + * - soft reset passes: XRES likely open or stuck de-asserted + * - soft reset fails: XRES likely stuck asserted + * + * RETURN: + * 0 = Success + * !0 = Failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *bus_toggled - pointer to where to store if bus toggled + * *irq_toggled - pointer to where to store if IRQ toggled + * *xres_toggled - pointer to where to store if XRES toggled + * *err_str - pointer to error string buffer + ******************************************************************************/ +static int pt_bist_xres_test(struct device *dev, + u8 *bus_toggled, u8 *irq_toggled, u8 *xres_toggled, u8 *err_str) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_platform_data *pdata = dev_get_platdata(dev); +#ifdef TTDL_PTVIRTDUT_SUPPORT + u8 release_irq[3] = {0xFF, 0xFF, 0x03}; +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + u8 *read_buf = NULL; + u8 mode = PT_MODE_UNKNOWN; + int rc = 0; + int t = 0; + int timeout = 300; + + read_buf = kzalloc(PT_MAX_PIP1_MSG_SIZE, GFP_KERNEL); + if (read_buf == NULL) { + rc = -ENOMEM; + goto exit; + } + + /* Clear the startup bit mask, reset and enum will re-populate it */ + cd->startup_status = STARTUP_STATUS_START; + pt_debug(dev, DL_DEBUG, "%s: Startup Status Reset\n", __func__); + + if ((!pdata->core_pdata->rst_gpio) || (!pdata->core_pdata->xres)) { + strlcpy(err_str, "- Net not configured or available", PT_ERR_STR_SIZE); + rc = -ENODEV; + goto exit; + } + + + /* Ensure we have nothing pending on active bus */ + pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN); +#ifdef TTDL_PTVIRTDUT_SUPPORT + if (pt_check_irq_asserted(cd) && cd->route_bus_virt_dut) { + /* Force virtual DUT to release IRQ */ + pt_pr_buf(cd->dev, DL_DEBUG, release_irq, + (int)sizeof(release_irq), ">>> User CMD"); + pt_adap_write_read_specific(cd, 3, release_irq, NULL, 0); + } +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + + /* Perform a hard XRES toggle and wait for reset sentinel */ + mutex_lock(&cd->system_lock); + cd->hid_reset_cmd_state = 1; + mutex_unlock(&cd->system_lock); + pt_debug(dev, DL_INFO, "%s: Perform a hard reset\n", __func__); + rc = pt_hw_hard_reset(cd); + + /* Set timeout to 1s for the flashless case where a BL could be done */ + if (cd->flashless_dut) + timeout = 1000; + + /* + * To avoid the case that next PIP command can be confused by BL/FW + * sentinel's "wakeup" event, chekcing hid_reset_cmd_state which is + * followed by "wakeup event" function can lower the failure rate. + */ + t = wait_event_timeout(cd->wait_q, + ((cd->startup_status != STARTUP_STATUS_START) + && (cd->hid_reset_cmd_state == 0)), + msecs_to_jiffies(timeout)); + if (IS_TMO(t)) { + pt_debug(cd->dev, DL_ERROR, + "%s: TMO waiting for sentinel\n", __func__); + *xres_toggled = 0; + strlcpy(err_str, "- likely open. (No Reset Sentinel)", PT_ERR_STR_SIZE); + + /* + * Possibly bad FW, Try entering BL and wait for reset sentinel. + * To enter the BL we need to generate an XRES so first try to + * launch the applicaiton + */ + if (cd->mode == PT_MODE_BOOTLOADER) + pt_pip2_launch_app(dev, PT_CORE_CMD_PROTECTED); + pt_debug(dev, DL_INFO, "%s: Enter BL with a hard reset\n", + __func__); + mode = PT_MODE_IGNORE; + rc = _pt_request_pip2_enter_bl(dev, &mode, NULL); + if (rc) { + pt_debug(dev, DL_ERROR, "%s Failed to enter BL\n", + __func__); + *xres_toggled = 0; + strlcpy(err_str, "- likely open or shorted to VDDI.", PT_ERR_STR_SIZE); + goto exit; + } else { + /* Wait for the BL sentinel */ + t = wait_event_timeout(cd->wait_q, + (cd->startup_status != STARTUP_STATUS_START), + msecs_to_jiffies(500)); + if (IS_TMO(t)) { + pt_debug(cd->dev, DL_ERROR, + "%s: TMO waiting for BL sentinel\n", + __func__); + *xres_toggled = 0; + strlcpy(err_str, + "- likely open or shorted to VDDI.", PT_ERR_STR_SIZE); + rc = -ETIME; + goto exit; + } + } + } + mutex_lock(&cd->system_lock); + cd->hid_reset_cmd_state = 0; + mutex_unlock(&cd->system_lock); + + /* Look for BL or FW reset sentinels */ + if (!rc && ((cd->startup_status & STARTUP_STATUS_BL_RESET_SENTINEL) || + (cd->startup_status & STARTUP_STATUS_FW_RESET_SENTINEL))) { + pt_debug(dev, DL_INFO, "%s: hard XRES pass\n", __func__); + /* If a sentinel was seen, all nets are working */ + *xres_toggled = 1; + *irq_toggled = 1; + /* + * For SPI test, bus read result can be confused as FW sentinel + * if MISO(slave) is connected to MISO(host). + */ + if (cd->bus_ops->bustype == BUS_I2C) + *bus_toggled = 1; + } else { + /* + * Hard reset failed, however some additional information + * could be determined. Try a soft reset to see if DUT resets + * with the possible outcomes: + * - if it resets the line is not stuck asserted + * - if it does not reset the line could be stuck asserted + */ + *xres_toggled = 0; + rc = pt_hw_soft_reset(cd, PT_CORE_CMD_PROTECTED); + msleep(30); + pt_debug(dev, DL_INFO, "%s: TP_XRES BIST soft reset rc=%d", + __func__, rc); + if (rc) { + strlcpy(err_str, "- likely open.", PT_ERR_STR_SIZE); + pt_debug(dev, DL_ERROR, + "%s: Hard reset failed, soft reset failed %s\n", + __func__, err_str); + goto exit; + } + if (cd->startup_status & STARTUP_STATUS_BL_RESET_SENTINEL || + cd->startup_status & STARTUP_STATUS_FW_RESET_SENTINEL) { + strlcpy(err_str, + "- likely open or stuck high, soft reset OK", PT_ERR_STR_SIZE); + pt_debug(dev, DL_ERROR, + "%s: Hard reset failed, soft reset passed-%s\n", + __func__, err_str); + } else if (cd->startup_status == 0) { + strlcpy(err_str, "- likely stuck high.", PT_ERR_STR_SIZE); + pt_debug(dev, DL_ERROR, + "%s: Hard reset failed, soft reset failed-%s\n", + __func__, err_str); + } else { + strlcpy(err_str, "- open or stuck.", PT_ERR_STR_SIZE); + pt_debug(dev, DL_ERROR, + "%s: Hard and Soft reset failed - %s\n", + __func__, err_str); + } + } + +exit: + kfree(read_buf); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_bist_slave_irq_test + * + * SUMMARY: Tests the connectivity of the Master/Slave IRQ net + * + * This test will ensure there is a good IRQ connection between the master + * DUT and the slave DUT(s). After power up the STATUS command is sent + * and the 'Slave Detect' bits are verified to ensure the master DUT + * saw each slave trigger the IRQ with it's reset sentinel. + * + * RETURN: + * 0 = Success + * !0 = Failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *slave - pointer to one entry in the slave info array + ******************************************************************************/ +static int pt_bist_slave_irq_test(struct device *dev, + struct pt_bist_data *slave) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u8 mode = PT_MODE_UNKNOWN; + u8 status; + u8 boot; + u8 read_buf[12]; + u8 detected = 0; + u8 last_err = -1; + u16 actual_read_len; + int result = 0; + int rc = 0; + + /* + * Ensure DUT is in the BL where the STATUS cmd will report the slave + * detect bits. If the DUT was in FW, entering the BL will cause an + * XRES signal which will inadvertently test the XRES net as well + */ + rc = _pt_request_pip2_enter_bl(dev, &mode, &result); + if (rc) { + pt_debug(cd->dev, DL_WARN, "%s: Error entering BL rc=%d\n", + __func__, rc); + if (slave->irq_err_str) + strlcpy(slave->irq_err_str, + "- State could not be determined.", PT_ERR_STR_SIZE); + goto exit; + } + + /* Use the STATUS command to retrieve the slave detect bit(s) */ + rc = _pt_request_pip2_send_cmd(cd->dev, PT_CORE_CMD_UNPROTECTED, + PIP2_CMD_ID_STATUS, NULL, 0, read_buf, + &actual_read_len); + if (!rc) { + pt_pr_buf(cd->dev, DL_INFO, read_buf, actual_read_len, + "PIP2 STATUS"); + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + boot = read_buf[PIP2_RESP_BODY_OFFSET] & slave->mask; + + /* Slave detect is only valid if status ok and in boot exec */ + if (status == PIP2_RSP_ERR_NONE && + boot == PIP2_STATUS_BOOT_EXEC) { + detected = read_buf[PIP2_RESP_BODY_OFFSET + 2] & + SLAVE_DETECT_MASK; + } else { + if (slave->irq_err_str) + strlcpy(slave->irq_err_str, + "- State could not be determined", PT_ERR_STR_SIZE); + rc = -EPERM; + } + } else { + pt_debug(cd->dev, DL_WARN, "%s: STATUS cmd failure\n", + __func__); + if (slave->irq_err_str) + strlcpy(slave->irq_err_str, + "- State could not be determined.", PT_ERR_STR_SIZE); + goto exit; + } + + /* + * Retrieve boot error regardless of the state of the slave detect + * bit because the IRQ could have been stuck high or low. + */ + rc = _pt_request_pip2_send_cmd(cd->dev, PT_CORE_CMD_UNPROTECTED, + PIP2_CMD_ID_GET_LAST_ERRNO, NULL, 0, + read_buf, &actual_read_len); + if (!rc) { + pt_pr_buf(cd->dev, DL_INFO, read_buf, actual_read_len, + "PIP2 GET_LAST_ERRNO"); + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + last_err = read_buf[PIP2_RESP_BODY_OFFSET]; + if (last_err) { + pt_debug(cd->dev, DL_WARN, + "%s: Master Boot Last Err = 0x%02X\n", + __func__, last_err); + } + } else { + pt_debug(cd->dev, DL_WARN, + "%s: GET_LAST_ERRNO cmd failure\n", __func__); + if (slave->irq_err_str) + strlcpy(slave->irq_err_str, + "- stuck, likely shorted to GND.", PT_ERR_STR_SIZE); + } + +exit: + pt_debug(cd->dev, DL_INFO, + "%s: rc=%d detected=0x%02X boot_err=0x%02X\n", + __func__, rc, detected, last_err); + + /* + * Clear any possible false positives: + * - An invalid image error as BIST doesn't need valid FW + * - A Flash file too small as BIST doesn't need FLASH + * - FLASH access errors when in no-flash mode + */ + if ((last_err == PIP2_RSP_ERR_INVALID_IMAGE) || + (last_err == PIP2_RSP_ERR_BUF_TOO_SMALL) || + (last_err == PIP2_RSP_ERR_BAD_ADDRESS && cd->flashless_dut) || + (last_err == PIP2_RSP_ERR_BAD_FRAME && cd->flashless_dut)) { + pt_debug(cd->dev, DL_INFO, "%s: Cleared boot error: 0x%02X\n", + __func__, last_err); + last_err = PIP2_RSP_ERR_NONE; + } + + /* Attempt to add a hint based on boot error and detection */ + if (slave->irq_err_str) { + if (last_err && detected) + scnprintf(slave->irq_err_str,PT_ERR_STR_SIZE, "%s 0x%02X", + "- Likely stuck low. Boot Error:", + last_err); + else if (last_err && !detected) + scnprintf(slave->irq_err_str, PT_ERR_STR_SIZE, "%s 0x%02X", + "- Likely stuck high. Boot Error:", + last_err); + else if (detected) + strlcpy(slave->irq_err_str, + "- Likely stuck low. No Critical Boot Error", PT_ERR_STR_SIZE); + else if (!detected) + strlcpy(slave->irq_err_str, + "- Likely stuck high. No Critical Boot Error.", PT_ERR_STR_SIZE); + } + + slave->irq_toggled = (detected && !last_err) ? true : false; + /* Leave as UNTEST if slave not detected */ + if (detected) + slave->bus_toggled = !last_err ? true : false; + slave->detected = detected; + slave->boot_err = last_err; + + pt_debug(cd->dev, DL_INFO, "%s: %s=0x%02X, %s=0x%02X, %s=0x%02X\n", + __func__, + "Detected", detected, + "slave_irq_toggled", slave->irq_toggled, + "slave_bus_toggled", slave->bus_toggled); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_bist_slave_xres_test + * + * SUMMARY: Tests the connectivity of the Master/Slave TP_XRES net + * + * This test will ensure there is a good TP_XRES connection between the + * master DUT and the slave DUT(s). After toggling the XRES pin to the + * master, the STATUS command is sent and the 'Slave Detect' bits are + * verified to ensure the master DUT saw each slave trigger the IRQ with + * it's reset sentinel. + * + * RETURN: + * 0 = Success + * !0 = Failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *slave - pointer to one entry in the slave info array + ******************************************************************************/ +static int pt_bist_slave_xres_test(struct device *dev, + struct pt_bist_data *slave) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int rc = 0; + + /* Force a reset to force the 'slave detect' bits to be re-acquired */ + mutex_lock(&cd->system_lock); + cd->hid_reset_cmd_state = 1; + mutex_unlock(&cd->system_lock); + pt_debug(dev, DL_INFO, "%s: Perform a hard reset\n", __func__); + pt_hw_hard_reset(cd); + msleep(100); + + rc = pt_bist_slave_irq_test(dev, slave); + pt_debug(dev, DL_INFO, "%s: IRQ test rc = %d\n", __func__, rc); + + if (!rc && slave->irq_toggled == false) { + /* + * If the slave IRQ did not toggle, either the slave_detect + * bit was not set or we had a boot error. If the slave + * detect was not set the slave did not reset causing a boot + * error. + */ + if (slave->xres_err_str && !slave->detected) { + strlcpy(slave->xres_err_str, slave->irq_err_str, PT_ERR_STR_SIZE); + pt_debug(dev, DL_INFO, + "%s: detected=0 irq_err_str = %s\n", + __func__, slave->irq_err_str); + } else if (slave->xres_err_str && slave->boot_err > 0) { + scnprintf(slave->xres_err_str, PT_ERR_STR_SIZE, "%s 0x%02X", + "- likely open or an IRQ issue. Boot Error:", + slave->boot_err); + pt_debug(dev, DL_INFO, + "%s: boot_err=%d xres_err_str = %s\n", + __func__, + slave->boot_err, slave->xres_err_str); + } else { + pt_debug(dev, DL_WARN, "%s: No xres_err_str buffer\n", + __func__); + } + } + + if (!rc) + slave->xres_toggled = slave->irq_toggled ? true : false; + else + slave->xres_toggled = false; + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_bist_slave_bus_test + * + * SUMMARY: Tests the connectivity of the Master/Slave SPI bus net + * + * This test will ensure a good SPI bus connection between the + * master DUT and the slave DUT(s). This bus connection is ensured by + * opening file 0 (SRAM loader). If there is a slave and the open fails + * then there is a master/slave communication issue. Opening file 0 on + * the master will open it on the slave as well if the slave was detected. + * + * RETURN: + * 0 = Success + * !0 = Failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *slave - pointer to one entry in the slave info array + ******************************************************************************/ +static int pt_bist_slave_bus_test(struct device *dev, + struct pt_bist_data *slave) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u8 mode = PT_MODE_UNKNOWN; + u8 bus_toggled = false; + u8 file_handle; + int result = 0; + int rc = 0; + + rc = _pt_request_pip2_enter_bl(dev, &mode, &result); + if (rc) { + pt_debug(cd->dev, DL_WARN, "%s: Error entering BL rc=%d\n", + __func__, rc); + if (slave->bus_err_str) + strlcpy(slave->bus_err_str, + "- State could not be determined.", PT_ERR_STR_SIZE); + goto exit; + } + + pt_debug(dev, DL_INFO, "%s Attempt open file 0\n", __func__); + file_handle = _pt_pip2_file_open(dev, PIP2_RAM_FILE); + if (file_handle != PIP2_RAM_FILE) { + rc = -ENOENT; + bus_toggled = false; + pt_debug(dev, DL_WARN, + "%s Failed to open bin file\n", __func__); + if (slave->bus_err_str) + strlcpy(slave->bus_err_str, + "- Bus open, shorted or DUT in reset", PT_ERR_STR_SIZE); + goto exit; + } else { + bus_toggled = true; + if (file_handle != _pt_pip2_file_close(dev, file_handle)) { + pt_debug(dev, DL_WARN, + "%s: File Close failed, file_handle=%d\n", + __func__, file_handle); + } + } + +exit: + /* If the master was able to send/recv a PIP msg, the IRQ must be ok */ + slave->irq_toggled = bus_toggled; + slave->bus_toggled = bus_toggled; + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_bist_slave_test + * + * SUMMARY: Tests XRES, SPI BUS and IRQ for the slave DUT + * + * RETURN: + * 0 = Success + * !0 = Failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *slave - pointer to one entry in the bist info array + * slave_id - id of the slave chip + ******************************************************************************/ +static int pt_bist_slave_test(struct device *dev, + struct pt_bist_data *slave, int slave_id) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + char *pr_buf; + int rc = 0; + + slave->mask = 0x01 << slave_id; + slave->bus_toggled = 0x0F; /* untested */ + slave->irq_toggled = 0x0F; /* untested */ + slave->xres_toggled = 0x0F; /* untested */ + + pr_buf = slave->print_buf; + if (!pr_buf) { + rc = -ENOMEM; + goto print_results; + } + + slave->bus_err_str = kzalloc(PT_ERR_STR_SIZE, + GFP_KERNEL); + slave->irq_err_str = kzalloc(PT_ERR_STR_SIZE, + GFP_KERNEL); + slave->xres_err_str = kzalloc(PT_ERR_STR_SIZE, + GFP_KERNEL); + + /* ensure no malloc failure */ + if (!slave->bus_err_str || + !slave->irq_err_str || + !slave->xres_err_str) + goto print_results; + + memset(slave->bus_err_str, 0, PT_ERR_STR_SIZE); + memset(slave->irq_err_str, 0, PT_ERR_STR_SIZE); + memset(slave->xres_err_str, 0, PT_ERR_STR_SIZE); + + /* --------------- SLAVE XRES BIST TEST --------------- */ + if ((cd->ttdl_bist_select & PT_BIST_SLAVE_XRES_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start Slave %d XRES BIST -----", + __func__, slave_id); + slave->xres_toggled = 0xFF; + rc = pt_bist_slave_xres_test(dev, slave); + if (slave->bus_toggled == 1 && + slave->irq_toggled == 1 && + slave->xres_toggled == 1) + goto print_results; + } + + /* --------------- SLAVE IRQ BIST TEST --------------- */ + if ((cd->ttdl_bist_select & PT_BIST_SLAVE_IRQ_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start Slave %d IRQ BIST -----", + __func__, slave_id); + slave->irq_toggled = 0xFF; + rc = pt_bist_slave_irq_test(dev, slave); + pt_debug(dev, DL_INFO, + "%s: slave_irq_toggled = 0x%02X\n", + __func__, slave->irq_toggled); + if (slave->irq_toggled == 1) { + slave->bus_toggled = 1; + goto print_results; + } + } + + /* --------------- SLAVE BUS BIST TEST --------------- */ + if ((cd->ttdl_bist_select & PT_BIST_SLAVE_BUS_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start Slave %d BUS BIST -----", + __func__, slave_id); + slave->bus_toggled = 0xFF; + rc = pt_bist_slave_bus_test(dev, slave); + } + +print_results: + /* --------------- PRINT OUT BIST RESULTS ---------------*/ + pt_debug(dev, DL_INFO, "%s: ----- BIST Print Results ----", __func__); + if (!slave->bus_err_str || + !slave->irq_err_str || + !slave->xres_err_str) { + slave->pr_index = scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE, + "M/S%d SPI (MISO,MOSI,CS,CLK): [UNTEST]\n" + "M/S%d IRQ connection: [UNTEST]\n" + "M/S%d TP_XRES connection: [UNTEST]\n", + slave_id, slave_id, slave_id); + } else { + if (slave->irq_toggled == 1) + memset(slave->irq_err_str, 0, PT_ERR_STR_SIZE); + if (slave->xres_toggled == 1) + memset(slave->xres_err_str, 0, PT_ERR_STR_SIZE); + if (slave->bus_toggled == 1) + memset(slave->bus_err_str, 0, PT_ERR_STR_SIZE); + + slave->status = 0; + if (cd->ttdl_bist_select & PT_BIST_SLAVE_BUS_TEST) + slave->status += slave->bus_toggled; + if (cd->ttdl_bist_select & PT_BIST_SLAVE_IRQ_TEST) + slave->status += slave->irq_toggled; + if (cd->ttdl_bist_select & PT_BIST_SLAVE_XRES_TEST) + slave->status += slave->xres_toggled; + pt_debug(dev, DL_WARN, + "%s: status = %d (Slave %d: %d,%d,%d)\n", + __func__, slave->status, slave_id, + slave->bus_toggled, + slave->irq_toggled, + slave->xres_toggled); + + slave->pr_index = scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE, + "M/S%d SPI (MISO,MOSI,CS,CLK): %s %s\n" + "M/S%d IRQ connection: %s %s\n" + "M/S%d TP_XRES connection: %s %s\n", + slave_id, + slave->bus_toggled == 0x0F ? + "[UNTEST]" : + slave->bus_toggled == 1 ? + "[ OK ]" : + "[FAILED]", + slave->bus_err_str, + slave_id, + slave->irq_toggled == 0x0F ? + "[UNTEST]" : + slave->irq_toggled == 1 ? + "[ OK ]" : + "[FAILED]", + slave->irq_err_str, + slave_id, + slave->xres_toggled == 0x0F ? + "[UNTEST]" : + slave->xres_toggled == 1 ? + "[ OK ]" : + "[FAILED]", + slave->xres_err_str); + } + + kfree(slave->bus_err_str); + kfree(slave->irq_err_str); + kfree(slave->xres_err_str); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_bist_host_test + * + * SUMMARY: Tests XRES, SPI BUS and IRQ for the host DUT + * + * RETURN: + * 0 = Success + * !0 = Failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *host - pointer to the entry in the bist info array + ******************************************************************************/ +static int pt_bist_host_test(struct device *dev, + struct pt_bist_data *host) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + char *pr_buf; + int rc = 0; + + u8 bus_toggled = 0x0F; /* default to untested */ + u8 i2c_toggled = 0x0F; /* default to untested */ + u8 spi_toggled = 0x0F; /* default to untested */ + u8 irq_toggled = 0x0F; /* default to untested */ + u8 xres_toggled = 0x0F; /* default to untested */ + + pr_buf = host->print_buf; + if (!pr_buf) { + rc = -ENOMEM; + goto print_results; + } + + host->bus_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); + host->irq_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); + host->xres_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); + if (!host->bus_err_str || + !host->irq_err_str || + !host->xres_err_str) + goto print_results; + + memset(host->xres_err_str, 0, PT_ERR_STR_SIZE); + memset(host->irq_err_str, 0, PT_ERR_STR_SIZE); + memset(host->bus_err_str, 0, PT_ERR_STR_SIZE); + + /* --------------- TP_XRES BIST TEST --------------- */ + if ((cd->ttdl_bist_select & PT_BIST_TP_XRES_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start TP_XRES BIST -----", __func__); + rc = pt_bist_xres_test(dev, &bus_toggled, &irq_toggled, + &xres_toggled, host->xres_err_str); + /* Done if the rest of all nets toggled */ + if (bus_toggled == 1 && irq_toggled == 1 && xres_toggled == 1) + goto print_results; + } + + /* Flush bus in case a PIP response is waiting from previous test */ + pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL); + + /* --------------- IRQ BIST TEST --------------- */ + if ((cd->ttdl_bist_select & PT_BIST_IRQ_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start IRQ BIST -----", __func__); + bus_toggled = 0xFF; + irq_toggled = 0xFF; + rc = pt_bist_irq_test(dev, &bus_toggled, &irq_toggled, + &xres_toggled, host->irq_err_str); + /* If this net failed clear results from previous net */ + if (irq_toggled != 1) { + xres_toggled = 0x0F; + memset(host->xres_err_str, 0, PT_ERR_STR_SIZE); + } + if (bus_toggled == 1 && irq_toggled == 1) + goto print_results; + } + + /* Flush bus in case a PIP response is waiting from previous test */ + pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL); + + /* --------------- BUS BIST TEST --------------- */ + if ((cd->ttdl_bist_select & PT_BIST_BUS_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start BUS BIST -----", __func__); + bus_toggled = 0xFF; + rc = pt_bist_bus_test(dev, &bus_toggled, host->bus_err_str); + /* If this net failed clear results from previous net */ + if (bus_toggled == 0) { + irq_toggled = 0x0F; + memset(host->irq_err_str, 0, PT_ERR_STR_SIZE); + } + } + +print_results: + /* --------------- PRINT OUT BIST RESULTS ---------------*/ + pt_debug(dev, DL_INFO, "%s: ----- BIST Print Results ----", __func__); + + /* Cannot print if any memory allocation issues */ + if (!host->bus_err_str || !host->irq_err_str || !host->xres_err_str) { + host->pr_index = scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE, + "Status: %d\n" + "I2C (SDA,SCL): [UNTEST]\n" + "SPI (MISO,MOSI,CS,CLK): [UNTEST]\n" + "IRQ connection: [UNTEST]\n" + "TP_XRES connection: [UNTEST]\n", -ENOMEM); + } else { + host->status = 0; + if (bus_toggled == 1) + memset(host->bus_err_str, 0, PT_ERR_STR_SIZE); + if (irq_toggled == 1) + memset(host->irq_err_str, 0, PT_ERR_STR_SIZE); + if (xres_toggled == 1) + memset(host->xres_err_str, 0, PT_ERR_STR_SIZE); + + if (cd->ttdl_bist_select & PT_BIST_BUS_TEST) + host->status += bus_toggled; + if (cd->ttdl_bist_select & PT_BIST_IRQ_TEST) + host->status += irq_toggled; + if (cd->ttdl_bist_select & PT_BIST_TP_XRES_TEST) + host->status += xres_toggled; + pt_debug(dev, DL_WARN, "%s: status = %d (%d,%d,%d)\n", + __func__, host->status, bus_toggled, irq_toggled, + xres_toggled); + + if (cd->bus_ops->bustype == BUS_I2C) + i2c_toggled = bus_toggled; + else + spi_toggled = bus_toggled; + + host->pr_index = scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE, + "I2C (SDA,SCL): %s %s\n" + "SPI (MISO,MOSI,CS,CLK): %s %s\n" + "IRQ connection: %s %s\n" + "TP_XRES connection: %s %s\n", + i2c_toggled == 0x0F ? "[UNTEST]" : + i2c_toggled == 1 ? "[ OK ]" : "[FAILED]", + i2c_toggled == 0x0F ? "" : host->bus_err_str, + spi_toggled == 0x0F ? "[UNTEST]" : + spi_toggled == 1 ? "[ OK ]" : "[FAILED]", + spi_toggled == 0x0F ? "" : host->bus_err_str, + irq_toggled == 0x0F ? "[UNTEST]" : + irq_toggled == 1 ? "[ OK ]" : "[FAILED]", + host->irq_err_str, + xres_toggled == 0x0F ? "[UNTEST]" : + xres_toggled == 1 ? "[ OK ]" : "[FAILED]", + host->xres_err_str); + } + + kfree(host->bus_err_str); + kfree(host->irq_err_str); + kfree(host->xres_err_str); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_ttdl_bist_show + * + * SUMMARY: Show method for the ttdl_bist sysfs node. This built in self test + * will test that I2C/SPI, IRQ and TP_XRES pins are operational. + * + * NOTE: This function will reset the DUT and the startup_status bit + * mask. A pt_enum will be queued after completion. + * + * NOTE: The order of the net tests is done to optimize the time it takes + * to run. The first test is capable of verifying all nets, each subsequent + * test is only run if the previous was not able to see all nets toggle. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_ttdl_bist_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + ssize_t ret = 0; + u8 tests; + int slave_id = 0; + int num_slaves = 0; + int rc = 0; + int num_tests = 0; + int status = 1; /* 0 = Pass, !0 = fail */ + struct pt_bist_data host; + struct pt_bist_data slaves[PT_MAX_DEVICES]; + int idx_status = 0; + + host.print_buf = kzalloc(PT_MAX_PR_BUF_SIZE, GFP_KERNEL); + if (!host.print_buf) { + ret = scnprintf(buf, strlen(buf), + "Status: 1\n" + "Failed to alloc memory"); + return ret; + } + + /* Load up slave info array when slaves present */ + num_slaves = cd->num_devices - 1; + + /* Turn off the TTDL WD during the test */ + pt_stop_wd_timer(cd); + + /* Shorten default PIP cmd timeout while running BIST */ + cd->pip_cmd_timeout = 200; + + /* Count the number of tests to run */ + tests = cd->ttdl_bist_select; + while (tests) { + num_tests += tests & 1; + tests >>= 1; + } + pt_debug(dev, DL_WARN, "%s: BIST select = 0x%02X, run %d tests\n", + __func__, cd->ttdl_bist_select, num_tests); + + /* Suppress auto BL to avoid loader thread sending PIP during xres */ + if (cd->flashless_dut) { + pt_debug(dev, DL_INFO, "%s Flashless Auto_BL - SUPPRESS\n", + __func__); + mutex_lock(&cd->system_lock); + cd->flashless_auto_bl = PT_SUPPRESS_AUTO_BL; + mutex_unlock(&cd->system_lock); + } + + /* --------------- TP HOST BIST TEST --------------- */ + host.status = 0; + host.pr_index = 0; + rc = pt_bist_host_test(dev, &host); + pt_debug(dev, DL_INFO, "%s print_idx = %d\n", + __func__, host.pr_index); + + status = host.status; + + /* --------------- TP SLAVE BIST TEST --------------- */ + for (slave_id = 0; slave_id < num_slaves; slave_id++) { + slaves[slave_id].print_buf = kzalloc(PT_MAX_PR_BUF_SIZE, + GFP_KERNEL); + if (!slaves[slave_id].print_buf) { + ret = scnprintf(buf, strlen(buf), + "Status: 1\n" + "Failed to alloc memory"); + goto exit; + } + slaves[slave_id].status = 0; + slaves[slave_id].pr_index = 0; + pt_bist_slave_test(dev, &slaves[slave_id], slave_id); + status += slaves[slave_id].status; + } + + idx_status = scnprintf(buf, strlen(buf), "Status: %d\n", + status == num_tests ? 0 : 1); + memcpy(buf + idx_status, host.print_buf, host.pr_index); + ret = idx_status + host.pr_index; + + for (slave_id = 0; slave_id < num_slaves; slave_id++) { + memcpy(buf + ret, slaves[slave_id].print_buf, + slaves[slave_id].pr_index); + ret += slaves[slave_id].pr_index; + } + +exit: + /* Restore PIP command timeout */ + cd->pip_cmd_timeout = cd->pip_cmd_timeout_default; + + /* + * We're done! - Perform a hard XRES toggle, allowing BL + * to load FW if there is any in Flash + */ + mutex_lock(&cd->system_lock); + cd->hid_reset_cmd_state = 0; + mutex_unlock(&cd->system_lock); + + pt_debug(dev, DL_INFO, + "%s: TTDL BIST Complete - Final reset\n", __func__); + + if (cd->flashless_dut) { + /* + * For Flashless solution, FW update is triggered after BL is + * seen that several miliseconds delay is needed. + */ + pt_debug(dev, DL_INFO, "%s Flashless Auto_BL - ALLOW\n", + __func__); + mutex_lock(&cd->system_lock); + cd->flashless_auto_bl = PT_ALLOW_AUTO_BL; + mutex_unlock(&cd->system_lock); + + /* Reset DUT and wait 100ms to see if loader started */ + pt_hw_hard_reset(cd); + msleep(100); + if (cd->fw_updating) { + pt_debug(dev, DL_INFO, + "%s: ----- BIST Wait FW Loading ----", + __func__); + rc = _pt_request_wait_for_enum_state( + dev, 4000, STARTUP_STATUS_COMPLETE); + } + } else { + if (cd->mode == PT_MODE_BOOTLOADER) { + rc = pt_pip2_launch_app(dev, PT_CORE_CMD_PROTECTED); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s Failed to launch app\n", __func__); + rc = pt_hw_hard_reset(cd); + } + } + + /* + * If FW exists the BL may have just started or will start soon, + * so the FW sentinel may be on it's way but with no FW it will + * not arrive, wait for it before deciding if we need to queue + * an enum. + */ + rc = _pt_request_wait_for_enum_state( + dev, 400, STARTUP_STATUS_FW_RESET_SENTINEL); + if ((cd->startup_status & STARTUP_STATUS_FW_RESET_SENTINEL) && + (cd->startup_status & STARTUP_STATUS_COMPLETE) == 0) { + pt_debug(dev, DL_INFO, "%s: ----- BIST Enum ----", + __func__); + pt_queue_enum(cd); + rc = _pt_request_wait_for_enum_state( + dev, 2000, STARTUP_STATUS_COMPLETE); + } + } + msleep(20); + + /* Put TTDL back into a known state, issue a ttdl enum if needed */ + pt_debug(dev, DL_INFO, "%s: Startup_status = 0x%04X\n", + __func__, cd->startup_status); + + kfree(host.print_buf); + for (slave_id = 0; slave_id < num_slaves; slave_id++) + kfree(slaves[slave_id].print_buf); + + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_ttdl_bist_store + * + * SUMMARY: Store method for the ttdl_bist sysfs node. + * + * RETURN: Size of passed in buffer if successful + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to command buffer + * size - size of buf + ******************************************************************************/ +static ssize_t pt_ttdl_bist_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u32 input_data[2] = {0}; + int length; + int rc = 0; + + /* Maximum input of one value */ + length = _pt_ic_parse_input(dev, buf, size, input_data, + ARRAY_SIZE(input_data)); + if (length != 1) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto exit; + } else { + mutex_lock(&cd->system_lock); + cd->ttdl_bist_select = input_data[0]; + mutex_unlock(&cd->system_lock); + } + +exit: + if (rc) + return rc; + return size; +} +static DEVICE_ATTR(ttdl_bist, 0644, pt_ttdl_bist_show, + pt_ttdl_bist_store); + +/******************************************************************************* + * FUNCTION: pt_flush_bus_store + * + * SUMMARY: Store method for the flush_bus sysfs node. + * + * RETURN: Size of passed in buffer if successful + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to command buffer + * size - size of buf + ******************************************************************************/ +static ssize_t pt_flush_bus_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u32 input_data[2] = {0}; + int length; + int rc = 0; + + /* Maximum input of one value */ + length = _pt_ic_parse_input(dev, buf, size, input_data, + ARRAY_SIZE(input_data)); + if (length != 1) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto exit; + } + + mutex_lock(&cd->system_lock); + if (input_data[0] == 0) + cd->flush_bus_type = PT_FLUSH_BUS_BASED_ON_LEN; + else if (input_data[0] == 1) + cd->flush_bus_type = PT_FLUSH_BUS_FULL_256_READ; + else + rc = -EINVAL; + mutex_unlock(&cd->system_lock); + +exit: + if (rc) + return rc; + return size; +} + +/******************************************************************************* + * FUNCTION: pt_flush_bus_show + * + * SUMMARY: Show method for the flush_bus sysfs node that flushes the active bus + * based on either the size of the first two bytes or a blind 256 bytes. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_flush_bus_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + ssize_t bytes = 0; + struct pt_core_data *cd = dev_get_drvdata(dev); + + mutex_lock(&cd->system_lock); + bytes = pt_flush_bus(cd, cd->flush_bus_type, NULL); + + ret = scnprintf(buf, strlen(buf), + "Status: 0\n" + "%s: %zd\n", + "Bytes flushed", bytes); + + mutex_unlock(&cd->system_lock); + return ret; +} +static DEVICE_ATTR(flush_bus, 0644, pt_flush_bus_show, + pt_flush_bus_store); + +/******************************************************************************* + * FUNCTION: pt_pip2_ping_test_store + * + * SUMMARY: Store method for the pip2_ping_test sysfs node. + * + * NOTE: The max PIP2 packet size is 255 (payload for PING 247) however + * someone may want to test sending invalid packet lengths so any values + * up to 255 are allowed. + * + * RETURN: Size of passed in buffer if successful + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to command buffer + * size - size of buf + ******************************************************************************/ +static ssize_t pt_pip2_ping_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u32 input_data[2]; + int length; + int rc = 0; + + /* Maximum input of one value */ + length = _pt_ic_parse_input(dev, buf, size, input_data, + ARRAY_SIZE(input_data)); + if (length != 1) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto exit; + } + + mutex_lock(&cd->system_lock); + if (input_data[1] >= 0 && input_data[0] <= PT_MAX_PIP2_MSG_SIZE) + cd->ping_test_size = input_data[0]; + else + rc = -EINVAL; + mutex_unlock(&cd->system_lock); + +exit: + if (rc) + return rc; + return size; +} + +/******************************************************************************* + * FUNCTION: pt_ping_test_show + * + * SUMMARY: Show method for the ping_test_show sysfs node that sends the PIP2 + * PING command and ramps up the optional payload from 0 to + * ping_test_size. + * The max payload size is 247: + * (255 - 2 byte reg address - 4 byte header - 2 byte CRC) + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_pip2_ping_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + ssize_t ret; + int last_packet_size; + int rc = 0; + + rc = pt_pip2_ping_test(dev, cd->ping_test_size, &last_packet_size); + + if (rc) { + ret = scnprintf(buf, strlen(buf), "Status: %d\n", rc); + return ret; + } + ret = scnprintf(buf, strlen(buf), + "Status: %d\n" + "PING payload test passed with packet sizes 0 - %d\n", + (last_packet_size == cd->ping_test_size ? 0 : 1), + last_packet_size); + + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_t_refresh_store + * + * SUMMARY: Store method for the t-refresh sysfs node that will takes a passed + * in integer as the number of interrupts to count. A timer is started to + * calculate the total time it takes to see that number of interrupts. + * + * RETURN: Size of passed in buffer if successful + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_t_refresh_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + u32 input_data[2]; + int length; + int rc = 0; + + /* Maximum input of one value */ + length = _pt_ic_parse_input(dev, buf, size, input_data, + ARRAY_SIZE(input_data)); + if (length != 1) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto exit; + } + + mutex_lock(&cd->system_lock); + pt_debug(dev, DL_INFO, "%s: Input value: %d\n", __func__, + input_data[0]); + if (input_data[0] >= 0 && input_data[0] <= 1000) { + cd->t_refresh_total = input_data[0]; + cd->t_refresh_count = 0; + cd->t_refresh_active = 1; + } else { + pt_debug(dev, DL_WARN, "%s: Invalid value\n", __func__); + rc = -EINVAL; + } + mutex_unlock(&cd->system_lock); + +exit: + pt_debug(dev, DL_WARN, "%s: rc = %d\n", __func__, rc); + if (rc) + return rc; + return size; +} + +/******************************************************************************* + * FUNCTION: pt_t_refresh_show + * + * SUMMARY: Show method for the t-refresh sysfs node that will show the results + * of the T-Refresh timer counting the time it takes to see a user defined + * number of interrupts. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_t_refresh_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct pt_core_data *cd = dev_get_drvdata(dev); + u32 whole; + u16 fraction; + + mutex_lock(&cd->system_lock); + + /* Check if we have counted the number requested */ + if (cd->t_refresh_count != cd->t_refresh_total) { + ret = scnprintf(buf, strlen(buf), + "Status: 0\n" + "%s: %d\n", + "Still counting... current IRQ count", + cd->t_refresh_count); + } else { + /* Ensure T-Refresh is de-activated */ + cd->t_refresh_active = 0; + + whole = cd->t_refresh_time / cd->t_refresh_count; + fraction = cd->t_refresh_time % cd->t_refresh_count; + fraction = fraction * 1000 / cd->t_refresh_count; + + ret = scnprintf(buf, strlen(buf), + "Status: 0\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d\n" + "%s: %d.%02d\n", + "Requested IRQ Count ", cd->t_refresh_total, + "IRQ Counted ", cd->t_refresh_count, + "Total Time Elapsed (ms) ", (int)cd->t_refresh_time, + "Average T-Refresh (ms) ", whole, fraction); + } + mutex_unlock(&cd->system_lock); + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_dut_status_show + * + * SUMMARY: Show method for DUT status sysfs node. Display DUT's scan state, and + * more items such as operation mode,easywake state are added in the future. + * + * RETURN: Char buffer with printed scan status information + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_dut_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 sys_mode = FW_SYS_MODE_UNDEFINED; + u8 mode = PT_MODE_UNKNOWN; + char *outputstring[7] = {"BOOT", "SCANNING", "DEEP_SLEEP", + "TEST", "DEEP_STANDBY", "UNDEFINED", "n/a"}; + struct pt_core_data *cd = dev_get_drvdata(dev); + ssize_t ret; + u16 calculated_crc = 0; + u16 stored_crc = 0; + u8 status; + int rc = 0; + + /* In STANDBY the DUT will not repond to any PIP cmd */ + if (cd->fw_sys_mode_in_standby_state) { + mode = PT_MODE_OPERATIONAL; + sys_mode = FW_SYS_MODE_DEEP_STANDBY; + goto print_limited_results; + } + + /* Retrieve mode and FW system mode which can only be 0-4 */ + rc = pt_get_fw_sys_mode(cd, &sys_mode, &mode); + if (rc || mode == PT_MODE_UNKNOWN) { + ret = snprintf(buf, PT_MAX_PRBUF_SIZE, + "%s: %d\n" + "%s: n/a\n" + "%s: n/a\n" + "%s: n/a\n" + "%s: n/a\n", + "Status", rc, + "Active Exec ", + "FW System Mode ", + "Stored CRC ", + "Calculated CRC "); + return ret; + } else { + if (mode == PT_MODE_OPERATIONAL) { + if (sys_mode > FW_SYS_MODE_MAX) + sys_mode = FW_SYS_MODE_UNDEFINED; + if (sys_mode != FW_SYS_MODE_TEST) + goto print_limited_results; + + rc = pt_pip_verify_config_block_crc_(cd, + PT_TCH_PARM_EBID, &status, + &calculated_crc, &stored_crc); + if (rc) + goto print_limited_results; + + ret = snprintf(buf, PT_MAX_PRBUF_SIZE, + "%s: %d\n" + "%s: %s\n" + "%s: %s\n" + "%s: 0x%04X\n" + "%s: 0x%04X\n", + "Status", rc, + "Active Exec ", "FW", + "FW System Mode ", outputstring[sys_mode], + "Stored CRC ", stored_crc, + "Calculated CRC ", calculated_crc); + return ret; + } else { + /* When in BL or unknon mode Active Exec is "n/a" */ + sys_mode = FW_SYS_MODE_UNDEFINED + 1; + } + } + +print_limited_results: + ret = snprintf(buf, PT_MAX_PRBUF_SIZE, + "%s: %d\n" + "%s: %s\n" + "%s: %s\n" + "%s: n/a\n" + "%s: n/a\n", + "Status", rc, + "Active Exec ", + mode == PT_MODE_OPERATIONAL ? "FW" : "BL", + "FW System Mode ", outputstring[sys_mode], + "Stored CRC ", + "Calculated CRC "); + + return ret; +} +#endif /* TTDL_DIAGNOSTICS */ + + +/******************************************************************************* + * Structures of sysfs attributes for all DUT dependent sysfs node + ******************************************************************************/ +static struct attribute *early_attrs[] = { + &dev_attr_hw_version.attr, + &dev_attr_drv_version.attr, + &dev_attr_drv_ver.attr, + &dev_attr_fw_version.attr, + &dev_attr_sysinfo.attr, +#ifndef TTDL_KERNEL_SUBMISSION + &dev_attr_pip2_cmd_rsp.attr, + &dev_attr_command.attr, + &dev_attr_drv_debug.attr, + &dev_attr_hw_reset.attr, + &dev_attr_ttdl_restart.attr, +#ifdef TTDL_DIAGNOSTICS + &dev_attr_ttdl_status.attr, + &dev_attr_pip2_enter_bl.attr, + &dev_attr_pip2_exit_bl.attr, + &dev_attr_err_gpio.attr, + &dev_attr_flush_bus.attr, + &dev_attr_ttdl_bist.attr, +#endif /* TTDL_DIAGNOSTICS */ +#endif /* !TTDL_KERNEL_SUBMISSION */ + NULL, +}; + +static struct attribute_group early_attr_group = { + .attrs = early_attrs, +}; + +static struct device_attribute pip2_attributes[] = { + __ATTR(pip2_version, 0444, pt_pip2_version_show, NULL), + __ATTR(pip2_gpio_read, 0444, pt_pip2_gpio_read_show, NULL), +#ifdef TTDL_DIAGNOSTICS + __ATTR(pip2_bin_hdr, 0444, pt_pip2_bin_hdr_show, NULL), + __ATTR(pip2_ping_test, 0644, pt_pip2_ping_test_show, + pt_pip2_ping_test_store), +#endif +}; + +static struct device_attribute attributes[] = { + __ATTR(dut_debug, 0644, + pt_dut_debug_show, pt_drv_debug_store), + __ATTR(sleep_status, 0444, pt_sleep_status_show, NULL), + __ATTR(panel_id, 0444, pt_panel_id_show, NULL), + __ATTR(get_param, 0644, + pt_get_param_show, pt_get_param_store), +#ifdef EASYWAKE_TSG6 + __ATTR(easy_wakeup_gesture, 0644, pt_easy_wakeup_gesture_show, + pt_easy_wakeup_gesture_store), + __ATTR(easy_wakeup_gesture_id, 0444, + pt_easy_wakeup_gesture_id_show, NULL), + __ATTR(easy_wakeup_gesture_data, 0444, + pt_easy_wakeup_gesture_data_show, NULL), +#endif +#ifdef TTDL_DIAGNOSTICS + __ATTR(platform_data, 0444, pt_platform_data_show, NULL), + __ATTR(drv_irq, 0644, pt_drv_irq_show, pt_drv_irq_store), + __ATTR(dut_status, 0444, pt_dut_status_show, NULL), + __ATTR(t_refresh, 0644, pt_t_refresh_show, pt_t_refresh_store), +#endif /* TTDL_DIAGNOSTICS */ +}; + +/******************************************************************************* + * FUNCTION: add_sysfs_interfaces + * + * SUMMARY: Creates all DUT dependent sysfs nodes owned by the core + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int add_sysfs_interfaces(struct device *dev) +{ + int i; + int j = 0; + struct pt_core_data *cd = dev_get_drvdata(dev); + + for (i = 0; i < ARRAY_SIZE(attributes); i++) { + if (device_create_file(dev, attributes + i)) + goto undo; + } + + pt_debug(dev, DL_INFO, "%s: Active DUT Generation: %d", + __func__, cd->active_dut_generation); + if (cd->active_dut_generation == DUT_PIP2_CAPABLE) { + for (j = 0; j < ARRAY_SIZE(pip2_attributes); j++) { + if (device_create_file(dev, pip2_attributes + j)) + goto undo; + } + } + return 0; +undo: + for (i--; i >= 0; i--) + device_remove_file(dev, attributes + i); + for (j--; j >= 0; j--) + device_remove_file(dev, pip2_attributes + j); + pt_debug(dev, DL_ERROR, "%s: failed to create sysfs interface\n", + __func__); + return -ENODEV; +} + +/******************************************************************************* + * FUNCTION: remove_sysfs_interfaces + * + * SUMMARY: Removes all DUT dependent sysfs nodes owned by the core + * + * RETURN: void + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static void remove_sysfs_interfaces(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(attributes); i++) + device_remove_file(dev, attributes + i); + for (i = 0; i < ARRAY_SIZE(pip2_attributes); i++) + device_remove_file(dev, pip2_attributes + i); +} + +/******************************************************************************* + * FUNCTION: remove_sysfs_and_modules + * + * SUMMARY: Removes all DUT dependent sysfs nodes and modules + * + * RETURN: void + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static void remove_sysfs_and_modules(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + /* Queued work should be removed before to release loader module */ + call_atten_cb(cd, PT_ATTEN_CANCEL_LOADER, 0); + pt_release_modules(cd); + pt_btn_release(dev); + pt_mt_release(dev); + remove_sysfs_interfaces(dev); +} +#endif /* !TTDL_KERNEL_SUBMISSION */ + + +/******************************************************************************* + ******************************************************************************* + * FUNCTION: pt_probe + * + * SUMMARY: Probe of the core module. + * + * NOTE: For the Parade Technologies development platform (PtSBC) the + * probe functionality is split into two functions; pt_probe() and + * pt_probe_complete(). the initial setup is done in this function which + * then creates a WORK task which runs after the probe timer expires. This + * ensures the I2C/SPI is up on the PtSBC in time for TTDL. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *ops - pointer to the bus + * *dev - pointer to the device structure + * irq - IRQ + * xfer_buf_size - size of the buffer + ******************************************************************************/ +int pt_probe(const struct pt_bus_ops *ops, struct device *dev, + u16 irq, size_t xfer_buf_size) +{ + struct pt_core_data *cd; + struct pt_platform_data *pdata = dev_get_platdata(dev); + enum pt_atten_type type; + int rc = 0; +#ifndef PT_PTSBC_SUPPORT + u8 pip_ver_major; + u8 pip_ver_minor; + u32 status = STARTUP_STATUS_START; +#endif +#ifdef TTDL_PTVIRTDUT_SUPPORT + int retry = 3; +#endif + + if (!pdata || !pdata->core_pdata || !pdata->mt_pdata) { + pt_debug(dev, DL_ERROR, "%s: Missing platform data\n", + __func__); + rc = -ENODEV; + goto error_no_pdata; + } + + if (pdata->core_pdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) { + if (!pdata->core_pdata->power) { + pt_debug(dev, DL_ERROR, + "%s: Missing platform data function\n", + __func__); + rc = -ENODEV; + goto error_no_pdata; + } + } + + /* get context and debug print buffers */ + cd = kzalloc(sizeof(*cd), GFP_KERNEL); + if (!cd) { + rc = -ENOMEM; + goto error_alloc_data; + } + + /* Initialize device info */ + cd->dev = dev; + cd->pdata = pdata; + cd->cpdata = pdata->core_pdata; + cd->bus_ops = ops; + cd->debug_level = PT_INITIAL_DEBUG_LEVEL; + cd->show_timestamp = PT_INITIAL_SHOW_TIME_STAMP; + scnprintf(cd->core_id, 20, "%s%d", PT_CORE_NAME, core_number++); + cd->hw_detected = false; + cd->pip2_prot_active = false; + cd->pip2_send_user_cmd = false; + cd->bl_pip_ver_ready = false; + cd->app_pip_ver_ready = false; + cd->pip2_cmd_tag_seq = 0x08; /* PIP2 TAG=1 and 3 bit SEQ=0 */ + cd->get_param_id = 0; + cd->watchdog_enabled = 0; + cd->startup_retry_count = 0; + cd->core_probe_complete = 0; + cd->fw_system_mode = FW_SYS_MODE_BOOT; + cd->pip_cmd_timeout = PT_PIP_CMD_DEFAULT_TIMEOUT; + cd->pip_cmd_timeout_default = PT_PIP_CMD_DEFAULT_TIMEOUT; + cd->flashless_dut = 0; + cd->flashless_auto_bl = PT_SUPPRESS_AUTO_BL; + cd->bl_with_no_int = 0; + cd->cal_cache_in_host = PT_FEATURE_DISABLE; + cd->num_devices = 1; + cd->tthe_hid_usb_format = PT_TTHE_TUNER_FORMAT_HID_I2C; + + if (cd->cpdata->config_dut_generation == CONFIG_DUT_PIP2_CAPABLE) { + cd->set_dut_generation = true; + cd->active_dut_generation = DUT_PIP2_CAPABLE; + } else if (cd->cpdata->config_dut_generation == CONFIG_DUT_PIP1_ONLY) { + cd->set_dut_generation = true; + cd->active_dut_generation = DUT_PIP1_ONLY; + } else { + cd->set_dut_generation = false; + cd->active_dut_generation = DUT_UNKNOWN; + } + + /* Initialize with platform data */ + cd->watchdog_force_stop = cd->cpdata->watchdog_force_stop; +#ifdef PT_PTSBC_SUPPORT + /* Extend first WD to allow DDI to complete configuration */ + cd->watchdog_interval = PT_PTSBC_INIT_WATCHDOG_TIMEOUT; +#else + cd->watchdog_interval = PT_WATCHDOG_TIMEOUT; +#endif + cd->hid_cmd_state = 1; + cd->fw_updating = false; + +#ifdef TTDL_DIAGNOSTICS + cd->t_refresh_active = 0; + cd->t_refresh_count = 0; + cd->pip2_crc_error_count = 0; + cd->wd_xres_count = 0; + cd->bl_retry_packet_count = 0; + cd->file_erase_timeout_count = 0; + cd->show_tt_data = false; + cd->flush_bus_type = PT_FLUSH_BUS_BASED_ON_LEN; + cd->err_gpio = 0; + cd->err_gpio_type = PT_ERR_GPIO_NONE; + cd->ttdl_bist_select = 0x07; + cd->force_pip2_seq = 0; +#endif /* TTDL_DIAGNOSTICS */ + +#ifdef TTDL_PTVIRTDUT_SUPPORT + /* + * This variable is only used for BATS test. The + * default value "true" has no effect on whether + * the PT_DETECT_HW build flag is enabled or not. + */ + cd->hw_detect_enabled = true; + cd->route_bus_virt_dut = 0; +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + + memset(cd->pip2_us_file_path, 0, PT_MAX_PATH_SIZE); + memcpy(cd->pip2_us_file_path, PT_PIP2_BIN_FILE_PATH, + sizeof(PT_PIP2_BIN_FILE_PATH)); + + pt_init_hid_descriptor(&cd->hid_desc); + /* Read and store the descriptor lengths */ + cd->hid_core.hid_report_desc_len = + le16_to_cpu(cd->hid_desc.report_desc_len); + cd->hid_core.hid_max_input_len = + le16_to_cpu(cd->hid_desc.max_input_len); + cd->hid_core.hid_max_output_len = + le16_to_cpu(cd->hid_desc.max_output_len); + + /* Initialize mutexes and spinlocks */ + mutex_init(&cd->module_list_lock); + mutex_init(&cd->system_lock); + mutex_init(&cd->sysfs_lock); + mutex_init(&cd->ttdl_restart_lock); + mutex_init(&cd->firmware_class_lock); + mutex_init(&cd->hid_report_lock); + spin_lock_init(&cd->spinlock); + + /* Initialize module list */ + INIT_LIST_HEAD(&cd->module_list); + + /* Initialize attention lists */ + for (type = 0; type < PT_ATTEN_NUM_ATTEN; type++) + INIT_LIST_HEAD(&cd->atten_list[type]); + + /* Initialize parameter list */ + INIT_LIST_HEAD(&cd->param_list); + + /* Initialize wait queue */ + init_waitqueue_head(&cd->wait_q); + + /* Initialize works */ + INIT_WORK(&cd->enum_work, pt_enum_work_function); + INIT_WORK(&cd->ttdl_restart_work, pt_restart_work_function); + INIT_WORK(&cd->watchdog_work, pt_watchdog_work); + + /* Initialize HID specific data */ + cd->hid_core.hid_vendor_id = (cd->cpdata->vendor_id) ? + cd->cpdata->vendor_id : HID_VENDOR_ID; + cd->hid_core.hid_product_id = (cd->cpdata->product_id) ? + cd->cpdata->product_id : HID_APP_PRODUCT_ID; + cd->hid_core.hid_desc_register = + cpu_to_le16(cd->cpdata->hid_desc_register); + + /* Set platform easywake value */ + cd->easy_wakeup_gesture = cd->cpdata->easy_wakeup_gesture; + + /* Set platform panel_id value */ + cd->panel_id_support = cd->cpdata->panel_id_support; + + if (cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) + /* Set Panel ID to default to 0 */ + cd->pid_for_loader = PT_PANEL_ID_DEFAULT; + else + /* Set Panel ID to Not Enabled */ + cd->pid_for_loader = PANEL_ID_NOT_ENABLED; + /* Initialize hw_version default to FFFF.FFFF.FF */ + snprintf(cd->hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF"); + + dev_set_drvdata(dev, cd); +#ifndef PT_PTSBC_SUPPORT + /* PtSBC builds will call this function in pt_probe_complete() */ + pt_add_core(dev); +#endif + + rc = sysfs_create_group(&dev->kobj, &early_attr_group); + if (rc) + pt_debug(cd->dev, DL_WARN, "%s:create early attrs failed\n", + __func__); +#ifndef TTDL_KERNEL_SUBMISSION + rc = device_create_bin_file(dev, &bin_attr_pt_response); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create node response\n", + __func__); + } +#endif /* !TTDL_KERNEL_SUBMISSION */ + + /* + * Save the pointer to a global value, which will be used + * in ttdl_restart function + */ + cd->bus_ops = ops; + + /* + * When the IRQ GPIO is not direclty accessible and no function is + * defined to get the IRQ status, the IRQ passed in must be assigned + * directly as the gpio_to_irq will not work. e.g. CHROMEOS + */ + if (!cd->cpdata->irq_stat) { + cd->irq = irq; + pt_debug(cd->dev, DL_WARN, "%s:No irq_stat, Set cd->irq = %d\n", + __func__, cd->irq); + } + +#ifdef PT_PTSBC_SUPPORT + /* + * For the PtSBC, on the first bring up, I2C/SPI will not be ready + * in time so complete probe with pt_probe_complete() after work + * probe timer expires. + */ + INIT_WORK(&cd->probe_work, pt_probe_work); +#if (KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE) + setup_timer(&cd->probe_timer, pt_probe_timer, (unsigned long)cd); +#else + timer_setup(&cd->probe_timer, pt_probe_timer, 0); +#endif + + /* Some host i2c/spi busses start late and then run too slow */ + pt_debug(cd->dev, DL_INFO, "%s:start wait for probe timer\n", + __func__); + mod_timer(&cd->probe_timer, jiffies + + msecs_to_jiffies(PT_CORE_PROBE_STARTUP_DELAY_MS)); + return rc; + +error_alloc_data: +error_no_pdata: + pt_debug(dev, DL_ERROR, "%s failed.\n", __func__); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_probe_complete + * + * SUMMARY: This function is only needed when PT_PTSBC_SUPPORT is enabled. + * For the PtSBC, the probe functionality is split into two functions; + * pt_probe() and pt_probe_complete(). The initial setup is done + * in pt_probe() and the rest is done here after I2C/SPI is up. This + * function also configures all voltage regulators for the PtSBC. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to the core data structure + ******************************************************************************/ +static int pt_probe_complete(struct pt_core_data *cd) +{ + int rc = -1; + u32 status = STARTUP_STATUS_START; + struct device *dev = cd->dev; + u8 pip_ver_major; + u8 pip_ver_minor; +#ifdef TTDL_PTVIRTDUT_SUPPORT + int retry = 3; +#endif + + pt_debug(cd->dev, DL_DEBUG, + "%s: PARADE Entering Probe complete function\n", __func__); + pt_add_core(cd->dev); +#endif /* --- End PT_PTSBC_SUPPORT --- */ + + /* Call platform init function before setting up the GPIO's */ + if (cd->cpdata->init) { + pt_debug(cd->dev, DL_INFO, "%s: Init HW\n", __func__); + rc = cd->cpdata->init(cd->cpdata, PT_MT_POWER_ON, cd->dev); + } else { + pt_debug(cd->dev, DL_WARN, "%s: No HW INIT function\n", + __func__); + rc = 0; + } + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, "%s: HW Init fail r=%d\n", + __func__, rc); + } + + /* Power on any needed regulator(s) */ + if (cd->cpdata->setup_power) { + pt_debug(cd->dev, DL_INFO, "%s: Device power on!\n", __func__); + rc = cd->cpdata->setup_power(cd->cpdata, + PT_MT_POWER_ON, cd->dev); + } else { + pt_debug(cd->dev, DL_WARN, "%s: No setup power function\n", + __func__); + rc = 0; + } + if (rc < 0) + pt_debug(cd->dev, DL_ERROR, "%s: Setup power on fail r=%d\n", + __func__, rc); + +#ifdef TTDL_DIAGNOSTICS + cd->watchdog_irq_stuck_count = 0; + cd->bus_transmit_error_count = 0; +#endif /* TTDL_DIAGNOSTICS */ + +#ifdef TTDL_PTVIRTDUT_SUPPORT + /* + * In the case that the variable hw_detect_enabled needs to be set to + * false as the default value, it needs to be set to true through + * drv_debug sysfs node when testing HW detect function. But the probe() + * function runs fast, and drv_debug may not set this variable to + * true in time. This "retry" is to avoid this issue. + */ +retry_hw_detect: + if (cd->hw_detect_enabled) { +#endif + if (cd->cpdata->detect) { + pt_debug(cd->dev, DL_INFO, "%s: Detect HW\n", __func__); + rc = cd->cpdata->detect(cd->cpdata, cd->dev, + pt_platform_detect_read); + if (!rc) { + cd->hw_detected = true; + pt_debug(cd->dev, DL_INFO, + "%s: HW detected\n", __func__); + } else { + cd->hw_detected = false; + pt_debug(cd->dev, DL_INFO, + "%s: No HW detected\n", __func__); + rc = -ENODEV; + goto error_detect; + } + } else { + pt_debug(dev, DL_WARN, + "%s: PARADE No HW detect function pointer\n", + __func__); + /* + * "hw_reset" is not needed in the "if" statement, + * because "hw_reset" is already included in "hw_detect" + * function. + */ + rc = pt_hw_hard_reset(cd); + if (rc) + pt_debug(cd->dev, DL_ERROR, + "%s: FAILED to execute HARD reset\n", + __func__); + } +#ifdef TTDL_PTVIRTDUT_SUPPORT + } else { + if (retry--) { + msleep(50); + goto retry_hw_detect; + } + } +#endif + + if (cd->cpdata->setup_irq) { + pt_debug(cd->dev, DL_INFO, "%s: setup IRQ\n", __func__); + rc = cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_REG, cd->dev); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error, couldn't setup IRQ\n", __func__); + goto error_setup_irq; + } + } else { + pt_debug(dev, DL_ERROR, + "%s: IRQ function pointer not setup\n", + __func__); + goto error_setup_irq; + } + +#if (KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE) + setup_timer(&cd->watchdog_timer, pt_watchdog_timer, + (unsigned long)cd); +#else + timer_setup(&cd->watchdog_timer, pt_watchdog_timer, 0); +#endif + pt_stop_wd_timer(cd); + +#ifdef TTHE_TUNER_SUPPORT + mutex_init(&cd->tthe_lock); + cd->tthe_debugfs = debugfs_create_file(PT_TTHE_TUNER_FILE_NAME, + 0644, NULL, cd, &tthe_debugfs_fops); +#endif + rc = device_init_wakeup(dev, 1); + if (rc < 0) + pt_debug(dev, DL_ERROR, "%s: Error, device_init_wakeup rc:%d\n", + __func__, rc); + + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + /* If IRQ asserted, read out all from buffer to release INT pin */ + if (cd->cpdata->irq_stat) { + pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN); + } else { + /* Force a read in case the reset sentinel already arrived */ + rc = pt_read_input(cd); + if (!rc) + pt_parse_input(cd); + } + + /* Without sleep DUT is not ready and will NAK the first write */ + msleep(150); + + /* Attempt to set the DUT generation if not yet set */ + if (cd->active_dut_generation == DUT_UNKNOWN) { + if (cd->bl_pip_ver_ready || + (cd->app_pip_ver_ready && + IS_PIP_VER_GE(&cd->sysinfo, 1, 12))) { + cd->active_dut_generation = DUT_PIP2_CAPABLE; + pt_debug(dev, DL_INFO, "%s: dut generation is %d\n", + __func__, cd->active_dut_generation); + } else { + rc = _pt_detect_dut_generation(cd->dev, + &status, &cd->active_dut_generation, + &cd->mode); + if ((cd->active_dut_generation == DUT_UNKNOWN) + || rc) { + pt_debug(cd->dev, DL_ERROR, + " === DUT Gen unknown, Skip Enum ===\n"); + goto skip_enum; + } + } + } + + _pt_request_active_pip_protocol(cd->dev, PT_CORE_CMD_PROTECTED, + &pip_ver_major, &pip_ver_minor); + if (pip_ver_major == 2) { + cd->bl_pip_ver_ready = true; + pt_debug(dev, DL_WARN, + " === PIP2.%d Detected, Attempt to launch APP ===\n", + pip_ver_minor); + cd->hw_detected = true; + } else if (pip_ver_major == 1) { + cd->app_pip_ver_ready = true; + pt_debug(dev, DL_WARN, + " === PIP1.%d Detected ===\n", pip_ver_minor); + cd->hw_detected = true; + } else { + cd->sysinfo.ttdata.pip_ver_major = 0; + cd->sysinfo.ttdata.pip_ver_minor = 0; + cd->app_pip_ver_ready = false; + cd->hw_detected = false; + pt_debug(dev, DL_ERROR, + " === PIP Version Not Detected, Skip Enum ===\n"); + /* For legacy DUTS proceed, enum will attempt to launch app */ + if (cd->active_dut_generation != DUT_PIP1_ONLY) + goto skip_enum; + } + + rc = pt_enum_with_dut(cd, false, &status); + pt_debug(dev, DL_WARN, "%s: cd->startup_status=0x%04X status=0x%04X\n", + __func__, cd->startup_status, status); + if (rc == -ENODEV) { + pt_debug(cd->dev, DL_ERROR, + "%s: Enumeration Failed r=%d\n", __func__, rc); +#ifndef PT_PTSBC_SUPPORT + /* For PtSBC don't error out, allow TTDL to stay up */ + goto error_after_startup; +#endif + } + + /* Suspend scanning until probe is complete to avoid asyc touches */ + pt_pip_suspend_scanning_(cd); + +#ifndef TTDL_KERNEL_SUBMISSION + if (cd->hw_detected) { + pt_debug(dev, DL_INFO, "%s: Add sysfs interfaces\n", + __func__); + rc = add_sysfs_interfaces(dev); + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: Error, fail sysfs init\n", __func__); + goto error_after_startup; + } + } else { + pt_debug(dev, DL_WARN, + "%s: No HW detected, sysfs interfaces not added\n", + __func__); + } +#endif /* !TTDL_KERNEL_SUBMISSION */ + +skip_enum: + pm_runtime_put_sync(dev); + + pt_debug(dev, DL_INFO, "%s: Probe: MT, BTN\n", __func__); + rc = pt_mt_probe(dev); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: Error, fail mt probe\n", + __func__); + goto error_after_sysfs_create; + } + + rc = pt_btn_probe(dev); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: Error, fail btn probe\n", + __func__); + goto error_after_startup_mt; + } + + pt_probe_modules(cd); + +#ifdef CONFIG_HAS_EARLYSUSPEND + pt_setup_early_suspend(cd); +#elif defined(CONFIG_FB) + pt_setup_fb_notifier(cd); +#endif + +#ifdef NEED_SUSPEND_NOTIFIER + cd->pm_notifier.notifier_call = pt_pm_notifier; + register_pm_notifier(&cd->pm_notifier); +#endif + pt_pip_resume_scanning_(cd); + + mutex_lock(&cd->system_lock); + cd->startup_status |= status; + cd->core_probe_complete = 1; + mutex_unlock(&cd->system_lock); + + pt_debug(dev, DL_WARN, "%s: TTDL Core Probe Completed Successfully\n", + __func__); + return 0; + + +error_after_startup_mt: + pr_err("%s PARADE error_after_startup_mt\n", __func__); + pt_mt_release(dev); +error_after_sysfs_create: + pr_err("%s PARADE error_after_sysfs_create\n", __func__); + pm_runtime_disable(dev); +#if (KERNEL_VERSION(3, 16, 0) > LINUX_VERSION_CODE) + device_wakeup_disable(dev); +#endif + device_init_wakeup(dev, 0); + cancel_work_sync(&cd->enum_work); + pt_stop_wd_timer(cd); + pt_free_si_ptrs(cd); +#ifndef TTDL_KERNEL_SUBMISSION + remove_sysfs_interfaces(dev); +#endif /* !TTDL_KERNEL_SUBMISSION */ + +error_after_startup: + pr_err("%s PARADE error_after_startup\n", __func__); + del_timer(&cd->watchdog_timer); + if (cd->cpdata->setup_irq) + cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_FREE, dev); +error_setup_irq: +error_detect: +#ifdef PT_PTSBC_SUPPORT + del_timer(&cd->probe_timer); +#endif + if (cd->cpdata->init) + cd->cpdata->init(cd->cpdata, PT_MT_POWER_OFF, dev); + if (cd->cpdata->setup_power) + cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev); + sysfs_remove_group(&dev->kobj, &early_attr_group); + pt_del_core(dev); + dev_set_drvdata(dev, NULL); + kfree(cd); +#ifndef PT_PTSBC_SUPPORT +error_alloc_data: +error_no_pdata: +#endif + pr_err("%s failed.\n", __func__); + return rc; +} +EXPORT_SYMBOL_GPL(pt_probe); + +/******************************************************************************* + * FUNCTION: pt_release + * + * SUMMARY: This function does the following cleanup: + * - Releases all probed modules + * - Stops the watchdog + * - Cancels all pending work tasks + * - Removes all created sysfs nodes + * - Removes all created debugfs nodes + * - Frees the IRQ + * - Deletes the core + * - Frees all pointers and HID reports + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to the core data structure + ******************************************************************************/ +int pt_release(struct pt_core_data *cd) +{ + struct device *dev = cd->dev; + + /* + * Suspend the device before freeing the startup_work and stopping + * the watchdog since sleep function restarts watchdog on failure + */ + pm_runtime_suspend(dev); + pm_runtime_disable(dev); + + /* + * Any 'work' that can trigger a new thread should be canceled first. + * The watchdog is also stopped again because another thread could have + * restarted it. The irq_work is cancelled last because it is used for + * all I2C/SPI communication. + */ + pt_stop_wd_timer(cd); +#ifdef PT_PTSBC_SUPPORT + cancel_work_sync(&cd->probe_work); +#endif + call_atten_cb(cd, PT_ATTEN_CANCEL_LOADER, 0); + cancel_work_sync(&cd->ttdl_restart_work); + cancel_work_sync(&cd->enum_work); + pt_stop_wd_timer(cd); +#ifdef PT_PTSBC_SUPPORT + cancel_work_sync(&cd->irq_work); +#endif + + pt_release_modules(cd); + pt_proximity_release(dev); + pt_btn_release(dev); + pt_mt_release(dev); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&cd->es); +#elif defined(CONFIG_FB) + fb_unregister_client(&cd->fb_notifier); +#endif + +#ifdef NEED_SUSPEND_NOTIFIER + unregister_pm_notifier(&cd->pm_notifier); +#endif + +#if (KERNEL_VERSION(3, 16, 0) > LINUX_VERSION_CODE) + device_wakeup_disable(dev); +#endif + device_init_wakeup(dev, 0); + +#ifdef TTHE_TUNER_SUPPORT + mutex_lock(&cd->tthe_lock); + cd->tthe_exit = 1; + wake_up(&cd->wait_q); + mutex_unlock(&cd->tthe_lock); + debugfs_remove(cd->tthe_debugfs); +#endif + + sysfs_remove_group(&dev->kobj, &early_attr_group); + +#ifndef TTDL_KERNEL_SUBMISSION + remove_sysfs_interfaces(dev); +#endif /* !TTDL_KERNEL_SUBMISSION */ + + disable_irq_nosync(cd->irq); + if (cd->cpdata->setup_irq) + cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_FREE, dev); + if (cd->cpdata->init) + cd->cpdata->init(cd->cpdata, PT_MT_POWER_OFF, dev); + if (cd->cpdata->setup_power) + cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev); + dev_set_drvdata(dev, NULL); + pt_del_core(dev); + pt_free_si_ptrs(cd); + pt_free_hid_reports(cd); + kfree(cd); + return 0; +} +EXPORT_SYMBOL_GPL(pt_release); + + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Core Driver"); +MODULE_AUTHOR("Parade Technologies "); diff --git a/pt/pt_debug.c b/pt/pt_debug.c new file mode 100644 index 0000000000..a959e6e089 --- /dev/null +++ b/pt/pt_debug.c @@ -0,0 +1,556 @@ +/* + * pt_debug.c + * Parade TrueTouch(TM) Standard Product Debug Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.paradetech.com + */ + +#include "pt_regs.h" + +#define PT_DEBUG_NAME "pt_debug" + +struct pt_debug_data { + struct device *dev; + struct pt_sysinfo *si; + uint32_t interrupt_count; + uint32_t formated_output; + struct mutex sysfs_lock; + u8 pr_buf[PT_MAX_PRBUF_SIZE]; +}; + +static struct pt_core_commands *cmd; + +static struct pt_module debug_module; + +/******************************************************************************* + * FUNCTION: pt_get_debug_data + * + * SUMMARY: Inline function to get pt_debug_data pointer from debug module. + * + * RETURN: + * pointer to pt_debug_data structure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static inline struct pt_debug_data *pt_get_debug_data( + struct device *dev) +{ + return pt_get_module_data(dev, &debug_module); +} + +/******************************************************************************* + * FUNCTION: pt_pr_buf_op_mode + * + * SUMMARY: Formats touch/button report to pr_buf that combined xy_mode and + * xy_data. The feature is required by TTHE. + * + * PARAMETERS: + * *dd - pointer to pt_debug_data structure + * *pr_buf - pointer to print buffer + * *si - pointer to sysinfo structure + * cur_touch - number of current touch + ******************************************************************************/ +static void pt_pr_buf_op_mode(struct pt_debug_data *dd, u8 *pr_buf, + struct pt_sysinfo *si, u8 cur_touch) +{ + const char fmt[] = "%02X "; + int max = (PT_MAX_PRBUF_SIZE - 1) - sizeof(PT_PR_TRUNCATED); + u8 report_id = si->xy_mode[2]; + int header_size = 0; + int report_size = 0; + int total_size = 0; + int i, k; + + if (report_id == si->desc.tch_report_id) { + header_size = si->desc.tch_header_size; + report_size = cur_touch * si->desc.tch_record_size; + } else if (report_id == si->desc.btn_report_id) { + header_size = BTN_INPUT_HEADER_SIZE; + report_size = BTN_REPORT_SIZE; + } + total_size = header_size + report_size; + + pr_buf[0] = 0; + for (i = k = 0; i < header_size && i < max; i++, k += 3) + scnprintf(pr_buf + k, PT_MAX_PRBUF_SIZE, fmt, si->xy_mode[i]); + + for (i = 0; i < report_size && i < max; i++, k += 3) + scnprintf(pr_buf + k, PT_MAX_PRBUF_SIZE, fmt, si->xy_data[i]); + + pr_info("%s=%s%s\n", "pt_OpModeData", pr_buf, + total_size <= max ? "" : PT_PR_TRUNCATED); +} + +/******************************************************************************* + * FUNCTION: pt_debug_print + * + * SUMMARY: This function prints header to show data size and data_name and + * content of "pr_buf" with hex base. + * + * PARAMETERS: + * *dev - pointer to device structure + * *pr_buf - pointer to input buffer which stores the formated data + * *sptr - pointer to the buffer to print + * size - size of data elements + * *data_name - data name to print + ******************************************************************************/ +static void pt_debug_print(struct device *dev, u8 *pr_buf, u8 *sptr, + int size, const char *data_name) +{ + int i, j; + int elem_size = sizeof("XX ") - 1; + int max = (PT_MAX_PRBUF_SIZE - 1) / elem_size; + int limit = size < max ? size : max; + + if (limit < 0) + limit = 0; + + pr_buf[0] = 0; + for (i = j = 0; i < limit; i++, j += elem_size) + scnprintf(pr_buf + j, PT_MAX_PRBUF_SIZE - j, "%02X ", sptr[i]); + + if (size) + pr_info("%s[0..%d]=%s%s\n", data_name, size - 1, pr_buf, + size <= max ? "" : PT_PR_TRUNCATED); + else + pr_info("%s[]\n", data_name); +} + +/******************************************************************************* + * FUNCTION: pt_debug_formated + * + * SUMMARY: Formats and prints touch & button report. + * + * PARAMETERS: + * *dev - pointer to device structure + * *pr_buf - pointer to print buffer + * *si - pointer to sysinfo structure + * cur_touch - number of current touch + ******************************************************************************/ +static void pt_debug_formated(struct device *dev, u8 *pr_buf, + struct pt_sysinfo *si, u8 num_cur_tch) +{ + u8 report_id = si->xy_mode[2]; + int header_size = 0; + int report_size = 0; + u8 data_name[] = "touch[99]"; + int max_print_length = 20; + int i; + + if (report_id == si->desc.tch_report_id) { + header_size = si->desc.tch_header_size; + report_size = num_cur_tch * si->desc.tch_record_size; + } else if (report_id == si->desc.btn_report_id) { + header_size = BTN_INPUT_HEADER_SIZE; + report_size = BTN_REPORT_SIZE; + } + + /* xy_mode */ + pt_debug_print(dev, pr_buf, si->xy_mode, header_size, "xy_mode"); + + /* xy_data */ + if (report_size > max_print_length) { + pr_info("xy_data[0..%d]:\n", report_size); + for (i = 0; i < report_size - max_print_length; + i += max_print_length) { + pt_debug_print(dev, pr_buf, si->xy_data + i, + max_print_length, " "); + } + if (report_size - i) + pt_debug_print(dev, pr_buf, si->xy_data + i, + report_size - i, " "); + } else { + pt_debug_print(dev, pr_buf, si->xy_data, report_size, + "xy_data"); + } + + /* touches */ + if (report_id == si->desc.tch_report_id) { + for (i = 0; i < num_cur_tch; i++) { + scnprintf(data_name, sizeof(data_name) - 1, + "touch[%u]", i); + pt_debug_print(dev, pr_buf, + si->xy_data + (i * si->desc.tch_record_size), + si->desc.tch_record_size, data_name); + } + } + + /* buttons */ + if (report_id == si->desc.btn_report_id) + pt_debug_print(dev, pr_buf, si->xy_data, report_size, + "button"); +} + +/******************************************************************************* + * FUNCTION: pt_xy_worker + * + * SUMMARY: Read xy_data for all touches for debug. + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dd - pointer to pt_debug_data structure + ******************************************************************************/ +static int pt_xy_worker(struct pt_debug_data *dd) +{ + struct device *dev = dd->dev; + struct pt_sysinfo *si = dd->si; + u8 report_reg = si->xy_mode[TOUCH_COUNT_BYTE_OFFSET]; + u8 num_cur_tch = GET_NUM_TOUCHES(report_reg); + uint32_t formated_output; + + mutex_lock(&dd->sysfs_lock); + dd->interrupt_count++; + formated_output = dd->formated_output; + mutex_unlock(&dd->sysfs_lock); + + /* Interrupt */ + pr_info("Interrupt(%u)\n", dd->interrupt_count); + + if (formated_output) + pt_debug_formated(dev, dd->pr_buf, si, num_cur_tch); + else + /* print data for TTHE */ + pt_pr_buf_op_mode(dd, dd->pr_buf, si, num_cur_tch); + + pr_info("\n"); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_debug_attention + * + * SUMMARY: Wrapper function for pt_xy_worker() to subcribe into TTDL attention + * list. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_debug_attention(struct device *dev) +{ + struct pt_debug_data *dd = pt_get_debug_data(dev); + struct pt_sysinfo *si = dd->si; + u8 report_id = si->xy_mode[2]; + int rc = 0; + + if (report_id != si->desc.tch_report_id + && report_id != si->desc.btn_report_id) + return 0; + + /* core handles handshake */ + rc = pt_xy_worker(dd); + if (rc < 0) + pt_debug(dev, DL_ERROR, "%s: xy_worker error r=%d\n", + __func__, rc); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_status_show + * + * SUMMARY: The show method for the int_count sysfs node. This node displays + * the count of interrupt. + * + * PARAMETERS: + * *dev - pointer to Device structure + * *attr - pointer to the device attribute structure + * *buf - pointer to buffer to print + ******************************************************************************/ +static ssize_t pt_interrupt_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_debug_data *dd = pt_get_debug_data(dev); + int val; + + mutex_lock(&dd->sysfs_lock); + val = dd->interrupt_count; + mutex_unlock(&dd->sysfs_lock); + + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Interrupt Count: %d\n", val); +} + +/******************************************************************************* + * FUNCTION: pt_interrupt_count_store + * + * SUMMARY: The store method for the int_count sysfs node that allows the count + * of interrput to be cleared. + * + * RETURN: Size of passed in buffer + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_interrupt_count_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_debug_data *dd = pt_get_debug_data(dev); + + mutex_lock(&dd->sysfs_lock); + dd->interrupt_count = 0; + mutex_unlock(&dd->sysfs_lock); + return size; +} + +static DEVICE_ATTR(int_count, 0600, + pt_interrupt_count_show, pt_interrupt_count_store); + +/******************************************************************************* + * FUNCTION: pt_formated_output_show + * + * SUMMARY: Show method for the formated_output sysfs node that will show + * whether to format data to buffer or print directly. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_formated_output_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_debug_data *dd = pt_get_debug_data(dev); + int val; + + mutex_lock(&dd->sysfs_lock); + val = dd->formated_output; + mutex_unlock(&dd->sysfs_lock); + + return scnprintf(buf, PT_MAX_PRBUF_SIZE, + "Formated debug output: %x\n", val); +} + +/******************************************************************************* + * FUNCTION: pt_formated_output_store + * + * SUMMARY: The store method for the formated_output sysfs node. Allows the + * setting to format data to buffer or print directly. + * + * RETURN: Size of passed in buffer + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_formated_output_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_debug_data *dd = pt_get_debug_data(dev); + unsigned long value; + int rc; + + rc = kstrtoul(buf, 10, &value); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__); + return size; + } + + /* Expecting only 0 or 1 */ + if (value != 0 && value != 1) { + pt_debug(dev, DL_ERROR, "%s: Invalid value %lu\n", + __func__, value); + return size; + } + + mutex_lock(&dd->sysfs_lock); + dd->formated_output = value; + mutex_unlock(&dd->sysfs_lock); + return size; +} + +static DEVICE_ATTR(formated_output, 0600, + pt_formated_output_show, pt_formated_output_store); + +/******************************************************************************* + * FUNCTION: pt_mt_probe + * + * SUMMARY: The probe function for debug module to create sysfs nodes and + * subscribe attention list. + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + * **data - double pointer tothe pt_debug_data structure to be created here + ******************************************************************************/ +static int pt_debug_probe(struct device *dev, void **data) +{ + struct pt_debug_data *dd; + int rc; + + /* get context and debug print buffers */ + dd = kzalloc(sizeof(*dd), GFP_KERNEL); + if (!dd) { + rc = -ENOMEM; + goto pt_debug_probe_alloc_failed; + } + + rc = device_create_file(dev, &dev_attr_int_count); + if (rc) { + pt_debug(dev, DL_ERROR, "%s: Error, could not create int_count\n", + __func__); + goto pt_debug_probe_create_int_count_failed; + } + + rc = device_create_file(dev, &dev_attr_formated_output); + if (rc) { + pt_debug(dev, DL_ERROR, "%s: Error, could not create formated_output\n", + __func__); + goto pt_debug_probe_create_formated_failed; + } + + mutex_init(&dd->sysfs_lock); + dd->dev = dev; + *data = dd; + + dd->si = cmd->request_sysinfo(dev); + if (!dd->si) { + pt_debug(dev, DL_ERROR, "%s: Fail get sysinfo pointer from core\n", + __func__); + rc = -ENODEV; + goto pt_debug_probe_sysinfo_failed; + } + + rc = cmd->subscribe_attention(dev, PT_ATTEN_IRQ, PT_DEBUG_NAME, + pt_debug_attention, PT_MODE_OPERATIONAL); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: Error, could not subscribe attention cb\n", + __func__); + goto pt_debug_probe_subscribe_failed; + } + + return 0; + +pt_debug_probe_subscribe_failed: +pt_debug_probe_sysinfo_failed: + device_remove_file(dev, &dev_attr_formated_output); +pt_debug_probe_create_formated_failed: + device_remove_file(dev, &dev_attr_int_count); +pt_debug_probe_create_int_count_failed: + kfree(dd); +pt_debug_probe_alloc_failed: + pt_debug(dev, DL_ERROR, "%s failed.\n", __func__); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_debug_release + * + * SUMMARY: Remove function for debug module that does following cleanup: + * - Unsubscibe all registered attention tasks + * - Removes all created sysfs nodes + * + * PARAMETERS: + * *dev - pointer to device structure + * *data - pointer to the pt_debug_data structure + ******************************************************************************/ +static void pt_debug_release(struct device *dev, void *data) +{ + struct pt_debug_data *dd = data; + int rc; + + rc = cmd->unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_DEBUG_NAME, + pt_debug_attention, PT_MODE_OPERATIONAL); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: Error, could not un-subscribe attention\n", + __func__); + goto pt_debug_release_exit; + } + +pt_debug_release_exit: + device_remove_file(dev, &dev_attr_formated_output); + device_remove_file(dev, &dev_attr_int_count); + kfree(dd); +} + +static struct pt_module debug_module = { + .name = PT_DEBUG_NAME, + .probe = pt_debug_probe, + .release = pt_debug_release, +}; + +/******************************************************************************* + * FUNCTION: pt_debug_init + * + * SUMMARY: Initialize function for debug module which to register + * debug_module into TTDL module list. + * + * RETURN: + * 0 = success + ******************************************************************************/ +static int __init pt_debug_init(void) +{ + int rc; + + cmd = pt_get_commands(); + if (!cmd) + return -EINVAL; + + rc = pt_register_module(&debug_module); + if (rc < 0) { + pr_err("%s: Error, failed registering module\n", + __func__); + return rc; + } + + pr_info("%s: Parade TTSP Debug Driver (Built %s) rc=%d\n", + __func__, PT_DRIVER_VERSION, rc); + return 0; +} +module_init(pt_debug_init); + +/******************************************************************************* + * FUNCTION: pt_debug_exit + * + * SUMMARY: Exit function for debug module which to unregister debug_module + * from TTDL module list. + * + ******************************************************************************/ +static void __exit pt_debug_exit(void) +{ + pt_unregister_module(&debug_module); +} +module_exit(pt_debug_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Debug Driver"); +MODULE_AUTHOR("Parade Technologies "); diff --git a/pt/pt_device_access.c b/pt/pt_device_access.c new file mode 100644 index 0000000000..af9e62b56a --- /dev/null +++ b/pt/pt_device_access.c @@ -0,0 +1,6830 @@ +#ifndef TTDL_KERNEL_SUBMISSION +/* + * pt_device_access.c + * Parade TrueTouch(TM) Standard Product Device Access Module. + * Configuration and Test command/status user interface. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.paradetech.com + */ + +#include "pt_regs.h" +#include + +#include +#include +#include + +#define PT_CMCP_THRESHOLD_FILE_NAME "pt_thresholdfile.csv" +#define CMCP_THRESHOLD_FILE_NAME "ttdl_cmcp_thresholdfile.csv" + +/* Max test case number */ +#define MAX_CASE_NUM (23) + +/* ASCII */ +#define ASCII_LF (0x0A) +#define ASCII_CR (0x0D) +#define ASCII_COMMA (0x2C) +#define ASCII_ZERO (0x30) +#define ASCII_NINE (0x39) + +/* Max characters of test case name */ +#define NAME_SIZE_MAX (50) + +/* Max characters of project information */ +#define NAME_PROJECT_INFO_MAX (128) + +/* Max sensor and button number */ +#define MAX_BUTTONS (PIP1_SYSINFO_MAX_BTN) +#define MAX_SENSORS (5120) +#define MAX_TX_SENSORS (128) +#define MAX_RX_SENSORS (128) + +/* Multiply by 2 for double (min, max) values */ +#define TABLE_BUTTON_MAX_SIZE (MAX_BUTTONS * 2) +#define TABLE_SENSOR_MAX_SIZE (MAX_SENSORS * 2) +#define TABLE_TX_MAX_SIZE (MAX_TX_SENSORS*2) +#define TABLE_RX_MAX_SIZE (MAX_RX_SENSORS*2) + +#define CM_PANEL_DATA_OFFSET (6) +#define CM_BTN_DATA_OFFSET (6) +#define CP_PANEL_DATA_OFFSET (6) +#define CP_BTN_DATA_OFFSET (6) +#define MAX_BUF_LEN (100000) +#define RETRIEVE_PANEL_SCAN_HDR (10) + +enum print_buffer_format { + PT_PR_FORMAT_DEFAULT = 0, + PT_PR_FORMAT_U8_SPACE = 1, + PT_PR_FORMAT_U16_SPACE = 2, + PT_PR_FORMAT_U8_NO_SPACE = 3, + PT_PR_FORMAT_U32_SPACE = 4, + PT_PR_FORMAT_UNDEFINE +}; + +/* cmcp csv file information */ +struct configuration { + /* One more space in the arrary below is left for null character */ + char proj_info[NAME_PROJECT_INFO_MAX + 1]; + u32 cm_range_limit_row; + u32 cm_range_limit_col; + u32 cm_min_limit_cal; + u32 cm_max_limit_cal; + u32 cm_max_delta_sensor_percent; + u32 cm_max_delta_button_percent; + u32 min_button; + u32 max_button; + u32 cp_max_delta_sensor_rx_percent; + u32 cp_max_delta_sensor_tx_percent; + u32 cm_min_max_table_btn[TABLE_BUTTON_MAX_SIZE]; + u32 cp_min_max_table_btn[TABLE_BUTTON_MAX_SIZE]; + u32 cm_min_max_table_sensor[TABLE_SENSOR_MAX_SIZE]; + u32 cp_min_max_table_rx[TABLE_RX_MAX_SIZE]; + u32 cp_min_max_table_tx[TABLE_TX_MAX_SIZE]; + u32 cm_min_max_table_btn_size; + u32 cp_min_max_table_btn_size; + u32 cm_min_max_table_sensor_size; + u32 cp_min_max_table_rx_size; + u32 cp_min_max_table_tx_size; + u32 cp_max_delta_button_percent; + u32 cm_max_table_gradient_cols_percent[TABLE_TX_MAX_SIZE]; + u32 cm_max_table_gradient_cols_percent_size; + u32 cm_max_table_gradient_rows_percent[TABLE_RX_MAX_SIZE]; + u32 cm_max_table_gradient_rows_percent_size; + u32 cm_excluding_row_edge; + u32 cm_excluding_col_edge; + u32 rx_num; + u32 tx_num; + u32 btn_num; + u32 cm_enabled; + u32 cp_enabled; + u32 is_valid_or_not; +}; + +/* Test case search definition */ +struct test_case_search { + char name[NAME_SIZE_MAX]; /* Test case name */ + u32 name_size; /* Test case name size */ + u32 offset; /* Test case offset */ +}; + +/* Test case field definition */ +struct test_case_field { + char *name; /* Test case name */ + u32 name_size; /* Test case name size */ + u32 type; /* Test case type */ + u32 *bufptr; /* Buffer to store value information */ + u32 exist_or_not;/* Test case exist or not */ + u32 data_num; /* Buffer data number */ + u32 line_num; /* Buffer line number */ +}; + +/* Test case type */ +enum test_case_type { + TEST_CASE_TYPE_NO, + TEST_CASE_TYPE_ONE, + TEST_CASE_TYPE_MUL, + TEST_CASE_TYPE_MUL_LINES, + TEST_CASE_TYPE_STRING, +}; + +/* Test case order in test_case_field_array */ +enum case_order { + PROJ_VERSION, + CM_TEST_INPUTS, + CM_EXCLUDING_COL_EDGE, + CM_EXCLUDING_ROW_EDGE, + CM_GRADIENT_CHECK_COL, + CM_GRADIENT_CHECK_ROW, + CM_RANGE_LIMIT_ROW, + CM_RANGE_LIMIT_COL, + CM_MIN_LIMIT_CAL, + CM_MAX_LIMIT_CAL, + CM_MAX_DELTA_SENSOR_PERCENT, + CM_MAX_DELTA_BUTTON_PERCENT, + PER_ELEMENT_MIN_MAX_TABLE_BUTTON, + PER_ELEMENT_MIN_MAX_TABLE_SENSOR, + CP_TEST_INPUTS, + CP_MAX_DELTA_SENSOR_RX_PERCENT, + CP_MAX_DELTA_SENSOR_TX_PERCENT, + CP_MAX_DELTA_BUTTON_PERCENT, + CP_PER_ELEMENT_MIN_MAX_BUTTON, + MIN_BUTTON, + MAX_BUTTON, + PER_ELEMENT_MIN_MAX_RX, + PER_ELEMENT_MIN_MAX_TX, + CASE_ORDER_MAX, +}; + +enum cmcp_test_item { + CMCP_FULL = 0, + CMCP_CM_PANEL, + CMCP_CP_PANEL, + CMCP_CM_BTN, + CMCP_CP_BTN, +}; + +#define CM_ENABLED 0x10 +#define CP_ENABLED 0x20 +#define CM_PANEL (0x01 | CM_ENABLED) +#define CP_PANEL (0x02 | CP_ENABLED) +#define CM_BTN (0x04 | CM_ENABLED) +#define CP_BTN (0x08 | CP_ENABLED) +#define CMCP_FULL_CASE\ + (CM_PANEL | CP_PANEL | CM_BTN | CP_BTN | CM_ENABLED | CP_ENABLED) + +#define PT_DEVICE_ACCESS_NAME "pt_device_access" +#define PT_INPUT_ELEM_SZ (sizeof("0xHH") + 1) + +#define PIP_CMD_MAX_LENGTH ((1 << 16) - 1) + +#ifdef TTHE_TUNER_SUPPORT +struct heatmap_param { + bool scan_start; + enum scan_data_type_list data_type; /* raw, base, diff */ + int num_element; +}; +#endif +#define ABS(x) (((x) < 0) ? -(x) : (x)) + +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#define PT_MAX_CONFIG_BYTES 256 +#define PT_TTHE_TUNER_GET_PANEL_DATA_FILE_NAME "get_panel_data" +#define TTHE_TUNER_MAX_BUF (PT_MAX_PRBUF_SIZE * 8) + +struct pt_device_access_data { + struct device *dev; + struct pt_sysinfo *si; + struct mutex sysfs_lock; + bool sysfs_nodes_created; + struct kobject mfg_test; + u8 panel_scan_retrieve_id; + u8 panel_scan_type_id; + u8 get_idac_data_id; + u8 calibrate_sensing_mode; + u8 calibrate_initialize_baselines; + u8 baseline_sensing_mode; + u8 fw_self_test_id; + u8 fw_self_test_format; + u16 fw_self_test_param_len; + u8 fw_self_test_param[PT_FW_SELF_TEST_MAX_PARM]; + struct pt_cal_ext_data cal_ext_data; + struct dentry *panel_scan_debugfs; + int panel_scan_size; + u8 panel_scan_data_buf[TTHE_TUNER_MAX_BUF]; + struct mutex debugfs_lock; +#ifdef TTHE_TUNER_SUPPORT + struct heatmap_param heatmap; + struct dentry *tthe_get_panel_data_debugfs; + u8 tthe_get_panel_data_is_open; +#endif + struct dentry *cmcp_results_debugfs; + struct dentry *base_dentry; + struct dentry *mfg_test_dentry; + u8 ic_buf[10 * PT_MAX_PRBUF_SIZE]; + u8 response_buf[PT_MAX_PRBUF_SIZE]; + struct mutex cmcp_threshold_lock; + u8 *cmcp_threshold_data; + int cmcp_threshold_size; + bool cmcp_threshold_loading; + struct work_struct cmcp_threshold_update; + int builtin_cmcp_threshold_status; + bool is_manual_upgrade_enabled; + struct configuration *configs; + struct cmcp_data *cmcp_info; + struct result *result; + struct test_case_search *test_search_array; + struct test_case_field *test_field_array; + int cmcp_test_items; + int test_executed; + int cmcp_range_check; + int cmcp_force_calibrate; + int cmcp_test_in_progress; +}; + +struct cmcp_data { + struct gd_sensor *gd_sensor_col; + struct gd_sensor *gd_sensor_row; + int32_t *cm_data_panel; + int32_t *cp_tx_data_panel; + int32_t *cp_rx_data_panel; + int32_t *cp_tx_cal_data_panel; + int32_t *cp_rx_cal_data_panel; + int32_t cp_sensor_rx_delta; + int32_t cp_sensor_tx_delta; + int32_t cp_button_delta; + int32_t *cm_btn_data; + int32_t *cp_btn_data; + int32_t *cm_sensor_column_delta; + int32_t *cm_sensor_row_delta; + int32_t cp_btn_cal; + int32_t cm_btn_cal; + int32_t cp_button_ave; + int32_t cm_ave_data_panel; + int32_t cp_tx_ave_data_panel; + int32_t cp_rx_ave_data_panel; + int32_t cm_cal_data_panel; + int32_t cm_ave_data_btn; + int32_t cm_cal_data_btn; + int32_t cm_delta_data_btn; + int32_t cm_sensor_delta; + + int32_t tx_num; + int32_t rx_num; + int32_t btn_num; +}; + +struct result { + int32_t config_ver; + int32_t revision_ctrl; + int32_t device_id_high; + int32_t device_id_low; + /* Sensor Cm validation */ + bool cm_test_pass; + bool cm_sensor_validation_pass; + bool cm_sensor_row_delta_pass; + bool cm_sensor_col_delta_pass; + bool cm_sensor_gd_row_pass; + bool cm_sensor_gd_col_pass; + bool cm_sensor_calibration_pass; + bool cm_sensor_delta_pass; + bool cm_button_validation_pass; + bool cm_button_delta_pass; + + int32_t *cm_sensor_raw_data; + int32_t cm_sensor_calibration; + int32_t cm_sensor_delta; + int32_t *cm_button_raw_data; + int32_t cm_button_delta; + + /* Sensor Cp validation */ + bool cp_test_pass; + bool cp_sensor_delta_pass; + bool cp_sensor_rx_delta_pass; + bool cp_sensor_tx_delta_pass; + bool cp_sensor_average_pass; + bool cp_button_delta_pass; + bool cp_button_average_pass; + bool cp_rx_validation_pass; + bool cp_tx_validation_pass; + bool cp_button_validation_pass; + + int32_t *cp_sensor_rx_raw_data; + int32_t *cp_sensor_tx_raw_data; + int32_t cp_sensor_rx_delta; + int32_t cp_sensor_tx_delta; + int32_t cp_sensor_rx_calibration; + int32_t cp_sensor_tx_calibration; + int32_t *cp_button_raw_data; + int32_t cp_button_delta; + + /*other validation*/ + bool short_test_pass; + bool test_summary; +}; + +static struct pt_core_commands *cmd; + +static struct pt_module device_access_module; + +static ssize_t pt_run_and_get_selftest_result(struct device *dev, + int protect, char *buf, size_t buf_len, u8 test_id, + u16 read_length, u8 get_result_on_pass, + bool print_results, u8 print_format); + +static int _pt_calibrate_idacs_cmd(struct device *dev, + u8 sensing_mode, u8 *status); + +static int pt_perform_calibration(struct device *dev); + +/******************************************************************************* + * FUNCTION: pt_get_device_access_data + * + * SUMMARY: Inline function to get pt_device_access_data. + * + * RETURN: + * pointer to pt_device_access_data structure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static inline struct pt_device_access_data *pt_get_device_access_data( + struct device *dev) +{ + return pt_get_module_data(dev, &device_access_module); +} + +/******************************************************************************* + * FUNCTION: cmcp_check_config_fw_match + * + * SUMMARY: Checks if tx,rx and btn num of firmware match with configuration. + * + * RETURN: + * 0 = match + * !0 = doesn't match + * + * PARAMETERS: + * *dev - pointer to device structure + * *configuration - pointer to configuration structure + ******************************************************************************/ +static int cmcp_check_config_fw_match(struct device *dev, + struct configuration *configuration) +{ + struct pt_device_access_data *dad + = pt_get_device_access_data(dev); + int32_t tx_num = dad->configs->tx_num; + int32_t rx_num = dad->configs->rx_num; + int32_t button_num = dad->configs->btn_num; + int ret = 0; + + if (tx_num != dad->si->sensing_conf_data.tx_num) { + pt_debug(dev, DL_ERROR, + "%s: TX number mismatch! CSV=%d DUT=%d\n", + __func__, tx_num, dad->si->sensing_conf_data.tx_num); + ret = -EINVAL; + } + + if (rx_num != dad->si->sensing_conf_data.rx_num) { + pt_debug(dev, DL_ERROR, + "%s: RX number mismatch! CSV=%d DUT=%d\n", + __func__, rx_num, dad->si->sensing_conf_data.rx_num); + ret = -EINVAL; + } + + if (button_num != dad->si->num_btns) { + pt_debug(dev, DL_ERROR, + "%s: Button number mismatch! CSV=%d DUT=%d\n", + __func__, button_num, dad->si->num_btns); + ret = -EINVAL; + } + + return ret; +} + +/******************************************************************************* + * FUNCTION: validate_cm_test_results + * + * SUMMARY: Checks cm test results and outputs each test and a summary result + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *configuration - pointer to configuration structure + * *cmcp_info - pointer to cmcp_data structure to store cmcp data from fw + * *result - pointer to result structure + * *pass - pointer to bool value + * test_item - flag to store all test item are requested + ******************************************************************************/ +static int validate_cm_test_results(struct device *dev, + struct configuration *configuration, struct cmcp_data *cmcp_info, + struct result *result, bool *pass, int test_item) +{ + int32_t tx_num = cmcp_info->tx_num; + int32_t rx_num = cmcp_info->rx_num; + int32_t button_num = cmcp_info->btn_num; + uint32_t sensor_num = tx_num * rx_num; + int32_t *cm_sensor_data = cmcp_info->cm_data_panel; + int32_t cm_button_delta; + int32_t cm_sensor_calibration; + int32_t *cm_button_data = cmcp_info->cm_btn_data; + struct gd_sensor *gd_sensor_col = cmcp_info->gd_sensor_col; + struct gd_sensor *gd_sensor_row = cmcp_info->gd_sensor_row; + int32_t *cm_sensor_column_delta = cmcp_info->cm_sensor_column_delta; + int32_t *cm_sensor_row_delta = cmcp_info->cm_sensor_row_delta; + int ret = 0; + int i, j; + + pt_debug(dev, DL_INFO, "%s: start\n", __func__); + + if ((test_item & CM_PANEL) == CM_PANEL) { + pt_debug(dev, DL_INFO, + "Check each sensor Cm data for min max value\n "); + + /* Check each sensor Cm data for min/max values */ + result->cm_sensor_validation_pass = true; + + for (i = 0; i < sensor_num; i++) { + int row = i % rx_num; + int col = i / rx_num; + int32_t cm_sensor_min = + configuration->cm_min_max_table_sensor[(row*tx_num+col)*2]; + int32_t cm_sensor_max = + configuration->cm_min_max_table_sensor[(row*tx_num+col)*2+1]; + if ((cm_sensor_data[i] < cm_sensor_min) || + (cm_sensor_data[i] > cm_sensor_max)) { + pt_debug(dev, DL_WARN, + "%s: Sensor[%d,%d]:%d (%d,%d)\n", + "Cm sensor min/max test", + row, col, + cm_sensor_data[i], + cm_sensor_min, cm_sensor_max); + result->cm_sensor_validation_pass = false; + } + } + + /*check cm gradient column data*/ + result->cm_sensor_gd_col_pass = true; + for (i = 0; i < configuration->cm_max_table_gradient_cols_percent_size; + i++) { + if ((gd_sensor_col + i)->gradient_val > + 10 * configuration->cm_max_table_gradient_cols_percent[i]) { + pt_debug(dev, DL_WARN, + "%s: cm_max_table_gradient_cols_percent[%d]:%d, gradient_val:%d\n", + __func__, i, + configuration->cm_max_table_gradient_cols_percent[i], + (gd_sensor_col + i)->gradient_val); + result->cm_sensor_gd_col_pass = false; + } + } + + /*check cm gradient row data*/ + result->cm_sensor_gd_row_pass = true; + for (j = 0; j < configuration->cm_max_table_gradient_rows_percent_size; + j++) { + if ((gd_sensor_row + j)->gradient_val > + 10 * configuration->cm_max_table_gradient_rows_percent[j]) { + pt_debug(dev, DL_WARN, + "%s: cm_max_table_gradient_rows_percent[%d]:%d, gradient_val:%d\n", + __func__, j, + configuration->cm_max_table_gradient_rows_percent[j], + (gd_sensor_row + j)->gradient_val); + result->cm_sensor_gd_row_pass = false; + } + } + + result->cm_sensor_row_delta_pass = true; + result->cm_sensor_col_delta_pass = true; + result->cm_sensor_calibration_pass = true; + result->cm_sensor_delta_pass = true; + + /* Check each row Cm data with neighbor for difference */ + for (i = 0; i < tx_num; i++) { + for (j = 1; j < rx_num; j++) { + int32_t cm_sensor_row_diff = + ABS(cm_sensor_data[i * rx_num + j] - + cm_sensor_data[i * rx_num + j - 1]); + cm_sensor_row_delta[i * rx_num + j - 1] = + cm_sensor_row_diff; + if (cm_sensor_row_diff > + configuration->cm_range_limit_row) { + pt_debug(dev, DL_DEBUG, + "%s: Sensor[%d,%d]:%d (%d)\n", + "Cm sensor row range limit test", + j, i, cm_sensor_row_diff, + configuration->cm_range_limit_row); + result->cm_sensor_row_delta_pass = false; + } + } + } + + /* Check each column Cm data with neighbor for difference */ + for (i = 1; i < tx_num; i++) { + for (j = 0; j < rx_num; j++) { + int32_t cm_sensor_col_diff = + ABS((int)cm_sensor_data[i * rx_num + j] - + (int)cm_sensor_data[(i - 1) * rx_num + j]); + cm_sensor_column_delta[(i - 1) * rx_num + j] = + cm_sensor_col_diff; + if (cm_sensor_col_diff > + configuration->cm_range_limit_col) { + pt_debug(dev, DL_DEBUG, + "%s: Sensor[%d,%d]:%d (%d)\n", + "Cm sensor column range limit test", + j, i, cm_sensor_col_diff, + configuration->cm_range_limit_col); + result->cm_sensor_col_delta_pass = false; + } + } + } + + /* Check sensor calculated Cm for min/max values */ + cm_sensor_calibration = cmcp_info->cm_cal_data_panel; + if (cm_sensor_calibration < + configuration->cm_min_limit_cal || + cm_sensor_calibration > configuration->cm_max_limit_cal) { + pt_debug(dev, DL_DEBUG, "%s: Cm_cal:%d (%d,%d)\n", + "Cm sensor Cm_cal min/max test", + cm_sensor_calibration, + configuration->cm_min_limit_cal, + configuration->cm_max_limit_cal); + result->cm_sensor_calibration_pass = false; + } + + /* Check sensor Cm delta for range limit */ + if (cmcp_info->cm_sensor_delta > + (10 * configuration->cm_max_delta_sensor_percent)) { + pt_debug(dev, DL_DEBUG, + "%s: Cm_sensor_delta:%d (%d)\n", + "Cm sensor delta range limit test", + cmcp_info->cm_sensor_delta, + configuration->cm_max_delta_sensor_percent); + result->cm_sensor_delta_pass = false; + } + + result->cm_test_pass = result->cm_sensor_gd_col_pass + && result->cm_sensor_gd_row_pass + && result->cm_sensor_validation_pass + && result->cm_sensor_row_delta_pass + && result->cm_sensor_col_delta_pass + && result->cm_sensor_calibration_pass + && result->cm_sensor_delta_pass; + } + + if (((test_item & CM_BTN) == CM_BTN) && (cmcp_info->btn_num)) { + /* Check each button Cm data for min/max values */ + result->cm_button_validation_pass = true; + for (i = 0; i < button_num; i++) { + int32_t cm_button_min = + configuration->cm_min_max_table_btn[i * 2]; + int32_t cm_button_max = + configuration->cm_min_max_table_btn[i * 2 + 1]; + if ((cm_button_data[i] <= cm_button_min) || + (cm_button_data[i] >= cm_button_max)) { + pt_debug(dev, DL_DEBUG, + "%s: Button[%d]:%d (%d,%d)\n", + "Cm button min/max test", + i, cm_button_data[i], + cm_button_min, cm_button_max); + result->cm_button_validation_pass = false; + } + } + + /* Check button Cm delta for range limit */ + result->cm_button_delta_pass = true; + + cm_button_delta = ABS((cmcp_info->cm_ave_data_btn - + cmcp_info->cm_cal_data_btn) * 100 / + cmcp_info->cm_ave_data_btn); + if (cm_button_delta > + configuration->cm_max_delta_button_percent) { + pt_debug(dev, DL_INFO, + "%s: Cm_button_delta:%d (%d)\n", + "Cm button delta range limit test", + cm_button_delta, + configuration->cm_max_delta_button_percent); + result->cm_button_delta_pass = false; + } + + result->cm_test_pass = result->cm_test_pass && + result->cm_button_validation_pass && + result->cm_button_delta_pass; + } + + if (pass) + *pass = result->cm_test_pass; + + return ret; +} + +/******************************************************************************* + * FUNCTION: validate_cp_test_results + * + * SUMMARY: Checks cp test results and outputs each test and a summary result. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *configuration - pointer to configuration structure + * *cmcp_info - pointer to cmcp_data structure to store cmcp data from fw + * *result - pointer to result structure + * *pass - pointer to bool value + * test_item - flag to store all test item are requested + ******************************************************************************/ +static int validate_cp_test_results(struct device *dev, + struct configuration *configuration, struct cmcp_data *cmcp_info, + struct result *result, bool *pass, int test_item) +{ + int i = 0; + uint32_t configuration_rx_num; + uint32_t configuration_tx_num; + int32_t *cp_sensor_tx_data = cmcp_info->cp_tx_data_panel; + int32_t *cp_sensor_rx_data = cmcp_info->cp_rx_data_panel; + int32_t cp_button_delta; + int32_t cp_button_average; + + result->cp_test_pass = true; + configuration_rx_num = configuration->cp_min_max_table_rx_size/2; + configuration_tx_num = configuration->cp_min_max_table_tx_size/2; + + pt_debug(dev, DL_INFO, "%s start\n", __func__); + + if ((test_item & CP_PANEL) == CP_PANEL) { + int32_t cp_sensor_tx_delta; + int32_t cp_sensor_rx_delta; + + /* Check Sensor Cp delta for range limit */ + result->cp_sensor_delta_pass = true; + /*check cp_sensor_tx_delta */ + for (i = 0; i < configuration_tx_num; i++) { + cp_sensor_tx_delta = + ABS((cmcp_info->cp_tx_cal_data_panel[i]- + cmcp_info->cp_tx_data_panel[i]) * 100 / + cmcp_info->cp_tx_data_panel[i]); + + if (cp_sensor_tx_delta > + configuration->cp_max_delta_sensor_tx_percent) { + pt_debug(dev, DL_DEBUG, + "%s: Cp_sensor_tx_delta:%d (%d)\n", + "Cp sensor delta range limit test", + cp_sensor_tx_delta, + configuration->cp_max_delta_sensor_tx_percent); + result->cp_sensor_delta_pass = false; + } + } + + /*check cp_sensor_rx_delta */ + for (i = 0; i < configuration_rx_num; i++) { + cp_sensor_rx_delta = + ABS((cmcp_info->cp_rx_cal_data_panel[i] - + cmcp_info->cp_rx_data_panel[i]) * 100 / + cmcp_info->cp_rx_data_panel[i]); + if (cp_sensor_rx_delta > + configuration->cp_max_delta_sensor_rx_percent) { + pt_debug(dev, DL_DEBUG, + "%s: Cp_sensor_rx_delta:%d(%d)\n", + "Cp sensor delta range limit test", + cp_sensor_rx_delta, + configuration->cp_max_delta_sensor_rx_percent); + result->cp_sensor_delta_pass = false; + } + } + + /* Check sensor Cp rx for min/max values */ + result->cp_rx_validation_pass = true; + for (i = 0; i < configuration_rx_num; i++) { + int32_t cp_rx_min = + configuration->cp_min_max_table_rx[i * 2]; + int32_t cp_rx_max = + configuration->cp_min_max_table_rx[i * 2 + 1]; + if ((cp_sensor_rx_data[i] <= cp_rx_min) || + (cp_sensor_rx_data[i] >= cp_rx_max)) { + pt_debug(dev, DL_DEBUG, + "%s: Cp Rx[%d]:%d (%d,%d)\n", + "Cp Rx min/max test", + i, (int)cp_sensor_rx_data[i], + cp_rx_min, cp_rx_max); + result->cp_rx_validation_pass = false; + } + } + + /* Check sensor Cp tx for min/max values */ + result->cp_tx_validation_pass = true; + for (i = 0; i < configuration_tx_num; i++) { + int32_t cp_tx_min = + configuration->cp_min_max_table_tx[i * 2]; + int32_t cp_tx_max = + configuration->cp_min_max_table_tx[i * 2 + 1]; + if ((cp_sensor_tx_data[i] <= cp_tx_min) || + (cp_sensor_tx_data[i] >= cp_tx_max)) { + pt_debug(dev, DL_DEBUG, + "%s: Cp Tx[%d]:%d(%d,%d)\n", + "Cp Tx min/max test", + i, cp_sensor_tx_data[i], + cp_tx_min, cp_tx_max); + result->cp_tx_validation_pass = false; + } + } + + result->cp_test_pass = result->cp_test_pass + && result->cp_sensor_delta_pass + && result->cp_rx_validation_pass + && result->cp_tx_validation_pass; + } + + if (((test_item & CP_BTN) == CP_BTN) && (cmcp_info->btn_num)) { + result->cp_button_delta_pass = true; + + /* Check button Cp delta for range limit */ + cp_button_delta = ABS((cmcp_info->cp_btn_cal + - cmcp_info->cp_button_ave) * 100 / + cmcp_info->cp_button_ave); + if (cp_button_delta > + configuration->cp_max_delta_button_percent) { + pt_debug(dev, DL_INFO, + "%s: Cp_button_delta:%d (%d)\n", + "Cp button delta range limit test", + cp_button_delta, + configuration->cp_max_delta_button_percent); + result->cp_button_delta_pass = false; + } + + /* Check button Cp average for min/max values */ + result->cp_button_average_pass = true; + cp_button_average = cmcp_info->cp_button_ave; + if (cp_button_average < configuration->min_button || + cp_button_average > configuration->max_button) { + pt_debug(dev, DL_INFO, + "%s: Button Cp average fails min/max test\n", + __func__); + pt_debug(dev, DL_INFO, + "%s: Cp_button_average:%d (%d,%d)\n", + "Cp button average min/max test", + cp_button_average, + configuration->min_button, + configuration->max_button); + result->cp_button_average_pass = false; + } + + /* Check each button Cp data for min/max values */ + result->cp_button_validation_pass = true; + for (i = 0; i < cmcp_info->btn_num; i++) { + int32_t cp_button_min = + configuration->cp_min_max_table_btn[i * 2]; + int32_t cp_button_max = + configuration->cp_min_max_table_btn[i * 2 + 1]; + if ((cmcp_info->cp_btn_data[i] <= cp_button_min) || + (cmcp_info->cp_btn_data[i] >= cp_button_max)) { + pt_debug(dev, DL_DEBUG, + "%s: Button[%d]:%d (%d,%d)\n", + "Cp button min/max test", + i, cmcp_info->cp_btn_data[i], + cp_button_min, cp_button_max); + result->cp_button_validation_pass = false; + } + } + + result->cp_test_pass = result->cp_test_pass + && result->cp_button_delta_pass + && result->cp_button_average_pass + && result->cp_button_validation_pass; + } + + if (pass) + *pass = result->cp_test_pass; + + return 0; +} + +/******************************************************************************* + * FUNCTION: calculate_gradient_row + * + * SUMMARY: Calculates gradient value for rows. + * + * PARAMETERS: + * *gd_sensor_row_head - pointer to gd_sensor structure + * row_num - number of row + * exclude_row_edge - flag to exclude row edge(1:exclude; 0:include) + * exclude_col_edge - flag to exclude column edge(1:exclude; 0:include) + ******************************************************************************/ +static void calculate_gradient_row(struct gd_sensor *gd_sensor_row_head, + uint16_t row_num, int exclude_row_edge, int exclude_col_edge) +{ + int i = 0; + uint16_t cm_min_cur = 0; + uint16_t cm_max_cur = 0; + uint16_t cm_ave_cur = 0; + uint16_t cm_ave_next = 0; + uint16_t cm_ave_prev = 0; + struct gd_sensor *p = gd_sensor_row_head; + + if (exclude_row_edge) { + for (i = 0; i < row_num; i++) { + if (!exclude_col_edge) { + cm_ave_cur = (p + i)->cm_ave; + cm_min_cur = (p + i)->cm_min; + cm_max_cur = (p + i)->cm_max; + if (i < (row_num-1)) + cm_ave_next = (p + i+1)->cm_ave; + if (i > 0) + cm_ave_prev = (p + i-1)->cm_ave; + } else { + cm_ave_cur = (p + i)->cm_ave_exclude_edge; + cm_min_cur = (p + i)->cm_min_exclude_edge; + cm_max_cur = (p + i)->cm_max_exclude_edge; + if (i < (row_num-1)) + cm_ave_next = + (p + i+1)->cm_ave_exclude_edge; + if (i > 0) + cm_ave_prev = + (p + i-1)->cm_ave_exclude_edge; + } + + if (cm_ave_cur == 0) + cm_ave_cur = 1; + + /*multiple 1000 to increate accuracy*/ + if ((i == 0) || (i == (row_num-1))) { + (p + i)->gradient_val = + (cm_max_cur - cm_min_cur) * 1000 / + cm_ave_cur; + } else if (i == 1) { + (p + i)->gradient_val = (cm_max_cur - cm_min_cur + + ABS(cm_ave_cur - cm_ave_next)) * 1000 / + cm_ave_cur; + } else { + (p + i)->gradient_val = (cm_max_cur - cm_min_cur + + ABS(cm_ave_cur - cm_ave_prev)) * 1000 / + cm_ave_cur; + } + } + } else if (!exclude_row_edge) { + for (i = 0; i < row_num; i++) { + if (!exclude_col_edge) { + cm_ave_cur = (p + i)->cm_ave; + cm_min_cur = (p + i)->cm_min; + cm_max_cur = (p + i)->cm_max; + if (i < (row_num-1)) + cm_ave_next = (p + i+1)->cm_ave; + if (i > 0) + cm_ave_prev = (p + i-1)->cm_ave; + } else { + cm_ave_cur = (p + i)->cm_ave_exclude_edge; + cm_min_cur = (p + i)->cm_min_exclude_edge; + cm_max_cur = (p + i)->cm_max_exclude_edge; + if (i < (row_num-1)) + cm_ave_next = + (p + i+1)->cm_ave_exclude_edge; + if (i > 0) + cm_ave_prev = + (p + i-1)->cm_ave_exclude_edge; + } + + if (cm_ave_cur == 0) + cm_ave_cur = 1; + /*multiple 1000 to increate accuracy*/ + if (i <= 1) + (p + i)->gradient_val = (cm_max_cur - cm_min_cur + + ABS(cm_ave_cur - cm_ave_next)) * 1000 / + cm_ave_cur; + else + (p + i)->gradient_val = (cm_max_cur - cm_min_cur + + ABS(cm_ave_cur - cm_ave_prev)) * 1000 / + cm_ave_cur; + } + } +} + +/******************************************************************************* + * FUNCTION: calculate_gradient_col + * + * SUMMARY: Calculates gradient value for columns. + * + * PARAMETERS: + * *gd_sensor_row_head - pointer to gd_sensor structure + * col_num - number of column + * exclude_row_edge - flag to exclude row edge(1:exclude; 0:include) + * exclude_col_edge - flag to exclude column edge(1:exclude; 0:include) + ******************************************************************************/ +static void calculate_gradient_col(struct gd_sensor *gd_sensor_row_head, + uint16_t col_num, int exclude_row_edge, int exclude_col_edge) +{ + int i = 0; + int32_t cm_min_cur = 0; + int32_t cm_max_cur = 0; + int32_t cm_ave_cur = 0; + int32_t cm_ave_next = 0; + int32_t cm_ave_prev = 0; + struct gd_sensor *p = gd_sensor_row_head; + + if (!exclude_col_edge) { + for (i = 0; i < col_num; i++) { + if (!exclude_row_edge) { + cm_ave_cur = (p + i)->cm_ave; + cm_min_cur = (p + i)->cm_min; + cm_max_cur = (p + i)->cm_max; + if (i < (col_num-1)) + cm_ave_next = (p + i+1)->cm_ave; + if (i > 0) + cm_ave_prev = (p + i-1)->cm_ave; + } else { + cm_ave_cur = (p + i)->cm_ave_exclude_edge; + cm_min_cur = (p + i)->cm_min_exclude_edge; + cm_max_cur = (p + i)->cm_max_exclude_edge; + if (i < (col_num-1)) + cm_ave_next = + (p + i+1)->cm_ave_exclude_edge; + if (i > 0) + cm_ave_prev = + (p + i-1)->cm_ave_exclude_edge; + } + if (cm_ave_cur == 0) + cm_ave_cur = 1; + /*multiple 1000 to increate accuracy*/ + if (i <= 1) + (p + i)->gradient_val = (cm_max_cur - cm_min_cur + + ABS(cm_ave_cur - cm_ave_next)) * 1000 / + cm_ave_cur; + else + (p + i)->gradient_val = (cm_max_cur - cm_min_cur + + ABS(cm_ave_cur - cm_ave_prev)) * 1000 / + cm_ave_cur; + } + } else if (exclude_col_edge) { + for (i = 0; i < col_num; i++) { + if (!exclude_row_edge) { + cm_ave_cur = (p + i)->cm_ave; + cm_min_cur = (p + i)->cm_min; + cm_max_cur = (p + i)->cm_max; + if (i < (col_num-1)) + cm_ave_next = (p + i+1)->cm_ave; + if (i > 0) + cm_ave_prev = (p + i-1)->cm_ave; + } else { + cm_ave_cur = (p + i)->cm_ave_exclude_edge; + cm_min_cur = (p + i)->cm_min_exclude_edge; + cm_max_cur = (p + i)->cm_max_exclude_edge; + if (i < (col_num-1)) + cm_ave_next = + (p + i+1)->cm_ave_exclude_edge; + if (i > 0) + cm_ave_prev = + (p + i-1)->cm_ave_exclude_edge; + } + + if (cm_ave_cur == 0) + cm_ave_cur = 1; + /*multiple 1000 to increate accuracy*/ + if ((i == 0) || (i == (col_num - 1))) + (p + i)->gradient_val = + (cm_max_cur - cm_min_cur) * 1000 / + cm_ave_cur; + else if (i == 1) + (p + i)->gradient_val = + (cm_max_cur - cm_min_cur + + ABS(cm_ave_cur - cm_ave_next)) + * 1000 / cm_ave_cur; + else + (p + i)->gradient_val = + (cm_max_cur - cm_min_cur + + ABS(cm_ave_cur - cm_ave_prev)) + * 1000 / cm_ave_cur; + } + } +} + +/******************************************************************************* + * FUNCTION: fill_gd_sensor_table + * + * SUMMARY: Fills cm calculation result and exclude parameter to gd_sensor + * structure. + * + * PARAMETERS: + * *head - pointer to gd_sensor structure + * index - index of row or column + * cm_max - maximum of cm + * cm_min - minmum of cm + * cm_ave - average of cm + * cm_max_exclude_edge - maximum of cm without edge data + * cm_min_exclude_edge - minmum of cm without edge data + * cm_ave_exclude_edge - average of cm without edge data + ******************************************************************************/ +static void fill_gd_sensor_table(struct gd_sensor *head, int32_t index, + int32_t cm_max, int32_t cm_min, int32_t cm_ave, + int32_t cm_max_exclude_edge, int32_t cm_min_exclude_edge, + int32_t cm_ave_exclude_edge) +{ + (head + index)->cm_max = cm_max; + (head + index)->cm_min = cm_min; + (head + index)->cm_ave = cm_ave; + (head + index)->cm_ave_exclude_edge = cm_ave_exclude_edge; + (head + index)->cm_max_exclude_edge = cm_max_exclude_edge; + (head + index)->cm_min_exclude_edge = cm_min_exclude_edge; +} + +/******************************************************************************* + * FUNCTION: calculate_gd_info + * + * SUMMARY: Calculates gradient panel sensor column and row by calling + * function calculate_gradient_col() & calculate_gradient_row(). + * + * PARAMETERS: + * *head - pointer to gd_sensor structure + * index - index of row or column + * cm_max - maximum of cm + * cm_min - minmum of cm + * cm_ave - average of cm + * cm_max_exclude_edge - maximum of cm without edge data + * cm_min_exclude_edge - minmum of cm without edge data + * cm_ave_exclude_edge - average of cm without edge data + ******************************************************************************/ +static void calculate_gd_info(struct gd_sensor *gd_sensor_col, + struct gd_sensor *gd_sensor_row, int tx_num, int rx_num, + int32_t *cm_sensor_data, int cm_excluding_row_edge, + int cm_excluding_col_edge) +{ + int32_t cm_max; + int32_t cm_min; + int32_t cm_ave; + int32_t cm_max_exclude_edge; + int32_t cm_min_exclude_edge; + int32_t cm_ave_exclude_edge; + int32_t cm_data; + int i; + int j; + + /*calculate all the gradient related info for column*/ + for (i = 0; i < tx_num; i++) { + /*re-initialize for a new col*/ + cm_max = cm_sensor_data[i * rx_num]; + cm_min = cm_max; + cm_ave = 0; + cm_max_exclude_edge = cm_sensor_data[i * rx_num + 1]; + cm_min_exclude_edge = cm_max_exclude_edge; + cm_ave_exclude_edge = 0; + + for (j = 0; j < rx_num; j++) { + cm_data = cm_sensor_data[i * rx_num + j]; + if (cm_data > cm_max) + cm_max = cm_data; + if (cm_data < cm_min) + cm_min = cm_data; + cm_ave += cm_data; + /*calculate exclude edge data*/ + if ((j > 0) && (j < (rx_num-1))) { + if (cm_data > cm_max_exclude_edge) + cm_max_exclude_edge = cm_data; + if (cm_data < cm_min_exclude_edge) + cm_min_exclude_edge = cm_data; + cm_ave_exclude_edge += cm_data; + } + } + cm_ave /= rx_num; + cm_ave_exclude_edge /= (rx_num-2); + fill_gd_sensor_table(gd_sensor_col, i, cm_max, cm_min, cm_ave, + cm_max_exclude_edge, cm_min_exclude_edge, cm_ave_exclude_edge); + } + + calculate_gradient_col(gd_sensor_col, tx_num, cm_excluding_row_edge, + cm_excluding_col_edge); + + /*calculate all the gradient related info for row*/ + for (j = 0; j < rx_num; j++) { + /*re-initialize for a new row*/ + cm_max = cm_sensor_data[j]; + cm_min = cm_max; + cm_ave = 0; + cm_max_exclude_edge = cm_sensor_data[rx_num + j]; + cm_min_exclude_edge = cm_max_exclude_edge; + cm_ave_exclude_edge = 0; + for (i = 0; i < tx_num; i++) { + cm_data = cm_sensor_data[i * rx_num + j]; + if (cm_data > cm_max) + cm_max = cm_data; + if (cm_data < cm_min) + cm_min = cm_data; + cm_ave += cm_data; + /*calculate exclude edge data*/ + if ((i > 0) && (i < (tx_num-1))) { + if (cm_data > cm_max_exclude_edge) + cm_max_exclude_edge = cm_data; + if (cm_data < cm_min_exclude_edge) + cm_min_exclude_edge = cm_data; + cm_ave_exclude_edge += cm_data; + } + } + cm_ave /= tx_num; + cm_ave_exclude_edge /= (tx_num-2); + fill_gd_sensor_table(gd_sensor_row, j, cm_max, cm_min, cm_ave, + cm_max_exclude_edge, cm_min_exclude_edge, cm_ave_exclude_edge); + } + calculate_gradient_row(gd_sensor_row, rx_num, cm_excluding_row_edge, + cm_excluding_col_edge); +} + +/******************************************************************************* + * FUNCTION: pt_get_cmcp_info + * + * SUMMARY: Function to include following work: + * 1) run short test and get result + * 2) run selftest to get cm_panel data, cm_cal_data_panel data, calculate + * cm_ave_data_panel, cm_sensor_delta and gradient by column and row. + * 3) run selftest to get cp_panel data, cp_cal_data_panel data, cacluate + * cp_ave_data_panel, cp_sensor_delta for tx and rx. + * 4) run selftest to get cm_btn data, cm_cal_data_btn data, cacluate + * cm_delta_data_btn data, cm_ave_data_btn data. + * 5) run selftest to get cp_btn data, cp_btn_cal data, cacluate + * cp_button_delta data, cp_button_ave data. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dad - pointer to pt_device_access_data structure + * *cmcp_info - pointer to cmcp_data structure + ******************************************************************************/ +static int pt_get_cmcp_info(struct pt_device_access_data *dad, + struct cmcp_data *cmcp_info) +{ + struct device *dev; + int32_t *cm_data_panel = cmcp_info->cm_data_panel; + int32_t *cp_tx_data_panel = cmcp_info->cp_tx_data_panel; + int32_t *cp_rx_data_panel = cmcp_info->cp_rx_data_panel; + int32_t *cp_tx_cal_data_panel = cmcp_info->cp_tx_cal_data_panel; + int32_t *cp_rx_cal_data_panel = cmcp_info->cp_rx_cal_data_panel; + int32_t *cm_btn_data = cmcp_info->cm_btn_data; + int32_t *cp_btn_data = cmcp_info->cp_btn_data; + struct gd_sensor *gd_sensor_col = cmcp_info->gd_sensor_col; + struct gd_sensor *gd_sensor_row = cmcp_info->gd_sensor_row; + struct result *result = dad->result; + int32_t cp_btn_cal = 0; + int32_t cp_btn_ave = 0; + int32_t cm_ave_data_panel = 0; + int32_t cm_ave_data_btn = 0; + int32_t cp_tx_ave_data_panel = 0; + int32_t cp_rx_ave_data_panel = 0; + u8 tmp_buf[3]; + int tx_num; + int rx_num; + int btn_num; + int rc = 0; + int i; + + dev = dad->dev; + cmcp_info->tx_num = dad->si->sensing_conf_data.tx_num; + cmcp_info->rx_num = dad->si->sensing_conf_data.rx_num; + cmcp_info->btn_num = dad->si->num_btns; + + tx_num = cmcp_info->tx_num; + rx_num = cmcp_info->rx_num; + btn_num = cmcp_info->btn_num; + pt_debug(dev, DL_INFO, "%s tx_num=%d", __func__, tx_num); + pt_debug(dev, DL_INFO, "%s rx_num=%d", __func__, rx_num); + pt_debug(dev, DL_INFO, "%s btn_num=%d", __func__, btn_num); + + /*short test*/ + result->short_test_pass = true; + rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED, + tmp_buf, sizeof(tmp_buf), + PT_ST_ID_AUTOSHORTS, PIP_CMD_MAX_LENGTH, + PT_ST_DONT_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT); + if (rc) { + pt_debug(dev, DL_ERROR, "short test not supported"); + goto exit; + } + if (dad->ic_buf[1] != 0) + result->short_test_pass = false; + + /*Get cm_panel data*/ + rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED, + tmp_buf, sizeof(tmp_buf), + PT_ST_ID_CM_PANEL, PIP_CMD_MAX_LENGTH, + PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT); + if (rc) { + pt_debug(dev, DL_ERROR, "Get CM Panel not supported"); + goto exit; + } + if (cm_data_panel != NULL) { + for (i = 0; i < tx_num * rx_num; i++) { + cm_data_panel[i] = + 10*(dad->ic_buf[CM_PANEL_DATA_OFFSET+i*2] + 256 + * dad->ic_buf[CM_PANEL_DATA_OFFSET+i*2+1]); + pt_debug(dev, DL_DEBUG, + "cm_data_panel[%d]=%d\n", + i, cm_data_panel[i]); + cm_ave_data_panel += cm_data_panel[i]; + } + cm_ave_data_panel /= (tx_num * rx_num); + cmcp_info->cm_ave_data_panel = cm_ave_data_panel; + } + + /* Calculate gradient panel sensor column/row here */ + calculate_gd_info(gd_sensor_col, gd_sensor_row, tx_num, rx_num, + cm_data_panel, 1, 1); + for (i = 0; i < tx_num; i++) { + pt_debug(dev, DL_DEBUG, + "i=%d max=%d,min=%d,ave=%d, gradient=%d", i, + gd_sensor_col[i].cm_max, + gd_sensor_col[i].cm_min, + gd_sensor_col[i].cm_ave, + gd_sensor_col[i].gradient_val); + } + + for (i = 0; i < rx_num; i++) { + pt_debug(dev, DL_DEBUG, + "i=%d max=%d,min=%d,ave=%d, gradient=%d", i, + gd_sensor_row[i].cm_max, + gd_sensor_row[i].cm_min, + gd_sensor_row[i].cm_ave, + gd_sensor_row[i].gradient_val); + } + + /*Get cp data*/ + rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED, + tmp_buf, sizeof(tmp_buf), + PT_ST_ID_CP_PANEL, PIP_CMD_MAX_LENGTH, + PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT); + if (rc) { + pt_debug(dev, DL_ERROR, "Get CP Panel not supported"); + goto exit; + } + /*Get cp_tx_data_panel*/ + if (cp_tx_data_panel != NULL) { + for (i = 0; i < tx_num; i++) { + cp_tx_data_panel[i] = + 10*(dad->ic_buf[CP_PANEL_DATA_OFFSET+i*2] + + 256 * dad->ic_buf[CP_PANEL_DATA_OFFSET+i*2+1]); + pt_debug(dev, DL_DEBUG, + "cp_tx_data_panel[%d]=%d\n", + i, cp_tx_data_panel[i]); + cp_tx_ave_data_panel += cp_tx_data_panel[i]; + } + cp_tx_ave_data_panel /= tx_num; + cmcp_info->cp_tx_ave_data_panel = cp_tx_ave_data_panel; + } + + /*Get cp_tx_cal_data_panel*/ + if (cp_tx_cal_data_panel != NULL) { + for (i = 0; i < tx_num; i++) { + cp_tx_cal_data_panel[i] = + 10*(dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*2+i*2] + + 256 * dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*2+i*2+1]); + pt_debug(dev, DL_DEBUG, "cp_tx_cal_data_panel[%d]=%d\n", + i, cp_tx_cal_data_panel[i]); + } + } + + /*get cp_sensor_tx_delta,using the first sensor cal value for temp */ + /*multiple 1000 to increase accuracy*/ + cmcp_info->cp_sensor_tx_delta = ABS((cp_tx_cal_data_panel[0] + - cp_tx_ave_data_panel) * 1000 / cp_tx_ave_data_panel); + + /*Get cp_rx_data_panel*/ + if (cp_rx_data_panel != NULL) { + for (i = 0; i < rx_num; i++) { + cp_rx_data_panel[i] = + 10*(dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*4+i*2] + + 256 * dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*4+i*2+1]); + pt_debug(dev, DL_DEBUG, + "cp_rx_data_panel[%d]=%d\n", i, + cp_rx_data_panel[i]); + cp_rx_ave_data_panel += cp_rx_data_panel[i]; + } + cp_rx_ave_data_panel /= rx_num; + cmcp_info->cp_rx_ave_data_panel = cp_rx_ave_data_panel; + } + + /*Get cp_rx_cal_data_panel*/ + if (cp_rx_cal_data_panel != NULL) { + for (i = 0; i < rx_num; i++) { + cp_rx_cal_data_panel[i] = + 10 * (dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*4+rx_num*2+i*2] + + 256 * + dad->ic_buf[CP_PANEL_DATA_OFFSET+tx_num*4+rx_num*2+i*2+1]); + pt_debug(dev, DL_DEBUG, + "cp_rx_cal_data_panel[%d]=%d\n", i, + cp_rx_cal_data_panel[i]); + } + } + + /*get cp_sensor_rx_delta,using the first sensor cal value for temp */ + /*multiple 1000 to increase accuracy*/ + cmcp_info->cp_sensor_rx_delta = ABS((cp_rx_cal_data_panel[0] + - cp_rx_ave_data_panel) * 1000 / cp_rx_ave_data_panel); + + if (btn_num == 0) { + pt_debug(dev, DL_INFO, "%s: Skip Button Test\n", __func__); + goto skip_button_test; + } + + /*get cm btn data*/ + rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED, + tmp_buf, sizeof(tmp_buf), + PT_ST_ID_CM_BUTTON, PIP_CMD_MAX_LENGTH, + PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT); + if (rc) { + pt_debug(dev, DL_ERROR, "Get CM BTN not supported"); + goto exit; + } + if (cm_btn_data != NULL) { + for (i = 0; i < btn_num; i++) { + cm_btn_data[i] = + 10 * (dad->ic_buf[CM_BTN_DATA_OFFSET+i*2] + + 256 * dad->ic_buf[CM_BTN_DATA_OFFSET+i*2+1]); + pt_debug(dev, DL_DEBUG, + " cm_btn_data[%d]=%d\n", + i, cm_btn_data[i]); + cm_ave_data_btn += cm_btn_data[i]; + } + cm_ave_data_btn /= btn_num; + cmcp_info->cm_ave_data_btn = cm_ave_data_btn; + } + + /*get cp btn data*/ + rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED, + tmp_buf, sizeof(tmp_buf), + PT_ST_ID_CP_BUTTON, PIP_CMD_MAX_LENGTH, + PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT); + if (rc) { + pt_debug(dev, DL_ERROR, "Get CP BTN not supported"); + goto exit; + } + if (cp_btn_data != NULL) { + for (i = 0; i < btn_num; i++) { + cp_btn_data[i] = + 10 * (dad->ic_buf[CP_BTN_DATA_OFFSET+i*2] + + 256 * dad->ic_buf[CP_BTN_DATA_OFFSET+i*2+1]); + cp_btn_ave += cp_btn_data[i]; + pt_debug(dev, DL_DEBUG, + "cp_btn_data[%d]=%d\n", + i, cp_btn_data[i]); + } + cp_btn_ave /= btn_num; + cp_btn_cal = 10*(dad->ic_buf[CP_BTN_DATA_OFFSET+i*2] + + 256 * dad->ic_buf[CP_BTN_DATA_OFFSET+i*2+1]); + cmcp_info->cp_button_ave = cp_btn_ave; + cmcp_info->cp_btn_cal = cp_btn_cal; + /*multiple 1000 to increase accuracy*/ + cmcp_info->cp_button_delta = ABS((cp_btn_cal + - cp_btn_ave) * 1000 / cp_btn_ave); + pt_debug(dev, DL_INFO, " cp_btn_cal=%d\n", + cp_btn_cal); + pt_debug(dev, DL_INFO, " cp_btn_ave=%d\n", + cp_btn_ave); + } + +skip_button_test: +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_get_cm_cal + * + * SUMMARY: Function to include following work: + * 1) run selftest to get cm_cal_data_panel, cm_sensor_delta + * 2) run selftest to get cm_cal_data_btn, cm_delta_data_btn + * + * NOTE: + * This function depends on the calculation result of pt_get_cmcp_info() + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dad - pointer to pt_device_access_data structure + * *cmcp_info - pointer to cmcp_data structure + ******************************************************************************/ +static int pt_get_cm_cal(struct pt_device_access_data *dad, + struct cmcp_data *cmcp_info) +{ + struct device *dev; + int32_t *cm_data_panel = cmcp_info->cm_data_panel; + int32_t *cm_btn_data = cmcp_info->cm_btn_data; + u8 tmp_buf[3]; + int rc = 0; + int i; + + dev = dad->dev; + + /*Get cm_cal data*/ + rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED, + tmp_buf, sizeof(tmp_buf), + PT_ST_ID_CM_PANEL, PIP_CMD_MAX_LENGTH, + PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT); + if (rc) { + pt_debug(dev, DL_ERROR, "Get CM Panel not supported"); + goto exit; + } + if (cm_data_panel != NULL) { + i = cmcp_info->tx_num * cmcp_info->rx_num; + cmcp_info->cm_cal_data_panel = + 10 * (dad->ic_buf[CM_PANEL_DATA_OFFSET + i * 2] + + 256 * dad->ic_buf[CM_PANEL_DATA_OFFSET + i * 2 + 1]); + /*multiple 1000 to increase accuracy*/ + cmcp_info->cm_sensor_delta = + ABS((cmcp_info->cm_ave_data_panel - + cmcp_info->cm_cal_data_panel) * + 1000 / cmcp_info->cm_ave_data_panel); + } + + if (cmcp_info->btn_num == 0) { + pt_debug(dev, DL_INFO, "%s: Skip Button Test\n", __func__); + goto skip_button_test; + } + + /*get cm_btn_cal data*/ + rc = pt_run_and_get_selftest_result(dev, PT_CORE_CMD_UNPROTECTED, + tmp_buf, sizeof(tmp_buf), + PT_ST_ID_CM_BUTTON, PIP_CMD_MAX_LENGTH, + PT_ST_GET_RESULTS, PT_ST_NOPRINT, PT_PR_FORMAT_DEFAULT); + if (rc) { + pt_debug(dev, DL_ERROR, "Get CM BTN not supported"); + goto exit; + } + if (cm_btn_data != NULL) { + i = cmcp_info->btn_num; + cmcp_info->cm_cal_data_btn = + 10 * (dad->ic_buf[CM_BTN_DATA_OFFSET + i * 2] + + 256 * dad->ic_buf[CM_BTN_DATA_OFFSET + i * 2 + 1]); + /*multiple 1000 to increase accuracy*/ + cmcp_info->cm_delta_data_btn = ABS( + (cmcp_info->cm_ave_data_btn - cmcp_info->cm_cal_data_btn) * + 1000 / cmcp_info->cm_ave_data_btn); + pt_debug(dev, DL_INFO, " cm_btn_cal=%d\n", + cmcp_info->cm_cal_data_btn); + } + +skip_button_test: +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_free_cmcp_buf + * + * SUMMARY: Free pointers in cmcp_data structure + * + * PARAMETERS: + * *cmcp_info - pointer to cmcp_data structure + ******************************************************************************/ +static void pt_free_cmcp_buf(struct cmcp_data *cmcp_info) +{ + if (cmcp_info->gd_sensor_col != NULL) + kfree(cmcp_info->gd_sensor_col); + if (cmcp_info->gd_sensor_row != NULL) + kfree(cmcp_info->gd_sensor_row); + if (cmcp_info->cm_data_panel != NULL) + kfree(cmcp_info->cm_data_panel); + if (cmcp_info->cp_tx_data_panel != NULL) + kfree(cmcp_info->cp_tx_data_panel); + if (cmcp_info->cp_rx_data_panel != NULL) + kfree(cmcp_info->cp_rx_data_panel); + if (cmcp_info->cp_tx_cal_data_panel != NULL) + kfree(cmcp_info->cp_tx_cal_data_panel); + if (cmcp_info->cp_rx_cal_data_panel != NULL) + kfree(cmcp_info->cp_rx_cal_data_panel); + if (cmcp_info->cm_btn_data != NULL) + kfree(cmcp_info->cm_btn_data); + if (cmcp_info->cp_btn_data != NULL) + kfree(cmcp_info->cp_btn_data); + if (cmcp_info->cm_sensor_column_delta != NULL) + kfree(cmcp_info->cm_sensor_column_delta); + if (cmcp_info->cm_sensor_row_delta != NULL) + kfree(cmcp_info->cm_sensor_row_delta); +} + +/******************************************************************************* + * FUNCTION: pt_cmcp_get_test_item + * + * SUMMARY: Parses enum cmcp_test_item to integer value test_item as bitwise + * type. + * + * RETURN: integer value to indidate available test item with bitwise type + * + * PARAMETERS: + * item_input - enum cmcp_test_item + ******************************************************************************/ +static int pt_cmcp_get_test_item(int item_input) +{ + int test_item = 0; + + switch (item_input) { + case CMCP_FULL: + test_item = CMCP_FULL_CASE; + break; + case CMCP_CM_PANEL: + test_item = CM_PANEL; + break; + case CMCP_CP_PANEL: + test_item = CP_PANEL; + break; + case CMCP_CM_BTN: + test_item = CM_BTN; + break; + case CMCP_CP_BTN: + test_item = CP_BTN; + break; + } + return test_item; +} + +/******************************************************************************* + * FUNCTION: pt_cmcp_test_show + * + * SUMMARY: Show method for cmcp_test sysfs node. Allows to perform cmcp test + * with following steps: + * 1) Get cmcp test items which from threhold file + * 2) Check whether cmcp test items match with firmware + * 3) Set parameter to force single TX + * 4) Do calibration if requested + * 5) Get all cmcp data from FW and do calculation + * 6) Set parameter to restore to multi tx + * 7) Do calibration if requested + * 8) Check scan state,try to fix if it is not right + * 9) Start watchdog + * 10) Validate cm and cp test results if requested + * 11) Fill the test result + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_cmcp_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_device_access_data *dad + = pt_get_device_access_data(dev); + struct cmcp_data *cmcp_info = dad->cmcp_info; + struct result *result = dad->result; + struct configuration *configuration = dad->configs; + bool final_pass = true; + static const char * const cmcp_test_case_array[] = {"Full Cm/Cp test", + "Cm panel test", "Cp panel test", + "Cm button test", "Cp button test"}; + int index = 0; + int test_item = 0; + int no_builtin_file = 0; + int rc = 0; + int self_test_result_1 = 0; + int self_test_result_2 = 0; + u8 sys_mode = FW_SYS_MODE_UNDEFINED; + u8 retry = 3; + + dev = dad->dev; + if ((configuration == NULL) || (cmcp_info == NULL)) + goto exit; + + mutex_lock(&dad->sysfs_lock); + + if (dad->cmcp_test_in_progress) { + mutex_unlock(&dad->sysfs_lock); + goto cmcp_not_ready; + } + dad->cmcp_test_in_progress = 1; + + dad->test_executed = 0; + test_item = pt_cmcp_get_test_item(dad->cmcp_test_items); + + if (dad->builtin_cmcp_threshold_status < 0) { + pt_debug(dev, DL_WARN, "%s: No cmcp threshold file.\n", + __func__); + no_builtin_file = 1; + mutex_unlock(&dad->sysfs_lock); + goto start_testing; + } + + if (dad->cmcp_test_items < 0) { + pt_debug(dev, DL_ERROR, + "%s: Invalid test item! Should be 0~4!\n", __func__); + mutex_unlock(&dad->sysfs_lock); + goto invalid_item; + } + + pt_debug(dev, DL_INFO, "%s: Test item is %s, %d\n", + __func__, cmcp_test_case_array[dad->cmcp_test_items], + test_item); + + if ((dad->si->num_btns == 0) + && ((dad->cmcp_test_items == CMCP_CM_BTN) + || (dad->cmcp_test_items == CMCP_CP_BTN))) { + pt_debug(dev, DL_WARN, + "%s: FW doesn't support button!\n", __func__); + mutex_unlock(&dad->sysfs_lock); + goto invalid_item_btn; + } + + mutex_unlock(&dad->sysfs_lock); + + if (cmcp_check_config_fw_match(dev, configuration)) + goto mismatch; + +start_testing: + pt_debug(dev, DL_INFO, "%s: Start Cm/Cp test!\n", __func__); + result->cm_test_pass = true; + result->cp_test_pass = true; + + /*stop watchdog*/ + rc = cmd->request_stop_wd(dev); + if (rc) + pt_debug(dev, DL_ERROR, "stop watchdog failed"); + + /* Make sure the device is awake */ + pm_runtime_get_sync(dev); + /* Resource protect */ + rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error on request exclusive rc = %d\n", + __func__, rc); + } + + /*force single tx*/ + rc = cmd->nonhid_cmd->set_param(dev, + PT_CORE_CMD_UNPROTECTED, 0x1F, 1, 1); + if (rc) + pt_debug(dev, DL_ERROR, "force single tx failed"); + + /*suspend_scanning */ + rc = cmd->nonhid_cmd->suspend_scanning(dev, PT_CORE_CMD_UNPROTECTED); + if (rc) + pt_debug(dev, DL_ERROR, "suspend_scanning failed"); + + /* Do calibration if requested */ + if (!dad->cmcp_force_calibrate) { + pt_debug(dev, DL_INFO, "do calibration in single tx mode"); + rc = pt_perform_calibration(dev); + if (rc) + pt_debug(dev, DL_ERROR, "calibration failed"); + } + /*resume_scanning */ + rc = cmd->nonhid_cmd->resume_scanning(dev, PT_CORE_CMD_UNPROTECTED); + if (rc) + pt_debug(dev, DL_ERROR, "resume_scanning failed"); + + /*get all cmcp data from FW*/ + self_test_result_1 = pt_get_cmcp_info(dad, cmcp_info); + if (self_test_result_1) + pt_debug(dev, DL_ERROR, "pt_get_cmcp_info failed"); + + /*restore to multi tx*/ + rc = cmd->nonhid_cmd->set_param(dev, + PT_CORE_CMD_UNPROTECTED, 0x1F, 0, 1); + if (rc) + pt_debug(dev, DL_ERROR, "restore multi tx failed"); + + /*suspend_scanning */ + rc = cmd->nonhid_cmd->suspend_scanning(dev, 0); + if (rc) + pt_debug(dev, DL_ERROR, "suspend_scanning failed"); + + /* Do calibration if requested */ + if (!dad->cmcp_force_calibrate) { + pt_debug(dev, DL_INFO, "do calibration in multi tx mode"); + rc = pt_perform_calibration(dev); + if (rc) + pt_debug(dev, DL_ERROR, "calibration failed"); + } + /*resume_scanning */ + rc = cmd->nonhid_cmd->resume_scanning(dev, PT_CORE_CMD_UNPROTECTED); + if (rc) + pt_debug(dev, DL_ERROR, "resume_scanning failed"); + + /*get cm cal data from FW*/ + self_test_result_2 = pt_get_cm_cal(dad, cmcp_info); + if (self_test_result_2) + pt_debug(dev, DL_ERROR, "pt_get_cm_cal failed"); + + /* check scan state,try to fix if it is not right*/ + while (retry--) { + rc = cmd->request_get_fw_mode(dev, PT_CORE_CMD_UNPROTECTED, + &sys_mode, NULL); + + if (sys_mode != FW_SYS_MODE_SCANNING) { + pt_debug(dev, DL_ERROR, + "%s: fw mode: %d, retry: %d, rc = %d\n", + __func__, sys_mode, retry, rc); + rc = cmd->nonhid_cmd->resume_scanning(dev, + PT_CORE_CMD_UNPROTECTED); + } + } + + rc = cmd->release_exclusive(dev); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error on release exclusive rc = %d\n", + __func__, rc); + } + pm_runtime_put(dev); + + /*start watchdog*/ + rc = cmd->request_start_wd(dev); + if (rc) + pt_debug(dev, DL_ERROR, "start watchdog failed"); + + if (self_test_result_1 || self_test_result_2) + goto self_test_failed; + + /* The tests are finished without failure */ + mutex_lock(&dad->sysfs_lock); + dad->test_executed = 1; + mutex_unlock(&dad->sysfs_lock); + + if (no_builtin_file) + goto no_builtin; + + if ((test_item) & (CM_ENABLED)) + validate_cm_test_results(dev, configuration, cmcp_info, + result, &final_pass, test_item); + + if ((test_item) & (CP_ENABLED)) + validate_cp_test_results(dev, configuration, cmcp_info, + result, &final_pass, test_item); + + if ((dad->cmcp_test_items == CMCP_FULL) + && (dad->cmcp_range_check == 0)) { + /*full test and full check*/ + result->test_summary = result->cm_test_pass + && result->cp_test_pass + && result->short_test_pass; + } else if ((dad->cmcp_test_items == CMCP_FULL) + && (dad->cmcp_range_check == 1)) { + /*full test and basic check*/ + result->test_summary = result->cm_sensor_gd_col_pass + && result->cm_sensor_gd_row_pass + && result->cm_sensor_validation_pass + && result->cp_rx_validation_pass + && result->cp_tx_validation_pass + && result->short_test_pass; + } else if (dad->cmcp_test_items == CMCP_CM_PANEL) { + /*cm panel test result only*/ + result->test_summary = result->cm_sensor_gd_col_pass + && result->cm_sensor_gd_row_pass + && result->cm_sensor_validation_pass + && result->cm_sensor_row_delta_pass + && result->cm_sensor_col_delta_pass + && result->cm_sensor_calibration_pass + && result->cm_sensor_delta_pass; + } else if (dad->cmcp_test_items == CMCP_CP_PANEL) { + /*cp panel test result only*/ + result->test_summary = result->cp_sensor_delta_pass + && result->cp_rx_validation_pass + && result->cp_tx_validation_pass; + } else if (dad->cmcp_test_items == CMCP_CM_BTN) { + /*cm button test result only*/ + result->test_summary = result->cm_button_validation_pass + && result->cm_button_delta_pass; + } else if (dad->cmcp_test_items == CMCP_CP_BTN) { + /*cp button test result only*/ + result->test_summary = result->cp_button_delta_pass + && result->cp_button_average_pass + && result->cp_button_validation_pass; + } + + if (result->test_summary) { + pt_debug(dev, DL_INFO, + "%s: Finish Cm/Cp test! All Test Passed\n", __func__); + index = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 1\n"); + } else { + pt_debug(dev, DL_INFO, + "%s: Finish Cm/Cp test! Range Check Failure\n", + __func__); + index = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 6\n"); + } + goto cmcp_ready; + +mismatch: + index = snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: 2\nInput cmcp threshold file mismatches with FW\n"); + goto cmcp_ready; +invalid_item_btn: + index = snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: 3\nFW doesn't support button!\n"); + goto cmcp_ready; +invalid_item: + index = snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: 4\nWrong test item or range check input!\nOnly support below items:\n0 - Cm/Cp Panel & Button with Gradient (Typical)\n1 - Cm Panel with Gradient\n2 - Cp Panel\n3 - Cm Button\n4 - Cp Button\nOnly support below range check:\n0 - Full Range Checking (default)\n1 - Basic Range Checking(TSG5 style)\n"); + goto cmcp_ready; +self_test_failed: + index = snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: 5\nget self test ID not supported!\n"); + goto cmcp_ready; +cmcp_not_ready: + index = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 0\n"); + goto cmcp_ready; +no_builtin: + index = snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: 7\nNo cmcp threshold file!\n"); +cmcp_ready: + mutex_lock(&dad->sysfs_lock); + dad->cmcp_test_in_progress = 0; + mutex_unlock(&dad->sysfs_lock); +exit: + return index; +} + +/******************************************************************************* + * FUNCTION: pt_cmcp_test_store + * + * SUMMARY: The store method for cmcp_test sysfs node.Allows the user to + * configure which cm/cp tests will be executed on the "cat" of this node. + * + * RETURN: Size of passed in buffer + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_cmcp_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_device_access_data *dad + = pt_get_device_access_data(dev); + u8 test_item = 0; + u8 range_check = 0; + u8 force_calibrate = 0; + u32 input_data[4]; + int ret = 0; + static const char * const cmcp_test_case_array[] = {"Full Cm/Cp test", + "Cm panel test", "Cp panel test", + "Cm button test", "Cp button test"}; + static const char * const cmcp_test_range_check_array[] = { + "Full (default)", "Basic"}; + static const char * const cmcp_test_force_cal_array[] = { + "Calibrate When Testing (default)", "No Calibration"}; + ssize_t length = 0; + + pm_runtime_get_sync(dev); + mutex_lock(&dad->sysfs_lock); + + length = cmd->parse_sysfs_input(dev, buf, size, input_data, + ARRAY_SIZE(input_data)); + if (length <= 0 || length > 3) { + pt_debug(dev, DL_ERROR, "%s: Input format error!\n", + __func__); + dad->cmcp_test_items = -EINVAL; + ret = -EINVAL; + goto error; + } + + /* Get test item */ + test_item = input_data[0]; + /* Get range check */ + if (length >= 2) + range_check = input_data[1]; + /* Get force calibration */ + if (length == 3) + force_calibrate = input_data[2]; + + /* + * Test item limitation: + * 0: Perform all Tests + * 1: CM Panel with Gradient + * 2: CP Panel + * 3: CM Button + * 4: CP Button + * Ranage check limitation: + * 0: full check + * 1: basic check + * Force calibrate limitation: + * 0: do calibration + * 1: don't do calibration + */ + if ((test_item < 0) || (test_item > 4) || (range_check > 1) + || (force_calibrate > 1)) { + pt_debug(dev, DL_ERROR, + "%s: Test item should be 0~4; Range check should be 0~1; Force calibrate should be 0~1\n", + __func__); + dad->cmcp_test_items = -EINVAL; + ret = -EINVAL; + goto error; + } + /* + * If it is not all Test, then range_check should be 0 + * because other test does not has concept of basic check + */ + if (test_item > 0 && test_item < 5) + range_check = 0; + + dad->cmcp_test_items = test_item; + dad->cmcp_range_check = range_check; + dad->cmcp_force_calibrate = force_calibrate; + pt_debug(dev, DL_INFO, + "%s: Test item=%s; Range check=%s; Force cal=%s.\n", + __func__, + cmcp_test_case_array[test_item], + cmcp_test_range_check_array[range_check], + cmcp_test_force_cal_array[force_calibrate]); + +error: + mutex_unlock(&dad->sysfs_lock); + pm_runtime_put(dev); + + if (ret) + return ret; + + return size; +} + +static DEVICE_ATTR(cmcp_test, 0600, + pt_cmcp_test_show, pt_cmcp_test_store); + +/******************************************************************************* + * FUNCTION: prepare_print_string + * + * SUMMARY: Formats input buffer to out buffer with string type,and increases + * the index by size of formated data. + * + * RETURN: + * index plus with size of formated data + * + * PARAMETERS: + * *out_buf - output buffer to store formated data + * *in_buf - input buffer to be formated + * index - index in output buffer for appending content + ******************************************************************************/ +int prepare_print_string(char *out_buf, char *in_buf, int index) +{ + if ((out_buf == NULL) || (in_buf == NULL)) + return index; + index += scnprintf(&out_buf[index], MAX_BUF_LEN - index, + "%s", in_buf); + return index; +} + +/******************************************************************************* + * FUNCTION: prepare_print_string + * + * SUMMARY: Formats input buffer to out buffer with decimal base,and increases + * the index by size of formated data. + * + * RETURN: + * index plus with size of formated data + * + * PARAMETERS: + * *out_buf - output buffer to store formated data + * *in_buf - input buffer to be formated + * index - index in output buffer for appending content + * data_num - data number in input buffer + ******************************************************************************/ +int prepare_print_data(char *out_buf, int32_t *in_buf, int index, int data_num) +{ + int i; + + if ((out_buf == NULL) || (in_buf == NULL)) + return index; + for (i = 0; i < data_num; i++) + index += scnprintf(&out_buf[index], MAX_BUF_LEN - index, + "%d,", in_buf[i]); + return index; +} + +/******************************************************************************* + * FUNCTION: save_header + * + * SUMMARY: Appends "header" for cmcp test result to output buffer. + * + * RETURN: + * index plus with size of formated data + * + * PARAMETERS: + * *out_buf - output buffer to store formated data + * index - index in output buffer for appending content + * *result - pointer to result structure + ******************************************************************************/ +int save_header(char *out_buf, int index, struct configuration *config, + struct result *result) +{ + struct rtc_time tm; + char time_buf[100] = {0}; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)) + struct timespec64 ts; + + ktime_get_real_ts64(&ts); + rtc_time64_to_tm(ts.tv_sec, &tm); +#else + struct timex txc; + + do_gettimeofday(&(txc.time)); + rtc_time_to_tm(txc.time.tv_sec, &tm); +#endif + + scnprintf(time_buf, 100, "%d/%d/%d,TIME,%d:%d:%d,", tm.tm_year+1900, + tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + + index = prepare_print_string(out_buf, ",.header,\n", index); + index = prepare_print_string(out_buf, ",DATE,", index); + index = prepare_print_string(out_buf, &time_buf[0], index); + index = prepare_print_string(out_buf, ",\n", index); + index = prepare_print_string(out_buf, ",SW_VERSION,", index); + index = prepare_print_string(out_buf, PT_DRIVER_VERSION, index); + index = prepare_print_string(out_buf, ",\n", index); + if (config) { + index = prepare_print_string(out_buf, ",PROJ_VERSION,", index); + index = prepare_print_string(out_buf, config->proj_info, index); + index = prepare_print_string(out_buf, ",\n", index); + } + + index = prepare_print_string(out_buf, ",.end,\n", index); + index = prepare_print_string(out_buf, ",.engineering data,\n", index); + + return index; +} + +/******************************************************************************* + * FUNCTION: print_silicon_id + * + * SUMMARY: Formats input buffer(silicon id) to out buffer with + * string type,and increases the index by size of formated data. + * + * RETURN: + * index plus with size of formated data + * + * PARAMETERS: + * *out_buf - output buffer to store formated data + * *in_buf - input buffer to be formated + * index - index in output buffer for appending content + ******************************************************************************/ +static int print_silicon_id(char *out_buf, char *in_buf, int index) +{ + index = prepare_print_string(out_buf, ",1,", index); + index = prepare_print_string(out_buf, &in_buf[0], index); + return index; +} + +/******************************************************************************* + * FUNCTION: save_engineering_data + * + * SUMMARY: Generates cmcp test result with *.csv format to output buffer, but + * it doesn't include the header. + * + * RETURN: + * index plus with size of formated data + * + * PARAMETERS: + * *dev - pointer to device structure + * *out_buf - output buffer to store formated data + * index - index in output buffer for appending content + * *cmcp_info - pointer to cmcp_data structure + * *configuration - pointer to configuration structure + * *result - pointer to result structure + * test_item - test control in bitwise + * no_builtin_file - flag to determin if builtin-file exist + ******************************************************************************/ +int save_engineering_data(struct device *dev, char *out_buf, int index, + struct cmcp_data *cmcp_info, struct configuration *configuration, + struct result *result, int test_item, int no_builtin_file) +{ + int i; + int j; + int tx_num = cmcp_info->tx_num; + int rx_num = cmcp_info->rx_num; + int btn_num = cmcp_info->btn_num; + int tmp = 0; + uint32_t fw_revision_control; + uint32_t fw_config_ver; + char device_id[20] = {0}; + struct pt_device_access_data *dad + = pt_get_device_access_data(dev); + + fw_revision_control = dad->si->ttdata.revctrl; + fw_config_ver = dad->si->ttdata.fw_ver_conf; + /*calculate silicon id*/ + result->device_id_low = 0; + result->device_id_high = 0; + + for (i = 0; i < 4; i++) + result->device_id_low = + (result->device_id_low << 8) + dad->si->ttdata.mfg_id[i]; + + for (i = 4; i < 8; i++) + result->device_id_high = + (result->device_id_high << 8) + dad->si->ttdata.mfg_id[i]; + + scnprintf(device_id, 20, "%x%x", + result->device_id_high, result->device_id_low); + + /*print test summary*/ + index = print_silicon_id(out_buf, &device_id[0], index); + if (result->test_summary) + index = prepare_print_string(out_buf, ",PASS,\n", index); + else + index = prepare_print_string(out_buf, ",FAIL,\n", index); + + /*revision ctrl number*/ + index = print_silicon_id(out_buf, &device_id[0], index); + index = prepare_print_string(out_buf, ",FW revision Control,", index); + index = prepare_print_data(out_buf, &fw_revision_control, index, 1); + index = prepare_print_string(out_buf, "\n", index); + + /*config version*/ + index = print_silicon_id(out_buf, &device_id[0], index); + index = prepare_print_string(out_buf, ",CONFIG_VER,", index); + index = prepare_print_data(out_buf, &fw_config_ver, index, 1); + index = prepare_print_string(out_buf, "\n", index); + + /* Shorts test */ + index = print_silicon_id(out_buf, &device_id[0], index); + if (result->short_test_pass) + index = prepare_print_string(out_buf, ",Shorts,PASS,\n", index); + else + index = prepare_print_string(out_buf, ",Shorts,FAIL,\n", index); + + if ((test_item & CM_ENABLED) == CM_ENABLED) { + /*print BUTNS_CM_DATA_ROW00*/ + if (((test_item & CM_BTN) == CM_BTN) && (btn_num > 0)) { + index = print_silicon_id(out_buf, &device_id[0], index); + index = prepare_print_string(out_buf, + ",Sensor Cm Validation,BUTNS_CM_DATA_ROW00,", + index); + index = prepare_print_data(out_buf, + &cmcp_info->cm_btn_data[0], + index, + btn_num); + index = prepare_print_string(out_buf, "\n", index); + } + + if ((test_item & CM_PANEL) == CM_PANEL) { + /*print CM_DATA_ROW*/ + for (i = 0; i < rx_num; i++) { + index = print_silicon_id(out_buf, &device_id[0], + index); + index = prepare_print_string(out_buf, + ",Sensor Cm Validation,CM_DATA_ROW", + index); + index = prepare_print_data(out_buf, &i, + index, 1); + for (j = 0; j < tx_num; j++) + index = prepare_print_data(out_buf, + &cmcp_info->cm_data_panel[j*rx_num+i], + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + } + + if (!no_builtin_file) { + /*print CM_MAX_GRADIENT_COLS_PERCENT*/ + index = print_silicon_id(out_buf, + &device_id[0], index); + index = prepare_print_string(out_buf, + ",Sensor Cm Validation,CM_MAX_GRADIENT_COLS_PERCENT,", + index); + for (i = 0; i < tx_num; i++) { + char tmp_buf[10] = {0}; + + scnprintf(tmp_buf, 10, "%d.%d,", + cmcp_info->gd_sensor_col[i].gradient_val / 10, + cmcp_info->gd_sensor_col[i].gradient_val % 10); + index = prepare_print_string(out_buf, + &tmp_buf[0], index); + } + index = prepare_print_string(out_buf, + "\n", index); + + /*print CM_MAX_GRADIENT_ROWS_PERCENT*/ + index = print_silicon_id(out_buf, + &device_id[0], index); + index = prepare_print_string(out_buf, + ",Sensor Cm Validation,CM_MAX_GRADIENT_ROWS_PERCENT,", + index); + for (i = 0; i < rx_num; i++) { + char tmp_buf[10] = {0}; + + scnprintf(tmp_buf, 10, "%d.%d,", + cmcp_info->gd_sensor_row[i].gradient_val / 10, + cmcp_info->gd_sensor_row[i].gradient_val % 10); + index = prepare_print_string(out_buf, + &tmp_buf[0], index); + } + index = prepare_print_string(out_buf, + "\n", index); + + if (!dad->cmcp_range_check) { + /*print CM_DELTA_COLUMN*/ + for (i = 0; i < rx_num; i++) { + index = print_silicon_id( + out_buf, + &device_id[0], index); + index = prepare_print_string( + out_buf, + ",Sensor Cm Validation,DELTA_COLUMNS_ROW", + index); + index = prepare_print_data( + out_buf, + &i, index, 1); + index = prepare_print_data( + out_buf, + &tmp, index, 1); + for (j = 1; j < tx_num; j++) + index = prepare_print_data( + out_buf, + &cmcp_info->cm_sensor_column_delta[(j-1)*rx_num+i], + index, 1); + index = prepare_print_string( + out_buf, + "\n", index); + } + + /*print CM_DELTA_ROW*/ + index = print_silicon_id(out_buf, + &device_id[0], + index); + index = prepare_print_string(out_buf, + ",Sensor Cm Validation,DELTA_ROWS_ROW", + index); + index = prepare_print_data(out_buf, + &tmp, index, 1); + for (j = 0; j < tx_num; j++) + index = prepare_print_data( + out_buf, + &tmp, index, 1); + index = prepare_print_string(out_buf, + "\n", index); + + for (i = 1; i < rx_num; i++) { + index = print_silicon_id( + out_buf, + &device_id[0], + index); + index = prepare_print_string( + out_buf, + ",Sensor Cm Validation,DELTA_ROWS_ROW", + index); + index = prepare_print_data( + out_buf, &i, + index, 1); + for (j = 0; j < tx_num; j++) + index = prepare_print_data( + out_buf, + &cmcp_info->cm_sensor_row_delta[j*rx_num+i-1], + index, 1); + index = prepare_print_string( + out_buf, + "\n", index); + } + + /*print pass/fail Sensor Cm Validation*/ + index = print_silicon_id(out_buf, &device_id[0], + index); + if (result->cm_test_pass) + index = prepare_print_string(out_buf, + ",Sensor Cm Validation,PASS,\n", + index); + else + index = prepare_print_string(out_buf, + ",Sensor Cm Validation,FAIL,\n", + index); + } + } + } + + if (!no_builtin_file) { + if (((test_item & CM_BTN) == CM_BTN) && (btn_num > 0) + && (!dad->cmcp_range_check)) { + char tmp_buf[10] = {0}; + /*print Button Element by Element */ + index = print_silicon_id(out_buf, &device_id[0], + index); + if (result->cm_button_validation_pass) + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Button Element by Element,PASS\n", + index); + else + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Button Element by Element,FAIL\n", + index); + + /* + *print Sensor Cm Validation + *- Buttons Range Buttons Range + */ + index = print_silicon_id(out_buf, + &device_id[0], index); + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Buttons Range,Buttons Range,", + index); + scnprintf(tmp_buf, 10, "%d.%d,", + cmcp_info->cm_delta_data_btn / 10, + cmcp_info->cm_delta_data_btn % 10); + index = prepare_print_string(out_buf, + &tmp_buf[0], index); + index = prepare_print_string(out_buf, + "\n", index); + + /*print Sensor Cm Validation + *-Buttons Range Cm_button_avg + */ + index = print_silicon_id(out_buf, + &device_id[0], index); + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Buttons Range,Cm_button_avg,", + index); + index = prepare_print_data(out_buf, + &cmcp_info->cm_ave_data_btn, + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + + /*print Sensor Cm Validation + * -Buttons Range Cm_button_avg + */ + index = print_silicon_id(out_buf, + &device_id[0], index); + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Buttons Range,Cm_button_cal,", + index); + index = prepare_print_data(out_buf, + &cmcp_info->cm_cal_data_btn, + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + + /*print Sensor Cm Validation + *-Buttons Range pass/fail + */ + index = print_silicon_id(out_buf, + &device_id[0], index); + if (result->cm_button_delta_pass) + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Buttons Range,PASS,LIMITS,", + index); + else + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Buttons Range,FAIL,LIMITS,", + index); + index = prepare_print_data(out_buf, + &configuration->cm_max_delta_button_percent, + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + } + + if ((test_item & CM_PANEL) == CM_PANEL && + !dad->cmcp_range_check) { + char tmp_buf[10] = {0}; + /*print Cm_sensor_cal */ + index = print_silicon_id(out_buf, + &device_id[0], index); + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Calibration,Cm_sensor_cal,", + index); + index = prepare_print_data(out_buf, + &cmcp_info->cm_cal_data_panel, + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + + /*print Cm_sensor_cal limit*/ + index = print_silicon_id(out_buf, + &device_id[0], index); + if (result->cm_sensor_calibration_pass) + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Calibration,PASS,LIMITS,", + index); + else + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Calibration,FAIL,LIMITS,", + index); + index = prepare_print_data(out_buf, + &configuration->cm_min_limit_cal, + index, 1); + index = prepare_print_data(out_buf, + &configuration->cm_max_limit_cal, + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + + /*print Columns Delta Matrix*/ + index = print_silicon_id(out_buf, + &device_id[0], index); + if (result->cm_sensor_col_delta_pass) + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Columns Delta Matrix,PASS,LIMITS,", + index); + else + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Columns Delta Matrix,FAIL,LIMITS,", + index); + index = prepare_print_data(out_buf, + &configuration->cm_range_limit_col, + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + + /*print Cm Validation - Element by Element*/ + index = print_silicon_id(out_buf, + &device_id[0], index); + if (result->cm_sensor_validation_pass) + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Element by Element,PASS,", + index); + else + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Element by Element,FAIL,", + index); + index = prepare_print_string(out_buf, + "\n", index); + + /*print Cm Validation -Gradient Cols*/ + index = print_silicon_id(out_buf, + &device_id[0], index); + if (result->cm_sensor_gd_col_pass) + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Gradient Cols,PASS,", + index); + else + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Gradient Cols,FAIL,", + index); + index = prepare_print_string(out_buf, + "\n", index); + + /*print Cm Validation -Gradient Rows*/ + index = print_silicon_id(out_buf, + &device_id[0], index); + if (result->cm_sensor_gd_row_pass) + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Gradient Rows,PASS,", + index); + else + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Gradient Rows,FAIL,", + index); + index = prepare_print_string(out_buf, + "\n", index); + + + /* + * Print Sensor Cm Validation + * -Rows Delta Matrix + */ + index = print_silicon_id(out_buf, + &device_id[0], index); + if (result->cm_sensor_row_delta_pass) + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Rows Delta Matrix,PASS,LIMITS,", + index); + else + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Rows Delta Matrix,FAIL,LIMITS,", + index); + index = prepare_print_data(out_buf, + &configuration->cm_range_limit_row, + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + + /*print Cm_sensor_avg */ + index = print_silicon_id(out_buf, + &device_id[0], index); + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Sensor Range,Cm_sensor_avg,", + index); + index = prepare_print_data(out_buf, + &cmcp_info->cm_ave_data_panel, + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + + /*printSensor Cm Validation - + * Sensor Range, Sensor Range + */ + index = print_silicon_id(out_buf, + &device_id[0], index); + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Sensor Range,Sensor Range,", + index); + scnprintf(tmp_buf, 10, "%d.%d,", + cmcp_info->cm_sensor_delta / 10, + cmcp_info->cm_sensor_delta % 10); + index = prepare_print_string(out_buf, + &tmp_buf[0], index); + index = prepare_print_string(out_buf, + "\n", index); + + /*print Sensor Cm Validation - Sensor Range*/ + index = print_silicon_id(out_buf, + &device_id[0], index); + if (result->cm_sensor_delta_pass) + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Sensor Range,PASS,LIMITS,", + index); + else + index = prepare_print_string(out_buf, + ",Sensor Cm Validation - Sensor Range,FAIL,LIMITS,", + index); + index = prepare_print_data(out_buf, + &configuration->cm_max_delta_sensor_percent, + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + } + } + } + + if ((test_item & CP_ENABLED) == CP_ENABLED) { + if (((test_item & CP_BTN) == CP_BTN) && (btn_num > 0)) { + /*print BUTNS_CP_DATA_ROW00 */ + index = print_silicon_id(out_buf, &device_id[0], index); + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,BUTNS_CP_DATA_ROW00,", + index); + index = prepare_print_data(out_buf, + &cmcp_info->cp_btn_data[0], + index, btn_num); + index = prepare_print_string(out_buf, + "\n", index); + + if (!no_builtin_file && !dad->cmcp_range_check) { + /*print Cp Button Element by Element */ + index = print_silicon_id(out_buf, &device_id[0], + index); + if (result->cp_button_validation_pass) + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check - Button Element by Element,PASS\n", + index); + else + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check - Button Element by Element,FAIL\n", + index); + + /*print cp_button_ave */ + index = print_silicon_id(out_buf, + &device_id[0], index); + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,Cp_button_avg,", + index); + index = prepare_print_data(out_buf, + &cmcp_info->cp_button_ave, + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + + /*print Cp_button_cal */ + index = print_silicon_id(out_buf, + &device_id[0], index); + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,Cp_button_cal,", + index); + index = prepare_print_data(out_buf, + &cmcp_info->cp_btn_cal, + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + } + } + + if ((test_item & CP_PANEL) == CP_PANEL) { + /*print CP_DATA_RX */ + index = print_silicon_id(out_buf, &device_id[0], index); + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,CP_DATA_RX,", index); + index = prepare_print_data(out_buf, + &cmcp_info->cp_rx_data_panel[0], index, rx_num); + index = prepare_print_string(out_buf, "\n", index); + + /*print CP_DATA_TX */ + index = print_silicon_id(out_buf, &device_id[0], index); + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,CP_DATA_TX,", index); + index = prepare_print_data(out_buf, + &cmcp_info->cp_tx_data_panel[0], index, tx_num); + index = prepare_print_string(out_buf, "\n", index); + } + if (((test_item & CP_BTN) == CP_BTN) && (btn_num > 0) + && !dad->cmcp_range_check) { + if (!no_builtin_file) { + char tmp_buf[10] = {0}; + /*print Cp_delta_button */ + index = print_silicon_id(out_buf, &device_id[0], + index); + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,Cp_delta_button,", + index); + scnprintf(tmp_buf, 10, "%d.%d,", + cmcp_info->cp_button_delta / 10, + cmcp_info->cp_button_delta % 10); + index = prepare_print_string(out_buf, + &tmp_buf[0], index); + index = prepare_print_string(out_buf, "\n", + index); + } + } + if ((test_item & CP_PANEL) == CP_PANEL && + !dad->cmcp_range_check) { + if (!no_builtin_file) { + char tmp_buf[10] = {0}; + /*print Cp_delta_rx */ + index = print_silicon_id(out_buf, &device_id[0], + index); + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,Cp_delta_rx,", + index); + scnprintf(tmp_buf, 10, "%d.%d,", + cmcp_info->cp_sensor_rx_delta / 10, + cmcp_info->cp_sensor_rx_delta % 10); + index = prepare_print_string(out_buf, + &tmp_buf[0], index); + index = prepare_print_string(out_buf, "\n", + index); + + /*print Cp_delta_tx */ + index = print_silicon_id(out_buf, &device_id[0], + index); + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,Cp_delta_tx,", + index); + scnprintf(tmp_buf, 10, "%d.%d,", + cmcp_info->cp_sensor_tx_delta / 10, + cmcp_info->cp_sensor_tx_delta % 10); + index = prepare_print_string(out_buf, + &tmp_buf[0], index); + index = prepare_print_string(out_buf, "\n", + index); + + /*print Cp_sensor_avg_rx */ + index = print_silicon_id(out_buf, &device_id[0], + index); + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,Cp_sensor_avg_rx,", + index); + index = prepare_print_data(out_buf, + &cmcp_info->cp_rx_ave_data_panel, + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + + /*print Cp_sensor_avg_tx */ + index = print_silicon_id(out_buf, + &device_id[0], index); + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,Cp_sensor_avg_tx,", + index); + index = prepare_print_data(out_buf, + &cmcp_info->cp_tx_ave_data_panel, + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + + /*print Cp_sensor_cal_rx */ + index = print_silicon_id(out_buf, + &device_id[0], index); + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,Cp_sensor_cal_rx,", + index); + index = prepare_print_data(out_buf, + &cmcp_info->cp_rx_cal_data_panel[0], + index, rx_num); + index = prepare_print_string(out_buf, + "\n", index); + + /*print Cp_sensor_cal_tx */ + index = print_silicon_id(out_buf, + &device_id[0], index); + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,Cp_sensor_cal_tx,", + index); + index = prepare_print_data(out_buf, + &cmcp_info->cp_tx_cal_data_panel[0], + index, tx_num); + index = prepare_print_string(out_buf, + "\n", index); + } + } + + if (!no_builtin_file && !dad->cmcp_range_check) { + /*print cp test limits */ + index = print_silicon_id(out_buf, &device_id[0], index); + if (result->cp_test_pass) + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,PASS, LIMITS,", + index); + else + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,FAIL, LIMITS,", + index); + + index = prepare_print_string(out_buf, + "CP_MAX_DELTA_SENSOR_RX_PERCENT,", index); + index = prepare_print_data(out_buf, + &configuration->cp_max_delta_sensor_rx_percent, + index, 1); + index = prepare_print_string(out_buf, + "CP_MAX_DELTA_SENSOR_TX_PERCENT,", index); + index = prepare_print_data(out_buf, + &configuration->cp_max_delta_sensor_tx_percent, + index, 1); + index = prepare_print_string(out_buf, + "CP_MAX_DELTA_BUTTON_PERCENT,", index); + index = prepare_print_data(out_buf, + &configuration->cp_max_delta_button_percent, + index, 1); + index = prepare_print_string(out_buf, "\n", index); + } + } + + if (!no_builtin_file) { + if ((test_item & CM_ENABLED) == CM_ENABLED) { + if ((test_item & CM_PANEL) == CM_PANEL) { + /*print columns gradient limit*/ + index = prepare_print_string(out_buf, + ",Sensor Cm Validation,MAX_LIMITS,CM_MAX_GRADIENT_COLS_PERCENT,", + index); + index = prepare_print_data(out_buf, + &configuration->cm_max_table_gradient_cols_percent[0], + index, + configuration->cm_max_table_gradient_cols_percent_size); + index = prepare_print_string(out_buf, + "\n", index); + /*print rows gradient limit*/ + index = prepare_print_string(out_buf, + ",Sensor Cm Validation,MAX_LIMITS,CM_MAX_GRADIENT_ROWS_PERCENT,", + index); + index = prepare_print_data(out_buf, + &configuration->cm_max_table_gradient_rows_percent[0], + index, + configuration->cm_max_table_gradient_rows_percent_size); + index = prepare_print_string(out_buf, + "\n", index); + + /*print cm max limit*/ + for (i = 0; i < rx_num; i++) { + index = prepare_print_string(out_buf, + ",Sensor Cm Validation,MAX_LIMITS,CM_DATA_ROW", + index); + index = prepare_print_data(out_buf, + &i, index, 1); + for (j = 0; j < tx_num; j++) + index = prepare_print_data( + out_buf, + &configuration->cm_min_max_table_sensor[i*tx_num*2+j*2+1], + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + } + } + + if (((test_item & CM_BTN) == CM_BTN) && (btn_num > 0)) { + index = prepare_print_string(out_buf, + ",Sensor Cm Validation,MAX LIMITS,M_BUTNS,", + index); + for (j = 0; j < btn_num; j++) { + index = prepare_print_data(out_buf, + &configuration->cm_min_max_table_btn[2*j+1], + index, 1); + } + index = prepare_print_string(out_buf, + "\n", index); + } + + index = prepare_print_string(out_buf, + ",Sensor Cm Validation MAX LIMITS\n", index); + + if ((test_item & CM_PANEL) == CM_PANEL) { + /*print cm min limit*/ + for (i = 0; i < rx_num; i++) { + index = prepare_print_string(out_buf, + ",Sensor Cm Validation,MIN_LIMITS,CM_DATA_ROW", + index); + index = prepare_print_data(out_buf, &i, + index, 1); + for (j = 0; j < tx_num; j++) + index = prepare_print_data( + out_buf, + &configuration->cm_min_max_table_sensor[i*tx_num*2 + j*2], + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + } + } + + if (((test_item & CM_BTN) == CM_BTN) && (btn_num > 0)) { + index = prepare_print_string(out_buf, + ",Sensor Cm Validation,MIN LIMITS,M_BUTNS,", + index); + for (j = 0; j < btn_num; j++) { + index = prepare_print_data(out_buf, + &configuration->cm_min_max_table_btn[2*j], + index, 1); + } + index = prepare_print_string(out_buf, + "\n", index); + } + index = prepare_print_string(out_buf, + ",Sensor Cm Validation MIN LIMITS\n", index); + } + + if ((test_item & CP_ENABLED) == CP_ENABLED) { + if ((test_item & CP_PANEL) == CP_PANEL) { + /*print cp tx max limit*/ + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,MAX_LIMITS,TX,", + index); + for (i = 0; i < tx_num; i++) + index = prepare_print_data(out_buf, + &configuration->cp_min_max_table_tx[i*2+1], + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + + /*print cp rx max limit*/ + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,MAX_LIMITS,RX,", + index); + for (i = 0; i < rx_num; i++) + index = prepare_print_data(out_buf, + &configuration->cp_min_max_table_rx[i*2+1], + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + } + + /*print cp btn max limit*/ + if (((test_item & CP_BTN) == CP_BTN) && (btn_num > 0)) { + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,MAX_LIMITS,S_BUTNS,", + index); + for (i = 0; i < btn_num; i++) + index = prepare_print_data(out_buf, + &configuration->cp_min_max_table_btn[i*2+1], + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + } + + if ((test_item & CP_PANEL) == CP_PANEL) { + /*print cp tx min limit*/ + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,MIN_LIMITS,TX,", + index); + for (i = 0; i < tx_num; i++) + index = prepare_print_data(out_buf, + &configuration->cp_min_max_table_tx[i*2], + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + + /*print cp rx min limit*/ + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,MIN_LIMITS,RX,", + index); + for (i = 0; i < rx_num; i++) + index = prepare_print_data(out_buf, + &configuration->cp_min_max_table_rx[i*2], + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + } + + /*print cp btn min limit*/ + if (((test_item & CP_BTN) == CP_BTN) && (btn_num > 0)) { + index = prepare_print_string(out_buf, + ",Self-cap Calibration Check,MIN_LIMITS,S_BUTNS,", + index); + for (i = 0; i < btn_num; i++) + index = prepare_print_data(out_buf, + &configuration->cp_min_max_table_btn[i*2], + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + } + } + } + return index; +} + +/******************************************************************************* + * FUNCTION: result_save + * + * SUMMARY: Malloc memory for output buffer and populate with the cmcp test + * header and results in the csv file format. + * + * NOTE: It supports simple_read_from_buffer() to read data multiple times to + * the buffer. + * + * RETURN: + * Size of data printed to "buf" + * + * PARAMETERS: + * *dev - pointer to device structure + * *buf - the user space buffer to read to + * *configuration - pointer to configuration structure + * *result - pointer to result structure + * *cmcp_info - pointer to cmcp_data structure + * *ppos - the current position in the buffer + * count - the maximum number of bytes to read + * test_item - test control in bitwise + * no_builtin_file - flag to determine if builtin-file exist + ******************************************************************************/ +int result_save(struct device *dev, char *buf, + struct configuration *configuration, struct result *result, + struct cmcp_data *cmcp_info, loff_t *ppos, size_t count, int test_item, + int no_builtin_file) +{ + u8 *out_buf = NULL; + int index = 0; + int byte_left; + + out_buf = kzalloc(MAX_BUF_LEN, GFP_KERNEL); + if (configuration == NULL) + pt_debug(dev, DL_WARN, "config is NULL"); + if (result == NULL) + pt_debug(dev, DL_WARN, "result is NULL"); + if (cmcp_info == NULL) + pt_debug(dev, DL_WARN, "cmcp_info is NULL"); + + index = save_header(out_buf, index, configuration, result); + index = save_engineering_data(dev, out_buf, index, + cmcp_info, configuration, result, + test_item, no_builtin_file); + byte_left = simple_read_from_buffer(buf, count, ppos, out_buf, index); + + kfree(out_buf); + return byte_left; +} + +/******************************************************************************* + * FUNCTION: cmcp_results_debugfs_open + * + * SUMMARY: Open method for cmcp_results debugfs node. + * + * RETURN: 0 = success + * + * PARAMETERS: + * *inode - file inode number + * *filp - file pointer to debugfs file + ******************************************************************************/ +static int cmcp_results_debugfs_open(struct inode *inode, + struct file *filp) +{ + filp->private_data = inode->i_private; + return 0; +} + +/******************************************************************************* + * FUNCTION: cmcp_results_debugfs_close + * + * SUMMARY: Close method for cmcp_results debugfs node. + * + * RETURN: 0 = success + * + * PARAMETERS: + * *inode - file inode number + * *filp - file pointer to debugfs file + ******************************************************************************/ +static int cmcp_results_debugfs_close(struct inode *inode, + struct file *filp) +{ + filp->private_data = NULL; + return 0; +} + +/******************************************************************************* + * FUNCTION: cmcp_results_debugfs_read + * + * SUMMARY: Read method for cmcp_results debugfs node. This function prints + * cmcp test results to user buffer. + * + * RETURN: Size of debugfs data print + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t cmcp_results_debugfs_read(struct file *filp, + char __user *buf, size_t count, loff_t *ppos) +{ + struct pt_device_access_data *dad = filp->private_data; + struct device *dev; + struct cmcp_data *cmcp_info = dad->cmcp_info; + struct result *result = dad->result; + struct configuration *configuration = dad->configs; + int ret = 0; + int test_item; + int no_builtin_file = 0; + int test_executed = 0; + + dev = dad->dev; + + mutex_lock(&dad->sysfs_lock); + test_executed = dad->test_executed; + test_item = pt_cmcp_get_test_item(dad->cmcp_test_items); + if (dad->builtin_cmcp_threshold_status < 0) { + pt_debug(dev, DL_WARN, + "%s: No cmcp threshold file.\n", __func__); + no_builtin_file = 1; + } + mutex_unlock(&dad->sysfs_lock); + + if (test_executed) + /*save result to buf*/ + ret = result_save(dev, buf, configuration, result, cmcp_info, + ppos, count, test_item, no_builtin_file); + else { + char warning_info[] = + "No test result available!\n"; + pt_debug(dev, DL_ERROR, + "%s: No test result available!\n", __func__); + + return simple_read_from_buffer(buf, count, ppos, warning_info, + strlen(warning_info)); + } + + return ret; +} + +static const struct file_operations cmcp_results_debugfs_fops = { + .open = cmcp_results_debugfs_open, + .release = cmcp_results_debugfs_close, + .read = cmcp_results_debugfs_read, + .write = NULL, +}; + +/******************************************************************************* + * FUNCTION: cmcp_return_offset_of_new_case + * + * SUMMARY: Returns the buffer offset of new test case + * + * NOTE: There are two static variable inside this function. + * + * RETURN: offset index for new case + * + * PARAMETERS: + * *bufPtr - pointer to input buffer + * first_time - flag to initialize some static variable + * (0:init; 1:don't init) + * *pFileEnd - pointer to the end of file for safe check + ******************************************************************************/ +u32 cmcp_return_offset_of_new_case(const char *bufPtr, u32 first_time, + const char *pFileEnd) +{ + static u32 offset, first_search; + + if (first_time == 0) { + first_search = 0; + offset = 0; + } + + if (first_search != 0) { + /* Search one case */ + for (;;) { + /* Search ASCII_LF */ + while (bufPtr < pFileEnd) { + if (*bufPtr++ != ASCII_LF) + offset++; + else + break; + } + if (bufPtr >= pFileEnd) + break; + offset++; + /* + * Single line: end loop + * Multiple lines: continue loop + */ + if (*bufPtr != ASCII_COMMA) + break; + } + } else + first_search = 1; + + return offset; +} + +/******************************************************************************* + * FUNCTION: cmcp_get_case_info_from_threshold_file + * + * SUMMARY: Gets test case information from cmcp threshold file + * + * RETURN: + * Number of test cases + * + * PARAMETERS: + * *dev - pointer to Device structure + * *buf - pointer to input file + * *search_array - pointer to test_case_search structure + * file_size - size of input file for safe check + ******************************************************************************/ +u32 cmcp_get_case_info_from_threshold_file(struct device *dev, const char *buf, + struct test_case_search *search_array, u32 file_size) +{ + u32 case_num = 0, buffer_offset = 0, name_count = 0, first_search = 0; + const char *pFileEnd = buf + file_size; + + pt_debug(dev, DL_INFO, "%s: Search cmcp threshold file\n", + __func__); + + /* Get all the test cases */ + for (case_num = 0; case_num < MAX_CASE_NUM; case_num++) { + buffer_offset = + cmcp_return_offset_of_new_case(&buf[buffer_offset], + first_search, pFileEnd); + first_search = 1; + + if (buf[buffer_offset] == 0) + break; + + for (name_count = 0; name_count < NAME_SIZE_MAX; name_count++) { + /* File end */ + if (buf[buffer_offset + name_count] == ASCII_COMMA) + break; + + search_array[case_num].name[name_count] = + buf[buffer_offset + name_count]; + } + + /* Exit when buffer offset is larger than file size */ + if (buffer_offset >= file_size) + break; + + search_array[case_num].name_size = name_count; + search_array[case_num].offset = buffer_offset; + /* + * pt_debug(dev, DL_INFO, "Find case %d: Name is %s; + * Name size is %d; Case offset is %d\n", + * case_num, + * search_array[case_num].name, + * search_array[case_num].name_size, + * search_array[case_num].offset); + */ + } + + return case_num; +} + +/******************************************************************************* + * FUNCTION: cmcp_compose_data + * + * SUMMARY: Composes one value based on data of each bit + * + * RETURN: + * Value that composed from buffer + * + * PARAMETERS: + * *buf - pointer to input file + * count - number of data elements in *buf in decimal + ******************************************************************************/ +int cmcp_compose_data(char *buf, u32 count) +{ + u32 base_array[] = {1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9}; + int value = 0; + u32 index = 0; + + for (index = 0; index < count; index++) + value += buf[index] * base_array[count - 1 - index]; + + return value; +} + +/******************************************************************************* + * FUNCTION: cmcp_return_one_value + * + * SUMMARY: Parses csv file at a given row and offset and combines multiple + * "bits' as a single value. Handles data over multiple lines and various + * end-of-line characters. + * + * NOTE: There is a static value to calculate line count inside this function. + * + * RETURN: + * Value that parsed from buffer + * + * PARAMETERS: + * *dev - pointer to devices structure + * *buf - pointer to input buffer + * *offset - offset index of input buffer + * *line_num - store line count + * pFileEnd - pointer to the end of threshold file + ******************************************************************************/ +int cmcp_return_one_value(struct device *dev, const char *buf, u32 *offset, + u32 *line_num, const char *pFileEnd) +{ + int value = -1; + char tmp_buffer[10]; + u32 count = 0; + u32 tmp_offset = *offset; + static u32 line_count = 1; + + /* Bypass extra commas */ + while (((buf + tmp_offset + 1) < pFileEnd) + && buf[tmp_offset] == ASCII_COMMA + && buf[tmp_offset + 1] == ASCII_COMMA) + tmp_offset++; + + if ((buf + tmp_offset + 1) >= pFileEnd) + goto exit; + + /* Windows and Linux difference at the end of one line */ + if (buf[tmp_offset] == ASCII_COMMA && buf[tmp_offset + 1] == ASCII_CR) { + if ((buf + tmp_offset + 2) < pFileEnd) { + if (buf[tmp_offset + 2] == ASCII_LF) + tmp_offset += 2; + } else + goto exit; + } else if (buf[tmp_offset] == ASCII_COMMA && + buf[tmp_offset + 1] == ASCII_LF) + tmp_offset += 1; + else if (buf[tmp_offset] == ASCII_COMMA + && buf[tmp_offset + 1] == ASCII_CR) + tmp_offset += 1; + + if ((buf + tmp_offset + 1) >= pFileEnd) + goto exit; + + /* New line for multiple lines */ + if ((buf[tmp_offset] == ASCII_LF || buf[tmp_offset] == ASCII_CR) && + buf[tmp_offset + 1] == ASCII_COMMA) { + tmp_offset++; + line_count++; + pt_debug(dev, DL_DEBUG, "%s: Line Count = %d\n", + __func__, line_count); + } + + /* Beginning */ + if (buf[tmp_offset] == ASCII_COMMA) { + tmp_offset++; + for (;;) { + if ((buf + tmp_offset) >= pFileEnd) + break; + + if ((buf[tmp_offset] >= ASCII_ZERO) + && (buf[tmp_offset] <= ASCII_NINE)) { + tmp_buffer[count++] = + buf[tmp_offset] - ASCII_ZERO; + tmp_offset++; + } else { + if (count != 0) { + value = cmcp_compose_data(tmp_buffer, + count); + /*pt_debug(dev, DL_DEBUG, */ + /* ",%d", value);*/ + } else { + /* 0 indicates no data available */ + value = -1; + } + break; + } + } + } else { + /* Multiple line: line count */ + *line_num = line_count; + /* Reset for next case */ + line_count = 1; + } + +exit: + *offset = tmp_offset; + + return value; +} + +/******************************************************************************* + * FUNCTION: cmcp_get_one_string + * + * SUMMARY: Parses csv file at a given row and offset and find out one string + * which must start with a comma and should end with a comma. The character + * LF/CR will also be taken as the symbol to end the search. + * + * NOTE: There is a static value to calculate line count inside this function. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to devices structure + * *input_buf - pointer to input buffer + * *offset - offset index of input buffer + * *out_buf - pointer to store string + * out_buf_len - size of out_buf + * pFileEnd - pointer to the end of threshold file + ******************************************************************************/ +static int cmcp_get_one_string(struct device *dev, const char *input_buf, + u32 *offset, char *out_buf, u32 out_buf_len, + const char *pFileEnd) +{ + u8 token = ASCII_COMMA; + u32 i; + u32 temp_offset = *offset + 1; /* skip first comma */ + u32 pre_index; /* to store the start index of string */ + u32 next_index; /* to store the index of comma after string */ + u32 total_len = pFileEnd - input_buf; /* valid range of index */ + + memset(out_buf, 0, out_buf_len); + + pre_index = next_index = temp_offset; + for (i = temp_offset; i < total_len; i++) { + if (input_buf[i] == token || input_buf[i] == ASCII_LF || + input_buf[i] == ASCII_CR) { + next_index = i; + /* + * next_index could be equal to pre_index if string is + * null. The max size of string is restricted by macro + * NAME_PROJECT_INFO_MAX. + */ + if (pre_index < next_index) + memcpy(out_buf, input_buf + pre_index, + MIN(next_index - pre_index, + NAME_PROJECT_INFO_MAX)); + *offset = next_index; + return 0; + } + + if (input_buf[i] == ASCII_LF || input_buf[i] == ASCII_CR) + break; + } + + return -ENODATA; +} + +/******************************************************************************* + * FUNCTION: cmcp_get_configuration_info + * + * SUMMARY: Gets cmcp configuration information. + * + * PARAMETERS: + * *dev - pointer to devices structure + * *buf - pointer to input buffer + * *search_array - pointer to test_case_search structure + * case_count - number of test cases + * *field_array - pointer to test_case_field structure + * *config - pointer to configuration structure + * file_size - file size of threshold file + ******************************************************************************/ +void cmcp_get_configuration_info(struct device *dev, + const char *buf, struct test_case_search *search_array, + u32 case_count, struct test_case_field *field_array, + struct configuration *config, u32 file_size) +{ + u32 count = 0, sub_count = 0; + u32 exist_or_not = 0; + u32 value_offset = 0; + int retval = 0; + u32 data_num = 0; + u32 line_num = 1; + const char *pFileEnd = buf + file_size; + + pt_debug(dev, DL_INFO, + "%s: Fill configuration struct per cmcp threshold file\n", + __func__); + + /* Search cases */ + for (count = 0; count < MAX_CASE_NUM; count++) { + exist_or_not = 0; + for (sub_count = 0; sub_count < case_count; sub_count++) { + if (!strncmp(field_array[count].name, + search_array[sub_count].name, + field_array[count].name_size)) { + exist_or_not = 1; + break; + } + } + + field_array[count].exist_or_not = exist_or_not; + + pt_debug(dev, DL_DEBUG, + "%s: Field Array[%d] exists: %d, type: %d\n", + __func__, count, exist_or_not, field_array[count].type); + + /* Clear data number */ + data_num = 0; + + if (exist_or_not == 1) { + switch (field_array[count].type) { + case TEST_CASE_TYPE_NO: + field_array[count].data_num = 0; + field_array[count].line_num = 1; + break; + case TEST_CASE_TYPE_ONE: + value_offset = search_array[sub_count].offset + + search_array[sub_count].name_size; + *field_array[count].bufptr = + cmcp_return_one_value(dev, buf, + &value_offset, 0, pFileEnd); + field_array[count].data_num = 1; + field_array[count].line_num = 1; + break; + case TEST_CASE_TYPE_STRING: + value_offset = search_array[sub_count].offset + + search_array[sub_count].name_size; + cmcp_get_one_string( + dev, buf, &value_offset, + (char *)field_array[count].bufptr, + NAME_PROJECT_INFO_MAX, pFileEnd); + field_array[count].data_num = 1; + field_array[count].line_num = 1; + break; + case TEST_CASE_TYPE_MUL: + case TEST_CASE_TYPE_MUL_LINES: + line_num = 1; + value_offset = search_array[sub_count].offset + + search_array[sub_count].name_size; + for (;;) { + retval = cmcp_return_one_value( + dev, buf, &value_offset, &line_num, + pFileEnd); + if (retval >= 0) { + *field_array[count].bufptr++ = + retval; + data_num++; + } else + break; + } + + field_array[count].data_num = data_num; + field_array[count].line_num = line_num; + break; + default: + break; + } + pt_debug(dev, DL_DEBUG, + "%s: %s: Data count is %d, line number is %d\n", + __func__, + field_array[count].name, + field_array[count].data_num, + field_array[count].line_num); + } else + pt_debug(dev, DL_ERROR, "%s: !!! %s doesn't exist\n", + __func__, field_array[count].name); + } +} + +/******************************************************************************* + * FUNCTION: cmcp_get_basic_info + * + * SUMMARY: Gets basic information for cmcp test, such as available test item, + * number of tx, rx, button. + * + * PARAMETERS: + * *dev - pointer to devices structure + * *field_array - pointer to test_case_field structure + * *config - pointer to configuration structure + ******************************************************************************/ +void cmcp_get_basic_info(struct device *dev, + struct test_case_field *field_array, struct configuration *config) +{ + u32 tx_num = 0; + u32 index = 0; + + config->is_valid_or_not = 1; /* Set to valid by default */ + config->cm_enabled = 0; + config->cp_enabled = 0; + + if (field_array[CM_TEST_INPUTS].exist_or_not) + config->cm_enabled = 1; + if (field_array[CP_TEST_INPUTS].exist_or_not) + config->cp_enabled = 1; + + /* Get basic information only when CM and CP are enabled */ + if (config->cm_enabled && config->cp_enabled) { + pt_debug(dev, DL_INFO, + "%s: Find CM and CP thresholds\n", __func__); + + config->rx_num = + field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].line_num; + tx_num = + (field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].data_num >> 1) + /field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].line_num; + config->tx_num = tx_num; + + config->btn_num = + field_array[PER_ELEMENT_MIN_MAX_TABLE_BUTTON].data_num >> 1; + + config->cm_min_max_table_btn_size = + field_array[PER_ELEMENT_MIN_MAX_TABLE_BUTTON].data_num; + config->cm_min_max_table_sensor_size = + field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].data_num; + config->cp_min_max_table_rx_size = + field_array[PER_ELEMENT_MIN_MAX_RX].data_num; + config->cp_min_max_table_tx_size = + field_array[PER_ELEMENT_MIN_MAX_TX].data_num; + config->cm_max_table_gradient_cols_percent_size = + field_array[CM_GRADIENT_CHECK_COL].data_num; + config->cm_max_table_gradient_rows_percent_size = + field_array[CM_GRADIENT_CHECK_ROW].data_num; + config->cp_min_max_table_btn_size = + field_array[CP_PER_ELEMENT_MIN_MAX_BUTTON].data_num; + + /* *** Detailed Debug Information *** */ + pt_debug(dev, DL_DEBUG, "%d\n", + config->cm_excluding_col_edge); + pt_debug(dev, DL_DEBUG, "%d\n", + config->cm_excluding_row_edge); + for (index = 0; + index < config->cm_max_table_gradient_cols_percent_size; + index++) + pt_debug(dev, DL_DEBUG, "%d\n", + config->cm_max_table_gradient_cols_percent[index]); + for (index = 0; + index < config->cm_max_table_gradient_rows_percent_size; + index++) + pt_debug(dev, DL_DEBUG, "%d\n", + config->cm_max_table_gradient_rows_percent[index]); + pt_debug(dev, DL_DEBUG, "%d\n", + config->cm_range_limit_row); + pt_debug(dev, DL_DEBUG, "%d\n", + config->cm_range_limit_col); + pt_debug(dev, DL_DEBUG, "%d\n", + config->cm_min_limit_cal); + pt_debug(dev, DL_DEBUG, "%d\n", + config->cm_max_limit_cal); + pt_debug(dev, DL_DEBUG, "%d\n", + config->cm_max_delta_sensor_percent); + pt_debug(dev, DL_DEBUG, "%d\n", + config->cm_max_delta_button_percent); + for (index = 0; + index < config->cm_min_max_table_btn_size; index++) + pt_debug(dev, DL_DEBUG, "%d\n", + config->cm_min_max_table_btn[index]); + for (index = 0; + index < config->cm_min_max_table_sensor_size; index++) + pt_debug(dev, DL_DEBUG, "%d\n", + config->cm_min_max_table_sensor[index]); + pt_debug(dev, DL_DEBUG, "%d\n", + config->cp_max_delta_sensor_rx_percent); + pt_debug(dev, DL_DEBUG, "%d\n", + config->cp_max_delta_sensor_tx_percent); + pt_debug(dev, DL_DEBUG, "%d\n", + config->cp_max_delta_button_percent); + pt_debug(dev, DL_DEBUG, "%d\n", + config->min_button); + pt_debug(dev, DL_DEBUG, "%d\n", + config->max_button); + + for (index = 0; + index < config->cp_min_max_table_btn_size; index++) + pt_debug(dev, DL_DEBUG, "%d\n", + config->cp_min_max_table_btn[index]); + for (index = 0; + index < config->cp_min_max_table_rx_size; index++) + pt_debug(dev, DL_DEBUG, "%d\n", + config->cp_min_max_table_rx[index]); + for (index = 0; + index < config->cp_min_max_table_tx_size; index++) + pt_debug(dev, DL_DEBUG, "%d\n", + config->cp_min_max_table_tx[index]); + /* *** End of Detailed Debug Information *** */ + + /* Invalid mutual data length */ + if ((field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].data_num >> + 1) % field_array[PER_ELEMENT_MIN_MAX_TABLE_SENSOR].line_num) { + config->is_valid_or_not = 0; + pt_debug(dev, DL_ERROR, "Invalid mutual data length\n"); + } + } else { + if (!config->cm_enabled) + pt_debug(dev, DL_ERROR, + "%s: Miss CM thresholds or CM data format is wrong!\n", + __func__); + + if (!config->cp_enabled) + pt_debug(dev, DL_ERROR, + "%s: Miss CP thresholds or CP data format is wrong!\n", + __func__); + + config->rx_num = 0; + config->tx_num = 0; + config->btn_num = 0; + config->is_valid_or_not = 0; + } + + pt_debug(dev, DL_DEBUG, + "%s:\n" + "Input file is %s!\n" + "CM test: %s\n" + "CP test: %s\n" + "rx_num is %d\n" + "tx_num is %d\n" + "btn_num is %d\n", + __func__, + config->is_valid_or_not == 1 ? "VALID" : "!!! INVALID !!!", + config->cm_enabled == 1 ? "Found" : "Not found", + config->cp_enabled == 1 ? "Found" : "Not found", + config->rx_num, + config->tx_num, + config->btn_num); +} + +/******************************************************************************* + * FUNCTION: cmcp_test_case_field_init + * + * SUMMARY: Initialize the structure test_field_array. + * + * PARAMETERS: + * *test_field_array - pointer to test_case_field structure + * *configuration - pointer to configuration structure + ******************************************************************************/ +void cmcp_test_case_field_init(struct test_case_field *test_field_array, + struct configuration *configs) +{ + struct test_case_field test_case_field_array[MAX_CASE_NUM] = { + {"PROJ_VERSION", 12, TEST_CASE_TYPE_STRING, + (void *)&configs->proj_info, 0, 0, 0}, + {"CM TEST INPUTS", 14, TEST_CASE_TYPE_NO, + NULL, 0, 0, 0}, + {"CM_EXCLUDING_COL_EDGE", 21, TEST_CASE_TYPE_ONE, + &configs->cm_excluding_col_edge, 0, 0, 0}, + {"CM_EXCLUDING_ROW_EDGE", 21, TEST_CASE_TYPE_ONE, + &configs->cm_excluding_row_edge, 0, 0, 0}, + {"CM_GRADIENT_CHECK_COL", 21, TEST_CASE_TYPE_MUL, + &configs->cm_max_table_gradient_cols_percent[0], + 0, 0, 0}, + {"CM_GRADIENT_CHECK_ROW", 21, TEST_CASE_TYPE_MUL, + &configs->cm_max_table_gradient_rows_percent[0], + 0, 0, 0}, + {"CM_RANGE_LIMIT_ROW", 18, TEST_CASE_TYPE_ONE, + &configs->cm_range_limit_row, 0, 0, 0}, + {"CM_RANGE_LIMIT_COL", 18, TEST_CASE_TYPE_ONE, + &configs->cm_range_limit_col, 0, 0, 0}, + {"CM_MIN_LIMIT_CAL", 16, TEST_CASE_TYPE_ONE, + &configs->cm_min_limit_cal, 0, 0, 0}, + {"CM_MAX_LIMIT_CAL", 16, TEST_CASE_TYPE_ONE, + &configs->cm_max_limit_cal, 0, 0, 0}, + {"CM_MAX_DELTA_SENSOR_PERCENT", 27, TEST_CASE_TYPE_ONE, + &configs->cm_max_delta_sensor_percent, 0, 0, 0}, + {"CM_MAX_DELTA_BUTTON_PERCENT", 27, TEST_CASE_TYPE_ONE, + &configs->cm_max_delta_button_percent, 0, 0, 0}, + {"PER_ELEMENT_MIN_MAX_TABLE_BUTTON", 32, TEST_CASE_TYPE_MUL, + &configs->cm_min_max_table_btn[0], 0, 0, 0}, + {"PER_ELEMENT_MIN_MAX_TABLE_SENSOR", 32, + TEST_CASE_TYPE_MUL_LINES, + &configs->cm_min_max_table_sensor[0], 0, 0, 0}, + {"CP TEST INPUTS", 14, TEST_CASE_TYPE_NO, + NULL, 0, 0, 0}, + {"CP_PER_ELEMENT_MIN_MAX_BUTTON", 29, TEST_CASE_TYPE_MUL, + &configs->cp_min_max_table_btn[0], 0, 0, 0}, + {"CP_MAX_DELTA_SENSOR_RX_PERCENT", 30, TEST_CASE_TYPE_ONE, + &configs->cp_max_delta_sensor_rx_percent, + 0, 0, 0}, + {"CP_MAX_DELTA_SENSOR_TX_PERCENT", 30, TEST_CASE_TYPE_ONE, + &configs->cp_max_delta_sensor_tx_percent, + 0, 0, 0}, + {"CP_MAX_DELTA_BUTTON_PERCENT", 27, TEST_CASE_TYPE_ONE, + &configs->cp_max_delta_button_percent, 0, 0, 0}, + {"MIN_BUTTON", 10, TEST_CASE_TYPE_ONE, + &configs->min_button, 0, 0, 0}, + {"MAX_BUTTON", 10, TEST_CASE_TYPE_ONE, + &configs->max_button, 0, 0, 0}, + {"PER_ELEMENT_MIN_MAX_RX", 22, TEST_CASE_TYPE_MUL, + &configs->cp_min_max_table_rx[0], 0, 0, 0}, + {"PER_ELEMENT_MIN_MAX_TX", 22, TEST_CASE_TYPE_MUL, + &configs->cp_min_max_table_tx[0], 0, 0, 0}, + }; + + memcpy(test_field_array, test_case_field_array, + sizeof(struct test_case_field) * MAX_CASE_NUM); +} + +/******************************************************************************* + * FUNCTION: pt_parse_cmcp_threshold_file_common + * + * SUMMARY: Parses cmcp threshold file and stores to the data structure. + * + * PARAMETERS: + * *dev - pointer to devices structure + * *buf - pointer to input buffer + * file_size - file size + ******************************************************************************/ +static ssize_t pt_parse_cmcp_threshold_file_common( + struct device *dev, const char *buf, u32 file_size) +{ + struct pt_device_access_data *dad + = pt_get_device_access_data(dev); + ssize_t rc = 0; + u32 case_count = 0; + + pt_debug(dev, DL_INFO, + "%s: Start parsing cmcp threshold file. File size is %d\n", + __func__, file_size); + + cmcp_test_case_field_init(dad->test_field_array, dad->configs); + + /* Get all the cases from .csv file */ + case_count = cmcp_get_case_info_from_threshold_file(dev, + buf, dad->test_search_array, file_size); + + pt_debug(dev, DL_INFO, + "%s: Number of cases found in CSV file: %d\n", + __func__, case_count); + + /* Search cases */ + cmcp_get_configuration_info(dev, + buf, + dad->test_search_array, case_count, dad->test_field_array, + dad->configs, file_size); + + /* Get basic information */ + cmcp_get_basic_info(dev, dad->test_field_array, dad->configs); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_cmcp_threshold_loading_store + * + * SUMMARY: The store method for the cmcp_threshold_loading sysfs node. The + * passed in value controls if threshold loading is performed. + * + * RETURN: Size of passed in buffer is success + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_cmcp_threshold_loading_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_device_access_data *dad = pt_get_device_access_data(dev); + ssize_t length; + u32 input_data[3]; + int rc = 0; + + length = cmd->parse_sysfs_input(dev, buf, size, input_data, + ARRAY_SIZE(input_data)); + + if (length != 1) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto exit; + } + + mutex_lock(&dad->cmcp_threshold_lock); + + if (input_data[0] == 1) + dad->cmcp_threshold_loading = true; + else if (input_data[0] == -1) + dad->cmcp_threshold_loading = false; + else if (input_data[0] == 0 && dad->cmcp_threshold_loading) { + dad->cmcp_threshold_loading = false; + + if (dad->cmcp_threshold_size == 0) { + pt_debug(dev, DL_ERROR, "%s: No cmcp threshold data\n", + __func__); + goto exit_free; + } + + /* Clear test executed flag */ + dad->test_executed = 0; + + pt_parse_cmcp_threshold_file_common(dev, + &dad->cmcp_threshold_data[0], dad->cmcp_threshold_size); + + /* Mark valid */ + dad->builtin_cmcp_threshold_status = 0; + /* Restore test item to default value when new file input */ + dad->cmcp_test_items = 0; + } else { + pt_debug(dev, DL_WARN, "%s: Invalid value\n", __func__); + rc = -EINVAL; + mutex_unlock(&dad->cmcp_threshold_lock); + goto exit; + } + +exit_free: + kfree(dad->cmcp_threshold_data); + dad->cmcp_threshold_data = NULL; + dad->cmcp_threshold_size = 0; + mutex_unlock(&dad->cmcp_threshold_lock); + +exit: + if (rc) + return rc; + return size; +} + +static DEVICE_ATTR(cmcp_threshold_loading, 0200, + NULL, pt_cmcp_threshold_loading_store); + +/******************************************************************************* + * FUNCTION: pt_cmcp_threshold_data_write + * + * SUMMARY: The write method for the cmcp_threshold_data_sysfs node. The passed + * in data (threshold file) is written to the threshold buffer. + * + * RETURN: Size of passed in buffer is success + * + * PARAMETERS: + * *filp - pointer to file structure + * *kobj - pointer to kobject structure + * *bin_attr - pointer to bin_attribute structure + * buf - pointer to cmd input buffer + * offset - offset index to store input buffer + * count - size of data in buffer + ******************************************************************************/ +static ssize_t pt_cmcp_threshold_data_write(struct file *filp, + struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t offset, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pt_device_access_data *dad + = pt_get_device_access_data(dev); + u8 *p; + + pt_debug(dev, DL_INFO, "%s: offset:%lld count:%zu\n", + __func__, offset, count); + + mutex_lock(&dad->cmcp_threshold_lock); + + if (!dad->cmcp_threshold_loading) { + mutex_unlock(&dad->cmcp_threshold_lock); + return -ENODEV; + } + + p = krealloc(dad->cmcp_threshold_data, offset + count, GFP_KERNEL); + if (!p) { + kfree(dad->cmcp_threshold_data); + dad->cmcp_threshold_data = NULL; + mutex_unlock(&dad->cmcp_threshold_lock); + return -ENOMEM; + } + dad->cmcp_threshold_data = p; + + memcpy(&dad->cmcp_threshold_data[offset], buf, count); + dad->cmcp_threshold_size += count; + + mutex_unlock(&dad->cmcp_threshold_lock); + + return count; +} + +static struct bin_attribute bin_attr_cmcp_threshold_data = { + .attr = { + .name = "cmcp_threshold_data", + .mode = 0200, + }, + .size = 0, + .write = pt_cmcp_threshold_data_write, +}; + + +/******************************************************************************* + * FUNCTION: pt_suspend_scan_cmd_ + * + * SUMMARY: Non-protected wrapper function for suspend scan command + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to devices structure + ******************************************************************************/ +static int pt_suspend_scan_cmd_(struct device *dev) +{ + int rc; + + rc = cmd->nonhid_cmd->suspend_scanning(dev, 0); + if (rc) + pt_debug(dev, DL_ERROR, "%s: Suspend scan failed rc = %d\n", + __func__, rc); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_resume_scan_cmd_ + * + * SUMMARY: Non-protected wrapper function for resume scan command + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to devices structure + ******************************************************************************/ +static int pt_resume_scan_cmd_(struct device *dev) +{ + int rc; + + rc = cmd->nonhid_cmd->resume_scanning(dev, 0); + if (rc) + pt_debug(dev, DL_ERROR, "%s: Resume scan failed rc = %d\n", + __func__, rc); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_exec_scan_cmd_ + * + * SUMMARY: Non-protected wrapper function for execute scan command + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to devices structure + * scan_type - type of panel scan to perform (PIP2 only) + ******************************************************************************/ +static int pt_exec_scan_cmd_(struct device *dev, u8 scan_type) +{ + int rc; + + rc = cmd->nonhid_cmd->exec_panel_scan(dev, PT_CORE_CMD_UNPROTECTED, + scan_type); + if (rc) + pt_debug(dev, DL_ERROR, + "%s: Heatmap start scan failed rc = %d\n", + __func__, rc); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_ret_scan_data_cmd_ + * + * SUMMARY: Non-protected wrapper function for retrieve panel data command + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * read_offset - read pointer offset + * read_count - length of data to read + * data_id - enumerated test ID to read selftest results from + * *response - pointer to store the read response status + * *config - pointer to store config data + * *actual_read_len - pointer to store data length actually read + * *return_buf - pointer to the read buffer + ******************************************************************************/ +static int pt_ret_scan_data_cmd_(struct device *dev, u16 read_offset, + u16 read_count, u8 data_id, u8 *response, u8 *config, + u16 *actual_read_len, u8 *return_buf) +{ + int rc; + + rc = cmd->nonhid_cmd->retrieve_panel_scan(dev, 0, read_offset, + read_count, data_id, response, config, actual_read_len, + return_buf); + if (rc) + pt_debug(dev, DL_ERROR, + "%s: Retrieve scan data failed rc = %d\n", + __func__, rc); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_get_data_structure_cmd_ + * + * SUMMARY: Non-protected wrapper function for get data structure command + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * read_offset - read pointer offset + * read_length - length of data to read + * data_id - data ID to read + * *status - pointer to store the read response status + * *data_format - pointer to store format of data read + * *actual_read_len - pointer to store data length actually read + * *data - pointer to store data read + ******************************************************************************/ +static int pt_get_data_structure_cmd_(struct device *dev, u16 read_offset, + u16 read_length, u8 data_id, u8 *status, u8 *data_format, + u16 *actual_read_len, u8 *data) +{ + int rc; + + rc = cmd->nonhid_cmd->get_data_structure(dev, 0, read_offset, + read_length, data_id, status, data_format, + actual_read_len, data); + if (rc) + pt_debug(dev, DL_ERROR, + "%s: Get data structure failed rc = %d\n", + __func__, rc); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_run_selftest_cmd_ + * + * SUMMARY: Non-protected wrapper function for run self test command + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * test_id - enumerated test ID to run + * write_idacs_to_flash - flag whether to write new IDACS to flash + * *status - pointer to store the read response status + * *summary_results - pointer to store the results summary + * *results_available - pointer to store if results are available + ******************************************************************************/ +static int pt_run_selftest_cmd_(struct device *dev, u8 test_id, + u8 write_idacs_to_flash, u8 *status, u8 *summary_result, + u8 *results_available) +{ + int rc; + + rc = cmd->nonhid_cmd->run_selftest(dev, 0, test_id, + write_idacs_to_flash, status, summary_result, + results_available); + if (rc) + pt_debug(dev, DL_ERROR, "%s: Run self test failed rc = %d\n", + __func__, rc); + return rc; +} + + +/******************************************************************************* + * FUNCTION: pt_get_selftest_result_cmd_ + * + * SUMMARY: Non-protected wrapper function for get self test result command + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * read_offset - read pointer offset + * read_length - length of data to read + * test_id - enumerated test ID to read selftest results from + * *status - pointer to store the read response status + * *actual_read_len - pointer to store data length actually read + * *data - pointer to where the data read is stored + ******************************************************************************/ +static int pt_get_selftest_result_cmd_(struct device *dev, + u16 read_offset, u16 read_length, u8 test_id, u8 *status, + u16 *actual_read_len, u8 *data) +{ + int rc; + + rc = cmd->nonhid_cmd->get_selftest_result(dev, 0, read_offset, + read_length, test_id, status, actual_read_len, data); + if (rc) + pt_debug(dev, DL_ERROR, + "%s: Get self test result failed rc = %d\n", + __func__, rc); + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_calibrate_ext_cmd + * + * SUMMARY: Wrapper function to function calibrate_ext() in pt_core_commands + * structure + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *cal_data - pointer to extended calibration data structure + * *status - pointer to where the command response status is stored + ******************************************************************************/ +static int _pt_calibrate_ext_cmd(struct device *dev, + struct pt_cal_ext_data *cal_data, u8 *status) +{ + int rc; + + rc = cmd->nonhid_cmd->calibrate_ext(dev, + PT_CORE_CMD_UNPROTECTED, cal_data, status); + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_calibrate_idacs_cmd + * + * SUMMARY: Wrapper function to function calibrate_idacs() in pt_core_commands + * structure + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * sensing_mode - sense mode to calibrate (0-5) + * *status - pointer to where the command response status is stored + ******************************************************************************/ +static int _pt_calibrate_idacs_cmd(struct device *dev, + u8 sensing_mode, u8 *status) +{ + int rc; + + rc = cmd->nonhid_cmd->calibrate_idacs(dev, 0, sensing_mode, status); + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_initialize_baselines_cmd + * + * SUMMARY: Wrapper function to call initialize_baselines() in pt_core_commands + * structure + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * sensing_mode - enumerated ID against which to initialize the baseline + * *status - pointer to where the command response statas is stored + ******************************************************************************/ +static int _pt_initialize_baselines_cmd(struct device *dev, + u8 sensing_mode, u8 *status) +{ + int rc; + + rc = cmd->nonhid_cmd->initialize_baselines(dev, 0, sensing_mode, + status); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_perform_calibration + * + * SUMMARY: For Gen5/6, Send the PIP1 Calibrate IDACs command (0x28). For TT/TC, + * send PIP1 Extended Calibrate command (0x30). + * + * NOTE: Panel scan must be suspended prior to calling this function. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_perform_calibration(struct device *dev) +{ + struct pt_cal_ext_data cal_data = {0}; + u8 dut_gen = cmd->request_dut_generation(dev); + u8 mode; + u8 status; + int rc; + + if (dut_gen == DUT_PIP1_ONLY) { + for (mode = 0; mode < 3; mode++) { + rc = _pt_calibrate_idacs_cmd(dev, mode, &status); + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: calibrate idac error, mode= %d, rc = %d\n", + __func__, mode, rc); + break; + } + } + } else { + memset(&cal_data, 0, sizeof(struct pt_cal_ext_data)); + rc = _pt_calibrate_ext_cmd(dev, &cal_data, &status); + if (rc < 0) + pt_debug(dev, DL_ERROR, + "%s: extended calibrate error, rc = %d\n", + __func__, rc); + } + + return rc; +} + +/******************************************************************************* + * FUNCTION: prepare_print_buffer + * + * SUMMARY: Format input buffer to out buffer with Hex base,and format "status" + * to decimal base. + * + * RETURN: + * size of formated data in output buffer + * + * PARAMETERS: + * status - Indicate test result:0(STATUS_SUCCESS),-1(STATUS_FAIL) + * *in_buf - input buffer to be formated + * length - length of input buffer + * *out_buf - output buffer to store formated data + * out_buf_size - length of output buffer + * out_format - format of output data (5 supported formats): + * PT_PR_FORMAT_DEFAULT : format all data as a column + * PT_PR_FORMAT_U8_SPACE : sort status bytes and self test results, + * and format the results as a row, each element include 1 byte + * PT_PR_FORMAT_U16_SPACE : sort status bytes and self test results, + * and format the results as a row, each element include 2 byte + * PT_PR_FORMAT_U8_NO_SPACE : sort status bytes and self test results, + * and format the results as a row, no space between the elements + * PT_PR_FORMAT_U32_SPACE : sort status bytes and self test results, + * and format the results as a row, each element include 4 byte + ******************************************************************************/ +static int prepare_print_buffer(int status, u8 *in_buf, int length, + u8 *out_buf, size_t out_buf_size, u8 out_format) +{ + int index = 0; + int data_length; + int i; + + index += scnprintf(out_buf, out_buf_size, "Status: %d\n", status); + + if (out_format == PT_PR_FORMAT_DEFAULT) { + for (i = 0; i < length; i++) + index += scnprintf(&out_buf[index], + out_buf_size - index, + "%02X\n", in_buf[i]); + } else { + index += scnprintf(&out_buf[index], + out_buf_size - index, + "Response Status[1-%d]: ", MIN(length, 3)); + for (i = 0; i < MIN(length, 3); i++) + index += scnprintf(&out_buf[index], + out_buf_size - index, + "%02X ", in_buf[i]); + index += scnprintf(&out_buf[index], out_buf_size - index, "\n"); + if (length <= 6) { + goto exit; + } else { + data_length = get_unaligned_le16(&in_buf[4]); + index += scnprintf(&out_buf[index], + out_buf_size - index, "RAW_DATA: "); + } + + if (out_format == PT_PR_FORMAT_U8_SPACE) { + for (i = 6; i < length; i++) + index += scnprintf(&out_buf[index], + out_buf_size - index, + "%02X ", in_buf[i]); + index += scnprintf(&out_buf[index], + out_buf_size - index, + ":(%d bytes)\n", data_length); + } else if (out_format == PT_PR_FORMAT_U16_SPACE) { + for (i = 6; (i + 1) < length; i += 2) + index += scnprintf(&out_buf[index], + out_buf_size - index, "%04X ", + get_unaligned_le16(&in_buf[i])); + index += scnprintf(&out_buf[index], + out_buf_size - index, + ":(%d words)\n", (length-6)/2); + } else if (out_format == PT_PR_FORMAT_U8_NO_SPACE) { + for (i = 6; i < length; i++) + index += scnprintf(&out_buf[index], + out_buf_size - index, + "%02X", in_buf[i]); + index += scnprintf(&out_buf[index], + out_buf_size - index, + ":(%d bytes)\n", data_length); + } else if (out_format == PT_PR_FORMAT_U32_SPACE) { + for (i = 6; (i + 1) < length; i += 4) + index += scnprintf(&out_buf[index], + out_buf_size - index, "%08X ", + get_unaligned_le32(&in_buf[i])); + index += scnprintf(&out_buf[index], + out_buf_size - index, + ":(%d 32bit values)\n", (length-6)/4); + } + } + +exit: + return index; +} + +/******************************************************************************* + * FUNCTION: pt_run_and_get_selftest_result + * + * SUMMARY: Run the selftest and store the test result in the + * pt_device_access_data struct. + * + * RETURN: + * >0 : Size of debugfs data to print + * <0 : failure + * 0 : success + * + * NOTE: "Status: x" - x will contain the first error code if any + * + * PARAMETERS: + * *dev - pointer to device structure + * protect - flag to call protected or non-protected + * *buf - pointer to print buf of return data + * buf_len - length of print buf of return data + * test_id - selftest id + * read_length - max length to stor return data + * get_result_on_pass - indicate whether to get result when finish test + * print_results - print results to log + * (true:get result;false:don't get result ) + * print_format - format of print results + ******************************************************************************/ +static ssize_t pt_run_and_get_selftest_result(struct device *dev, + int protect, char *buf, size_t buf_len, u8 test_id, + u16 read_length, u8 get_result_on_pass, bool print_results, + u8 print_format) +{ + struct pt_device_access_data *dad = pt_get_device_access_data(dev); + int status = STATUS_SUCCESS; + u8 cmd_status = STATUS_SUCCESS; + u8 summary_result = 0; + u8 sys_mode = FW_SYS_MODE_UNDEFINED; + u16 act_length = 0; + int length = 0; + int size = 0; + int rc; + + mutex_lock(&dad->sysfs_lock); + + pm_runtime_get_sync(dev); + + if (protect == PT_CORE_CMD_PROTECTED) { + rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error on request exclusive rc = %d\n", + __func__, rc); + status = -EPERM; + goto put_pm_runtime; + } + } + + /* Get the current scan state so we restore to the same at the end */ + rc = cmd->request_get_fw_mode(dev, PT_CORE_CMD_UNPROTECTED, &sys_mode, + NULL); + if (rc) { + status = rc; + goto release_exclusive; + } + + if (sys_mode != FW_SYS_MODE_TEST) { + rc = pt_suspend_scan_cmd_(dev); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error on suspend scan rc = %d\n", + __func__, rc); + status = -EPERM; + goto release_exclusive; + } + } + + /* Sleep for 20ms to allow the last scan to be available in FW */ + msleep(20); + + rc = pt_run_selftest_cmd_(dev, test_id, 0, + &cmd_status, &summary_result, NULL); + if (rc) { + /* Error sending self test */ + pt_debug(dev, DL_ERROR, + "%s: Error on run self test for test_id:%d rc = %d\n", + __func__, test_id, rc); + status = rc; + goto resume_scan; + } + if (cmd_status) { + /* Self test response status failure */ + pt_debug(dev, DL_WARN, + "%s: Test ID: 0x%02X resulted in status: 0x%02X\n", + __func__, test_id, cmd_status); + status = cmd_status; + } + + dad->si = cmd->request_sysinfo(dad->dev); + if (!dad->si) { + pt_debug(dad->dev, DL_ERROR, + "%s: Fail get sysinfo pointer from core\n", __func__); + if (status == STATUS_SUCCESS) + status = -EINVAL; + goto resume_scan; + } + if (IS_PIP_VER_GE(dad->si, 1, 11)) { + /* PIP1.11+ does not report the summary_result in byte 6 */ + summary_result = cmd_status; + } + + /* Form response buffer */ + dad->ic_buf[0] = cmd_status; + dad->ic_buf[1] = summary_result; + + pt_debug(dev, DL_INFO, "%s: Run Self Test cmd status = %d\n", + __func__, cmd_status); + pt_debug(dev, DL_INFO, "%s: Run Self Test result summary = %d\n", + __func__, summary_result); + + length = 2; + + /* + * For "auto_shorts" sysfs node, detailed data is + * retrieved only when the summary result is fail. + */ + if (get_result_on_pass == PT_ST_GET_RESULTS_BASED_ON_DATA && + test_id == PT_ST_ID_AUTOSHORTS && + summary_result == PT_ST_RESULT_PASS) + goto resume_scan; + + /* + * Get data if requested and the cmd status indicates that the test + * completed with either a pass or a fail. All other status codes + * indicate the test itself was not run so there is no data to retrieve + */ + if ((cmd_status == PT_ST_RESULT_PASS || + cmd_status == PT_ST_RESULT_FAIL) && + get_result_on_pass != PT_ST_DONT_GET_RESULTS) { + rc = pt_get_selftest_result_cmd_(dev, 0, read_length, + test_id, &cmd_status, &act_length, &dad->ic_buf[6]); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error on get self test result rc = %d\n", + __func__, rc); + if (status == STATUS_SUCCESS) + status = rc; + goto resume_scan; + } + + pt_debug(dev, DL_INFO, "%s: Get Self Test result status = %d\n", + __func__, cmd_status); + + /* Only store new status if no error on running self test */ + if (status == STATUS_SUCCESS) + status = cmd_status; + + dad->ic_buf[2] = cmd_status; + dad->ic_buf[3] = test_id; + dad->ic_buf[4] = LOW_BYTE(act_length); + dad->ic_buf[5] = HI_BYTE(act_length); + + length = 6 + act_length; + } + +resume_scan: + /* Only resume scanning if we suspended it */ + if (sys_mode == FW_SYS_MODE_SCANNING) + pt_resume_scan_cmd_(dev); + +release_exclusive: + if (protect == PT_CORE_CMD_PROTECTED) + cmd->release_exclusive(dev); + +put_pm_runtime: + pm_runtime_put(dev); + + /* Communication error */ + if (status < 0) + length = 0; + + if (print_results) { + size = prepare_print_buffer(status, dad->ic_buf, length, + buf, buf_len, print_format); + rc = size; + } + + mutex_unlock(&dad->sysfs_lock); + + return rc; +} + +struct pt_device_access_debugfs_data { + struct pt_device_access_data *dad; + ssize_t pr_buf_len; + u8 pr_buf[10 * PT_MAX_PRBUF_SIZE]; +}; + +/******************************************************************************* + * FUNCTION: pt_device_access_debugfs_open + * + * SUMMARY: Open the device_access debugfs node to initialize. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *inode - pointer to inode structure + * *filp - pointer to file structure + ******************************************************************************/ +static int pt_device_access_debugfs_open(struct inode *inode, + struct file *filp) +{ + struct pt_device_access_data *dad = inode->i_private; + struct pt_device_access_debugfs_data *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dad = dad; + + filp->private_data = data; + + return nonseekable_open(inode, filp); +} + +/******************************************************************************* + * FUNCTION: pt_device_access_debugfs_release + * + * SUMMARY: Close the device_access debugfs node to free pointer. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *inode - pointer to inode structure + * *filp - pointer to file structure + ******************************************************************************/ +static int pt_device_access_debugfs_release(struct inode *inode, + struct file *filp) +{ + kfree(filp->private_data); + + return 0; +} + +#define PT_DEBUGFS_FOPS(_name, _read, _write) \ +static const struct file_operations _name##_debugfs_fops = { \ + .open = pt_device_access_debugfs_open, \ + .release = pt_device_access_debugfs_release, \ + .read = _read, \ + .write = _write, \ +} + +/******************************************************************************* + * FUNCTION: panel_scan_debugfs_read + * + * SUMMARY: This function retrieves a full panel scan by sending the following + * PIP commands: + * 1) Suspend Scanning + * 2) Execute Panel Scan + * 3) Retrieve Panel Scan (n times to retrieve full scan) + * 4) Resume Scanning + * + * RETURN: Size of debugfs data print + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t panel_scan_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + struct pt_device_access_data *dad = data->dad; + struct device *dev = dad->dev; + struct pt_core_data *cd = dev_get_drvdata(dev); + int status = STATUS_FAIL; + u8 config; + u16 num_elem_read; + int length = 0; + u8 element_size = 0; + u8 *buf_out; + u8 *buf_offset; + u8 sys_mode = FW_SYS_MODE_UNDEFINED; + int elem_offset = 0; + int rc; + int print_idx = 0; + int i; + + mutex_lock(&dad->debugfs_lock); + buf_out = dad->panel_scan_data_buf; + if (!buf_out) + goto release_mutex; + + pm_runtime_get_sync(dev); + + /* + * This function will re-enter if the panel_scan_size is greater than + * count (count is the kernel page size which is typically 4096), on + * re-entry, *ppos will retain how far the last copy to user space + * completed + */ + if (*ppos) { + if (*ppos >= dad->panel_scan_size) + goto release_mutex; + + print_idx = simple_read_from_buffer(buf, count, ppos, + buf_out, dad->panel_scan_size); + + pt_debug(dev, DL_DEBUG, "%s: Sent %d bytes to user space\n", + __func__, print_idx); + + goto release_mutex; + } + + rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error on request exclusive rc = %d\n", + __func__, rc); + goto put_pm_runtime; + } + + /* Get the current scan state so we restore to the same at the end */ + rc = cmd->request_get_fw_mode(dev, PT_CORE_CMD_UNPROTECTED, &sys_mode, + NULL); + if (rc) { + status = rc; + goto release_exclusive; + } + + if (sys_mode != FW_SYS_MODE_TEST) { + rc = pt_suspend_scan_cmd_(dev); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error on suspend scan rc = %d\n", + __func__, rc); + goto release_exclusive; + } + } + + rc = pt_exec_scan_cmd_(dev, dad->panel_scan_type_id); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error on execute panel scan rc = %d\n", + __func__, rc); + goto resume_scan; + } + + /* Set length to max to read all */ + rc = pt_ret_scan_data_cmd_(dev, 0, 0xFFFF, + dad->panel_scan_retrieve_id, dad->ic_buf, &config, + &num_elem_read, NULL); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error on retrieve panel scan rc = %d\n", + __func__, rc); + goto resume_scan; + } + + length = get_unaligned_le16(&dad->ic_buf[0]); + buf_offset = dad->ic_buf + length; + element_size = config & 0x07; + elem_offset = num_elem_read; + while (num_elem_read > 0) { + rc = pt_ret_scan_data_cmd_(dev, elem_offset, 0xFFFF, + dad->panel_scan_retrieve_id, NULL, &config, + &num_elem_read, buf_offset); + if (rc) + goto resume_scan; + + length += num_elem_read * element_size; + buf_offset = dad->ic_buf + length; + elem_offset += num_elem_read; + if (num_elem_read < 0x7A) + break; + } + /* Reconstruct cmd header */ + put_unaligned_le16(length, &dad->ic_buf[0]); + put_unaligned_le16(elem_offset, &dad->ic_buf[7]); + + status = STATUS_SUCCESS; + +resume_scan: + /* Only resume scanning if we suspended it */ + if (sys_mode == FW_SYS_MODE_SCANNING) + pt_resume_scan_cmd_(dev); + +release_exclusive: + cmd->release_exclusive(dev); + +put_pm_runtime: + pm_runtime_put(dev); + + if (status == STATUS_FAIL) + length = 0; + if (cd->show_timestamp) + print_idx += scnprintf(buf_out + print_idx, TTHE_TUNER_MAX_BUF, + "[%u] SCAN_DATA:", pt_get_time_stamp()); + else + print_idx += scnprintf(buf_out + print_idx, TTHE_TUNER_MAX_BUF, + "SCAN_DATA:"); + + for (i = 0; i < length; i++) + print_idx += scnprintf(buf_out + print_idx, + TTHE_TUNER_MAX_BUF - print_idx, + "%02X ", dad->ic_buf[i]); + print_idx += scnprintf(buf_out + print_idx, + TTHE_TUNER_MAX_BUF - print_idx, + ":(%d bytes)\n", length); + + /* + * Save the size of the full scan which this function uses on re-entry + * to send the data back to user space in 'count' size chuncks + */ + dad->panel_scan_size = print_idx; + print_idx = simple_read_from_buffer(buf, count, ppos, buf_out, + print_idx); + pt_debug(dev, DL_DEBUG, "%s: Sent %d bytes to user space\n", + __func__, print_idx); + +release_mutex: + mutex_unlock(&dad->debugfs_lock); + return print_idx; +} + +/******************************************************************************* + * FUNCTION: panel_scan_debugfs_write + * + * SUMMARY: Store the type of panel scan the read method will perform. + * + * RETURN: Size of debugfs data write + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to write to + * count - the maximum number of bytes to write + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t panel_scan_debugfs_write(struct file *filp, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + struct pt_device_access_data *dad = data->dad; + ssize_t length; + u32 input_data[3]; + int rc = 0; + + rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos, + buf, count); + if (rc < 0) + return rc; + + count = rc; + + mutex_lock(&dad->debugfs_lock); + length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count, + input_data, ARRAY_SIZE(input_data)); + switch (length) { + case 1: + dad->panel_scan_retrieve_id = input_data[0]; + dad->panel_scan_type_id = 0; + break; + case 2: + dad->panel_scan_retrieve_id = input_data[0]; + dad->panel_scan_type_id = input_data[1]; + break; + default: + pt_debug(dad->dev, DL_ERROR, + "%s: Malformed input\n", __func__); + rc = -EINVAL; + } + mutex_unlock(&dad->debugfs_lock); + + if (rc) + return rc; + return count; +} + +/******************************************************************************* + * FUNCTION: panel_scan_debugfs_open + * + * SUMMARY: Open the panel_scan debugfs node to initialize. + * + * RETURN: 0 = success + * !0 = failure + * + * PARAMETERS: + * *inode - file inode number + * *filp - file pointer to debugfs file + ******************************************************************************/ +static int panel_scan_debugfs_open(struct inode *inode, + struct file *filp) +{ + struct pt_device_access_data *dad = inode->i_private; + struct pt_device_access_debugfs_data *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dad = dad; + data->pr_buf_len = 4 * PT_MAX_PRBUF_SIZE; + + filp->private_data = data; + + return nonseekable_open(inode, filp); +} + +/******************************************************************************* + * FUNCTION: panel_scan_debugfs_close + * + * SUMMARY: Close the panel_scan debugfs node to free pointer. + * + * RETURN: 0 = success + * + * PARAMETERS: + * *inode - file inode number + * *filp - file pointer to debugfs file + ******************************************************************************/ +static int panel_scan_debugfs_close(struct inode *inode, + struct file *filp) +{ + kfree(filp->private_data); + filp->private_data = NULL; + return 0; +} + +static const struct file_operations panel_scan_fops = { + .open = panel_scan_debugfs_open, + .release = panel_scan_debugfs_close, + .read = panel_scan_debugfs_read, + .write = panel_scan_debugfs_write, +}; + +/******************************************************************************* + * FUNCTION: get_idac_debugfs_read + * + * SUMMARY: Retrieve data structure with idac data id by sending the following + * PIP commands: + * 1) Suspend Scanning + * 2) Retrieve data structure + * 3) Resume Scanning + * The "Status: n" this node prints, 'n' will be: + * - zero for a full pass + * - negative for TTDL communication errors + * - positive for any FW status errors + * + * RETURN: Size of debugfs data print + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t get_idac_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + struct pt_device_access_data *dad = data->dad; + struct device *dev = dad->dev; + int status = STATUS_FAIL; + u8 cmd_status = 0; + u8 data_format = 0; + u16 act_length = 0; + int length = 0; + int rc; + + if (*ppos) + goto exit; + + mutex_lock(&dad->sysfs_lock); + + pm_runtime_get_sync(dev); + + rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc) { + status = rc; + pt_debug(dev, DL_ERROR, + "%s: Error on request exclusive rc = %d\n", + __func__, rc); + goto put_pm_runtime; + } + + rc = pt_suspend_scan_cmd_(dev); + if (rc) { + status = rc; + pt_debug(dev, DL_ERROR, "%s: Error on suspend scan rc = %d\n", + __func__, rc); + goto release_exclusive; + } + + rc = pt_get_data_structure_cmd_(dev, 0, PIP_CMD_MAX_LENGTH, + dad->get_idac_data_id, &cmd_status, &data_format, + &act_length, &dad->ic_buf[5]); + if (rc) { + status = rc; + pt_debug(dev, DL_ERROR, + "%s: Error on get data structure rc = %d\n", + __func__, rc); + goto resume_scan; + } + + dad->ic_buf[0] = cmd_status; + dad->ic_buf[1] = dad->get_idac_data_id; + dad->ic_buf[2] = LOW_BYTE(act_length); + dad->ic_buf[3] = HI_BYTE(act_length); + dad->ic_buf[4] = data_format; + + length = 5 + act_length; + + status = cmd_status; + +resume_scan: + pt_resume_scan_cmd_(dev); + +release_exclusive: + cmd->release_exclusive(dev); + +put_pm_runtime: + pm_runtime_put(dev); + + if (status == STATUS_FAIL) + length = 0; + + data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, length, + data->pr_buf, sizeof(data->pr_buf), PT_PR_FORMAT_DEFAULT); + + mutex_unlock(&dad->sysfs_lock); + +exit: + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +/******************************************************************************* + * FUNCTION: get_idac_debugfs_write + * + * SUMMARY: Store the data id of idac,the read method will perform. + * + * RETURN: Size of debugfs data write + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to write to + * count - the maximum number of bytes to write + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t get_idac_debugfs_write(struct file *filp, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + struct pt_device_access_data *dad = data->dad; + ssize_t length; + u32 input_data[2]; + int rc = 0; + + rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos, + buf, count); + if (rc < 0) + return rc; + + count = rc; + + mutex_lock(&dad->sysfs_lock); + + length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count, + input_data, ARRAY_SIZE(input_data)); + if (length != 1) { + pt_debug(dad->dev, DL_ERROR, + "%s: Malformed input\n", __func__); + rc = -EINVAL; + goto exit_unlock; + } + + dad->get_idac_data_id = input_data[0]; + +exit_unlock: + mutex_unlock(&dad->sysfs_lock); + + if (rc) + return rc; + + return count; +} + +PT_DEBUGFS_FOPS(get_idac, get_idac_debugfs_read, get_idac_debugfs_write); + +/******************************************************************************* + * FUNCTION: calibrate_ext_debugfs_read + * + * SUMMARY: Perform extended calibration command(0x30) which is flexible to + * calibrate each individual feature by adding extra parameter for calibration + * mode. + * + * NOTE: + * - This calibrate command requires the DUT to support PIP version >= 1.10 + * - The "Status:" included in the printout will be one of the following: + * <0 - Linux error code (PIP transmission error) + * 0 - Full pass + * >0 - PIP error status + * + * RETURN: Size of debugfs data print + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t calibrate_ext_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + struct pt_device_access_data *dad = data->dad; + struct device *dev = dad->dev; + int status = STATUS_FAIL; + int length = 0; + int rc; + + if (*ppos) + goto exit; + + dad->si = cmd->request_sysinfo(dad->dev); + if (!dad->si) { + pt_debug(dad->dev, DL_ERROR, + "%s: Fail get sysinfo pointer from core\n", + __func__); + status = -EIO; + data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, 0, + data->pr_buf, sizeof(data->pr_buf), + PT_PR_FORMAT_DEFAULT); + goto exit; + } + + if (!IS_PIP_VER_GE(dad->si, 1, 10)) { + pt_debug(dad->dev, DL_ERROR, + "%s: extended calibration command is not supported\n", + __func__); + status = -EPROTONOSUPPORT; + data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, 0, + data->pr_buf, sizeof(data->pr_buf), + PT_PR_FORMAT_DEFAULT); + goto exit; + } + + if (dad->cal_ext_data.mode == PT_CAL_EXT_MODE_UNDEFINED) { + pt_debug(dad->dev, DL_ERROR, + "%s: No parameters provided for calibration command\n", + __func__); + status = -EINVAL; + data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, 0, + data->pr_buf, sizeof(data->pr_buf), + PT_PR_FORMAT_DEFAULT); + goto exit; + } + + mutex_lock(&dad->sysfs_lock); + + pm_runtime_get_sync(dev); + + rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc) { + status = rc; + pt_debug(dev, DL_ERROR, + "%s: Error on request exclusive rc = %d\n", + __func__, rc); + goto put_pm_runtime; + } + + rc = pt_suspend_scan_cmd_(dev); + if (rc) { + status = rc; + pt_debug(dev, DL_ERROR, "%s: Error on suspend scan rc = %d\n", + __func__, rc); + goto release_exclusive; + } + + rc = _pt_calibrate_ext_cmd(dev, &dad->cal_ext_data, &dad->ic_buf[0]); + if (rc) { + status = rc; + pt_debug(dev, DL_ERROR, + "%s: Error on calibrate_ext rc = %d\n", + __func__, rc); + goto resume_scan; + } + + /* + * Include PIP errors as positive status codes and report the data. + * No PIP error "0x00" in the response indicates full success + */ + length = 1; + status = dad->ic_buf[0]; + +resume_scan: + pt_resume_scan_cmd_(dev); + +release_exclusive: + cmd->release_exclusive(dev); + +put_pm_runtime: + pm_runtime_put(dev); + + /* Negative status codes are bus transmission errors and have no data */ + if (status < 0) + length = 0; + + data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, length, + data->pr_buf, sizeof(data->pr_buf), PT_PR_FORMAT_DEFAULT); + + mutex_unlock(&dad->sysfs_lock); + +exit: + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +/******************************************************************************* + * FUNCTION: calibrate_ext_debugfs_write + * + * SUMMARY: Stores the calibration mode and up to three parameters to perform + * individual features. + * + * RETURN: Size of debugfs data write + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to write to + * count - the maximum number of bytes to write + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t calibrate_ext_debugfs_write(struct file *filp, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + struct pt_device_access_data *dad = data->dad; + ssize_t length; + u32 input_data[5]; + int rc = 0; + int i = 0; + + rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos, + buf, count); + if (rc < 0) + return rc; + + count = rc; + + mutex_lock(&dad->sysfs_lock); + + length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count, + input_data, ARRAY_SIZE(input_data)); + if ((length <= 4) && (length > 0)) { + for (i = length; i < 4; i++) + input_data[i] = 0; + + dad->cal_ext_data.mode = (u8)input_data[0]; + dad->cal_ext_data.data0 = (u8)input_data[1]; + dad->cal_ext_data.data1 = (u8)input_data[2]; + dad->cal_ext_data.data2 = (u8)input_data[3]; +#ifdef TTDL_DIAGNOSTICS + pt_debug(dad->dev, DL_INFO, + "%s: calibration mode=%d, data[0..2]=0x%02X %02X %02X\n", + __func__, + dad->cal_ext_data.mode, dad->cal_ext_data.data0, + dad->cal_ext_data.data1, dad->cal_ext_data.data2); +#endif + } else { + pt_debug(dad->dev, DL_ERROR, + "%s: Malformed input\n", __func__); + rc = -EINVAL; + goto exit_unlock; + } + +exit_unlock: + mutex_unlock(&dad->sysfs_lock); + + if (rc) + return rc; + + return count; +} + +PT_DEBUGFS_FOPS(calibrate_ext, + calibrate_ext_debugfs_read, calibrate_ext_debugfs_write); + +/******************************************************************************* + * FUNCTION: calibrate_debugfs_read + * + * SUMMARY: Perform calibration by sending the following PIP commands: + * 1) Suspend Scanning + * 2) Execute calibrate + * 3) Initialize baseline conditionally + * 4) Resume Scanning + * + * RETURN: Size of debugfs data print + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t calibrate_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + struct pt_device_access_data *dad = data->dad; + struct device *dev = dad->dev; + int status = STATUS_FAIL; + int length = 0; + int rc; + + if (*ppos) + goto exit; + + mutex_lock(&dad->sysfs_lock); + + pm_runtime_get_sync(dev); + + rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc) { + status = rc; + pt_debug(dev, DL_ERROR, + "%s: Error on request exclusive rc = %d\n", + __func__, rc); + goto put_pm_runtime; + } + + rc = pt_suspend_scan_cmd_(dev); + if (rc) { + status = rc; + pt_debug(dev, DL_ERROR, "%s: Error on suspend scan rc = %d\n", + __func__, rc); + goto release_exclusive; + } + + rc = _pt_calibrate_idacs_cmd(dev, dad->calibrate_sensing_mode, + &dad->ic_buf[0]); + if (rc) { + status = rc; + pt_debug(dev, DL_ERROR, + "%s: Error on calibrate idacs rc = %d\n", + __func__, rc); + goto resume_scan; + } + + length = 1; + + /* Check if baseline initialization is requested */ + if (dad->calibrate_initialize_baselines) { + /* Perform baseline initialization for all modes */ + rc = _pt_initialize_baselines_cmd(dev, PT_IB_SM_MUTCAP | + PT_IB_SM_SELFCAP | PT_IB_SM_BUTTON, + &dad->ic_buf[length]); + if (rc) { + status = rc; + pt_debug(dev, DL_ERROR, + "%s: Error on initialize baselines rc = %d\n", + __func__, rc); + goto resume_scan; + } + + length++; + } + + status = STATUS_SUCCESS; + +resume_scan: + pt_resume_scan_cmd_(dev); + +release_exclusive: + cmd->release_exclusive(dev); + +put_pm_runtime: + pm_runtime_put(dev); + + if (status == STATUS_FAIL) + length = 0; + + data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, length, + data->pr_buf, sizeof(data->pr_buf), PT_PR_FORMAT_DEFAULT); + + mutex_unlock(&dad->sysfs_lock); + +exit: + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +/******************************************************************************* + * FUNCTION: calibrate_debugfs_write + * + * SUMMARY: Stores the calibration sense mode and a flag to control if the + * baseline will be initialized for the read method of this node. + * + * RETURN: Size of debugfs data write + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to write to + * count - the maximum number of bytes to write + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t calibrate_debugfs_write(struct file *filp, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + struct pt_device_access_data *dad = data->dad; + ssize_t length; + u32 input_data[3]; + int rc = 0; + + rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos, + buf, count); + if (rc < 0) + return rc; + + count = rc; + + mutex_lock(&dad->sysfs_lock); + + length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count, + input_data, ARRAY_SIZE(input_data)); + if (length != 2) { + pt_debug(dad->dev, DL_ERROR, + "%s: Malformed input\n", __func__); + rc = -EINVAL; + goto exit_unlock; + } + + dad->calibrate_sensing_mode = input_data[0]; + dad->calibrate_initialize_baselines = input_data[1]; + +exit_unlock: + mutex_unlock(&dad->sysfs_lock); + + if (rc) + return rc; + + return count; +} + +PT_DEBUGFS_FOPS(calibrate, calibrate_debugfs_read, calibrate_debugfs_write); + +/******************************************************************************* + * FUNCTION: baseline_debugfs_read + * + * SUMMARY: Perform baseline initialization by sending the following PIP + * commands: + * 1) Suspend Scanning + * 2) Execute initialize baseline + * 3) Resume Scanning + * + * RETURN: Size of debugfs data print + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t baseline_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + struct pt_device_access_data *dad = data->dad; + struct device *dev = dad->dev; + int status = STATUS_FAIL; + int length = 0; + int rc; + + if (*ppos) + goto exit; + + mutex_lock(&dad->sysfs_lock); + + pm_runtime_get_sync(dev); + + rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc) { + status = rc; + pt_debug(dev, DL_ERROR, + "%s: Error on request exclusive rc = %d\n", + __func__, rc); + goto put_pm_runtime; + } + + rc = pt_suspend_scan_cmd_(dev); + if (rc) { + status = rc; + pt_debug(dev, DL_ERROR, "%s: Error on suspend scan rc = %d\n", + __func__, rc); + goto release_exclusive; + } + + rc = _pt_initialize_baselines_cmd(dev, dad->baseline_sensing_mode, + &dad->ic_buf[0]); + if (rc) { + status = rc; + pt_debug(dev, DL_ERROR, + "%s: Error on initialize baselines rc = %d\n", + __func__, rc); + goto resume_scan; + } + + length = 1; + + status = STATUS_SUCCESS; + +resume_scan: + pt_resume_scan_cmd_(dev); + +release_exclusive: + cmd->release_exclusive(dev); + +put_pm_runtime: + pm_runtime_put(dev); + + if (status == STATUS_FAIL) + length = 0; + + data->pr_buf_len = prepare_print_buffer(status, dad->ic_buf, length, + data->pr_buf, sizeof(data->pr_buf), PT_PR_FORMAT_DEFAULT); + + mutex_unlock(&dad->sysfs_lock); + +exit: + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +/******************************************************************************* + * FUNCTION: baseline_debugfs_write + * + * SUMMARY: Store the sense mode of base initialization, the read method will + * perform. + * + * RETURN: Size of debugfs data write + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to write to + * count - the maximum number of bytes to write + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t baseline_debugfs_write(struct file *filp, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + struct pt_device_access_data *dad = data->dad; + ssize_t length; + u32 input_data[2]; + int rc = 0; + + rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos, + buf, count); + if (rc < 0) + return rc; + + count = rc; + + mutex_lock(&dad->sysfs_lock); + + length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count, + input_data, ARRAY_SIZE(input_data)); + if (length != 1) { + pt_debug(dad->dev, DL_ERROR, + "%s: Malformed input\n", __func__); + rc = -EINVAL; + goto exit_unlock; + } + + dad->baseline_sensing_mode = input_data[0]; + +exit_unlock: + mutex_unlock(&dad->sysfs_lock); + + if (rc) + return rc; + + return count; +} + +PT_DEBUGFS_FOPS(baseline, baseline_debugfs_read, baseline_debugfs_write); + +/******************************************************************************* + * FUNCTION: auto_shorts_debugfs_read + * + * SUMMARY: Performs the "auto shorts" test and prints the result to the output + * buffer. + * + * RETURN: Size of debugfs data print + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t auto_shorts_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + + if (!*ppos) + /* Set length to PIP_CMD_MAX_LENGTH to read all */ + data->pr_buf_len = pt_run_and_get_selftest_result( + data->dad->dev, PT_CORE_CMD_PROTECTED, + data->pr_buf, sizeof(data->pr_buf), + PT_ST_ID_AUTOSHORTS, PIP_CMD_MAX_LENGTH, + PT_ST_GET_RESULTS_BASED_ON_DATA, PT_ST_PRINT_RESULTS, + PT_PR_FORMAT_DEFAULT); + + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +PT_DEBUGFS_FOPS(auto_shorts, auto_shorts_debugfs_read, NULL); + +/******************************************************************************* + * FUNCTION: opens_debugfs_read + * + * SUMMARY: Performs the "opens" test and prints the results to the output + * buffer. + * + * RETURN: Size of debugfs data print + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t opens_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + + if (!*ppos) + /* Set length to PIP_CMD_MAX_LENGTH to read all */ + data->pr_buf_len = pt_run_and_get_selftest_result( + data->dad->dev, PT_CORE_CMD_PROTECTED, + data->pr_buf, sizeof(data->pr_buf), + PT_ST_ID_OPENS, PIP_CMD_MAX_LENGTH, + PT_ST_DONT_GET_RESULTS, PT_ST_PRINT_RESULTS, + PT_PR_FORMAT_DEFAULT); + + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +PT_DEBUGFS_FOPS(opens, opens_debugfs_read, NULL); + +/******************************************************************************* + * FUNCTION: cm_panel_debugfs_read + * + * SUMMARY: Performs the "CM panel" test and prints the result to the output + * buffer. + * + * RETURN: Size of debugfs data print + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t cm_panel_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + + if (!*ppos) + /* Set length to PIP_CMD_MAX_LENGTH to read all */ + data->pr_buf_len = pt_run_and_get_selftest_result( + data->dad->dev, PT_CORE_CMD_PROTECTED, + data->pr_buf, sizeof(data->pr_buf), + PT_ST_ID_CM_PANEL, PIP_CMD_MAX_LENGTH, + PT_ST_GET_RESULTS, PT_ST_PRINT_RESULTS, + PT_PR_FORMAT_DEFAULT); + + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +PT_DEBUGFS_FOPS(cm_panel, cm_panel_debugfs_read, NULL); + +/******************************************************************************* + * FUNCTION: cp_panel_debugfs_read + * + * SUMMARY: Performs the "CP panel" test and prints the result to the output + * buffer. + * + * RETURN: Size of debugfs data print + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t cp_panel_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + + if (!*ppos) + /* Set length to PIP_CMD_MAX_LENGTH to read all */ + data->pr_buf_len = pt_run_and_get_selftest_result( + data->dad->dev, PT_CORE_CMD_PROTECTED, + data->pr_buf, sizeof(data->pr_buf), + PT_ST_ID_CP_PANEL, PIP_CMD_MAX_LENGTH, + PT_ST_GET_RESULTS, PT_ST_PRINT_RESULTS, + PT_PR_FORMAT_DEFAULT); + + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +PT_DEBUGFS_FOPS(cp_panel, cp_panel_debugfs_read, NULL); + +/******************************************************************************* + * FUNCTION: cm_button_debugfs_read + * + * SUMMARY: Performs the "CM buttons" test and prints the result to the output + * buffer. + * + * RETURN: Size of debugfs data print + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t cm_button_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + + if (!*ppos) + /* Set length to PIP_CMD_MAX_LENGTH to read all */ + data->pr_buf_len = pt_run_and_get_selftest_result( + data->dad->dev, PT_CORE_CMD_PROTECTED, + data->pr_buf, sizeof(data->pr_buf), + PT_ST_ID_CM_BUTTON, PIP_CMD_MAX_LENGTH, + PT_ST_GET_RESULTS, PT_ST_PRINT_RESULTS, + PT_PR_FORMAT_DEFAULT); + + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +PT_DEBUGFS_FOPS(cm_button, cm_button_debugfs_read, NULL); + +/******************************************************************************* + * FUNCTION: cp_button_debugfs_read + * + * SUMMARY: Performs the "CP buttons" test and prints the result to the output + * buffer. + * + * RETURN: Size of debugfs data print + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t cp_button_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + + if (!*ppos) + /* Set length to PIP_CMD_MAX_LENGTH to read all */ + data->pr_buf_len = pt_run_and_get_selftest_result( + data->dad->dev, PT_CORE_CMD_PROTECTED, + data->pr_buf, sizeof(data->pr_buf), + PT_ST_ID_CP_BUTTON, PIP_CMD_MAX_LENGTH, + PT_ST_GET_RESULTS, PT_ST_PRINT_RESULTS, + PT_PR_FORMAT_DEFAULT); + + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +PT_DEBUGFS_FOPS(cp_button, cp_button_debugfs_read, NULL); + +/******************************************************************************* + * FUNCTION: fw_self_test_debugfs_read + * + * SUMMARY: Performs the self test by firmware and prints the results to the + * output buffer. + * + * RETURN: Size of debugfs data print + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t fw_self_test_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + u8 ret_status; + u8 ret_self_test_id; + u8 sys_mode = FW_SYS_MODE_UNDEFINED; + u16 ret_act_load_len; + int rc; + struct pt_device_access_debugfs_data *data = filp->private_data; + struct pt_device_access_data *dad = data->dad; + + if (!*ppos) { + if (dad->fw_self_test_id == PT_ST_ID_INVALID || + dad->fw_self_test_format >= PT_PR_FORMAT_UNDEFINE) { + data->pr_buf_len = scnprintf(data->pr_buf, + sizeof(data->pr_buf), "Status: %d\n", -EINVAL); + pt_debug(dad->dev, DL_ERROR, + "%s: Malformed input\n", __func__); + goto exit; + } + + /* only send the load parameters cmd if param data exists */ + if (dad->fw_self_test_param_len > 0) { + rc = cmd->request_get_fw_mode(dad->dev, + PT_CORE_CMD_UNPROTECTED, &sys_mode, NULL); + if (rc) { + pt_debug(dad->dev, DL_ERROR, + "%s: ERR on request mode rc=%d\n", + __func__, rc); + data->pr_buf_len = scnprintf( + data->pr_buf, + sizeof(data->pr_buf), + "Status: %d\n", rc); + goto exit; + } + + if (sys_mode != FW_SYS_MODE_TEST) { + rc = pt_suspend_scan_cmd_(dad->dev); + if (rc) { + pt_debug(dad->dev, DL_ERROR, + "%s: ERR on sus scan rc=%d\n", + __func__, rc); + data->pr_buf_len = scnprintf( + data->pr_buf, + sizeof(data->pr_buf), + "Status: %d\n", rc); + goto exit; + } + } + cmd->nonhid_cmd->load_self_test_param(dad->dev, + PT_CORE_CMD_PROTECTED, + dad->fw_self_test_id, 0, + dad->fw_self_test_param_len, + dad->fw_self_test_param, &ret_status, + &ret_self_test_id, &ret_act_load_len); + if (ret_status) { + data->pr_buf_len = scnprintf(data->pr_buf, + sizeof(data->pr_buf), + "Status: %d\n", -EINVAL); + pt_debug(dad->dev, DL_ERROR, + "%s: Load Param Malformed input\n", + __func__); + goto resume_scan; + } + } + + /* Set length to PIP_CMD_MAX_LENGTH to read all */ + data->pr_buf_len = pt_run_and_get_selftest_result( + dad->dev, PT_CORE_CMD_PROTECTED, + data->pr_buf, sizeof(data->pr_buf), + dad->fw_self_test_id, PIP_CMD_MAX_LENGTH, + PT_ST_GET_RESULTS, PT_ST_PRINT_RESULTS, + dad->fw_self_test_format); + + /* Clear the parameters so next test won't use them */ + if (dad->fw_self_test_param_len > 0) { + cmd->nonhid_cmd->load_self_test_param(dad->dev, + PT_CORE_CMD_PROTECTED, + dad->fw_self_test_id, 0, 0, NULL, + &ret_status, &ret_self_test_id, + &ret_act_load_len); + } + + dad->fw_self_test_id = PT_ST_ID_INVALID; + dad->fw_self_test_format = PT_PR_FORMAT_UNDEFINE; + dad->fw_self_test_param_len = 0; + +resume_scan: + /* Only resume scanning if we suspended it */ + if (sys_mode == FW_SYS_MODE_SCANNING) + pt_resume_scan_cmd_(dad->dev); + } + +exit: + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +/******************************************************************************* + * FUNCTION: fw_self_test_debugfs_write + * + * SUMMARY: Store the self test ID and output format, the read method will + * perform. + * + * RETURN: Size of debugfs data write + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to write to + * count - the maximum number of bytes to write + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t fw_self_test_debugfs_write(struct file *filp, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct pt_device_access_debugfs_data *data = filp->private_data; + struct pt_device_access_data *dad = data->dad; + ssize_t length; + u32 input_data[PT_FW_SELF_TEST_MAX_PARM + 1]; + int rc = 0; + int i; + + rc = simple_write_to_buffer(data->pr_buf, sizeof(data->pr_buf), ppos, + buf, count); + if (rc < 0) + return rc; + + count = rc; + + mutex_lock(&dad->sysfs_lock); + + length = cmd->parse_sysfs_input(dad->dev, data->pr_buf, count, + input_data, PT_FW_SELF_TEST_MAX_PARM + 1); + if (length == 1) { + dad->fw_self_test_id = input_data[0]; + dad->fw_self_test_format = PT_PR_FORMAT_DEFAULT; + dad->fw_self_test_param_len = 0; + } else if (length == 2) { + dad->fw_self_test_id = input_data[0]; + dad->fw_self_test_format = input_data[1]; + dad->fw_self_test_param_len = 0; + } else if (length > 2 && (length <= PT_FW_SELF_TEST_MAX_PARM)) { + dad->fw_self_test_id = input_data[0]; + dad->fw_self_test_format = input_data[1]; + dad->fw_self_test_param_len = length - 2; + pt_debug(dad->dev, DL_INFO, + "%s: test_id=%d, format=%d, param_len=%d", + __func__, dad->fw_self_test_id, + dad->fw_self_test_format, dad->fw_self_test_param_len); + for (i = 0; i < dad->fw_self_test_param_len; i++) + dad->fw_self_test_param[i] = input_data[i + 2]; + } else { + pt_debug(dad->dev, DL_ERROR, + "%s: Malformed input\n", __func__); + rc = -EINVAL; + goto exit_unlock; + } + +exit_unlock: + mutex_unlock(&dad->sysfs_lock); + + if (rc) + return rc; + + return count; +} + +PT_DEBUGFS_FOPS(fw_self_test, + fw_self_test_debugfs_read, fw_self_test_debugfs_write); + +#ifdef TTHE_TUNER_SUPPORT +/******************************************************************************* + * FUNCTION: tthe_get_panel_data_debugfs_read + * + * SUMMARY: Performs a panel scan and prints the panel data into the output + * buffer. + * + * RETURN: Size of debugfs data print + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to read to + * count - the maximum number of bytes to read + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t tthe_get_panel_data_debugfs_read(struct file *filp, + char __user *buf, size_t count, loff_t *ppos) +{ + struct pt_device_access_data *dad = filp->private_data; + struct device *dev; + struct pt_core_data *cd; + u8 config; + u16 actual_read_len; + u16 length = 0; + u8 element_size = 0; + u8 *buf_offset; + u8 *buf_out; + int elem; + int elem_offset = 0; + int print_idx = 0; + int rc; + int rc1; + int i; + + mutex_lock(&dad->debugfs_lock); + dev = dad->dev; + cd = dev_get_drvdata(dev); + buf_out = dad->panel_scan_data_buf; + if (!buf_out) + goto release_mutex; + + pm_runtime_get_sync(dev); + + rc = cmd->request_exclusive(dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc) + goto put_runtime; + + if (dad->heatmap.scan_start) { + /* + * To fix CDT206291: avoid multiple scans when + * return data is larger than 4096 bytes in one cycle + */ + dad->heatmap.scan_start = 0; + + /* Start scan */ + rc = pt_exec_scan_cmd_(dev, 0); + if (rc) + goto release_exclusive; + } + + elem = dad->heatmap.num_element; + +#if defined(PT_ENABLE_MAX_ELEN) + if (elem > PT_MAX_ELEN) { + rc = pt_ret_scan_data_cmd_(dev, elem_offset, + PT_MAX_ELEN, dad->heatmap.data_type, dad->ic_buf, + &config, &actual_read_len, NULL); + } else{ + rc = pt_ret_scan_data_cmd_(dev, elem_offset, elem, + dad->heatmap.data_type, dad->ic_buf, &config, + &actual_read_len, NULL); + } +#else + rc = pt_ret_scan_data_cmd_(dev, elem_offset, elem, + dad->heatmap.data_type, dad->ic_buf, &config, + &actual_read_len, NULL); +#endif + if (rc) + goto release_exclusive; + + length = get_unaligned_le16(&dad->ic_buf[0]); + buf_offset = dad->ic_buf + length; + + element_size = config & PT_CMD_RET_PANEL_ELMNT_SZ_MASK; + + elem -= actual_read_len; + elem_offset = actual_read_len; + while (elem > 0) { +#ifdef PT_ENABLE_MAX_ELEN + if (elem > PT_MAX_ELEN) { + rc = pt_ret_scan_data_cmd_(dev, elem_offset, + PT_MAX_ELEN, dad->heatmap.data_type, NULL, &config, + &actual_read_len, buf_offset); + } else{ + rc = pt_ret_scan_data_cmd_(dev, elem_offset, elem, + dad->heatmap.data_type, NULL, &config, + &actual_read_len, buf_offset); + } +#else + + rc = pt_ret_scan_data_cmd_(dev, elem_offset, elem, + dad->heatmap.data_type, NULL, &config, + &actual_read_len, buf_offset); +#endif + if (rc) + goto release_exclusive; + + if (!actual_read_len) + break; + + length += actual_read_len * element_size; + buf_offset = dad->ic_buf + length; + elem -= actual_read_len; + elem_offset += actual_read_len; + } + + /* Reconstruct cmd header */ + put_unaligned_le16(length, &dad->ic_buf[0]); + put_unaligned_le16(elem_offset, &dad->ic_buf[7]); + +release_exclusive: + rc1 = cmd->release_exclusive(dev); +put_runtime: + pm_runtime_put(dev); + + if (rc) + goto release_mutex; + if (cd->show_timestamp) + print_idx += scnprintf(buf_out, TTHE_TUNER_MAX_BUF, + "[%u] PT_DATA:", pt_get_time_stamp()); + else + print_idx += scnprintf(buf_out, TTHE_TUNER_MAX_BUF, + "PT_DATA:"); + for (i = 0; i < length; i++) + print_idx += scnprintf(buf_out + print_idx, + TTHE_TUNER_MAX_BUF - print_idx, + "%02X ", dad->ic_buf[i]); + print_idx += scnprintf(buf_out + print_idx, + TTHE_TUNER_MAX_BUF - print_idx, + ":(%d bytes)\n", length); + rc = simple_read_from_buffer(buf, count, ppos, buf_out, print_idx); + print_idx = rc; + +release_mutex: + mutex_unlock(&dad->debugfs_lock); + return print_idx; +} + +/******************************************************************************* + * FUNCTION: tthe_get_panel_data_debugfs_write + * + * SUMMARY: Store the panel data type to retrieve and size of panel data, the + * read method will perform. + * + * RETURN: Size of debugfs data write + * + * PARAMETERS: + * *filp - file pointer to debugfs file + * *buf - the user space buffer to write to + * count - the maximum number of bytes to write + * *ppos - the current position in the buffer + ******************************************************************************/ +static ssize_t tthe_get_panel_data_debugfs_write(struct file *filp, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct pt_device_access_data *dad = filp->private_data; + struct device *dev = dad->dev; + ssize_t length; + int max_read; + u32 input_data[8]; + u8 *buf_in = dad->panel_scan_data_buf; + int ret; + + mutex_lock(&dad->debugfs_lock); + ret = copy_from_user(buf_in + (*ppos), buf, count); + if (ret) + goto exit; + buf_in[count] = 0; + + length = cmd->parse_sysfs_input(dev, buf_in, count, input_data, + ARRAY_SIZE(input_data)); + if (length <= 0) { + pt_debug(dev, DL_ERROR, + "%s: %s Group Data store\n", + __func__, "Malformed input for"); + goto exit; + } + + /* update parameter value */ + dad->heatmap.num_element = input_data[3] + (input_data[4] << 8); + dad->heatmap.data_type = input_data[5]; + + if (input_data[6] > 0) + dad->heatmap.scan_start = true; + else + dad->heatmap.scan_start = false; + + /* elem can not be bigger then buffer size */ + max_read = PT_CMD_RET_PANEL_HDR; + max_read += dad->heatmap.num_element * PT_CMD_RET_PANEL_ELMNT_SZ_MAX; + + if (max_read >= PT_MAX_PRBUF_SIZE) { + dad->heatmap.num_element = + (PT_MAX_PRBUF_SIZE - PT_CMD_RET_PANEL_HDR) + / PT_CMD_RET_PANEL_ELMNT_SZ_MAX; + pt_debug(dev, DL_INFO, "%s: Will get %d element\n", + __func__, dad->heatmap.num_element); + } + +exit: + mutex_unlock(&dad->debugfs_lock); + pt_debug(dev, DL_INFO, "%s: return count=%zu\n", + __func__, count); + return count; +} + +/******************************************************************************* + * FUNCTION: tthe_get_panel_data_debugfs_open + * + * SUMMARY: Open the get_panel_data debugfs node to initialize. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *inode - pointer to inode structure + * *filp - pointer to file structure + ******************************************************************************/ +static int tthe_get_panel_data_debugfs_open(struct inode *inode, + struct file *filp) +{ + struct pt_device_access_data *dad = inode->i_private; + + mutex_lock(&dad->debugfs_lock); + + if (dad->tthe_get_panel_data_is_open) { + mutex_unlock(&dad->debugfs_lock); + return -EBUSY; + } + + filp->private_data = inode->i_private; + + dad->tthe_get_panel_data_is_open = 1; + mutex_unlock(&dad->debugfs_lock); + return 0; +} + +/******************************************************************************* + * FUNCTION: tthe_get_panel_data_debugfs_close + * + * SUMMARY: Close the get_panel_data debugfs node to free pointer. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *inode - pointer to inode structure + * *filp - pointer to file structure + ******************************************************************************/ +static int tthe_get_panel_data_debugfs_close(struct inode *inode, + struct file *filp) +{ + struct pt_device_access_data *dad = filp->private_data; + + mutex_lock(&dad->debugfs_lock); + filp->private_data = NULL; + dad->tthe_get_panel_data_is_open = 0; + mutex_unlock(&dad->debugfs_lock); + + return 0; +} + +static const struct file_operations tthe_get_panel_data_fops = { + .open = tthe_get_panel_data_debugfs_open, + .release = tthe_get_panel_data_debugfs_close, + .read = tthe_get_panel_data_debugfs_read, + .write = tthe_get_panel_data_debugfs_write, +}; +#endif + +/******************************************************************************* + * FUNCTION: pt_setup_sysfs + * + * SUMMARY: Creates all device test dependent sysfs nodes owned by the + * device access file. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_setup_sysfs(struct device *dev) +{ + struct pt_device_access_data *dad + = pt_get_device_access_data(dev); + int rc = 0; + + pt_debug(dev, DL_INFO, "Entering %s\n", __func__); + + dad->base_dentry = debugfs_create_dir(dev_name(dev), NULL); + if (IS_ERR_OR_NULL(dad->base_dentry)) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create base directory\n", + __func__); + goto exit; + } + + dad->mfg_test_dentry = debugfs_create_dir("mfg_test", + dad->base_dentry); + if (IS_ERR_OR_NULL(dad->mfg_test_dentry)) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create mfg_test directory\n", + __func__); + goto unregister_base_dir; + } + + dad->panel_scan_debugfs = debugfs_create_file( + "panel_scan", 0644, dad->mfg_test_dentry, dad, + &panel_scan_fops); + if (IS_ERR_OR_NULL(dad->panel_scan_debugfs)) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create panel_scan\n", + __func__); + goto unregister_base_dir; + } + + if (IS_ERR_OR_NULL(debugfs_create_file("get_idac", 0600, + dad->mfg_test_dentry, dad, &get_idac_debugfs_fops))) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create get_idac\n", + __func__); + goto unregister_base_dir; + } + + if (IS_ERR_OR_NULL(debugfs_create_file("auto_shorts", 0400, + dad->mfg_test_dentry, dad, + &auto_shorts_debugfs_fops))) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create auto_shorts\n", + __func__); + goto unregister_base_dir; + } + + if (IS_ERR_OR_NULL(debugfs_create_file("opens", 0400, + dad->mfg_test_dentry, dad, &opens_debugfs_fops))) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create opens\n", + __func__); + goto unregister_base_dir; + } + + if (IS_ERR_OR_NULL(debugfs_create_file("calibrate_ext", + 0600, dad->mfg_test_dentry, + dad, &calibrate_ext_debugfs_fops))) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create calibrate_ext\n", + __func__); + goto unregister_base_dir; + } + + if (IS_ERR_OR_NULL(debugfs_create_file("calibrate", 0600, + dad->mfg_test_dentry, dad, &calibrate_debugfs_fops))) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create calibrate\n", + __func__); + goto unregister_base_dir; + } + + if (IS_ERR_OR_NULL(debugfs_create_file("baseline", 0600, + dad->mfg_test_dentry, dad, &baseline_debugfs_fops))) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create baseline\n", + __func__); + goto unregister_base_dir; + } + + if (IS_ERR_OR_NULL(debugfs_create_file("cm_panel", 0400, + dad->mfg_test_dentry, dad, &cm_panel_debugfs_fops))) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create cm_panel\n", + __func__); + goto unregister_base_dir; + } + + if (IS_ERR_OR_NULL(debugfs_create_file("cp_panel", 0400, + dad->mfg_test_dentry, dad, &cp_panel_debugfs_fops))) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create cp_panel\n", + __func__); + goto unregister_base_dir; + } + + if (IS_ERR_OR_NULL(debugfs_create_file("cm_button", 0400, + dad->mfg_test_dentry, dad, &cm_button_debugfs_fops))) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create cm_button\n", + __func__); + goto unregister_base_dir; + } + + if (IS_ERR_OR_NULL(debugfs_create_file("cp_button", 0400, + dad->mfg_test_dentry, dad, &cp_button_debugfs_fops))) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create cp_button\n", + __func__); + goto unregister_base_dir; + } + + if (IS_ERR_OR_NULL(debugfs_create_file("fw_self_test", 0600, + dad->mfg_test_dentry, dad, &fw_self_test_debugfs_fops))) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create fw_self_test\n", + __func__); + goto unregister_base_dir; + } + + dad->cmcp_results_debugfs = debugfs_create_file("cmcp_results", 0644, + dad->mfg_test_dentry, dad, &cmcp_results_debugfs_fops); + if (IS_ERR_OR_NULL(dad->cmcp_results_debugfs)) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create cmcp_results\n", + __func__); + dad->cmcp_results_debugfs = NULL; + goto unregister_base_dir; + } + +#ifdef TTHE_TUNER_SUPPORT + dad->tthe_get_panel_data_debugfs = debugfs_create_file( + PT_TTHE_TUNER_GET_PANEL_DATA_FILE_NAME, + 0644, NULL, dad, &tthe_get_panel_data_fops); + if (IS_ERR_OR_NULL(dad->tthe_get_panel_data_debugfs)) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create get_panel_data\n", + __func__); + dad->tthe_get_panel_data_debugfs = NULL; + goto unregister_base_dir; + } +#endif + + rc = device_create_file(dev, &dev_attr_cmcp_test); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create cmcp_test\n", + __func__); + goto unregister_base_dir; + } + + rc = device_create_file(dev, &dev_attr_cmcp_threshold_loading); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create cmcp_thresold_loading\n", + __func__); + goto unregister_cmcp_test; + } + + rc = device_create_bin_file(dev, &bin_attr_cmcp_threshold_data); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error, could not create cmcp_thresold_data\n", + __func__); + goto unregister_cmcp_thresold_loading; + } + + dad->sysfs_nodes_created = true; + return rc; + +unregister_cmcp_thresold_loading: + device_remove_file(dev, &dev_attr_cmcp_threshold_loading); +unregister_cmcp_test: + device_remove_file(dev, &dev_attr_cmcp_test); +unregister_base_dir: + debugfs_remove_recursive(dad->base_dentry); +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_setup_cmcp_attention + * + * SUMMARY: Function to be registered to TTDL attention list to setup sysfs and + * parse threshold file for device test. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_setup_cmcp_attention(struct device *dev) +{ + struct pt_device_access_data *dad + = pt_get_device_access_data(dev); + int rc = 0; + + dad->si = cmd->request_sysinfo(dev); + if (!dad->si) + return -EINVAL; + + rc = pt_setup_sysfs(dev); + schedule_work(&dad->cmcp_threshold_update); + + cmd->unsubscribe_attention(dev, PT_ATTEN_STARTUP, + PT_DEVICE_ACCESS_NAME, pt_setup_cmcp_attention, + 0); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_cmcp_parse_threshold_file + * + * SUMMARY: Parse cmcp threshold file and store it to the structure. + * + * PARAMETERS: + * *fw - pointer to firmware structure + * *context - expected read length of the response + ******************************************************************************/ +static void pt_cmcp_parse_threshold_file(const struct firmware *fw, + void *context) +{ + struct device *dev = context; + struct pt_device_access_data *dad = + pt_get_device_access_data(dev); + + if (!fw) { + pt_debug(dev, DL_WARN, + "%s: No builtin cmcp threshold file\n", + __func__); + goto exit; + } + + if (!fw->data || !fw->size) { + pt_debug(dev, DL_ERROR, + "%s: Invalid builtin cmcp threshold file\n", + __func__); + goto exit; + } + + pt_debug(dev, DL_WARN, "%s: Found cmcp threshold file.\n", + __func__); + + pt_parse_cmcp_threshold_file_common(dev, &fw->data[0], fw->size); + + dad->builtin_cmcp_threshold_status = 0; + release_firmware(fw); + return; + +exit: + if (fw) + release_firmware(fw); + + dad->builtin_cmcp_threshold_status = -EINVAL; +} + +/******************************************************************************* + * FUNCTION: pt_device_access_user_command + * + * SUMMARY: Wrapper function to call pt_cmcp_parse_threshold_file() by firmware + * class function. + * + * PARAMETERS: + * *cmcp_threshold_update - pointer to work_struct structure + ******************************************************************************/ +static void pt_parse_cmcp_threshold_builtin( + struct work_struct *cmcp_threshold_update) +{ + struct pt_device_access_data *dad = + container_of(cmcp_threshold_update, + struct pt_device_access_data, + cmcp_threshold_update); + struct device *dev = dad->dev; + struct pt_core_data *cd = dev_get_drvdata(dev); + const struct firmware *fw_entry = NULL; + int retval; + + dad->si = cmd->request_sysinfo(dev); + if (!dad->si) { + pt_debug(dev, DL_ERROR, + "%s: Fail get sysinfo pointer from core\n", + __func__); + return; + } + + pt_debug(dev, DL_INFO, + "%s: Enabling cmcp threshold class loader built-in\n", + __func__); + + /* Open threshold file */ + mutex_lock(&cd->firmware_class_lock); +#if (KERNEL_VERSION(3, 13, 0) > LINUX_VERSION_CODE) + retval = request_firmware(&fw_entry, CMCP_THRESHOLD_FILE_NAME, dev); +#else + retval = request_firmware_direct(&fw_entry, + CMCP_THRESHOLD_FILE_NAME, dev); +#endif + if (retval < 0) { + pt_debug(dev, DL_WARN, + "%s: Failed loading cmcp threshold file, attempting legacy file\n", + __func__); + /* Try legacy file name */ +#if (KERNEL_VERSION(3, 13, 0) > LINUX_VERSION_CODE) + retval = request_firmware(&fw_entry, + PT_CMCP_THRESHOLD_FILE_NAME, dev); +#else + retval = request_firmware_direct(&fw_entry, + PT_CMCP_THRESHOLD_FILE_NAME, dev); +#endif + if (retval < 0) { + mutex_unlock(&cd->firmware_class_lock); + dad->builtin_cmcp_threshold_status = -EINVAL; + pt_debug(dev, DL_WARN, + "%s: Fail request cmcp threshold class file load\n", + __func__); + goto exit; + } + } + pt_cmcp_parse_threshold_file(fw_entry, dev); + + mutex_unlock(&cd->firmware_class_lock); + +exit: + return; +} + +/******************************************************************************* + * FUNCTION: pt_device_access_probe + * + * SUMMARY: The probe function for the device access + * + * PARAMETERS: + * *dev - pointer to device structure + * **data - double pointer to pt_device_access_data data to be created here + ******************************************************************************/ +static int pt_device_access_probe(struct device *dev, void **data) +{ + struct pt_device_access_data *dad; + struct configuration *configurations; + struct cmcp_data *cmcp_info; + struct result *result; + + int tx_num = MAX_TX_SENSORS; + int rx_num = MAX_RX_SENSORS; + int btn_num = MAX_BUTTONS; + + struct test_case_field *test_case_field_array; + struct test_case_search *test_case_search_array; + int rc = 0; + + dad = kzalloc(sizeof(*dad), GFP_KERNEL); + if (!dad) { + rc = -ENOMEM; + goto pt_device_access_probe_data_failed; + } + + configurations = + kzalloc(sizeof(*configurations), GFP_KERNEL); + if (!configurations) { + rc = -ENOMEM; + goto pt_device_access_probe_configs_failed; + } + dad->configs = configurations; + + cmcp_info = kzalloc(sizeof(*cmcp_info), GFP_KERNEL); + if (!cmcp_info) { + rc = -ENOMEM; + goto pt_device_access_probe_cmcp_info_failed; + } + dad->cmcp_info = cmcp_info; + + cmcp_info->tx_num = tx_num; + cmcp_info->rx_num = rx_num; + cmcp_info->btn_num = btn_num; + + result = kzalloc(sizeof(*result), GFP_KERNEL); + if (!result) { + rc = -ENOMEM; + goto pt_device_access_probe_result_failed; + } + dad->result = result; + + test_case_field_array = + kzalloc(sizeof(*test_case_field_array) * MAX_CASE_NUM, + GFP_KERNEL); + if (!test_case_field_array) { + rc = -ENOMEM; + goto pt_device_access_probe_field_array_failed; + } + + test_case_search_array = + kzalloc(sizeof(*test_case_search_array) * MAX_CASE_NUM, + GFP_KERNEL); + if (!test_case_search_array) { + rc = -ENOMEM; + goto pt_device_access_probe_search_array_failed; + } + + cmcp_info->gd_sensor_col = (struct gd_sensor *) + kzalloc(tx_num * sizeof(struct gd_sensor), GFP_KERNEL); + if (cmcp_info->gd_sensor_col == NULL) + goto pt_device_access_probe_gd_sensor_col_failed; + + cmcp_info->gd_sensor_row = (struct gd_sensor *) + kzalloc(rx_num * sizeof(struct gd_sensor), GFP_KERNEL); + if (cmcp_info->gd_sensor_row == NULL) + goto pt_device_access_probe_gd_sensor_row_failed; + + cmcp_info->cm_data_panel = + kzalloc((tx_num * rx_num + 1) * sizeof(int32_t), GFP_KERNEL); + if (cmcp_info->cm_data_panel == NULL) + goto pt_device_access_probe_cm_data_panel_failed; + + cmcp_info->cp_tx_data_panel = + kzalloc(tx_num * sizeof(int32_t), GFP_KERNEL); + if (cmcp_info->cp_tx_data_panel == NULL) + goto pt_device_access_probe_cp_tx_data_panel_failed; + + cmcp_info->cp_tx_cal_data_panel = + kzalloc(tx_num * sizeof(int32_t), GFP_KERNEL); + if (cmcp_info->cp_tx_cal_data_panel == NULL) + goto pt_device_access_probe_cp_tx_cal_data_panel_failed; + + cmcp_info->cp_rx_data_panel = + kzalloc(rx_num * sizeof(int32_t), GFP_KERNEL); + if (cmcp_info->cp_rx_data_panel == NULL) + goto pt_device_access_probe_cp_rx_data_panel_failed; + + cmcp_info->cp_rx_cal_data_panel = + kzalloc(rx_num * sizeof(int32_t), GFP_KERNEL); + if (cmcp_info->cp_rx_cal_data_panel == NULL) + goto pt_device_access_probe_cp_rx_cal_data_panel_failed; + + cmcp_info->cm_btn_data = kcalloc(btn_num, sizeof(int32_t), GFP_KERNEL); + if (cmcp_info->cm_btn_data == NULL) + goto pt_device_access_probe_cm_btn_data_failed; + + cmcp_info->cp_btn_data = kcalloc(btn_num, sizeof(int32_t), GFP_KERNEL); + if (cmcp_info->cp_btn_data == NULL) + goto pt_device_access_probe_cp_btn_data_failed; + + cmcp_info->cm_sensor_column_delta = + kzalloc(rx_num * tx_num * sizeof(int32_t), GFP_KERNEL); + if (cmcp_info->cm_sensor_column_delta == NULL) + goto pt_device_access_probe_cm_sensor_column_delta_failed; + + cmcp_info->cm_sensor_row_delta = + kzalloc(tx_num * rx_num * sizeof(int32_t), GFP_KERNEL); + if (cmcp_info->cm_sensor_row_delta == NULL) + goto pt_device_access_probe_cm_sensor_row_delta_failed; + + mutex_init(&dad->sysfs_lock); + mutex_init(&dad->cmcp_threshold_lock); + dad->dev = dev; +#ifdef TTHE_TUNER_SUPPORT + mutex_init(&dad->debugfs_lock); + dad->heatmap.num_element = 200; +#endif + *data = dad; + + dad->test_field_array = test_case_field_array; + dad->test_search_array = test_case_search_array; + dad->test_executed = 0; + + dad->cal_ext_data.mode = PT_CAL_EXT_MODE_UNDEFINED; + dad->panel_scan_retrieve_id = 0; + dad->panel_scan_type_id = 0; + + INIT_WORK(&dad->cmcp_threshold_update, pt_parse_cmcp_threshold_builtin); + + /* get sysinfo */ + dad->si = cmd->request_sysinfo(dev); + if (dad->si) { + rc = pt_setup_sysfs(dev); + if (rc) + goto pt_device_access_setup_sysfs_failed; + } else { + pt_debug(dev, DL_ERROR, + "%s: Fail get sysinfo pointer from core p=%p\n", + __func__, dad->si); + cmd->subscribe_attention(dev, PT_ATTEN_STARTUP, + PT_DEVICE_ACCESS_NAME, + pt_setup_cmcp_attention, 0); + } + + schedule_work(&dad->cmcp_threshold_update); + + return 0; + +pt_device_access_setup_sysfs_failed: + kfree(cmcp_info->cm_sensor_row_delta); +pt_device_access_probe_cm_sensor_row_delta_failed: + kfree(cmcp_info->cm_sensor_column_delta); +pt_device_access_probe_cm_sensor_column_delta_failed: + kfree(cmcp_info->cp_btn_data); +pt_device_access_probe_cp_btn_data_failed: + kfree(cmcp_info->cm_btn_data); +pt_device_access_probe_cm_btn_data_failed: + kfree(cmcp_info->cp_rx_cal_data_panel); +pt_device_access_probe_cp_rx_cal_data_panel_failed: + kfree(cmcp_info->cp_rx_data_panel); +pt_device_access_probe_cp_rx_data_panel_failed: + kfree(cmcp_info->cp_tx_cal_data_panel); +pt_device_access_probe_cp_tx_cal_data_panel_failed: + kfree(cmcp_info->cp_tx_data_panel); +pt_device_access_probe_cp_tx_data_panel_failed: + kfree(cmcp_info->cm_data_panel); +pt_device_access_probe_cm_data_panel_failed: + kfree(cmcp_info->gd_sensor_row); +pt_device_access_probe_gd_sensor_row_failed: + kfree(cmcp_info->gd_sensor_col); +pt_device_access_probe_gd_sensor_col_failed: + kfree(test_case_search_array); +pt_device_access_probe_search_array_failed: + kfree(test_case_field_array); +pt_device_access_probe_field_array_failed: + kfree(result); +pt_device_access_probe_result_failed: + kfree(cmcp_info); +pt_device_access_probe_cmcp_info_failed: + kfree(configurations); +pt_device_access_probe_configs_failed: + kfree(dad); +pt_device_access_probe_data_failed: + pt_debug(dev, DL_ERROR, "%s failed.\n", __func__); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_device_access_release + * + * SUMMARY: Remove function for device_access module that does following + * cleanup: + * - Unsubscibe all registered attention tasks + * - Removes all created sysfs nodes + * - Frees all pointers + * + * PARAMETERS: + * *dev - pointer to device structure + * *data - pointer to the device_access data + ******************************************************************************/ +static void pt_device_access_release(struct device *dev, void *data) +{ + struct pt_device_access_data *dad = data; + + if (dad->sysfs_nodes_created) { + debugfs_remove(dad->cmcp_results_debugfs); + debugfs_remove_recursive(dad->base_dentry); +#ifdef TTHE_TUNER_SUPPORT + debugfs_remove(dad->tthe_get_panel_data_debugfs); +#endif + device_remove_file(dev, &dev_attr_cmcp_test); + device_remove_file(dev, &dev_attr_cmcp_threshold_loading); + device_remove_bin_file(dev, &bin_attr_cmcp_threshold_data); + kfree(dad->cmcp_threshold_data); + } else { + cmd->unsubscribe_attention(dev, PT_ATTEN_STARTUP, + PT_DEVICE_ACCESS_NAME, + pt_setup_cmcp_attention, 0); + } + + kfree(dad->test_search_array); + kfree(dad->test_field_array); + kfree(dad->configs); + pt_free_cmcp_buf(dad->cmcp_info); + kfree(dad->cmcp_info); + kfree(dad->result); + kfree(dad); +} + +static struct pt_module device_access_module = { + .name = PT_DEVICE_ACCESS_NAME, + .probe = pt_device_access_probe, + .release = pt_device_access_release, +}; + +/******************************************************************************* + * FUNCTION: pt_device_access_init + * + * SUMMARY: Initialize function for device access module which to register + * device_access_module into TTDL module list. + * + * RETURN: + * 0 = success + * !0 = failure + ******************************************************************************/ +static int __init pt_device_access_init(void) +{ + int rc; + + cmd = pt_get_commands(); + if (!cmd) + return -EINVAL; + + rc = pt_register_module(&device_access_module); + if (rc) { + pr_err("%s: Error, failed registering module\n", + __func__); + return rc; + } + + pr_info("%s: Parade TTSP Device Access Driver (Built %s) rc = %d\n", + __func__, PT_DRIVER_VERSION, rc); + return 0; +} +module_init(pt_device_access_init); + +/******************************************************************************* + * FUNCTION: pt_device_access_exit + * + * SUMMARY: Exit function for device access module which to unregister + * device_access_module from TTDL module list. + * + ******************************************************************************/ +static void __exit pt_device_access_exit(void) +{ + pt_unregister_module(&device_access_module); +} +module_exit(pt_device_access_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Device Access Driver"); +MODULE_AUTHOR("Parade Technologies "); +#endif /* !TTDL_KERNEL_SUBMISSION */ diff --git a/pt/pt_devtree.c b/pt/pt_devtree.c new file mode 100644 index 0000000000..b147a7d0d5 --- /dev/null +++ b/pt/pt_devtree.c @@ -0,0 +1,1128 @@ +/* + * pt_devtree.c + * Parade TrueTouch(TM) Standard Product Device Tree Support Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.paradetech.com + */ + +#include +#include +#include +#include +#include "pt_regs.h" +#include + +#define MAX_NAME_LENGTH 64 + +static bool is_create_and_get_pdata; + +enum pt_device_type { + DEVICE_MT, + DEVICE_BTN, + DEVICE_PEN, + DEVICE_PROXIMITY, + DEVICE_TYPE_MAX, +}; + +struct pt_device_pdata_func { + void * (*create_and_get_pdata)(struct device_node *dn); + void (*free_pdata)(void *ptr); +}; + +struct pt_pdata_ptr { + void **pdata; +}; + +#ifdef ENABLE_VIRTUAL_KEYS +static struct kobject *board_properties_kobj; + +struct pt_virtual_keys { + struct kobj_attribute kobj_attr; + u16 *data; + int size; +}; +#endif + +struct pt_extended_mt_platform_data { + struct pt_mt_platform_data pdata; +#ifdef ENABLE_VIRTUAL_KEYS + struct pt_virtual_keys vkeys; +#endif +}; + +/******************************************************************************* + * FUNCTION: get_inp_dev_name + * + * SUMMARY: Get the name of input device from dts. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev_node - pointer to device_node structure + * **inp_dev_name - double pointer to the name of input device + ******************************************************************************/ +static inline int get_inp_dev_name(struct device_node *dev_node, + const char **inp_dev_name) +{ + return of_property_read_string(dev_node, "parade,inp_dev_name", + inp_dev_name); +} + +/******************************************************************************* + * FUNCTION: create_and_get_u16_array + * + * SUMMARY: Create and get u16 array from dts. + * + * RETURN: + * success: the pointer of the created array + * fail : error code with type of error pointer + * + * PARAMETERS: + * *dev_node - pointer to device_node structure + * *name - name of device node + * size - number of u16 array elements + ******************************************************************************/ +static s16 *create_and_get_u16_array(struct device_node *dev_node, + const char *name, int *size) +{ + const __be32 *values; + s16 *val_array; + int len; + int sz; + int rc; + int i; + + values = of_get_property(dev_node, name, &len); + if (values == NULL) + return NULL; + + sz = len / sizeof(u32); + pr_debug("%s: %s size:%d\n", __func__, name, sz); + + val_array = kcalloc(sz, sizeof(s16), GFP_KERNEL); + if (!val_array) { + rc = -ENOMEM; + goto fail; + } + + for (i = 0; i < sz; i++) + val_array[i] = (s16)be32_to_cpup(values++); + + *size = sz; + + return val_array; + +fail: + return ERR_PTR(rc); +} + +/******************************************************************************* + * FUNCTION: create_and_get_touch_framework + * + * SUMMARY: Create and get touch framework structure from dts. + * + * RETURN: + * success: the pointer of the touch framework data + * fail : error code with type of error pointer + * + * PARAMETERS: + * *dev_node - pointer to device_node structure + ******************************************************************************/ +static struct touch_framework *create_and_get_touch_framework( + struct device_node *dev_node) +{ + struct touch_framework *frmwrk; + s16 *abs; + int size; + int rc; + + abs = create_and_get_u16_array(dev_node, "parade,abs", &size); + if (IS_ERR_OR_NULL(abs)) + return (void *)abs; + + /* Check for valid abs size */ + if (size % PT_NUM_ABS_SET) { + rc = -EINVAL; + goto fail_free_abs; + } + + frmwrk = kzalloc(sizeof(*frmwrk), GFP_KERNEL); + if (!frmwrk) { + rc = -ENOMEM; + goto fail_free_abs; + } + + frmwrk->abs = abs; + frmwrk->size = size; + + return frmwrk; + +fail_free_abs: + kfree(abs); + + return ERR_PTR(rc); +} + +/******************************************************************************* + * FUNCTION: free_touch_framework + * + * SUMMARY: Free all the pointer of touch framework structure. + * + * PARAMETERS: + * *frmwrk - pointer to touch framework structure + ******************************************************************************/ +static void free_touch_framework(struct touch_framework *frmwrk) +{ + kfree(frmwrk->abs); + kfree(frmwrk); +} + +#ifdef ENABLE_VIRTUAL_KEYS +#define VIRTUAL_KEY_ELEMENT_SIZE 5 +/******************************************************************************* + * FUNCTION: virtual_keys_show + * + * SUMMARY: Show method for the board_properties sysfs node that will show the + * information for all virtual keys + * + * RETURN: size of data written to sysfs node + * + * PARAMETERS: + * *kobj - pointer to kobject structure + * *attr - pointer to kobject attributes + * *buf - pointer to print output buffer + ******************************************************************************/ +static ssize_t virtual_keys_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct pt_virtual_keys *vkeys = container_of(attr, + struct pt_virtual_keys, kobj_attr); + u16 *data = vkeys->data; + int size = vkeys->size; + int index; + int i; + + index = 0; + for (i = 0; i < size; i += VIRTUAL_KEY_ELEMENT_SIZE) + index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index, + "0x01:%d:%d:%d:%d:%d\n", + data[i], data[i+1], data[i+2], data[i+3], data[i+4]); + + return index; +} + +/******************************************************************************* + * FUNCTION: setup_virtual_keys + * + * SUMMARY: Create virtual key data array from dts and set up dynamic sysfs for + * board_properties node. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev_node - pointer to device_node structure + * **inp_dev_name - double pointer to the name of input device + * *vkeys - pointer to virtual key structure + ******************************************************************************/ +static int setup_virtual_keys(struct device_node *dev_node, + const char *inp_dev_name, struct pt_virtual_keys *vkeys) +{ + char *name; + u16 *data; + int size; + int rc; + + data = create_and_get_u16_array(dev_node, "parade,virtual_keys", &size); + if (data == NULL) + return 0; + else if (IS_ERR(data)) { + rc = PTR_ERR(data); + goto fail; + } + + /* Check for valid virtual keys size */ + if (size % VIRTUAL_KEY_ELEMENT_SIZE) { + rc = -EINVAL; + goto fail_free_data; + } + + name = kzalloc(MAX_NAME_LENGTH, GFP_KERNEL); + if (!name) { + rc = -ENOMEM; + goto fail_free_data; + } + + snprintf(name, MAX_NAME_LENGTH, "virtualkeys.%s", inp_dev_name); + + vkeys->data = data; + vkeys->size = size; + + /* TODO: Instantiate in board file and export it */ + if (board_properties_kobj == NULL) + board_properties_kobj = + kobject_create_and_add("board_properties", NULL); + if (board_properties_kobj == NULL) { + pr_err("%s: Cannot get board_properties kobject!\n", __func__); + rc = -EINVAL; + goto fail_free_name; + } + + /* Initialize dynamic SysFs attribute */ + sysfs_attr_init(&vkeys->kobj_attr.attr); + vkeys->kobj_attr.attr.name = name; + vkeys->kobj_attr.attr.mode = 0444; + vkeys->kobj_attr.show = virtual_keys_show; + + rc = sysfs_create_file(board_properties_kobj, &vkeys->kobj_attr.attr); + if (rc) + goto fail_del_kobj; + + return 0; + +fail_del_kobj: + kobject_del(board_properties_kobj); +fail_free_name: + kfree(name); + vkeys->kobj_attr.attr.name = NULL; +fail_free_data: + kfree(data); + vkeys->data = NULL; +fail: + return rc; +} + +/******************************************************************************* + * FUNCTION: free_virtual_keys + * + * SUMMARY: Remove board_properties node and free all pointers. + * + * PARAMETERS: + * *vkeys - pointer to virtual key structure + ******************************************************************************/ +static void free_virtual_keys(struct pt_virtual_keys *vkeys) +{ + if (board_properties_kobj) + sysfs_remove_file(board_properties_kobj, + &vkeys->kobj_attr.attr); + + + kobject_del(board_properties_kobj); + board_properties_kobj = NULL; + + kfree(vkeys->data); + kfree(vkeys->kobj_attr.attr.name); +} +#endif + +/******************************************************************************* + * FUNCTION: create_and_get_mt_pdata + * + * SUMMARY: Create and get touch platform data from dts.Touch framework and + * virtual keys are set up in this function. + * + * RETURN: + * success: the pointer of the platform data + * fail : error code with type of error pointer + * + * PARAMETERS: + * *dev_node - pointer to device_node structure + ******************************************************************************/ +static void *create_and_get_mt_pdata(struct device_node *dev_node) +{ + struct pt_extended_mt_platform_data *ext_pdata; + struct pt_mt_platform_data *pdata; + u32 value; + int rc; + + ext_pdata = kzalloc(sizeof(*ext_pdata), GFP_KERNEL); + if (!ext_pdata) { + rc = -ENOMEM; + goto fail; + } + + pdata = &ext_pdata->pdata; + + rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name); + if (rc) + goto fail_free_pdata; + + /* Optional fields */ + rc = of_property_read_u32(dev_node, "parade,flags", &value); + if (!rc) + pdata->flags = value; + + rc = of_property_read_u32(dev_node, "parade,vkeys_x", &value); + if (!rc) + pdata->vkeys_x = value; + + rc = of_property_read_u32(dev_node, "parade,vkeys_y", &value); + if (!rc) + pdata->vkeys_y = value; + + /* Required fields */ + pdata->frmwrk = create_and_get_touch_framework(dev_node); + if (pdata->frmwrk == NULL) { + rc = -EINVAL; + goto fail_free_pdata; + } else if (IS_ERR(pdata->frmwrk)) { + rc = PTR_ERR(pdata->frmwrk); + goto fail_free_pdata; + } +#ifdef ENABLE_VIRTUAL_KEYS + rc = setup_virtual_keys(dev_node, pdata->inp_dev_name, + &ext_pdata->vkeys); + if (rc) { + pr_err("%s: Cannot setup virtual keys!\n", __func__); + goto fail_free_pdata; + } +#endif + return pdata; + +fail_free_pdata: + kfree(ext_pdata); +fail: + return ERR_PTR(rc); +} + +/******************************************************************************* + * FUNCTION: free_mt_pdata + * + * SUMMARY: Free all pointers that include touch framework, virtual keys and + * touch data. + * + * PARAMETERS: + * *vkeys - pointer to virtual key structure + ******************************************************************************/ +static void free_mt_pdata(void *pdata) +{ + struct pt_mt_platform_data *mt_pdata = + (struct pt_mt_platform_data *)pdata; + struct pt_extended_mt_platform_data *ext_mt_pdata = + container_of(mt_pdata, + struct pt_extended_mt_platform_data, pdata); + + free_touch_framework(mt_pdata->frmwrk); +#ifdef ENABLE_VIRTUAL_KEYS + free_virtual_keys(&ext_mt_pdata->vkeys); +#endif + kfree(ext_mt_pdata); +} + +/******************************************************************************* + * FUNCTION: create_and_get_btn_pdata + * + * SUMMARY: Create and get button platform data from dts. + * + * RETURN: + * success: the pointer of the platform data + * fail : error code with type of error pointer + * + * PARAMETERS: + * *dev_node - pointer to device_node structure + ******************************************************************************/ +static void *create_and_get_btn_pdata(struct device_node *dev_node) +{ + struct pt_btn_platform_data *pdata; + int rc; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + rc = -ENOMEM; + goto fail; + } + + rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name); + if (rc) + goto fail_free_pdata; + + return pdata; + +fail_free_pdata: + kfree(pdata); +fail: + return ERR_PTR(rc); +} + +/******************************************************************************* + * FUNCTION: free_btn_pdata + * + * SUMMARY: Free all pointers for button platform data. + * + * PARAMETERS: + * *vkeys - pointer to virtual key structure + ******************************************************************************/ +static void free_btn_pdata(void *pdata) +{ + struct pt_btn_platform_data *btn_pdata = + (struct pt_btn_platform_data *)pdata; + + kfree(btn_pdata); +} + +/******************************************************************************* + * FUNCTION: create_and_get_pen_pdata + * + * SUMMARY: Create and get pen platform data from dts. + * + * RETURN: + * success: the pointer of the platform data + * fail : error code with type of error pointer + * + * PARAMETERS: + * *dev_node - pointer to device_node structure + ******************************************************************************/ +static void *create_and_get_pen_pdata(struct device_node *dev_node) +{ + struct pt_pen_platform_data *pdata; + int rc; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + rc = -ENOMEM; + goto fail; + } + + rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name); + if (rc) + goto fail_free_pdata; + + return pdata; + +fail_free_pdata: + kfree(pdata); +fail: + return ERR_PTR(rc); +} + +/******************************************************************************* + * FUNCTION: free_pen_pdata + * + * SUMMARY: Free all pointers for pen platform data. + * + * PARAMETERS: + * *pdata - pointer to virtual key structure + ******************************************************************************/ +static void free_pen_pdata(void *pdata) +{ + struct pt_pen_platform_data *pen_pdata = + (struct pt_pen_platform_data *)pdata; + + kfree(pen_pdata); +} + + +/******************************************************************************* + * FUNCTION: create_and_get_proximity_pdata + * + * SUMMARY: Create and get proximity platform data from dts. + * + * RETURN: + * success: the pointer of the proximity platform data + * fail : error code with type of error pointer + * + * PARAMETERS: + * *dev_node - pointer to device_node structure + ******************************************************************************/ +static void *create_and_get_proximity_pdata(struct device_node *dev_node) +{ + struct pt_proximity_platform_data *pdata; + int rc; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + rc = -ENOMEM; + goto fail; + } + + rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name); + if (rc) + goto fail_free_pdata; + + pdata->frmwrk = create_and_get_touch_framework(dev_node); + if (pdata->frmwrk == NULL) { + rc = -EINVAL; + goto fail_free_pdata; + } else if (IS_ERR(pdata->frmwrk)) { + rc = PTR_ERR(pdata->frmwrk); + goto fail_free_pdata; + } + + return pdata; + +fail_free_pdata: + kfree(pdata); +fail: + return ERR_PTR(rc); +} + +/******************************************************************************* + * FUNCTION: free_proximity_pdata + * + * SUMMARY: Free all pointers for proximity platform data. + * + * PARAMETERS: + * *vkeys - pointer to virtual key structure + ******************************************************************************/ +static void free_proximity_pdata(void *pdata) +{ + struct pt_proximity_platform_data *proximity_pdata = + (struct pt_proximity_platform_data *)pdata; + + free_touch_framework(proximity_pdata->frmwrk); + + kfree(proximity_pdata); +} + +static struct pt_device_pdata_func device_pdata_funcs[DEVICE_TYPE_MAX] = { + [DEVICE_MT] = { + .create_and_get_pdata = create_and_get_mt_pdata, + .free_pdata = free_mt_pdata, + }, + [DEVICE_BTN] = { + .create_and_get_pdata = create_and_get_btn_pdata, + .free_pdata = free_btn_pdata, + }, + [DEVICE_PEN] = { + .create_and_get_pdata = create_and_get_pen_pdata, + .free_pdata = free_pen_pdata, + }, + [DEVICE_PROXIMITY] = { + .create_and_get_pdata = create_and_get_proximity_pdata, + .free_pdata = free_proximity_pdata, + }, +}; + +static struct pt_pdata_ptr pdata_ptr[DEVICE_TYPE_MAX]; + +static const char *device_names[DEVICE_TYPE_MAX] = { + [DEVICE_MT] = "parade,mt", + [DEVICE_BTN] = "parade,btn", + [DEVICE_PEN] = "parade,pen", + [DEVICE_PROXIMITY] = "parade,proximity", +}; + +/******************************************************************************* + * FUNCTION: set_pdata_ptr + * + * SUMMARY: Set platform data pointer for touch, button, proximity module. + * + * PARAMETERS: + * *pdata - pointer to platform data structure + ******************************************************************************/ +static void set_pdata_ptr(struct pt_platform_data *pdata) +{ + pdata_ptr[DEVICE_MT].pdata = (void **)&pdata->mt_pdata; + pdata_ptr[DEVICE_BTN].pdata = (void **)&pdata->btn_pdata; + pdata_ptr[DEVICE_PEN].pdata = (void **)&pdata->pen_pdata; + pdata_ptr[DEVICE_PROXIMITY].pdata = (void **)&pdata->prox_pdata; +} + +/******************************************************************************* + * FUNCTION: get_device_type + * + * SUMMARY: Determine the device type[mt,btn,proximity] from dts. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev_node - pointer to device_node structure + * *type - pointer to store the device type + ******************************************************************************/ +static int get_device_type(struct device_node *dev_node, + enum pt_device_type *type) +{ + const char *name; + enum pt_device_type t; + int rc; + + rc = of_property_read_string(dev_node, "name", &name); + if (rc) + return rc; + + for (t = 0; t < DEVICE_TYPE_MAX; t++) + if (!strncmp(name, device_names[t], MAX_NAME_LENGTH)) { + *type = t; + return 0; + } + + return -EINVAL; +} + +/******************************************************************************* + * FUNCTION: create_and_get_device_pdata + * + * SUMMARY: Create platform data for mt, btn, proximity module. + * + * RETURN: + * success: the pointer of the platform data + * fail : error code with type of error pointer + * + * PARAMETERS: + * *dev_node - pointer to device_node structure + * type - determine the device type + ******************************************************************************/ +static inline void *create_and_get_device_pdata(struct device_node *dev_node, + enum pt_device_type type) +{ + return device_pdata_funcs[type].create_and_get_pdata(dev_node); +} + +/******************************************************************************* + * FUNCTION: free_device_pdata + * + * SUMMARY: Free platform data for mt, btn, proximity module. + * + * PARAMETERS: + * type - determine the device type + ******************************************************************************/ +static inline void free_device_pdata(enum pt_device_type type) +{ + device_pdata_funcs[type].free_pdata(*pdata_ptr[type].pdata); +} + +/******************************************************************************* + * FUNCTION: create_and_get_touch_setting + * + * SUMMARY: Create and get touch settings from dts. + * + * RETURN: + * success: the pointer of touch settings + * fail : error code with type of error pointer + * + * PARAMETERS: + * *core_node - pointer to device_node structure + * name - name of touch setting + ******************************************************************************/ +static struct touch_settings *create_and_get_touch_setting( + struct device_node *core_node, const char *name) +{ + struct touch_settings *setting; + char *tag_name; + u32 tag_value; + u16 *data; + int size; + int rc; + + data = create_and_get_u16_array(core_node, name, &size); + if (IS_ERR_OR_NULL(data)) + return (void *)data; + + pr_debug("%s: Touch setting:'%s' size:%d\n", __func__, name, size); + + setting = kzalloc(sizeof(*setting), GFP_KERNEL); + if (!setting) { + rc = -ENOMEM; + goto fail_free_data; + } + + setting->data = (u8 *)data; + setting->size = size; + + tag_name = kzalloc(MAX_NAME_LENGTH, GFP_KERNEL); + if (!tag_name) { + rc = -ENOMEM; + goto fail_free_setting; + } + + snprintf(tag_name, MAX_NAME_LENGTH, "%s-tag", name); + + rc = of_property_read_u32(core_node, tag_name, &tag_value); + if (!rc) + setting->tag = tag_value; + + kfree(tag_name); + + return setting; + +fail_free_setting: + kfree(setting); +fail_free_data: + kfree(data); + + return ERR_PTR(rc); +} + +/******************************************************************************* + * FUNCTION: free_touch_setting + * + * SUMMARY: Free touch setting data. + * + * PARAMETERS: + * setting - pointer to touch setting + ******************************************************************************/ +static void free_touch_setting(struct touch_settings *setting) +{ + if (setting) { + kfree(setting->data); + kfree(setting); + } +} + +static char *touch_setting_names[PT_IC_GRPNUM_NUM] = { + NULL, /* PT_IC_GRPNUM_RESERVED */ + "parade,cmd_regs", /* PT_IC_GRPNUM_CMD_REGS */ + "parade,tch_rep", /* PT_IC_GRPNUM_TCH_REP */ + "parade,data_rec", /* PT_IC_GRPNUM_DATA_REC */ + "parade,test_rec", /* PT_IC_GRPNUM_TEST_REC */ + "parade,pcfg_rec", /* PT_IC_GRPNUM_PCFG_REC */ + "parade,tch_parm_val", /* PT_IC_GRPNUM_TCH_PARM_VAL */ + "parade,tch_parm_size", /* PT_IC_GRPNUM_TCH_PARM_SIZE */ + NULL, /* PT_IC_GRPNUM_RESERVED1 */ + NULL, /* PT_IC_GRPNUM_RESERVED2 */ + "parade,opcfg_rec", /* PT_IC_GRPNUM_OPCFG_REC */ + "parade,ddata_rec", /* PT_IC_GRPNUM_DDATA_REC */ + "parade,mdata_rec", /* PT_IC_GRPNUM_MDATA_REC */ + "parade,test_regs", /* PT_IC_GRPNUM_TEST_REGS */ + "parade,btn_keys", /* PT_IC_GRPNUM_BTN_KEYS */ + NULL, /* PT_IC_GRPNUM_TTHE_REGS */ +}; + +/******************************************************************************* + * FUNCTION: create_and_get_core_pdata + * + * SUMMARY: Create and get core module platform data from dts. + * + * RETURN: + * success: the pointer of core platform data + * fail : error code with type of error pointer + * + * PARAMETERS: + * *core_node - pointer to device_node structure + ******************************************************************************/ +static struct pt_core_platform_data *create_and_get_core_pdata( + struct device_node *core_node) +{ + struct pt_core_platform_data *pdata; + u32 value; + int rc; + int i; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + rc = -ENOMEM; + goto fail; + } + + /* Required fields */ + rc = of_property_read_u32(core_node, "parade,irq_gpio", &value); + if (rc) + goto fail_free; + pdata->irq_gpio = value; + + rc = of_property_read_u32(core_node, "parade,hid_desc_register", + &value); + if (rc) + goto fail_free; + pdata->hid_desc_register = value; + + /* Optional fields */ + /* rst_gpio is optional since a platform may use + * power cycling instead of using the XRES pin + */ + rc = of_property_read_u32(core_node, "parade,rst_gpio", &value); + if (!rc) + pdata->rst_gpio = value; + + rc = of_property_read_u32(core_node, "parade,ddi_rst_gpio", &value); + if (!rc) + pdata->ddi_rst_gpio = value; + + rc = of_property_read_u32(core_node, "parade,vddi_gpio", &value); + if (!rc) + pdata->vddi_gpio = value; + + rc = of_property_read_u32(core_node, "parade,vcc_gpio", &value); + if (!rc) + pdata->vcc_gpio = value; + + rc = of_property_read_u32(core_node, "parade,avdd_gpio", &value); + if (!rc) + pdata->avdd_gpio = value; + + rc = of_property_read_u32(core_node, "parade,avee_gpio", &value); + if (!rc) + pdata->avee_gpio = value; + + rc = of_property_read_u32(core_node, "parade,level_irq_udelay", &value); + if (!rc) + pdata->level_irq_udelay = value; + + rc = of_property_read_u32(core_node, "parade,vendor_id", &value); + if (!rc) + pdata->vendor_id = value; + + rc = of_property_read_u32(core_node, "parade,product_id", &value); + if (!rc) + pdata->product_id = value; + + rc = of_property_read_u32(core_node, "parade,flags", &value); + if (!rc) + pdata->flags = value; + + rc = of_property_read_u32(core_node, "parade,easy_wakeup_gesture", + &value); + if (!rc) + pdata->easy_wakeup_gesture = (u8)value; + + rc = of_property_read_u32(core_node, "parade,config_dut_generation", + &value); + if (!rc) + pdata->config_dut_generation = (u8)value; + else { + pr_err("%s: dut_generation is not configured, set default: DUT_PIP2_CAPABLE!\n", + __func__); + pdata->config_dut_generation = CONFIG_DUT_PIP2_CAPABLE; + } + + rc = of_property_read_u32(core_node, "parade,watchdog_force_stop", + &value); + if (!rc) { + if (value) + pdata->watchdog_force_stop = true; + else + pdata->watchdog_force_stop = false; + } else { + pr_err("%s: watchdog_force_stop is not configured, set default: false!\n", + __func__); + pdata->watchdog_force_stop = false; + } + + rc = of_property_read_u32(core_node, "parade,panel_id_support", + &value); + if (!rc) { + pdata->panel_id_support = (u8)value; + } else { + pr_err("%s: panel_id_support is not configured, set default: PT_PANEL_ID_DISABLE\n", + __func__); + pdata->panel_id_support = PT_PANEL_ID_DISABLE; + } + + for (i = 0; (unsigned int)i < ARRAY_SIZE(touch_setting_names); i++) { + if (touch_setting_names[i] == NULL) + continue; + + pdata->sett[i] = create_and_get_touch_setting(core_node, + touch_setting_names[i]); + if (IS_ERR(pdata->sett[i])) { + rc = PTR_ERR(pdata->sett[i]); + goto fail_free_sett; + } else if (pdata->sett[i] == NULL) + pr_debug("%s: No data for setting '%s'\n", __func__, + touch_setting_names[i]); + } + + pr_debug("%s: irq_gpio:%d rst_gpio:%d\n" + "hid_desc_reg:%d level_irq_udelay:%d vendor_id:%d prod_id:%d\n" + "flags:%d easy_wakeup_gesture:%d\n", __func__, + pdata->irq_gpio, pdata->rst_gpio, + pdata->hid_desc_register, + pdata->level_irq_udelay, pdata->vendor_id, pdata->product_id, + pdata->flags, pdata->easy_wakeup_gesture); + + pdata->xres = pt_xres; + pdata->init = pt_init; + pdata->power = pt_power; + pdata->detect = pt_detect; + pdata->irq_stat = pt_irq_stat; + pdata->setup_power = pt_setup_power; + pdata->setup_irq = pt_setup_irq; + + return pdata; + +fail_free_sett: + for (i--; i >= 0; i--) + free_touch_setting(pdata->sett[i]); +fail_free: + kfree(pdata); +fail: + return ERR_PTR(rc); +} + +/******************************************************************************* + * FUNCTION: free_core_pdata + * + * SUMMARY: Free the core module platform data and touch settings data. + * + * RETURN: + * success: the pointer of core platform data + * fail : error code with type of error pointer + * + * PARAMETERS: + * *core_node - pointer to device_node structure + ******************************************************************************/ +static void free_core_pdata(void *pdata) +{ + struct pt_core_platform_data *core_pdata = pdata; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(touch_setting_names); i++) + free_touch_setting(core_pdata->sett[i]); + kfree(core_pdata); +} + +/******************************************************************************* + * FUNCTION: pt_devtree_create_and_get_pdata + * + * SUMMARY: Parse dts and set up platform data for core module, multi-touch(mt) + * module, button(btn) module, proximity module.And Assign platform data + * pointer for loader module. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *adap_dev - pointer to device structure + ******************************************************************************/ +int pt_devtree_create_and_get_pdata(struct device *adap_dev) +{ + struct pt_platform_data *pdata; + struct device_node *core_node, *dev_node, *dev_node_fail; + enum pt_device_type type; + int count = 0; + int rc = 0; + + if (is_create_and_get_pdata == true) + return 0; + + if (!adap_dev->of_node) + return 0; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + adap_dev->platform_data = pdata; + set_pdata_ptr(pdata); + + /* There should be only one core node */ + for_each_child_of_node(adap_dev->of_node, core_node) { + const char *name; + + rc = of_property_read_string(core_node, "name", &name); + if (!rc) + pr_debug("%s: name:%s\n", __func__, name); + + pdata->core_pdata = create_and_get_core_pdata(core_node); + if (IS_ERR(pdata->core_pdata)) { + rc = PTR_ERR(pdata->core_pdata); + break; + } + + /* Increment reference count */ + of_node_get(core_node); + + for_each_child_of_node(core_node, dev_node) { + count++; + rc = get_device_type(dev_node, &type); + if (rc) + break; + *pdata_ptr[type].pdata + = create_and_get_device_pdata(dev_node, type); + if (IS_ERR(*pdata_ptr[type].pdata)) + rc = PTR_ERR(*pdata_ptr[type].pdata); + if (rc) + break; + /* Increment reference count */ + of_node_get(dev_node); + } + + if (rc) { + free_core_pdata(pdata->core_pdata); + of_node_put(core_node); + for_each_child_of_node(core_node, dev_node_fail) { + if (dev_node == dev_node_fail) + break; + rc = get_device_type(dev_node, &type); + if (rc) + break; + free_device_pdata(type); + of_node_put(dev_node); + } + break; + } + pdata->loader_pdata = &_pt_loader_platform_data; + } + is_create_and_get_pdata = true; + pr_debug("%s: %d child node(s) found\n", __func__, count); + + return rc; +} +EXPORT_SYMBOL_GPL(pt_devtree_create_and_get_pdata); + +/******************************************************************************* + * FUNCTION: pt_devtree_clean_pdata + * + * SUMMARY: Free all platform data for core module, multi-touch(mt) module, + * button(btn) module, proximity module. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *adap_dev - pointer to device structure + ******************************************************************************/ +int pt_devtree_clean_pdata(struct device *adap_dev) +{ + struct pt_platform_data *pdata; + struct device_node *core_node, *dev_node; + enum pt_device_type type; + int rc = 0; + + if (is_create_and_get_pdata == false) + return 0; + + if (!adap_dev->of_node) + return 0; + + pdata = dev_get_platdata(adap_dev); + set_pdata_ptr(pdata); + for_each_child_of_node(adap_dev->of_node, core_node) { + free_core_pdata(pdata->core_pdata); + of_node_put(core_node); + for_each_child_of_node(core_node, dev_node) { + rc = get_device_type(dev_node, &type); + if (rc) + break; + free_device_pdata(type); + of_node_put(dev_node); + } + } + is_create_and_get_pdata = false; + + return rc; +} +EXPORT_SYMBOL_GPL(pt_devtree_clean_pdata); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product DeviceTree Driver"); +MODULE_AUTHOR("Parade Technologies "); diff --git a/pt/pt_i2c.c b/pt/pt_i2c.c new file mode 100644 index 0000000000..111834d92d --- /dev/null +++ b/pt/pt_i2c.c @@ -0,0 +1,570 @@ +/* + * pt_i2c.c + * Parade TrueTouch(TM) Standard Product I2C Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.paradetech.com + */ + +#include "pt_regs.h" + +#include +#include + +#define PT_I2C_DATA_SIZE (2 * 256) + +#ifdef TTDL_PTVIRTDUT_SUPPORT +#define VIRT_DUT_BUF_SIZE 2048 +static unsigned char pt_dut_cmd_buf[VIRT_DUT_BUF_SIZE]; +static unsigned char pt_dut_out_buf[VIRT_DUT_BUF_SIZE]; +static int pt_dut_cmd_len; +static int pt_dut_out_len; +DEFINE_MUTEX(virt_i2c_lock); + +/******************************************************************************* + * FUNCTION: virt_i2c_transfer + * + * SUMMARY: Copies the current i2c output message to the temporary buffer + * used by the dut_cmd sysfs node + * + * RETURN VALUE: + * Number of messages transferred which in this function will be 1 + * + * PARAMETERS: + * *buf - pointer to i2c command + * len - length of command in the buffer + ******************************************************************************/ +static int virt_i2c_transfer(u8 *buf, int len) +{ + int rc = 0; + + mutex_lock(&virt_i2c_lock); + if (len <= sizeof(pt_dut_cmd_buf)) { + memcpy(pt_dut_cmd_buf, buf, len); + pt_dut_cmd_len = len; + rc = 1; + } else + rc = 0; + mutex_unlock(&virt_i2c_lock); + + return rc; +} + +/******************************************************************************* + * FUNCTION: virt_i2c_master_recv + * + * SUMMARY: Copies the i2c input message from the dut_out sysfs node into a + * temporary buffer. + * + * RETURN VALUE: + * Length of data transferred + * + * PARAMETERS: + * *dev - pointer to device struct + * *buf - pointer to i2c incoming report + * size - size to be read + ******************************************************************************/ +static int virt_i2c_master_recv(struct device *dev, u8 *buf, int size) +{ +#ifndef PT_POLL_RESP_BY_BUS + struct pt_core_data *cd = dev_get_drvdata(dev); + int i = 0; +#endif + + mutex_lock(&virt_i2c_lock); + memcpy(buf, pt_dut_out_buf, size); + + /* Set "empty buffer" */ + pt_dut_out_buf[1] = 0xFF; + pt_dut_out_len = 0; + mutex_unlock(&virt_i2c_lock); + +#ifndef PT_POLL_RESP_BY_BUS + if (cd->bl_with_no_int) { + /* + * Wait for IRQ gpio to be released, make read operation + * synchronize with PtVirtDut tool. + * Safety net: Exit after 500ms (50us * 10000 loops = 500ms) + */ + while (i < VIRT_MAX_IRQ_RELEASE_TIME_US && + !gpio_get_value(cd->cpdata->irq_gpio)) { + pt_debug(dev, DL_INFO, "%s: %d IRQ still Enabled\n", + __func__, i); + usleep_range(50, 60); + i += 50; + } + } +#endif + + pt_debug(dev, DL_INFO, + "%s: Copy msg from dut_out to i2c buffer, size=%d\n", + __func__, size); + + return size; +} + +/******************************************************************************* + * FUNCTION: pt_dut_cmd_show + * + * SUMMARY: The show function for the dut_cmd sysfs node. Provides read access + * to the pt_dut_cmd_buf and clears it after it has been read. + * + * RETURN VALUE: + * Number of bytes transferred + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_dut_cmd_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + int index = 0; + + /* Only print to sysfs if the buffer has data */ + mutex_lock(&virt_i2c_lock); + if (pt_dut_cmd_len > 0) { + for (i = 0; i < pt_dut_cmd_len; i++) + index += scnprintf(buf + index, strlen(buf), "%02X", + pt_dut_cmd_buf[i]); + index += scnprintf(buf + index, strlen(buf), "\n"); + } + pt_dut_cmd_len = 0; + mutex_unlock(&virt_i2c_lock); + return index; +} +static DEVICE_ATTR(dut_cmd, 0444, pt_dut_cmd_show, NULL); + +/******************************************************************************* + * FUNCTION: pt_dut_out_store + * + * SUMMARY: The store function for the dut_out sysfs node. Provides write + * access to the pt_dut_out_buf. The smallest valid PIP response is 2 + * bytes so don't update buffer if only 1 byte passed in. + * + * RETURN VALUE: + * Number of bytes read from virtual DUT + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_dut_out_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int loop_max = ARRAY_SIZE(pt_dut_out_buf); + int hex_str_len = strlen(buf)/2; + int i; + const char *pos = buf; + struct pt_core_data *cd = dev_get_drvdata(dev); + + /* Clear out the last message */ + mutex_lock(&virt_i2c_lock); + memset(pt_dut_out_buf, 0, VIRT_DUT_BUF_SIZE); + pt_dut_out_len = 0; + + /* Only update the dut_out buffer if at least 2 byte payload */ + if (size >= 2 && hex_str_len <= loop_max) { + /* Convert string of hex values to byte array */ + for (i = 0; i < hex_str_len; i++) { + pt_dut_out_buf[i] = ((HEXOF(*pos)) << 4) + + HEXOF(*(pos + 1)); + pos += 2; + } + /* + * In HID we send the full string, non HID we send based + * on the 2 byte length. + */ + if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) + pt_dut_out_len = hex_str_len; + else + pt_dut_out_len = get_unaligned_le16(&pt_dut_out_buf[0]); + } else if (size >= PT_PIP_1P7_EMPTY_BUF) { + /* Message too large, set to 'empty buffer' message */ + pt_dut_out_buf[1] = 0xFF; + pt_dut_out_len = 0; + } + mutex_unlock(&virt_i2c_lock); + + return size; +} +static DEVICE_ATTR(dut_out, 0200, NULL, pt_dut_out_store); +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + +/******************************************************************************* + * FUNCTION: pt_i2c_read_default + * + * SUMMARY: Read a certain number of bytes from the I2C bus + * + * PARAMETERS: + * *dev - pointer to Device structure + * *buf - pointer to buffer where the data read will be stored + * size - size to be read + ******************************************************************************/ +static int pt_i2c_read_default(struct device *dev, void *buf, int size) +{ +#ifdef TTDL_PTVIRTDUT_SUPPORT + struct pt_core_data *cd = dev_get_drvdata(dev); +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + struct i2c_client *client = to_i2c_client(dev); + int rc; + int read_size = size; + + if (!buf || !size || size > VIRT_DUT_BUF_SIZE) + return -EINVAL; + +#ifdef TTDL_PTVIRTDUT_SUPPORT + if (cd->route_bus_virt_dut) + rc = virt_i2c_master_recv(dev, buf, read_size); + else + rc = i2c_master_recv(client, buf, read_size); +#else + rc = i2c_master_recv(client, buf, read_size); +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + + return (rc < 0) ? rc : rc != read_size ? -EIO : 0; +} + +/******************************************************************************* + * FUNCTION: pt_i2c_read_default_nosize + * + * SUMMARY: Read from the I2C bus in two transactions first reading the HID + * packet size (2 bytes) followed by reading the rest of the packet based + * on the size initially read. + * NOTE: The empty buffer 'size' was redefined in PIP version 1.7. + * + * PARAMETERS: + * *dev - pointer to Device structure + * *buf - pointer to buffer where the data read will be stored + * max - max size that can be read + ******************************************************************************/ +static int pt_i2c_read_default_nosize(struct device *dev, u8 *buf, u32 max) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg msgs[2]; + u8 msg_count = 1; + int rc; + u32 size; +#ifdef TTDL_PTVIRTDUT_SUPPORT + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (cd->route_bus_virt_dut) { + mutex_lock(&virt_i2c_lock); + size = pt_dut_out_len; + mutex_unlock(&virt_i2c_lock); + pt_debug(dev, DL_INFO, "%s: pt_dut_out_len=%d\n", + __func__, pt_dut_out_len); + /* Only copy 2 bytes for "empty buffer" or "FW sentinel" */ + if (!size || size == 2 || size >= PT_PIP_1P7_EMPTY_BUF) + size = 2; + goto skip_read_len; + } + +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + + if (!buf) + return -EINVAL; + + msgs[0].addr = client->addr; + msgs[0].flags = (client->flags & I2C_M_TEN) | I2C_M_RD; + msgs[0].len = 2; + msgs[0].buf = buf; + rc = i2c_transfer(client->adapter, msgs, msg_count); + if (rc < 0 || rc != msg_count) + return (rc < 0) ? rc : -EIO; + + size = get_unaligned_le16(&buf[0]); + if (!size || size == 2 || size >= PT_PIP_1P7_EMPTY_BUF) + /* + * Before PIP 1.7, empty buffer is 0x0002; + * From PIP 1.7, empty buffer is 0xFFXX + */ + return 0; + + if (size > max) + return -EINVAL; + +#ifdef TTDL_PTVIRTDUT_SUPPORT +skip_read_len: + if (cd->route_bus_virt_dut) + rc = virt_i2c_master_recv(dev, buf, size); + else + rc = i2c_master_recv(client, buf, size); + + pt_debug(dev, DL_INFO, "%s: rc = %d\n", __func__, rc); +#else + rc = i2c_master_recv(client, buf, size); +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + return (rc < 0) ? rc : rc != (int)size ? -EIO : 0; +} + +/******************************************************************************* + * FUNCTION: pt_i2c_write_read_specific + * + * SUMMARY: Write the contents of write_buf to the I2C device and then read + * the response using pt_i2c_read_default_nosize() + * + * PARAMETERS: + * *dev - pointer to Device structure + * write_len - length of data buffer write_buf + * *write_buf - pointer to buffer to write + * *read_buf - pointer to buffer to read response into + * read_len - length to read, 0 to use pt_i2c_read_default_nosize + ******************************************************************************/ +static int pt_i2c_write_read_specific(struct device *dev, u16 write_len, + u8 *write_buf, u8 *read_buf, u16 read_len) +{ + struct i2c_client *client = to_i2c_client(dev); +#ifdef TTDL_PTVIRTDUT_SUPPORT + struct pt_core_data *cd = dev_get_drvdata(dev); +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + struct i2c_msg msgs[2]; + u8 msg_count = 1; + int rc; + + /* Ensure no packet larger than what the PIP spec allows */ + if (write_len > PT_MAX_PIP2_MSG_SIZE) + return -EINVAL; + + if (!write_buf || !write_len) { + if (!write_buf) + pt_debug(dev, DL_ERROR, + "%s write_buf is NULL", __func__); + if (!write_len) + pt_debug(dev, DL_ERROR, + "%s write_len is NULL", __func__); + return -EINVAL; + } + + msgs[0].addr = client->addr; + msgs[0].flags = client->flags & I2C_M_TEN; + msgs[0].len = write_len; + msgs[0].buf = write_buf; +#ifdef TTDL_PTVIRTDUT_SUPPORT + if (cd->route_bus_virt_dut) { + rc = virt_i2c_transfer(msgs[0].buf, msgs[0].len); + pt_debug(dev, DL_DEBUG, "%s: Virt transfer size = %d", + __func__, msgs[0].len); + } else + rc = i2c_transfer(client->adapter, msgs, msg_count); +#else + rc = i2c_transfer(client->adapter, msgs, msg_count); +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + + if (rc < 0 || rc != msg_count) + return (rc < 0) ? rc : -EIO; + + rc = 0; + + if (read_buf) { +#ifdef TTDL_PTVIRTDUT_SUPPORT + if (cd->route_bus_virt_dut && + cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) { + /* Simulate clock stretch */ + usleep_range(3000, 4000); + } +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + if (read_len > 0) + rc = pt_i2c_read_default(dev, read_buf, read_len); + else + rc = pt_i2c_read_default_nosize(dev, read_buf, + PT_I2C_DATA_SIZE); + } + + return rc; +} + +static struct pt_bus_ops pt_i2c_bus_ops = { + .bustype = BUS_I2C, + .read_default = pt_i2c_read_default, + .read_default_nosize = pt_i2c_read_default_nosize, + .write_read_specific = pt_i2c_write_read_specific, +}; + +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT +static const struct of_device_id pt_i2c_of_match[] = { + { .compatible = "parade,pt_i2c_adapter", }, + { } +}; +MODULE_DEVICE_TABLE(of, pt_i2c_of_match); +#endif + + +/******************************************************************************* + * FUNCTION: pt_i2c_probe + * + * SUMMARY: Probe functon for the I2C module + * + * PARAMETERS: + * *client - pointer to i2c client structure + * *i2c_id - pointer to i2c device structure + ******************************************************************************/ +static int pt_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + struct device *dev = &client->dev; +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT + const struct of_device_id *match; +#endif + int rc; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pt_debug(dev, DL_ERROR, "I2C functionality not Supported\n"); + return -EIO; + } + +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT + match = of_match_device(of_match_ptr(pt_i2c_of_match), dev); + if (match) { + rc = pt_devtree_create_and_get_pdata(dev); + if (rc < 0) + return rc; + } +#endif + +#ifdef TTDL_PTVIRTDUT_SUPPORT + /* + * When using the virtual DUT these files must be created before + * pt_probe is called. + */ + mutex_lock(&virt_i2c_lock); + device_create_file(dev, &dev_attr_dut_cmd); + device_create_file(dev, &dev_attr_dut_out); + mutex_unlock(&virt_i2c_lock); +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + + rc = pt_probe(&pt_i2c_bus_ops, &client->dev, client->irq, + PT_I2C_DATA_SIZE); + +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT + if (rc && match) + pt_devtree_clean_pdata(dev); +#endif +#ifdef TTDL_PTVIRTDUT_SUPPORT + if (rc) { + mutex_lock(&virt_i2c_lock); + device_remove_file(dev, &dev_attr_dut_cmd); + device_remove_file(dev, &dev_attr_dut_out); + mutex_unlock(&virt_i2c_lock); + } +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_i2c_remove + * + * SUMMARY: Remove functon for the I2C module + * + * PARAMETERS: + * *client - pointer to i2c client structure + ******************************************************************************/ +static int pt_i2c_remove(struct i2c_client *client) +{ +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT + const struct of_device_id *match; +#endif + struct device *dev = &client->dev; + struct pt_core_data *cd = i2c_get_clientdata(client); + + pt_release(cd); +#ifdef TTDL_PTVIRTDUT_SUPPORT + mutex_lock(&virt_i2c_lock); + device_remove_file(dev, &dev_attr_dut_cmd); + device_remove_file(dev, &dev_attr_dut_out); + mutex_unlock(&virt_i2c_lock); +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT + match = of_match_device(of_match_ptr(pt_i2c_of_match), dev); + if (match) + pt_devtree_clean_pdata(dev); +#endif + + return 0; +} + +static const struct i2c_device_id pt_i2c_id[] = { + { PT_I2C_NAME, 0, }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pt_i2c_id); + +static struct i2c_driver pt_i2c_driver = { + .driver = { + .name = PT_I2C_NAME, + .owner = THIS_MODULE, + .pm = &pt_pm_ops, +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT + .of_match_table = pt_i2c_of_match, +#endif + }, + .probe = pt_i2c_probe, + .remove = pt_i2c_remove, + .id_table = pt_i2c_id, +}; + +#if (KERNEL_VERSION(3, 3, 0) <= LINUX_VERSION_CODE) +module_i2c_driver(pt_i2c_driver); +#else +/******************************************************************************* + * FUNCTION: pt_i2c_init + * + * SUMMARY: Initialize function to register i2c module to kernel. + * + * RETURN: + * 0 = success + * !0 = failure + ******************************************************************************/ +static int __init pt_i2c_init(void) +{ + int rc = i2c_add_driver(&pt_i2c_driver); + + pr_info("%s: Parade TTDL I2C Driver (Build %s) rc=%d\n", + __func__, PT_DRIVER_VERSION, rc); + return rc; +} +module_init(pt_i2c_init); + +/******************************************************************************* + * FUNCTION: pt_i2c_exit + * + * SUMMARY: Exit function to unregister i2c module from kernel. + * + ******************************************************************************/ +static void __exit pt_i2c_exit(void) +{ + i2c_del_driver(&pt_i2c_driver); +} +module_exit(pt_i2c_exit); +#endif + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product I2C driver"); +MODULE_AUTHOR("Parade Technologies "); diff --git a/pt/pt_loader.c b/pt/pt_loader.c new file mode 100644 index 0000000000..c57657e346 --- /dev/null +++ b/pt/pt_loader.c @@ -0,0 +1,5956 @@ +/* + * pt_loader.c + * Parade TrueTouch(TM) Standard Product FW Loader Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.paradetech.com + */ + +#include "pt_regs.h" +#include + +#define PT_LOADER_NAME "pt_loader" + +#ifdef UPGRADE_FW_AND_CONFIG_IN_PROBE +/* Enable UPGRADE_FW_AND_CONFIG_IN_PROBE definition + * to perform FW and config upgrade during probe + * instead of scheduling a work for it + */ +/* #define UPGRADE_FW_AND_CONFIG_IN_PROBE */ +#endif + +#define PT_AUTO_LOAD_FOR_CORRUPTED_FW 1 +#define PT_LOADER_FW_UPGRADE_RETRY_COUNT 3 + +#define PT_FW_UPGRADE \ + (defined(CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE) \ + || defined(CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE)) + +#define PT_TTCONFIG_UPGRADE \ + (defined(CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE) \ + || defined(CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE)) + +/* Timeout values in ms. */ +#define PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT 3000 +#define PT_LDR_SWITCH_TO_APP_MODE_TIMEOUT 300 + +#define PT_MAX_STATUS_SIZE 32 + +#define PT_DATA_MAX_ROW_SIZE 256 +#define PT_DATA_ROW_SIZE 128 + +#define PT_ARRAY_ID_OFFSET 0 +#define PT_ROW_NUM_OFFSET 1 +#define PT_ROW_SIZE_OFFSET 3 +#define PT_ROW_DATA_OFFSET 5 + +#define PT_POST_TT_CFG_CRC_MASK 0x2 +static inline struct pt_loader_data *pt_get_loader_data( + struct device *dev); + +static struct pt_core_commands *cmd; + +#define PIP2_LAUNCH_APP_DELAY 400 + +struct pip2_file_read { + s8 para_num; + u8 file_handle; + int file_offset; + int file_max_size; + int file_read_size; + int file_print_size; + u8 *file_print_buf; + int file_print_left; +}; + +#ifdef TTDL_DIAGNOSTICS +struct pip2_file_crc { + s8 para_num; + u8 file_handle; + int file_offset; + int file_max_size; + int file_read_size; +}; +#endif + +struct pip2_loader_data { + struct device *dev; + struct completion pip2_fw_upgrade_complete; /* mutex for loader */ + u8 pip2_file_handle; +}; + +struct pt_loader_data { + struct device *dev; + struct pt_sysinfo *si; + u8 status_buf[PT_MAX_STATUS_SIZE]; + struct completion int_running; + struct completion calibration_complete; +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE + int builtin_bin_fw_status; + bool is_manual_upgrade_enabled; +#endif + struct work_struct fw_and_config_upgrade; + struct work_struct calibration_work; + struct pt_loader_platform_data *loader_pdata; +#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE + struct mutex config_lock; + u8 *config_data; + int config_size; + bool config_loading; +#endif + struct pip2_loader_data *pip2_data; + u8 pip2_load_file_no; + bool pip2_load_builtin; + u8 pip2_file_erase_file_no; + struct pip2_file_read pip2_file_data; +#ifdef TTDL_DIAGNOSTICS + struct pip2_file_crc pip2_fcrc; +#endif +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE + struct work_struct bl_from_file; + struct work_struct pip2_bl_from_file; +#endif +}; + +static u8 write_file_status; +#ifndef TTDL_KERNEL_SUBMISSION +static int pip2_erase_status; +static int pip2_erase_rc; +#endif /* !TTDL_KERNEL_SUBMISSION */ + +/* PIP2 update fw status codes. 1-99% are "active" */ +enum UPDATE_FW_STATUS { + UPDATE_FW_IDLE = 0, + UPDATE_FW_REQUEST_EXCLUSIVE = 1, + UPDATE_FW_ACTIVE_10 = 10, + UPDATE_FW_ACTIVE_90 = 90, + UPDATE_FW_ACTIVE_99 = 99, + UPDATE_FW_COMPLETE = 100, + UPDATE_FW_GENERAL_ERROR = 200, + UPDATE_FW_PIP_VERSION_ERROR = 201, + UPDATE_FW_VERSION_ERROR = 202, + UPDATE_FW_ERASE_ERROR = 203, + UPDATE_FW_FILE_CLOSE_ERROR = 204, + UPDATE_FW_WRITE_ERROR = 205, + UPDATE_FW_EXECUTE_ERROR = 206, + UPDATE_FW_RESET_ERROR = 207, + UPDATE_FW_MODE_ERROR = 208, + UPDATE_FW_ENTER_BL_ERROR = 209, + UPDATE_FW_FILE_OPEN_ERROR = 210, + UPDATE_FW_SENTINEL_NOT_SEEN = 211, + UPDATE_FW_EXCLUSIVE_ACCESS_ERROR = 212, + UPDATE_FW_NO_FW_PROVIDED = 213, + UPDATE_FW_INVALID_FW_IMAGE = 214, + UPDATE_FW_FILE_SEEK_ERROR = 215, + UPDATE_FW_MISALIGN_FW_IMAGE = 230, + UPDATE_FW_SYSTEM_NOMEM = 231, + UPDATE_FW_INIT_BL_ERROR = 232, + UPDATE_FW_PARSE_ROW_ERROR = 233, + UPDATE_FW_PROGRAM_ROW_ERROR = 234, + UPDATE_FW_EXIT_BL_ERROR = 235, + UPDATE_FW_CHECK_SUM_ERROR = 236, + UPDATE_FW_NO_PLATFORM_DATA = 237, + UPDATE_FW_NOT_SUPPORTED_FILE_NO = 238, + UPDATE_FW_UNDEFINED_ERROR = 255, + UPDATE_FW_INCREMENT = 300, + UPDATE_FW_INCREMENT_BY_1 = 301, + UPDATE_FW_INCREMENT_BY_5 = 305, + UPDATE_FW_UNDEFINED_INC = 400, +}; + +enum PIP2_FILE_ERASE_STATUS { + PIP2_FILE_ERASE_STATUS_DUT_BUSY = 101, + PIP2_FILE_ERASE_STATUS_ENTER_BL_ERROR = 102, +}; + +struct pt_dev_id { + u32 silicon_id; + u8 rev_id; + u32 bl_ver; +}; + +struct pt_hex_image { + u8 array_id; + u16 row_num; + u16 row_size; + u8 row_data[PT_DATA_ROW_SIZE]; +} __packed; + +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE +static int pt_pip2_upgrade_firmware_from_builtin(struct device *dev); +#endif + +typedef int (*PIP2_SEND_CMD)(struct device *, int, + u8, u8 *, u16, u8 *, u16 *); +static struct pt_module loader_module; + +/******************************************************************************* + * FUNCTION: pt_get_loader_data + * + * SUMMARY: Inline function to get pt_loader_data pointer from loader module. + * + * RETURN: + * pointer to pt_loader_data structure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static inline struct pt_loader_data *pt_get_loader_data( + struct device *dev) +{ + return pt_get_module_data(dev, &loader_module); +} + +#if PT_FW_UPGRADE || PT_TTCONFIG_UPGRADE +/******************************************************************************* + * FUNCTION: pt_calibrate_idacs + * + * SUMMARY: Calibrate idac for mutual-cap,buttons,self-cap by called functions. + * It needs stop panel scan during calibration. + * + * PARAMETERS: + * *calibration_work - pointer to work_struct structure + ******************************************************************************/ +static void pt_calibrate_idacs(struct work_struct *calibration_work) +{ + struct pt_loader_data *ld = container_of(calibration_work, + struct pt_loader_data, calibration_work); + struct device *dev = ld->dev; + struct pt_cal_ext_data cal_data = {0}; + u8 dut_gen = cmd->request_dut_generation(dev); + u8 mode; + u8 status; + int rc; + + pt_debug(dev, DL_INFO, "Entering %s\n", __func__); + rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) + goto exit; + + rc = cmd->nonhid_cmd->suspend_scanning(dev, 0); + if (rc < 0) + goto release; + + if (dut_gen == DUT_PIP1_ONLY) { + for (mode = 0; mode < 3; mode++) { + rc = cmd->nonhid_cmd->calibrate_idacs(dev, 0, mode, + &status); + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: calibrate idac error, rc = %d\n", + __func__, rc); + break; + } + } + } else { + /* + * Manual calibration is not need after fw 3.4.932110. + * Therefore, PT_LOADER_FLAG_NONE should be used to avoid + * manual calibration if the FW is newer than 3.4.932110. + */ + memset(&cal_data, 0, sizeof(struct pt_cal_ext_data)); + rc = cmd->nonhid_cmd->calibrate_ext(dev, 0, &cal_data, + &status); + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: extended calibrate error, rc = %d\n", + __func__, rc); + } + } + + rc = cmd->nonhid_cmd->resume_scanning(dev, 0); + if (rc < 0) + goto release; + + pt_debug(dev, DL_WARN, "%s: Calibration Done\n", __func__); + +release: + cmd->release_exclusive(dev); +exit: + complete(&ld->calibration_complete); +} + +/******************************************************************************* + * FUNCTION: pt_calibration_attention + * + * SUMMARY: Wrapper function to schedule calibration work used to subscribe into + * TTDL attention list.Once called will unsubscribe from attention list. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_calibration_attention(struct device *dev) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + int rc = 0; + + schedule_work(&ld->calibration_work); + + cmd->unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_LOADER_NAME, + pt_calibration_attention, 0); + + return rc; +} +#endif /* PT_FW_UPGRADE || PT_TTCONFIG_UPGRADE */ + +#if PT_FW_UPGRADE \ + || defined(CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE) + +/******************************************************************************* + * FUNCTION: pt_get_panel_id + * + * SUMMARY: Get panel id from core data. + * + * RETURN: + * panel id + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static u8 pt_get_panel_id(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + return cd->pid_for_loader; +} +#endif + + +#if (PT_FW_UPGRADE || PT_TTCONFIG_UPGRADE) +/******************************************************************************* + * FUNCTION: pt_check_firmware_version + * + * SUMMARY: Compare fw's version and revision control number with fw image's. + * + * RETURN: + * -1: Do not upgrade firmware + * 0: Version info same, let caller decide + * 1: Do a firmware upgrade + * + * PARAMETERS: + * *dev - pointer to device structure + * fw_ver_new - firmware version + * fw_revctrl_new - firmware revision control number + ******************************************************************************/ +static int pt_check_firmware_version(struct device *dev, + u32 fw_ver_new, u32 fw_revctrl_new) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + u32 fw_ver_img; + u32 fw_revctrl_img; + + fw_ver_img = ld->si->ttdata.fw_ver_major << 8; + fw_ver_img += ld->si->ttdata.fw_ver_minor; + + pt_debug(dev, DL_WARN, + "%s: Current FW ver:0x%04X New FW ver:0x%04X\n", __func__, + fw_ver_img, fw_ver_new); + + if (fw_ver_new > fw_ver_img) { + pt_debug(dev, DL_WARN, + "%s: Image is newer, will upgrade\n", __func__); + return 1; + } + + if (fw_ver_new < fw_ver_img) { + pt_debug(dev, DL_WARN, + "%s: Image is older, will NOT upgrade\n", __func__); + return -1; + } + + fw_revctrl_img = ld->si->ttdata.revctrl; + + pt_debug(dev, DL_WARN, + "%s: Current FW rev:%d New FW rev:%d\n", + __func__, fw_revctrl_img, fw_revctrl_new); + + if (fw_revctrl_new > fw_revctrl_img) { + pt_debug(dev, DL_WARN, + "%s: Image is newer, will upgrade\n", __func__); + return 1; + } + + if (fw_revctrl_new < fw_revctrl_img) { + pt_debug(dev, DL_WARN, + "%s: Image is older, will NOT upgrade\n", __func__); + return -1; + } + + return 0; +} + +#endif /* PT_FW_UPGRADE || PT_TTCONFIG_UPGRADE */ + +#if PT_FW_UPGRADE +/******************************************************************************* + * FUNCTION: pt_get_row_ + * + * SUMMARY: Copy the image data to the "row_buf". + * + * RETURN: + * pointer to image buffer plus copy size + * + * PARAMETERS: + * *dev - pointer to device structure + * *row_buf - pointer to written buffer to program chip + * *image_buf - pointer to image buffer + * size - size of written data + ******************************************************************************/ +static u8 *pt_get_row_(struct device *dev, u8 *row_buf, + u8 *image_buf, int size) +{ + memcpy(row_buf, image_buf, size); + return image_buf + size; +} + +/******************************************************************************* + * FUNCTION: pt_ldr_enter_ + * + * SUMMARY: Enter bootloader state and update device id(silicon id, rev id, + * bootloader version). + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *dev_id - pointer to device id + ******************************************************************************/ +static int pt_ldr_enter_(struct device *dev, struct pt_dev_id *dev_id) +{ + int rc; + u8 return_data[8]; + u8 mode = PT_MODE_OPERATIONAL; + + dev_id->silicon_id = 0; + dev_id->rev_id = 0; + dev_id->bl_ver = 0; + + /* + * Reset to get to a known state, sleep to allow sentinel processing. + * + * NOTE: This msleep MUST be greater than the auto launch timeout + * that is built into the FW. + */ + cmd->request_reset(dev, PT_CORE_CMD_UNPROTECTED); + msleep(20); + + rc = cmd->request_get_mode(dev, 0, &mode); + pt_debug(dev, DL_INFO, "%s:request_get_mode rc=%d mode=%d\n", + __func__, rc, mode); + if (rc) + return rc; + + if (mode == PT_MODE_UNKNOWN) + return -EINVAL; + + if (mode == PT_MODE_OPERATIONAL) { + rc = cmd->nonhid_cmd->start_bl(dev, PT_CORE_CMD_UNPROTECTED); + pt_debug(dev, DL_INFO, "%s:start_bl rc=%d\n", __func__, rc); + if (rc) + return rc; + rc = cmd->request_get_mode(dev, 0, &mode); + pt_debug(dev, DL_INFO, "%s:request_get_mode rc=%d mode=%d\n", + __func__, rc, mode); + if (rc) + return rc; + if (mode != PT_MODE_BOOTLOADER) + return -EINVAL; + } + + rc = cmd->nonhid_cmd->get_bl_info(dev, + PT_CORE_CMD_UNPROTECTED, return_data); + pt_debug(dev, DL_INFO, "%s:get_bl_info rc=%d\n", __func__, rc); + if (rc) + return rc; + + dev_id->silicon_id = get_unaligned_le32(&return_data[0]); + dev_id->rev_id = return_data[4]; + dev_id->bl_ver = return_data[5] + (return_data[6] << 8) + + (return_data[7] << 16); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_ldr_init_ + * + * SUMMARY: Erase the entire TrueTouch application, Configuration Data block, + * and Design Data block in flash and enables the host to execute the Program + * and Verify Row command to bootload the application image and data. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *pt_hex_image - pointer to hex image structure + ******************************************************************************/ +static int pt_ldr_init_(struct device *dev, + struct pt_hex_image *row_image) +{ + return cmd->nonhid_cmd->initiate_bl(dev, 0, 8, + (u8 *)pt_data_block_security_key, row_image->row_size, + row_image->row_data); +} + +/******************************************************************************* + * FUNCTION: pt_ldr_parse_row_ + * + * SUMMARY: Parse and copy the row buffer data to hex image structure. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *row_buf - pointer to row buffer + * *row_image - pointer to hex image structure + ******************************************************************************/ +static int pt_ldr_parse_row_(struct device *dev, u8 *row_buf, + struct pt_hex_image *row_image) +{ + int rc = 0; + + row_image->array_id = row_buf[PT_ARRAY_ID_OFFSET]; + row_image->row_num = get_unaligned_be16(&row_buf[PT_ROW_NUM_OFFSET]); + row_image->row_size = get_unaligned_be16(&row_buf[PT_ROW_SIZE_OFFSET]); + + if (row_image->row_size > ARRAY_SIZE(row_image->row_data)) { + pt_debug(dev, DL_ERROR, + "%s: row data buffer overflow\n", __func__); + rc = -EOVERFLOW; + goto pt_ldr_parse_row_exit; + } + + memcpy(row_image->row_data, &row_buf[PT_ROW_DATA_OFFSET], + row_image->row_size); +pt_ldr_parse_row_exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_ldr_prog_row_ + * + * SUMMARY: Program one row that the hex image structure data to the chip. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *row_image - pointer to hex image structure + ******************************************************************************/ +static int pt_ldr_prog_row_(struct device *dev, + struct pt_hex_image *row_image) +{ + int rc = 0; + u16 length = row_image->row_size + 3; + u8 *data = NULL; + u8 offset = 0; + + if (length > PT_DATA_MAX_ROW_SIZE) + return -EINVAL; + + data = kzalloc(length, GFP_KERNEL); + if (!data) + return -ENOMEM; + + data[offset++] = row_image->array_id; + data[offset++] = LOW_BYTE(row_image->row_num); + data[offset++] = HI_BYTE(row_image->row_num); + memcpy(data + 3, row_image->row_data, row_image->row_size); + rc = cmd->nonhid_cmd->prog_and_verify(dev, 0, length, data); + kfree(data); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_ldr_verify_chksum_ + * + * SUMMARY: Perform a full verification of the application integrity by + * calculating the CRC of the TrueTouch application image in flash and + * comparing it to the expected CRC stored in the TrueTouch application CRC + * value stored in the Metadata row. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_ldr_verify_chksum_(struct device *dev) +{ + u8 result; + int rc; + + rc = cmd->nonhid_cmd->verify_app_integrity(dev, 0, &result); + if (rc) + return rc; + + /* fail */ + if (result == 0) + return -EINVAL; + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_ldr_exit_ + * + * SUMMARY: Launch the application from bootloader. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_ldr_exit_(struct device *dev) +{ + return cmd->nonhid_cmd->launch_app(dev, 0); +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_update_bl_status + * + * SUMMARY: Update the value in the bl_status global by either setting to a + * specific value or incrementing it ensuring we don't go out of bounds. + * + * PARAMETERS: + * *dev - pointer to device structure + * value - Value to force. + ******************************************************************************/ +static u8 _pt_update_write_file_status(struct device *dev, u16 value) +{ + u8 inc = 0; + + pt_debug(dev, DL_DEBUG, + "%s: fw_status = %d, request val=%d\n", + __func__, write_file_status, value); + + /* Reset status if value is 0 */ + if (value == UPDATE_FW_IDLE) { + write_file_status = value; + pt_debug(dev, DL_WARN, "%s: ATM - Reset BL Status to %d\n", + __func__, value); + return write_file_status; + } + + /* Set to value if valid */ + if (value > UPDATE_FW_IDLE && value < UPDATE_FW_UNDEFINED_ERROR) { + if (value <= UPDATE_FW_COMPLETE && value > write_file_status) { + write_file_status = value; + pt_debug(dev, DL_DEBUG, "%s: Set BL Status to %d\n", + __func__, value); + } else if (value >= UPDATE_FW_GENERAL_ERROR) { + write_file_status = value; + pt_debug(dev, DL_WARN, + "%s: BL Status set to error code %d\n", + __func__, value); + } + return write_file_status; + } + + if (value > UPDATE_FW_INCREMENT && value < UPDATE_FW_UNDEFINED_INC) { + inc = value - UPDATE_FW_INCREMENT; + if (write_file_status + inc <= UPDATE_FW_COMPLETE) + write_file_status += inc; + else + pt_debug(dev, DL_ERROR, + "%s: Inc Request out of bounds: status=%d inc=%d\n", + __func__, write_file_status, inc); + } else + pt_debug(dev, DL_ERROR, + "%s: Value Request out of bounds: status=%d value=%d\n", + __func__, write_file_status, value); + + return write_file_status; +} + +/******************************************************************************* + * FUNCTION: pt_load_app_ + * + * SUMMARY: Program the firmware image to the chip. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *fw - pointer to the firmware + * fw_size - size of firmware + * update_status - store the update status of firmware + ******************************************************************************/ +static int pt_load_app_(struct device *dev, const u8 *fw, int fw_size, + u8 *update_status) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_dev_id *dev_id; + struct pt_hex_image *row_image; + u8 *row_buf; + size_t image_rec_size; + size_t row_buf_size = PT_DATA_MAX_ROW_SIZE; + int row_count = 0; + u8 *p; + u8 *last_row; + int rc; + int rc_tmp; + int percent_cmplt; + int total_row_count; + + if (update_status == NULL) { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, + "%s: update_status is NULL pointer %d\n", + __func__, rc); + return rc; + } else if (*update_status > UPDATE_FW_ACTIVE_10) { + pt_debug(dev, DL_WARN, + "%s: update_status is illegal = %d, fixed to %d\n", + __func__, *update_status, UPDATE_FW_ACTIVE_10); + *update_status = UPDATE_FW_ACTIVE_10; + } + + image_rec_size = sizeof(struct pt_hex_image); + if (fw_size % image_rec_size != 0) { + pt_debug(dev, DL_ERROR, + "%s: Firmware image is misaligned\n", __func__); + *update_status = UPDATE_FW_MISALIGN_FW_IMAGE; + rc = -EINVAL; + goto _pt_load_app_error; + } + *update_status += 1; + total_row_count = fw_size / image_rec_size - 1; + + pt_debug(dev, DL_INFO, "%s: start load app\n", __func__); +#ifdef TTHE_TUNER_SUPPORT + cmd->request_tthe_print(dev, NULL, 0, "start load app"); +#endif + + row_buf = kzalloc(row_buf_size, GFP_KERNEL); + row_image = kzalloc(sizeof(struct pt_hex_image), GFP_KERNEL); + dev_id = kzalloc(sizeof(struct pt_dev_id), GFP_KERNEL); + if (!row_buf || !row_image || !dev_id) { + *update_status = UPDATE_FW_SYSTEM_NOMEM; + rc = -ENOMEM; + goto _pt_load_app_exit; + } + *update_status += 1; + + cmd->request_stop_wd(dev); + + pt_debug(dev, DL_INFO, "%s: Send BL Loader Enter\n", __func__); +#ifdef TTHE_TUNER_SUPPORT + cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Enter"); +#endif + rc = pt_ldr_enter_(dev, dev_id); + if (rc) { + *update_status = UPDATE_FW_ENTER_BL_ERROR; + pt_debug(dev, DL_ERROR, "%s: Error cannot start Loader (ret=%d)\n", + __func__, rc); + goto _pt_load_app_exit; + } + pt_debug(dev, DL_INFO, + "%s: dev: silicon id=%08X rev=%02X bl=%08X\n", __func__, + dev_id->silicon_id, dev_id->rev_id, dev_id->bl_ver); + *update_status += 1; + + /* + * since start loader is successful, firmware mode can be assumed + * at bl mode directly. Updating this variable is helpful to detect + * firmware entered application mode. + */ + cd->mode = PT_MODE_BOOTLOADER; + + /* get last row */ + last_row = (u8 *)fw + fw_size - image_rec_size; + pt_get_row_(dev, row_buf, last_row, image_rec_size); + pt_ldr_parse_row_(dev, row_buf, row_image); + + /* initialise bootloader */ + rc = pt_ldr_init_(dev, row_image); + if (rc) { + *update_status = UPDATE_FW_ERASE_ERROR; + pt_debug(dev, DL_ERROR, "%s: Error cannot init Loader (ret=%d)\n", + __func__, rc); + goto _pt_load_app_exit; + } + *update_status += 5; + + pt_debug(dev, DL_INFO, + "%s: Send BL Loader Blocks\n", __func__); +#ifdef TTHE_TUNER_SUPPORT + cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Blocks"); +#endif + p = (u8 *)fw; + while (p < last_row) { + /* Get row */ + row_count += 1; + pt_debug(dev, DL_INFO, "%s: read row=%d\n", + __func__, row_count); + memset(row_buf, 0, row_buf_size); + p = pt_get_row_(dev, row_buf, p, image_rec_size); + + /* Don't update BL status on every pass */ + if (row_count / 8 * 8 == row_count) { + /* Calculate % complete for update_status sysfs */ + percent_cmplt = row_count * 100 / total_row_count; + if (percent_cmplt > UPDATE_FW_ACTIVE_99) + percent_cmplt = UPDATE_FW_ACTIVE_99; + *update_status = (percent_cmplt > *update_status) ? + percent_cmplt : *update_status; +#ifdef TTDL_DIAGNOSTICS + pt_debug(dev, DL_INFO, + "Wrote row num %d of total %d\n", + row_count, total_row_count); +#endif + } + + /* Parse row */ + pt_debug(dev, DL_INFO, "%s: p=%p buf=%p buf[0]=%02X\n", + __func__, p, row_buf, row_buf[0]); + rc = pt_ldr_parse_row_(dev, row_buf, row_image); + pt_debug(dev, DL_INFO, + "%s: array_id=%02X row_num=%04X(%d) row_size=%04X(%d)\n", + __func__, row_image->array_id, + row_image->row_num, row_image->row_num, + row_image->row_size, row_image->row_size); + if (rc) { + *update_status = UPDATE_FW_PARSE_ROW_ERROR; + pt_debug(dev, DL_ERROR, + "%s: Parse Row Error (a=%d r=%d ret=%d\n", + __func__, row_image->array_id, + row_image->row_num, rc); + goto _pt_load_app_exit; + } else { + pt_debug(dev, DL_INFO, + "%s: Parse Row (a=%d r=%d ret=%d\n", + __func__, row_image->array_id, + row_image->row_num, rc); + } + + /* program row */ + rc = pt_ldr_prog_row_(dev, row_image); + if (rc) { + *update_status = UPDATE_FW_PROGRAM_ROW_ERROR; + pt_debug(dev, DL_ERROR, + "%s: Prog Row Error (array=%d row=%d ret=%d)\n", + __func__, row_image->array_id, + row_image->row_num, rc); + goto _pt_load_app_exit; + } + + pt_debug(dev, DL_INFO, + "%s: array=%d row_cnt=%d row_num=%04X\n", + __func__, row_image->array_id, row_count, + row_image->row_num); + } + + /* exit loader */ + pt_debug(dev, DL_INFO, "%s: Send BL Loader Terminate\n", __func__); +#ifdef TTHE_TUNER_SUPPORT + cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Terminate"); +#endif + rc = pt_ldr_exit_(dev); + if (rc) { + *update_status = UPDATE_FW_EXIT_BL_ERROR; + pt_debug(dev, DL_ERROR, "%s: Error on exit Loader (ret=%d)\n", + __func__, rc); + + /* verify app checksum */ + rc_tmp = pt_ldr_verify_chksum_(dev); + if (rc_tmp) { + *update_status = UPDATE_FW_CHECK_SUM_ERROR; + pt_debug(dev, DL_ERROR, + "%s: ldr_verify_chksum fail r=%d\n", + __func__, rc_tmp); + } else + pt_debug(dev, DL_INFO, + "%s: APP Checksum Verified\n", __func__); + } else + *update_status = UPDATE_FW_ACTIVE_99; + +_pt_load_app_exit: + kfree(row_buf); + kfree(row_image); + kfree(dev_id); +_pt_load_app_error: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_upgrade_firmware + * + * SUMMARY: Program the firmware image and set call back for start up. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *fw_img - pointer to the firmware + * fw_size - size of firmware + * *update_status - store the update status of firmware + ******************************************************************************/ +static int pt_upgrade_firmware(struct device *dev, const u8 *fw_img, + int fw_size, u8 *update_status) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + struct pt_core_data *cd = dev_get_drvdata(dev); + int retry = PT_LOADER_FW_UPGRADE_RETRY_COUNT; + bool wait_for_calibration_complete = false; + int rc; + int t; + + _pt_update_write_file_status(dev, UPDATE_FW_IDLE); + + if (update_status == NULL) { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, + "%s: update_status is NULL pointer %d\n", + __func__, rc); + return rc; + } else if (*update_status > UPDATE_FW_ACTIVE_10) { + pt_debug(dev, DL_WARN, + "%s: update_status is illegal = %d, fixed to %d\n", + __func__, *update_status, UPDATE_FW_ACTIVE_10); + *update_status = UPDATE_FW_ACTIVE_10; + } + + /* Ensure no enum task is pending */ + pt_debug(dev, DL_WARN, "%s: Cancel enum work thread\n", __func__); + cancel_work_sync(&cd->enum_work); + + pm_runtime_get_sync(dev); + *update_status += 1; + + rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + *update_status = UPDATE_FW_EXCLUSIVE_ACCESS_ERROR; + goto exit; + } + *update_status += 1; + + t = *update_status; + while (retry--) { + /* Restore the update_status */ + *update_status = t; + rc = pt_load_app_(dev, fw_img, fw_size, update_status); + if (rc < 0) + pt_debug(dev, DL_ERROR, + "%s: Firmware update failed rc=%d, retry:%d\n", + __func__, rc, retry); + else + break; + msleep(20); + } + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: Firmware update failed with error code %d\n", + __func__, rc); + } else if (ld->loader_pdata && + (ld->loader_pdata->flags + & PT_LOADER_FLAG_CALIBRATE_AFTER_FW_UPGRADE)) { +#if (KERNEL_VERSION(3, 13, 0) <= LINUX_VERSION_CODE) + reinit_completion(&ld->calibration_complete); +#else + INIT_COMPLETION(ld->calibration_complete); +#endif + /* set up call back for startup */ + pt_debug(dev, DL_INFO, + "%s: Adding callback for calibration\n", __func__); + rc = cmd->subscribe_attention(dev, PT_ATTEN_STARTUP, + PT_LOADER_NAME, pt_calibration_attention, 0); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Failed adding callback for calibration\n", + __func__); + pt_debug(dev, DL_ERROR, + "%s: No calibration will be performed\n", + __func__); + rc = 0; + } else + wait_for_calibration_complete = true; + } + + cmd->release_exclusive(dev); + +exit: + if (!rc) { + cmd->request_enum(dev, true); + + /* + * Wait for FW reset sentinel from reset or execute for up + * to 500ms + */ + t = wait_event_timeout(cd->wait_q, + (cd->startup_status >= + STARTUP_STATUS_FW_RESET_SENTINEL) && + (cd->mode == PT_MODE_OPERATIONAL), + msecs_to_jiffies(PT_BL_WAIT_FOR_SENTINEL)); + if (IS_TMO(t)) { + pt_debug(dev, DL_WARN, + "%s: 0x%04X Timeout waiting for FW sentinel", + __func__, cd->startup_status); + } + + /* Double verify DUT is alive and well in Application mode */ + if (!(cd->startup_status & STARTUP_STATUS_FW_RESET_SENTINEL)) { + pt_debug(dev, DL_ERROR, + "%s FW sentinel not seen\n", __func__); + *update_status = UPDATE_FW_SENTINEL_NOT_SEEN; + } else if (cd->mode != PT_MODE_OPERATIONAL) { + *update_status = UPDATE_FW_MODE_ERROR; + pt_debug(dev, DL_ERROR, + "%s ERROR: Not in App mode as expected\n", + __func__); + } else { + *update_status = UPDATE_FW_COMPLETE; + pt_debug(dev, DL_INFO, + "%s == PIP1 FW upgrade finished ==\n", + __func__); + } + } else if (*update_status < UPDATE_FW_COMPLETE) { + *update_status = UPDATE_FW_UNDEFINED_ERROR; + pt_debug(dev, DL_ERROR, "%s undefined error!\n", __func__); + } + + pm_runtime_put_sync(dev); + + if (wait_for_calibration_complete) + wait_for_completion(&ld->calibration_complete); + + return rc; +} + +#endif /* PT_FW_UPGRADE */ + +#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE +/******************************************************************************* + * FUNCTION: pt_check_firmware_version_platform + * + * SUMMARY: The caller of function pt_check_firmware_version() to determine + * whether to load firmware from touch_firmware structure. + * + * RETURN: + * 0: Don't upgrade + * 1: Upgrade + * + * PARAMETERS: + * *dev - pointer to device structure + * *fw - pointer to the touch_firmware structure + ******************************************************************************/ +static int pt_check_firmware_version_platform(struct device *dev, + struct pt_touch_firmware *fw) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + u32 fw_ver_new; + u32 fw_revctrl_new; + int upgrade; + + if (!ld->si) { + pt_debug(dev, DL_WARN, + "%s: No firmware info found, DUT FW may be corrupted\n", + __func__); + return PT_AUTO_LOAD_FOR_CORRUPTED_FW; + } + + fw_ver_new = get_unaligned_be16(fw->ver + 2); + /* 4 middle bytes are not used */ + fw_revctrl_new = get_unaligned_be32(fw->ver + 8); + pt_debug(dev, DL_WARN, "%s: Built-in FW version 0x%04x rev %d\n", + __func__, fw_ver_new, fw_revctrl_new); + + upgrade = pt_check_firmware_version(dev, fw_ver_new, + fw_revctrl_new); + + if (upgrade > 0) + return 1; + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_get_platform_firmware + * + * SUMMARY: To get the pointer of right touch_firmware structure by panel id. + * + * RETURN: + * pointer to touch_firmware structure or null pointer if fail + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static struct pt_touch_firmware *pt_get_platform_firmware( + struct device *dev) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + struct pt_touch_firmware **fws; + struct pt_touch_firmware *fw; + u8 panel_id; + + panel_id = pt_get_panel_id(dev); + if (panel_id == PANEL_ID_NOT_ENABLED) { + pt_debug(dev, DL_WARN, + "%s: Panel ID not enabled, using legacy firmware\n", + __func__); + return ld->loader_pdata->fw; + } + + fws = ld->loader_pdata->fws; + if (!fws) { + pt_debug(dev, DL_ERROR, + "%s: No firmwares provided\n", __func__); + return NULL; + } + + /* Find FW according to the Panel ID */ + while ((fw = *fws++)) { + if (fw->panel_id == panel_id) { + pt_debug(dev, DL_WARN, + "%s: Found matching fw:%p with Panel ID: 0x%02X\n", + __func__, fw, fw->panel_id); + return fw; + } + pt_debug(dev, DL_WARN, + "%s: Found mismatching fw:%p with Panel ID: 0x%02X\n", + __func__, fw, fw->panel_id); + } + + return NULL; +} + +/******************************************************************************* + * FUNCTION: upgrade_firmware_from_platform + * + * SUMMARY: Get touch_firmware structure and perform upgrade if pass the + * firmware version check. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * forced - flag to force upgrade(1:force to upgrade) + ******************************************************************************/ +static int upgrade_firmware_from_platform(struct device *dev, + bool forced) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_touch_firmware *fw; + int rc = -ENODEV; + int upgrade; + int retry = 3; + +retry_bl: + if (!ld->loader_pdata) { + _pt_update_write_file_status(dev, UPDATE_FW_NO_PLATFORM_DATA); + pt_debug(dev, DL_ERROR, + "%s: No loader platform data\n", __func__); + return rc; + } + + fw = pt_get_platform_firmware(dev); + if (!fw || !fw->img || !fw->size) { + _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); + pt_debug(dev, DL_ERROR, + "%s: No platform firmware\n", __func__); + return rc; + } + + if (!fw->ver || !fw->vsize) { + _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); + pt_debug(dev, DL_ERROR, "%s: No platform firmware version\n", + __func__); + return rc; + } + + if (forced) + upgrade = forced; + else + upgrade = pt_check_firmware_version_platform(dev, fw); + + if (upgrade) { + rc = pt_upgrade_firmware(dev, + fw->img, fw->size, &write_file_status); + + /* An extra BL may be needed if default PID was wrong choice */ + if ((cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) && + !rc && !ld->si && retry--) { + pt_debug(dev, DL_WARN, "%s: ATM - An extra BL may be needed\n", + __func__); + /* Panel_ID coming from sysinfo, ensure we have it */ + ld->si = cmd->request_sysinfo(dev); + goto retry_bl; + } + } + + return rc; +} +#endif /* CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE */ + +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE +/******************************************************************************* + * FUNCTION: _pt_pip1_bl_from_file + * + * SUMMARY: Wrapper function for _pt_firmware_cont() to perform bl with an + * image file from userspace. + * + * PARAMETERS: + * *fw - pointer to firmware structure + * forced - flag to force upgrade(1:force to upgrade) + ******************************************************************************/ +static int _pt_pip1_bl_from_file(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int fw_size = 0; + int rc = 0; + u8 *fw_img = NULL; + u8 header_size = 0; + + _pt_update_write_file_status(dev, UPDATE_FW_IDLE); + + fw_img = kzalloc(PT_PIP2_MAX_FILE_SIZE, GFP_KERNEL); + if (!fw_img) { + _pt_update_write_file_status(dev, UPDATE_FW_SYSTEM_NOMEM); + rc = -ENOMEM; + goto exit; + } + + rc = cmd->nonhid_cmd->read_us_file(dev, cd->pip2_us_file_path, + fw_img, &fw_size); + if (rc) { + pt_debug(dev, DL_ERROR, "%s: No firmware provided to load\n", + __func__); + _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); + goto exit; + } + + if (!fw_img || !fw_size) { + pt_debug(dev, DL_ERROR, + "%s: No firmware received\n", __func__); + _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); + goto exit; + } + + header_size = fw_img[0]; + if (header_size >= (fw_size + 1)) { + pt_debug(dev, DL_ERROR, + "%s: Firmware format is invalid\n", __func__); + _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); + goto exit; + } + + pt_upgrade_firmware(dev, &(fw_img[header_size + 1]), + fw_size - (header_size + 1), &write_file_status); +exit: + kfree(fw_img); + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_firmware_cont + * + * SUMMARY: Firmware upgrade continue function that verifies the firmware size + * in the firmware class and then upgrades the firmware. + * + * PARAMETERS: + * *fw - pointer to firmware structure + * forced - flag to force upgrade(1:force to upgrade) + ******************************************************************************/ +static void _pt_firmware_cont(const struct firmware *fw, void *context) +{ + struct device *dev = context; + struct pt_loader_data *ld = pt_get_loader_data(dev); + u8 header_size = 0; + + _pt_update_write_file_status(dev, UPDATE_FW_IDLE); + if (!fw) { + pt_debug(dev, DL_ERROR, "%s: No firmware\n", __func__); + _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); + goto pt_firmware_cont_exit; + } + + if (!fw->data || !fw->size) { + pt_debug(dev, DL_ERROR, + "%s: No firmware received\n", __func__); + _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); + goto pt_firmware_cont_release_exit; + } + + header_size = fw->data[0]; + if (header_size >= (fw->size + 1)) { + pt_debug(dev, DL_ERROR, + "%s: Firmware format is invalid\n", __func__); + _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); + goto pt_firmware_cont_release_exit; + } + + pt_upgrade_firmware(dev, &(fw->data[header_size + 1]), + fw->size - (header_size + 1), &write_file_status); + +pt_firmware_cont_release_exit: + if (fw) + release_firmware(fw); + +pt_firmware_cont_exit: + ld->is_manual_upgrade_enabled = 0; +} + +/******************************************************************************* + * FUNCTION: pt_check_firmware_config_version + * + * SUMMARY: Compare fw's config version with fw image's. If they are differnt + * report a FW upgrade is needed. + * + * RETURN: + * -1: Do not upgrade firmware + * 0: Version info same, let caller decide + * 1: Do a firmware upgrade + * + * PARAMETERS: + * *dev - pointer to device structure + * image_config_ver - Image's firmware config version + ******************************************************************************/ +static int pt_check_firmware_config_version(struct device *dev, + u16 image_config_ver) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + u16 fw_config_ver; + + fw_config_ver = ld->si->ttdata.fw_ver_conf; + + pt_debug(dev, DL_WARN, + "%s: Current config ver:0x%04X New config ver:0x%04X\n", + __func__, fw_config_ver, image_config_ver); + + if (image_config_ver != fw_config_ver) { + pt_debug(dev, DL_WARN, + "%s: Image config ver is different, will upgrade\n", + __func__); + return 1; + } + + if (image_config_ver == fw_config_ver) { + pt_debug(dev, DL_WARN, + "%s: Image config ver is the same, will NOT upgrade\n", + __func__); + return 0; + } + + return -1; +} + +/******************************************************************************* + * FUNCTION: pt_check_firmware_version_builtin + * + * SUMMARY: The caller of function pt_check_firmware_version() to determine + * whether to load built-in firmware. + * + * RETURN: + * 0: Don't upgrade + * 1: Upgrade + * + * PARAMETERS: + * *dev - pointer to device structure + * *fw - pointer to the firmware structure + ******************************************************************************/ +static int pt_check_firmware_version_builtin(struct device *dev, + const struct firmware *fw) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + u16 fw_config_ver_new; + u32 fw_ver_new; + u32 fw_revctrl_new; + int upgrade; + + if (!ld->si) { + pt_debug(dev, DL_WARN, + "%s: No firmware info found, DUT FW may be corrupted\n", + __func__); + return PT_AUTO_LOAD_FOR_CORRUPTED_FW; + } + + fw_ver_new = get_unaligned_be16(fw->data + 3); + /* 4 middle bytes are not used */ + fw_revctrl_new = get_unaligned_be32(fw->data + 9); + /* Offset 17,18 is the TT Config version*/ + fw_config_ver_new = get_unaligned_be16(fw->data + 17); + + pt_debug(dev, DL_WARN, + "%s: Built-in FW version=0x%04x rev=%d config=0x%04X\n", + __func__, fw_ver_new, fw_revctrl_new, fw_config_ver_new); + + upgrade = pt_check_firmware_version(dev, fw_ver_new, + fw_revctrl_new); + + /* Only check config version if FW version was an exact match */ + if (upgrade == 0) + upgrade = pt_check_firmware_config_version(dev, + fw_config_ver_new); + + if (upgrade > 0) + return 1; + + return 0; +} + +/******************************************************************************* + * FUNCTION: _pt_firmware_cont_builtin + * + * SUMMARY: Perform upgrade if pass the firmware version check. + * + * PARAMETERS: + * *dev - pointer to device structure + * forced - flag to force upgrade(1:force to upgrade) + ******************************************************************************/ +static void _pt_firmware_cont_builtin(const struct firmware *fw, + void *context) +{ + struct device *dev = context; + struct pt_loader_data *ld = pt_get_loader_data(dev); + int upgrade; + + if (!fw) { + pt_debug(dev, DL_INFO, + "%s: No builtin firmware\n", __func__); + goto _pt_firmware_cont_builtin_exit; + } + + if (!fw->data || !fw->size) { + pt_debug(dev, DL_ERROR, + "%s: Invalid builtin firmware\n", __func__); + goto _pt_firmware_cont_builtin_exit; + } + + pt_debug(dev, DL_WARN, "%s: Found firmware\n", __func__); + + upgrade = pt_check_firmware_version_builtin(dev, fw); + if (upgrade) { + _pt_firmware_cont(fw, dev); + ld->builtin_bin_fw_status = 0; + return; + } + +_pt_firmware_cont_builtin_exit: + if (fw) + release_firmware(fw); + + ld->builtin_bin_fw_status = -EINVAL; +} + +/******************************************************************************* + * FUNCTION: upgrade_firmware_from_class + * + * SUMMARY: Create the firmware class but don't actually load any FW to the + * DUT. This creates all the sysfs nodes needed for a user to bootload + * the DUT with their own bin file. + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int upgrade_firmware_from_class(struct device *dev) +{ + int retval; + + pt_debug(dev, DL_INFO, + "%s: Enabling firmware class loader\n", __func__); + + retval = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, + dev_name(dev), dev, GFP_KERNEL, dev, + _pt_firmware_cont); + if (retval < 0) { + pt_debug(dev, DL_ERROR, + "%s: Fail request firmware class file load\n", + __func__); + return retval; + } + + return 0; +} + +#define FILENAME_LEN_MAX 64 +/******************************************************************************* + * FUNCTION: generate_firmware_filename + * + * SUMMARY: Generate firmware file name by panel id. Generates binary FW + * filename as following: + * - Panel ID not enabled: tt_fw.bin + * - Panel ID enabled: tt_fw_pidXX.bin + * + * RETURN: + * pointer to file name or null pointer if fail + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static char *generate_firmware_filename(struct device *dev) +{ + char *filename; + u8 panel_id; + + filename = kzalloc(FILENAME_LEN_MAX, GFP_KERNEL); + if (!filename) + return NULL; + + panel_id = pt_get_panel_id(dev); + if (panel_id == PANEL_ID_NOT_ENABLED) + snprintf(filename, FILENAME_LEN_MAX, "%s", PT_FW_FILE_NAME); + else + snprintf(filename, FILENAME_LEN_MAX, "%s_pid%02X%s", + PT_FW_FILE_PREFIX, panel_id, PT_FW_FILE_SUFFIX); + + pt_debug(dev, DL_INFO, "%s: Filename: %s\n", + __func__, filename); + + return filename; +} + +/******************************************************************************* + * FUNCTION: generate_silicon_id_firmware_filename + * + * SUMMARY: Generate firmware file name with the HW version prefix followed by + * panel id. Generates binary FW filename as following (where XXXX is the + * silicon ID): + * - Panel ID not enabled: XXXX_tt_fw.bin + * - Panel ID enabled: XXXX_tt_fw_pidXX.bin + * + * RETURN: + * pointer to file name or null pointer if fail + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static char *generate_silicon_id_firmware_filename(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + char *filename; + char si_id[5] = "DEAD"; + u8 panel_id; + + filename = kzalloc(FILENAME_LEN_MAX, GFP_KERNEL); + if (!filename) + return NULL; + + panel_id = pt_get_panel_id(dev); + memcpy(si_id, cd->hw_version, 4); + + if (panel_id == PANEL_ID_NOT_ENABLED) + snprintf(filename, FILENAME_LEN_MAX, "%s_%s", si_id, + PT_FW_FILE_NAME); + else + snprintf(filename, FILENAME_LEN_MAX, "%s_%s_pid%02X%s", + si_id, PT_FW_FILE_PREFIX, panel_id, PT_FW_FILE_SUFFIX); + + pt_debug(dev, DL_INFO, "%s: Filename: %s\n", __func__, filename); + + return filename; +} + +#define MAX_FILE_NAMES 2 +/******************************************************************************* + * FUNCTION: upgrade_firmware_from_builtin + * + * SUMMARY: Create the firmware class load FW by searching the name of built-in + * file. Then perform upgrade after getting the file. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int upgrade_firmware_from_builtin(struct device *dev) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + struct pt_core_data *cd = dev_get_drvdata(dev); + char *filename[MAX_FILE_NAMES]; + int index = 0; + int retval; + const struct firmware *fw_entry = NULL; + int retry = 3; + +retry_bl: + pt_debug(dev, DL_WARN, + "%s: Enabling firmware class loader built-in\n", + __func__); + + /* Load the supported file names in the search order */ + + /* 0 = Flash file name with optional PID "pt_fw<_PIDX>.bin" */ + filename[0] = generate_firmware_filename(dev); + if (!filename[0]) { + pt_debug(dev, DL_ERROR, + "%s: ERROR - Could not generate filename\n", __func__); + return -ENOMEM; + } + + /* + * 1 = Flash file name with Silicon ID prefix and optional PID + * "XXXX_pt_fw<_PIDX>.bin" + */ + filename[1] = generate_silicon_id_firmware_filename(dev); + if (!filename[1]) { + pt_debug(dev, DL_ERROR, + "%s: ERROR - Could not generate filename\n", __func__); + return -ENOMEM; + } + + mutex_lock(&cd->firmware_class_lock); + while (index < MAX_FILE_NAMES) { + pt_debug(dev, DL_WARN, "%s: Request FW file %s\n", __func__, + filename[index]); +#if (KERNEL_VERSION(3, 13, 0) > LINUX_VERSION_CODE) + retval = request_firmware(&fw_entry, filename[index], dev); +#else + retval = request_firmware_direct(&fw_entry, + filename[index], dev); +#endif + if (retval < 0) { + pt_debug(dev, DL_WARN, "%s: Fail request FW %s load\n", + __func__, filename[index]); + } else { + pt_debug(dev, DL_WARN, "%s: FW %s class file loading\n", + __func__, filename[index]); + break; + } + index++; + } + + /* Proceed with the BL if a matching file was found */ + if (index != MAX_FILE_NAMES) { + _pt_firmware_cont_builtin(fw_entry, dev); + /* An extra BL may be needed if default PID was wrong choice */ + if ((cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) && + !ld->si && retry--) { + pt_debug(dev, DL_WARN, "%s: ATM - An extra BL may be needed\n", + __func__); + /* Free allocated memory */ + index = 0; + while (index < MAX_FILE_NAMES) + kfree(filename[index++]); + /* Reset index to 0 */ + index = 0; + mutex_unlock(&cd->firmware_class_lock); + + /* Panel_ID coming from sysinfo, ensure we have it */ + ld->si = cmd->request_sysinfo(dev); + goto retry_bl; + } + retval = ld->builtin_bin_fw_status; + } + + index = 0; + while (index < MAX_FILE_NAMES) + kfree(filename[index++]); + + mutex_unlock(&cd->firmware_class_lock); + return retval; +} +#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ + +#ifndef TTDL_KERNEL_SUBMISSION +#if PT_TTCONFIG_UPGRADE +/******************************************************************************* + * FUNCTION: pt_write_config_row_ + * + * SUMMARY: Allow to program the data block area that includes configuration + * data, manufacturing data, design data. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * ebid - block id to determine the block name(configuration,etc) + * row_number - row number if written block + * row_size - row size of written data + * *data - pointer to the data to write + ******************************************************************************/ +static int pt_write_config_row_(struct device *dev, u8 ebid, + u16 row_number, u16 row_size, u8 *data) +{ + int rc; + u16 actual_write_len; + + rc = cmd->nonhid_cmd->write_data_block(dev, row_number, + row_size, ebid, data, (u8 *)pt_data_block_security_key, + &actual_write_len); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Fail Put EBID=%d row=%d cmd fail r=%d\n", + __func__, ebid, row_number, rc); + return rc; + } + + if (actual_write_len != row_size) { + pt_debug(dev, DL_ERROR, + "%s: Fail Put EBID=%d row=%d wrong write size=%d\n", + __func__, ebid, row_number, actual_write_len); + rc = -EINVAL; + } + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_upgrade_ttconfig + * + * SUMMARY: Program ttconfig_data with following steps: + * 1) Suspend scanning + * 2) Write data to the data block + * 3) Verify the crc for data block + * 4) Resume scanning + * 5) Set up call back for calibration if required + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *ttconfig_data - pointer to the config data to write to data block + * ttconfig_size - size of config data to write + ******************************************************************************/ +static int pt_upgrade_ttconfig(struct device *dev, + const u8 *ttconfig_data, int ttconfig_size) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + bool wait_for_calibration_complete = false; + u8 ebid = PT_TCH_PARM_EBID; + u16 row_size = PT_DATA_ROW_SIZE; + u16 table_size; + u16 row_count; + u16 residue; + u8 *row_buf; + u8 verify_crc_status; + u16 calculated_crc; + u16 stored_crc; + int rc = 0; + int i; + + table_size = ttconfig_size; + row_count = table_size / row_size; + row_buf = (u8 *)ttconfig_data; + pt_debug(dev, DL_INFO, "%s: size:%d row_size=%d row_count=%d\n", + __func__, table_size, row_size, row_count); + + pm_runtime_get_sync(dev); + + rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) + goto exit; + + rc = cmd->nonhid_cmd->suspend_scanning(dev, 0); + if (rc < 0) + goto release; + + for (i = 0; i < row_count; i++) { + pt_debug(dev, DL_INFO, "%s: row=%d size=%d\n", + __func__, i, row_size); + rc = pt_write_config_row_(dev, ebid, i, row_size, + row_buf); + if (rc) { + pt_debug(dev, DL_ERROR, "%s: Fail put row=%d r=%d\n", + __func__, i, rc); + break; + } + row_buf += row_size; + } + if (!rc) { + residue = table_size % row_size; + pt_debug(dev, DL_WARN, "%s: row=%d size=%d\n", + __func__, i, residue); + rc = pt_write_config_row_(dev, ebid, i, residue, + row_buf); + row_count++; + if (rc) + pt_debug(dev, DL_ERROR, "%s: Fail put row=%d r=%d\n", + __func__, i, rc); + } + + if (!rc) + pt_debug(dev, DL_WARN, + "%s: TT_CFG updated: rows:%d bytes:%d\n", + __func__, row_count, table_size); + + rc = cmd->nonhid_cmd->verify_cfg_block_crc(dev, 0, ebid, + &verify_crc_status, &calculated_crc, &stored_crc); + if (rc || verify_crc_status) + pt_debug(dev, DL_ERROR, + "%s: CRC Failed, ebid=%d, status=%d, scrc=%X ccrc=%X\n", + __func__, ebid, verify_crc_status, + calculated_crc, stored_crc); + else + pt_debug(dev, DL_INFO, + "%s: CRC PASS, ebid=%d, status=%d, scrc=%X ccrc=%X\n", + __func__, ebid, verify_crc_status, + calculated_crc, stored_crc); + + rc = cmd->nonhid_cmd->resume_scanning(dev, 0); + if (rc < 0) + goto release; + + if (ld->loader_pdata && + (ld->loader_pdata->flags + & PT_LOADER_FLAG_CALIBRATE_AFTER_TTCONFIG_UPGRADE)) { +#if (KERNEL_VERSION(3, 13, 0) <= LINUX_VERSION_CODE) + reinit_completion(&ld->calibration_complete); +#else + INIT_COMPLETION(ld->calibration_complete); +#endif + /* set up call back for startup */ + pt_debug(dev, DL_INFO, "%s: Adding callback for calibration\n", + __func__); + rc = cmd->subscribe_attention(dev, PT_ATTEN_STARTUP, + PT_LOADER_NAME, pt_calibration_attention, 0); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Failed adding callback for calibration\n", + __func__); + pt_debug(dev, DL_ERROR, + "%s: No calibration will be performed\n", + __func__); + rc = 0; + } else + wait_for_calibration_complete = true; + } + +release: + cmd->release_exclusive(dev); + +exit: + if (!rc) + cmd->request_enum(dev, true); + + pm_runtime_put_sync(dev); + + if (wait_for_calibration_complete) + wait_for_completion(&ld->calibration_complete); + + return rc; +} +#endif /* PT_TTCONFIG_UPGRADE */ + +#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE +/******************************************************************************* + * FUNCTION: pt_get_ttconfig_crc + * + * SUMMARY: Get crc from ttconfig data. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *ttconfig_data - pointer to the config data + * ttconfig_size - size of config data + * *crc - pointer to the crc of configure to be stored + ******************************************************************************/ +static int pt_get_ttconfig_crc(struct device *dev, + const u8 *ttconfig_data, int ttconfig_size, u16 *crc) +{ + u16 crc_loc; + + crc_loc = get_unaligned_le16(&ttconfig_data[2]); + if (ttconfig_size < crc_loc + 2) + return -EINVAL; + + *crc = get_unaligned_le16(&ttconfig_data[crc_loc]); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_get_ttconfig_version + * + * SUMMARY: Get version number from ttconfig data. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *ttconfig_data - pointer to the config data + * ttconfig_size - size of config data + * *version - pointer to the version of configure to be stored + ******************************************************************************/ +static int pt_get_ttconfig_version(struct device *dev, + const u8 *ttconfig_data, int ttconfig_size, u16 *version) +{ + if (ttconfig_size < PT_TTCONFIG_VERSION_OFFSET + + PT_TTCONFIG_VERSION_SIZE) + return -EINVAL; + + *version = get_unaligned_le16( + &ttconfig_data[PT_TTCONFIG_VERSION_OFFSET]); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_check_ttconfig_version + * + * SUMMARY: Check the configure version and crc value to determine whether to + * upgrade,the upgrade conditions as following: + * 1) To upgrade if the config version is newer than current config, but + * this check is based on the flag in loader plarform data. + * 2) To upgrade if config CRC is different. + * 3) Don't upgrade when can't match any of above conditions. + * + * RETURN: + * 0: Don't upgrade + * 1: Upgrade + * + * PARAMETERS: + * *dev - pointer to device structure + * *ttconfig_data - pointer to the config data + * ttconfig_size - size of config data + ******************************************************************************/ +static int pt_check_ttconfig_version(struct device *dev, + const u8 *ttconfig_data, int ttconfig_size) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + u16 cfg_crc_new; + int rc; + + if (!ld->si) + return 0; + + /* Check for config version */ + if (ld->loader_pdata->flags & + PT_LOADER_FLAG_CHECK_TTCONFIG_VERSION) { + u16 cfg_ver_new; + + rc = pt_get_ttconfig_version(dev, ttconfig_data, + ttconfig_size, &cfg_ver_new); + if (rc) + return 0; + + pt_debug(dev, DL_INFO, "%s: img_ver:0x%04X new_ver:0x%04X\n", + __func__, ld->si->ttdata.fw_ver_conf, cfg_ver_new); + + /* Check if config version is newer */ + if (cfg_ver_new > ld->si->ttdata.fw_ver_conf) { + pt_debug(dev, DL_WARN, + "%s: Config version newer, will upgrade\n", __func__); + return 1; + } + + pt_debug(dev, DL_WARN, + "%s: Config version is identical or older, will NOT upgrade\n", + __func__); + /* Check for config CRC */ + } else { + rc = pt_get_ttconfig_crc(dev, ttconfig_data, + ttconfig_size, &cfg_crc_new); + if (rc) + return 0; + + pt_debug(dev, DL_INFO, "%s: img_crc:0x%04X new_crc:0x%04X\n", + __func__, ld->si->ttconfig.crc, cfg_crc_new); + + if (cfg_crc_new != ld->si->ttconfig.crc) { + pt_debug(dev, DL_WARN, + "%s: Config CRC different, will upgrade\n", + __func__); + return 1; + } + + pt_debug(dev, DL_WARN, + "%s: Config CRC equal, will NOT upgrade\n", __func__); + } + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_check_ttconfig_version_platform + * + * SUMMARY: To call the function pt_check_ttconfig_version() to determine + * whether to load config if the firmware version match with current firmware. + * + * RETURN: + * 0: Don't upgrade + * 1: Upgrade + * + * PARAMETERS: + * *dev - pointer to device structure + * *ttconfig - pointer to touch_config structure + ******************************************************************************/ +static int pt_check_ttconfig_version_platform(struct device *dev, + struct pt_touch_config *ttconfig) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + u32 fw_ver_config; + u32 fw_revctrl_config; + + if (!ld->si) { + pt_debug(dev, DL_INFO, + "%s: No firmware info found, DUT FW may be corrupted\n", + __func__); + return 0; + } + + fw_ver_config = get_unaligned_be16(ttconfig->fw_ver + 2); + /* 4 middle bytes are not used */ + fw_revctrl_config = get_unaligned_be32(ttconfig->fw_ver + 8); + + /* FW versions should match */ + if (pt_check_firmware_version(dev, fw_ver_config, + fw_revctrl_config)) { + pt_debug(dev, DL_ERROR, + "%s: FW versions mismatch\n", __func__); + return 0; + } + + /* Check PowerOn Self Test, TT_CFG CRC bit */ + if ((ld->si->ttdata.post_code & PT_POST_TT_CFG_CRC_MASK) == 0) { + pt_debug(dev, DL_ERROR, + "%s: POST, TT_CFG failed (%X), will upgrade\n", + __func__, ld->si->ttdata.post_code); + return 1; + } + + return pt_check_ttconfig_version(dev, ttconfig->param_regs->data, + ttconfig->param_regs->size); +} + +/******************************************************************************* + * FUNCTION: pt_get_platform_ttconfig + * + * SUMMARY: To get the pointer of right touch_config structure by panel id. + * + * RETURN: + * pointer to touch_config structure or null pointer if fail + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static struct pt_touch_config *pt_get_platform_ttconfig( + struct device *dev) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + struct pt_touch_config **ttconfigs; + struct pt_touch_config *ttconfig; + u8 panel_id; + + panel_id = pt_get_panel_id(dev); + if (panel_id == PANEL_ID_NOT_ENABLED) { + /* TODO: Make debug message */ + pt_debug(dev, DL_INFO, + "%s: Panel ID not enabled, using legacy ttconfig\n", + __func__); + return ld->loader_pdata->ttconfig; + } + + ttconfigs = ld->loader_pdata->ttconfigs; + if (!ttconfigs) + return NULL; + + /* Find TT config according to the Panel ID */ + while ((ttconfig = *ttconfigs++)) { + if (ttconfig->panel_id == panel_id) { + /* TODO: Make debug message */ + pt_debug(dev, DL_INFO, + "%s: Found matching ttconfig:%p with Panel ID: 0x%02X\n", + __func__, ttconfig, ttconfig->panel_id); + return ttconfig; + } + pt_debug(dev, DL_ERROR, + "%s: Found mismatching ttconfig:%p with Panel ID: 0x%02X\n", + __func__, ttconfig, ttconfig->panel_id); + } + + return NULL; +} + +/******************************************************************************* + * FUNCTION: upgrade_ttconfig_from_platform + * + * SUMMARY: Get touch_firmware structure and perform upgrade if pass the + * firmware version check. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * forced - flag to force upgrade(1:force to upgrade) + ******************************************************************************/ +static int upgrade_ttconfig_from_platform(struct device *dev) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + struct pt_touch_config *ttconfig; + struct touch_settings *param_regs; + + int rc = -ENODEV; + int upgrade; + + if (!ld->loader_pdata) { + pt_debug(dev, DL_ERROR, + "%s: No loader platform data\n", __func__); + return rc; + } + + ttconfig = pt_get_platform_ttconfig(dev); + if (!ttconfig) { + pt_debug(dev, DL_ERROR, "%s: No ttconfig data\n", __func__); + return rc; + } + + param_regs = ttconfig->param_regs; + if (!param_regs) { + pt_debug(dev, DL_ERROR, "%s: No touch parameters\n", + __func__); + return rc; + } + + if (!param_regs->data || !param_regs->size) { + pt_debug(dev, DL_ERROR, + "%s: Invalid touch parameters\n", __func__); + return rc; + } + + if (!ttconfig->fw_ver || !ttconfig->fw_vsize) { + pt_debug(dev, DL_ERROR, + "%s: Invalid FW version for touch parameters\n", + __func__); + return rc; + } + + upgrade = pt_check_ttconfig_version_platform(dev, ttconfig); + if (upgrade) + return pt_upgrade_ttconfig(dev, param_regs->data, + param_regs->size); + + return rc; +} +#endif /* CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE */ + +#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE +/******************************************************************************* + * FUNCTION: pt_config_data_write + * + * SUMMARY: The write method for the config_data sysfs node. The passed + * in data (config file) is written to the config_data buffer. + * + * RETURN: Size of passed in buffer is success + * + * PARAMETERS: + * *filp - pointer to file structure + * *kobj - pointer to kobject structure + * *bin_attr - pointer to bin_attribute structure + * buf - pointer to cmd input buffer + * offset - offset index to store input buffer + * count - size of data in buffer + ******************************************************************************/ +static ssize_t pt_config_data_write(struct file *filp, + struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t offset, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pt_loader_data *data = pt_get_loader_data(dev); + u8 *p; + + pt_debug(dev, DL_INFO, "%s: offset:%lld count:%zu\n", + __func__, offset, count); + + mutex_lock(&data->config_lock); + + if (!data->config_loading) { + mutex_unlock(&data->config_lock); + return -ENODEV; + } + + p = krealloc(data->config_data, offset + count, GFP_KERNEL); + if (!p) { + kfree(data->config_data); + data->config_data = NULL; + mutex_unlock(&data->config_lock); + return -ENOMEM; + } + data->config_data = p; + + memcpy(&data->config_data[offset], buf, count); + data->config_size += count; + + mutex_unlock(&data->config_lock); + + return count; +} + +static struct bin_attribute bin_attr_config_data = { + .attr = { + .name = "config_data", + .mode = 0200, + }, + .size = 0, + .write = pt_config_data_write, +}; + +/******************************************************************************* + * FUNCTION: pt_verify_ttconfig_binary + * + * SUMMARY: Perform a simple size check if the firmware version match.And + * calculate the start pointer of config data to write and the size to write. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *bin_config_data - pointer to binary config data + * bin_config_size - size of binary config data + * **start - double pointer to config data where to be written + * *len - pointer to the size of config data to store + ******************************************************************************/ +static int pt_verify_ttconfig_binary(struct device *dev, + u8 *bin_config_data, int bin_config_size, u8 **start, int *len) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + int header_size; + u16 config_size; + u32 fw_ver_config; + u32 fw_revctrl_config; + + if (!ld->si) { + pt_debug(dev, DL_ERROR, + "%s: No firmware info found, DUT FW may be corrupted\n", + __func__); + return -ENODEV; + } + + /* + * We need 11 bytes for FW version control info and at + * least 6 bytes in config (Length + Max Length + CRC) + */ + header_size = bin_config_data[0] + 1; + if (header_size < 11 || header_size >= bin_config_size - 6) { + pt_debug(dev, DL_ERROR, + "%s: Invalid header size %d\n", __func__, + header_size); + return -EINVAL; + } + + fw_ver_config = get_unaligned_be16(&bin_config_data[1]); + /* 4 middle bytes are not used */ + fw_revctrl_config = get_unaligned_be32(&bin_config_data[7]); + + /* FW versions should match */ + if (pt_check_firmware_version(dev, fw_ver_config, + fw_revctrl_config)) { + pt_debug(dev, DL_ERROR, + "%s: FW versions mismatch\n", __func__); + return -EINVAL; + } + + config_size = get_unaligned_le16(&bin_config_data[header_size]); + /* Perform a simple size check (2 bytes for CRC) */ + if (config_size != bin_config_size - header_size - 2) { + pt_debug(dev, DL_ERROR, + "%s: Config size invalid\n", __func__); + return -EINVAL; + } + + *start = &bin_config_data[header_size]; + *len = bin_config_size - header_size; + + return 0; +} + +/* + * 1: Start loading TT Config + * 0: End loading TT Config and perform upgrade + *-1: Exit loading + */ + +/******************************************************************************* + * FUNCTION: pt_config_loading_store + * + * SUMMARY: The store method for the config_loading sysfs node. The + * passed in value controls if config loading is performed. + * + * RETURN: Size of passed in buffer is success + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_config_loading_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + long value; + u8 *start; + int length; + int rc; + + rc = kstrtol(buf, 10, &value); + if (rc < 0 || value < -1 || value > 1) { + pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__); + return size; + } + + mutex_lock(&ld->config_lock); + + if (value == 1) + ld->config_loading = true; + else if (value == -1) + ld->config_loading = false; + else if (value == 0 && ld->config_loading) { + ld->config_loading = false; + if (ld->config_size == 0) { + pt_debug(dev, DL_ERROR, + "%s: No config data\n", __func__); + goto exit_free; + } + + rc = pt_verify_ttconfig_binary(dev, + ld->config_data, ld->config_size, + &start, &length); + if (rc) + goto exit_free; + + rc = pt_upgrade_ttconfig(dev, start, length); + } + +exit_free: + kfree(ld->config_data); + ld->config_data = NULL; + ld->config_size = 0; + + mutex_unlock(&ld->config_lock); + + if (rc) + return rc; + + return size; +} + +static DEVICE_ATTR(config_loading, 0200, + NULL, pt_config_loading_store); +#endif /* CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE */ +#endif /* !TTDL_KERNEL_SUBMISSION */ + +#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE +/******************************************************************************* + * FUNCTION: pt_forced_upgrade_store + * + * SUMMARY: The store method for the forced_upgrade sysfs node. The firmware + * loading is forced to performed with platform upgrade strategy. + * + * RETURN: Size of passed in buffer is success + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_forced_upgrade_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int rc = upgrade_firmware_from_platform(dev, true); + + if (rc) + return rc; + return size; +} + +static DEVICE_ATTR(forced_upgrade, 0200, + NULL, pt_forced_upgrade_store); +#endif + +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE +/******************************************************************************* + * FUNCTION: pt_manual_upgrade_store + * + * SUMMARY: The store method for the forced_upgrade sysfs node that it is + * caller for function upgrade_firmware_from_class() to allow upgrade firmware + * manually. + * + * RETURN: Size of passed in buffer is success + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_manual_upgrade_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + u32 input_data[2] = {0}; + int length; + int rc = 0; + + length = cmd->parse_sysfs_input(dev, buf, size, input_data, + ARRAY_SIZE(input_data)); + + if (length != 1) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto exit; + } + + if (input_data[0] < 0 || input_data[0] > 1) { + pt_debug(dev, DL_WARN, "%s: Invalid arguments\n", __func__); + rc = -EINVAL; + goto exit; + } + + _pt_update_write_file_status(dev, UPDATE_FW_IDLE); + if (ld->is_manual_upgrade_enabled) { + rc = -EBUSY; + goto exit; + } + + ld->is_manual_upgrade_enabled = 1; + + rc = upgrade_firmware_from_class(ld->dev); + if (rc < 0) + ld->is_manual_upgrade_enabled = 0; + +exit: + if (rc) + return rc; + return size; +} + +#ifndef TTDL_KERNEL_SUBMISSION +static DEVICE_ATTR(manual_upgrade, 0200, NULL, pt_manual_upgrade_store); +#endif /* !TTDL_KERNEL_SUBMISSION */ +#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ + +/******************************************************************************* + * FUNCTION: pt_fw_and_config_upgrade + * + * SUMMARY: Perform all methods for firmware upgrade and config upgrade + * according to the definition of macro. + * + * PARAMETERS: + * *work_struct - pointer to work_struct structure + ******************************************************************************/ +static void pt_fw_and_config_upgrade( + struct work_struct *fw_and_config_upgrade) +{ + struct pt_loader_data *ld = container_of(fw_and_config_upgrade, + struct pt_loader_data, fw_and_config_upgrade); + struct device *dev = ld->dev; + struct pt_core_data *cd = dev_get_drvdata(dev); + int retry = 200; +#if PT_FW_UPGRADE \ + || defined(CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE) + u8 dut_gen = cmd->request_dut_generation(dev); +#endif + + /* + * Since the resume_scan command in pt_probe_complete() has + * no protection,it may cause problem for the commands in fw + * upgrade process during probe. Waiting for the probe to + * complete before performing fw upgrade can avoid this failure. + */ + while (!cd->core_probe_complete && retry--) + msleep(20); + + ld->si = cmd->request_sysinfo(dev); + if (!ld->si) + pt_debug(dev, DL_ERROR, + "%s: Fail get sysinfo pointer from core\n", + __func__); +#if !PT_FW_UPGRADE + pt_debug(dev, DL_INFO, + "%s: No FW upgrade method selected!\n", __func__); +#endif + +#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE + if (dut_gen == DUT_PIP1_ONLY) { + if (!upgrade_firmware_from_platform(dev, false)) + return; + } +#endif + +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE + if (dut_gen == DUT_PIP2_CAPABLE) { + if (!pt_pip2_upgrade_firmware_from_builtin(dev)) + return; + pt_debug(dev, DL_WARN, "%s: Builtin FW upgrade failed\n", + __func__); + } else { + if (!upgrade_firmware_from_builtin(dev)) + return; + } +#endif + +#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE + if (dut_gen == DUT_PIP1_ONLY) { + if (!upgrade_ttconfig_from_platform(dev)) + return; + } +#endif +} + +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE +#ifdef TTDL_DIAGNOSTICS +/******************************************************************************* + * FUNCTION: _pt_pip2_get_flash_info + * + * SUMMARY: Sends a FLASH_INFO command to the DUT logging the results to kmsg + * + * PARAMETERS: + * *dev - pointer to device structure + * *read_buf - pointer to the read buffer array to store the response + ******************************************************************************/ +static void _pt_pip2_get_flash_info(struct device *dev, u8 *read_buf) +{ + u16 actual_read_len; + int ret; + + /* Get flash info for debugging information */ + ret = cmd->nonhid_cmd->pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FLASH_INFO, + NULL, 0, read_buf, &actual_read_len); + if (!ret) { + pt_debug(dev, DL_DEBUG, + "%s --- FLASH Information ---\n", __func__); + pt_debug(dev, DL_DEBUG, + "%s Manufacturer ID: 0x%02x\n", + __func__, read_buf[PIP2_RESP_BODY_OFFSET]); + pt_debug(dev, DL_DEBUG, + "%s Memory Type : 0x%02x\n", + __func__, read_buf[PIP2_RESP_BODY_OFFSET + 1]); + pt_debug(dev, DL_DEBUG, + "%s Num Sectors : 0x%02x%02x%02x%02x\n", + __func__, read_buf[PIP2_RESP_BODY_OFFSET + 2], + read_buf[PIP2_RESP_BODY_OFFSET + 3], + read_buf[PIP2_RESP_BODY_OFFSET + 4], + read_buf[PIP2_RESP_BODY_OFFSET + 5]); + pt_debug(dev, DL_DEBUG, + "%s Sectors Size : 0x%02x%02x%02x%02x\n", + __func__, read_buf[PIP2_RESP_BODY_OFFSET + 6], + read_buf[PIP2_RESP_BODY_OFFSET + 7], + read_buf[PIP2_RESP_BODY_OFFSET + 8], + read_buf[PIP2_RESP_BODY_OFFSET + 9]); + pt_debug(dev, DL_DEBUG, + "%s Page Size : 0x%02x%02x%02x%02x\n", + __func__, read_buf[PIP2_RESP_BODY_OFFSET + 10], + read_buf[PIP2_RESP_BODY_OFFSET + 11], + read_buf[PIP2_RESP_BODY_OFFSET + 12], + read_buf[PIP2_RESP_BODY_OFFSET + 13]); + if (actual_read_len > 21) { + pt_debug(dev, DL_DEBUG, + "%s Status Reg1 : 0x%02x\n", + __func__, + read_buf[PIP2_RESP_BODY_OFFSET + 14]); + pt_debug(dev, DL_DEBUG, + "%s Status Reg2 : 0x%02x\n", + __func__, + read_buf[PIP2_RESP_BODY_OFFSET + 15]); + } + } +} +#endif /* TTDL_DIAGNOSTICS */ + +/******************************************************************************* + * FUNCTION: _pt_pip2_file_write_packet + * + * SUMMARY: Using the BL PIP2 commands to write a file. + * + * NOTE#1: This function support No Interrupt Solution and switch + * "pip2_send_cmd" function according to the global variable "bl_with_no_int". + * NOTE#2: The maximum write len is limited to 256 as well as the total buffer + * size is limited as PT_MAX_PIP2_MSG_SIZE. + * + * RETURNS: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * file_hd - file handle for file operation + * *write_buf - pointer of buffer to write + * write_len - length to write + * *status - pointer to store the STATUS in response + ******************************************************************************/ +static int _pt_pip2_file_write_packet(struct device *dev, u8 file_hd, + u8 *write_buf, u16 write_len, u8 *status) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + PIP2_SEND_CMD pip2_send_cmd; + int ret = 0; + u16 actual_read_len = 0; + u8 read_buf[PT_MAX_PIP2_MSG_SIZE]; + u8 buf[PT_MAX_PIP2_MSG_SIZE]; + + if (write_len > PIP2_FILE_WRITE_MAX_LEN_PER_PACKET) { + pt_debug(dev, DL_ERROR, "%s write_len (%d) is over size\n", + __func__, write_len); + return -EINVAL; + } + if (cd->bl_with_no_int) + pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd_no_int; + else + pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd; + + buf[0] = file_hd; + memcpy(&buf[1], write_buf, write_len); + ret = pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED, + PIP2_CMD_ID_FILE_WRITE, buf, write_len + 1, + read_buf, &actual_read_len); + if (status) + *status = read_buf[PIP2_RESP_STATUS_OFFSET]; + return ret; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_execute_app + * + * SUMMARY: Using the BL PIP2 commands to executes an image downloaded into the + * SRAM. + * + * RETURNS: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *status - pointer to store the STATUS in response + ******************************************************************************/ +static int _pt_pip2_execute_app(struct device *dev, u8 *status) +{ + int ret = 0; + u16 actual_read_len = 0; + u8 read_buf[PT_MAX_PIP2_MSG_SIZE]; + + ret = cmd->nonhid_cmd->pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED, + PIP2_CMD_ID_EXECUTE, NULL, 0, + read_buf, &actual_read_len); + if (status) + *status = read_buf[PIP2_RESP_STATUS_OFFSET]; + return ret; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_log_last_error + * + * SUMMARY: Sends a STATUS command to the DUT logging the results until all + * errors are cleared. Also sends a GET_LAST_ERRNO to get any Boot errors. + * This must be sent after the STATUS flush in order not to have this + * command cause another error. + * + * NOTE: This function support No Interrupt Solution and switch "pip2_send_cmd" + * function according to the global variable "bl_with_no_int". + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *read_buf - pointer to the read buffer array to store the response + ******************************************************************************/ +static int _pt_pip2_log_last_error(struct device *dev, u8 *read_buf) +{ + u16 actual_read_len; + u8 loop = 5; + u8 info = 0xFF; + u8 error = 0xFF; + int ret; + PIP2_SEND_CMD pip2_send_cmd; + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (cd->bl_with_no_int) + pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd_no_int; + else + pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd; + /* + * Send the STATUS command until no errors are found. + * The BL will store an error code for each layer of the stack, + * and each read will return one error. + */ + while (loop > 0 && error) { + + ret = pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_STATUS, + NULL, 0, read_buf, &actual_read_len); + + if (!ret) { + info = (u8)read_buf[PIP2_RESP_BODY_OFFSET]; + error = (u8)read_buf[PIP2_RESP_BODY_OFFSET + 1]; + + pt_debug(dev, DL_ERROR, + "%s: STATUS: Status=0x%02X BOOT=%d BUSY=%d INT=%d ERR_PHY=%d ERR_REG=%d ERROR=0x%02X", + __func__, + (u8)read_buf[PIP2_RESP_STATUS_OFFSET], + info & 0x01, + (info & 0x02) >> 1, + (info & 0x04) >> 2, + (info & 0x18) >> 3, + (info & 0xE0) >> 5, + error); + } + loop--; + } + + /* Send the GET_LAST_ERROR command to get the last BL startup error */ + ret = pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_GET_LAST_ERRNO, + NULL, 0, read_buf, &actual_read_len); + if (!ret) { + pt_debug(dev, DL_ERROR, + "%s: GET_LAST_ERR: Status=0x%02X ERRNO=0x%02X BOOTMODE=%d\n", + __func__, + (u8)read_buf[PIP2_RESP_STATUS_OFFSET], + (u8)read_buf[PIP2_RESP_BODY_OFFSET], + (u8)read_buf[PIP2_RESP_BODY_OFFSET + 1]); + } + + return ret; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_file_write_packet_and_log_err + * + * SUMMARY: Wrapper function for File Write. If meet failure, it will call + * function _pt_pip2_log_last_error() and do a retry within 3x. + * + * NOTE: This function support No Interrupt Solution and switch "pip2_send_cmd" + * function according to the global variable "bl_with_no_int". + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * file_hd - file handle for file operation + * *write_buf - pointer of buffer to write + * write_len - length to write + * last_write - flag for last_write (1: is last write) + ******************************************************************************/ +#define PIP2_FILE_WRITE_RETRY_MAX (3) +static int _pt_pip2_file_write_packet_and_log_err(struct device *dev, + u8 file_hd, u8 *write_buf, u16 write_len, u8 last_write) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int ret = 0; + int retry_packet = 0; + u8 read_buf[PT_MAX_PIP2_MSG_SIZE]; + u8 status = 0; + + do { + ret = _pt_pip2_file_write_packet(dev, file_hd, write_buf, + write_len, &status); + /* Write cmd successful with a fail status */ + if (ret) { + pt_debug(dev, DL_ERROR, "%s %d - Packet cmd error\n", + __func__, ret); + } else if (status) { + /* + * The last time through the loop when remain_bytes = + * write_len, no partial payload will remain, the last + * successful write (when writing to RAM) will respond + * with EOF status, writing to FLASH will respond with a + * standard success status of 0x00 + */ + if (last_write && (file_hd == PIP2_RAM_FILE) && + (status == PIP2_RSP_ERR_END_OF_FILE)) { + pt_debug(dev, DL_WARN, + "%s Last write, ret = 0x%02x\n", + __func__, status); + status = 0; + break; + } + + pt_debug(dev, DL_ERROR, "%s %d - Packet status error\n", + __func__, status); + /* Manually assign ret to negative value */ + ret = -EINVAL; + } else { + /* No issue is seen, and break the loop */ + break; + } + +#ifdef TTDL_DIAGNOSTICS + cd->bl_retry_packet_count++; + cmd->request_toggle_err_gpio(dev, PT_ERR_GPIO_BL_RETRY_PACKET); + pt_debug(dev, DL_WARN, "%s: === Retry Packet #%d ===\n", + __func__, retry_packet); +#endif + /* Get and log the last error(s) */ + _pt_pip2_log_last_error(dev, read_buf); + } while (retry_packet++ < PIP2_FILE_WRITE_RETRY_MAX); + + return ret; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_check_fw_ver + * + * SUMMARY: Compare the FW version in the bin file to the current FW. If the + * FW version in the bin file is greater an upgrade should be done. + * + * RETURN: + * -1: Do not upgrade firmware + * 0: Version info same, let caller decide + * 1: Do a firmware upgrade + * + * PARAMETERS: + * *dev - pointer to device structure + * *fw - pointer to the new FW image to load + * *hdr - pointer to the PIP2 bin file hdr structure + ******************************************************************************/ +static int _pt_pip2_check_fw_ver(struct device *dev, + const struct firmware *fw, struct pt_bin_file_hdr *hdr) +{ + u8 img_app_major_ver = fw->data[3]; + u8 img_app_minor_ver = fw->data[4]; + u32 img_app_rev_ctrl = fw->data[9]<<24 | fw->data[10]<<16 | + fw->data[11]<<8 | fw->data[12]; + + pt_debug(dev, DL_WARN, + "%s ATM - BL Image Version: %02x.%02x.%d\n", + __func__, img_app_major_ver, img_app_minor_ver, + img_app_rev_ctrl); + + pt_debug(dev, DL_WARN, + "%s ATM - Current FW Version: %02X.%02X.%d\n", + __func__, hdr->fw_major, hdr->fw_minor, hdr->fw_rev_ctrl); + + if ((256 * img_app_major_ver + img_app_minor_ver) > + (256 * hdr->fw_major + hdr->fw_minor)) { + pt_debug(dev, DL_WARN, + "ATM - bin file version > FW, will upgrade FW"); + return 1; + } + + if ((256 * img_app_major_ver + img_app_minor_ver) < + (256 * hdr->fw_major + hdr->fw_minor)) { + pt_debug(dev, DL_WARN, + "ATM - bin file version < FW, will NOT upgrade FW"); + return -1; + } + + if (img_app_rev_ctrl > hdr->fw_rev_ctrl) { + pt_debug(dev, DL_WARN, + "bin file rev ctrl > FW, will upgrade FW"); + return 1; + } + + if (img_app_rev_ctrl < hdr->fw_rev_ctrl) { + pt_debug(dev, DL_WARN, + "bin file rev ctrl > FW, will NOT upgrade FW"); + return -1; + } + + return 0; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_check_config_ver + * + * SUMMARY: Compare the Config version in the bin file to the current FW. If the + * config version in the bin file is greater an upgrade should be done. + * + * RETURN: + * -1: Do not upgrade firmware + * 0: Version info same, let caller decide + * 1: Do a firmware upgrade + * + * PARAMETERS: + * *dev - pointer to device structure + * *fw - pointer to the new FW image to load + * *hdr - pointer to the PIP2 bin file hdr structure + ******************************************************************************/ +static int _pt_pip2_check_config_ver(struct device *dev, + const struct firmware *fw, struct pt_bin_file_hdr *hdr) +{ + u16 fw_config_ver_new; + + /* Offset 17,18 is the TT Config version*/ + fw_config_ver_new = get_unaligned_be16(fw->data + 17); + pt_debug(dev, DL_WARN, + "%s ATM - BL Image Config Version: %d\n", + __func__, fw_config_ver_new); + + pt_debug(dev, DL_WARN, + "%s ATM - Current Config Version: %d\n", + __func__, hdr->config_ver); + + if (fw_config_ver_new > hdr->config_ver) { + pt_debug(dev, DL_WARN, + "ATM - bin file Config version > FW, will upgrade FW"); + return 1; + } else + pt_debug(dev, DL_WARN, + "ATM - bin file Config version <= FW, will NOT upgrade FW"); + + return 0; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_need_upgrade_due_to_fw_ver + * + * SUMMARY: Compare the FW version in the bin file to the current FW. If the + * FW version in the bin file is greater an upgrade should be done. + * + * PARAMETERS: + * *dev - pointer to device structure + * *fw - pointer to the new FW image to load + ******************************************************************************/ +static u8 _pt_pip2_need_upgrade_due_to_fw_ver(struct device *dev, + const struct firmware *fw) +{ + int ret; + u8 should_upgrade = 0; + struct pt_bin_file_hdr hdr = {0}; + + ret = cmd->request_pip2_bin_hdr(dev, &hdr); + if (ret != 0 || hdr.fw_major == 0xFF) { + pt_debug(dev, DL_WARN, + "App ver info not available, will upgrade FW"); + should_upgrade = 1; + goto exit; + } + + ret = _pt_pip2_check_fw_ver(dev, fw, &hdr); + if (ret == 0) + ret = _pt_pip2_check_config_ver(dev, fw, &hdr); + + if (ret > 0) + should_upgrade = 1; + +exit: + return should_upgrade; +} + +/******************************************************************************* + * FUNCTION: _pt_calibrate_flashless_dut + * + * SUMMARY: On a flashless DUT the FW would need to re-calibrate on every power + * cycle, so to speed up the BL process the FW does the calibraiton on the + * first power up and TTDL will read and store it in RAM to be able to + * restore it on subsequent resets of the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int _pt_calibrate_flashless_dut(struct device *dev) +{ + u8 rc = 0; + u8 cal_status = 1; + u16 cal_size = 0; + struct pt_cal_ext_data cal_data = {0}; + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_ttdata *ttdata = &cd->sysinfo.ttdata; + unsigned short cache_chip_id = 0; + unsigned short active_chip_id = 0; + + cmd->nonhid_cmd->manage_cal_data(dev, PT_CAL_DATA_INFO, &cal_size, + &cache_chip_id); + pt_debug(dev, DL_WARN, "%s: Stored CAL ID=0x%04X size=%d\n", + __func__, cache_chip_id, cal_size); + + active_chip_id = cmd->nonhid_cmd->calc_crc((u8 *)&ttdata->chip_rev, + 4 + PT_UID_SIZE); + pt_debug(dev, DL_WARN, "%s: Current Chip ID=0x%04X\n", + __func__, active_chip_id); + + if (cal_size == 0 || active_chip_id != cache_chip_id) { + memset(&cal_data, 0, sizeof(struct pt_cal_ext_data)); + + /* Calibrate_ext will also save CAL Data in TTDL cache */ + rc = cmd->nonhid_cmd->calibrate_ext(dev, 0, &cal_data, + &cal_status); + pt_debug(dev, DL_INFO, + "%s: Calibration Finished rc=%d\n", __func__, rc); + + /* Afer successful calibration read the stored size */ + if (!rc && cal_status == 0) { + rc = cmd->nonhid_cmd->manage_cal_data(dev, + PT_CAL_DATA_INFO, &cal_size, &cache_chip_id); + if (!rc) { + pt_debug(dev, DL_WARN, + "%s: First BL, Read %d Bytes of CAL\n", + __func__, cal_size); + } else { + pt_debug(dev, DL_ERROR, + "%s: First BL, Failed to read CAL\n", + __func__); + } + } else { + pt_debug(dev, DL_ERROR, + "%s: First BL, Calibration failed rc=%d\n", + __func__, rc); + } + rc = cmd->nonhid_cmd->resume_scanning(dev, 0); + if (rc) + pt_debug(dev, DL_ERROR, + "%s: First BL, Resume Scan failed rc=%d\n", + __func__, rc); + } else { + rc = cmd->nonhid_cmd->manage_cal_data(dev, PT_CAL_DATA_RESTORE, + &cal_size, &cache_chip_id); + if (!rc) + pt_debug(dev, DL_WARN, "%s: Restored CAL %d Bytes\n", + __func__, cal_size); + else + pt_debug(dev, DL_ERROR, "%s: Failed to restore CAL\n", + __func__); + + rc = cmd->nonhid_cmd->resume_scanning(dev, 0); + if (rc) + pt_debug(dev, DL_ERROR, + "%s: Resume Scan failed rc=%d\n", + __func__, rc); + } + return rc; +} + +/******************************************************************************* + * FUNCTION: _search_fw_from_builtin + * + * SUMMARY: Check the existence of builtin FW by filename and set the pointer + * to FW image + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *filename - pointer of file name + * **fw - pointer to store the pointer of FW image to load + ******************************************************************************/ +static int _search_fw_from_builtin(struct device *dev, char *filename, + const struct firmware **fw) +{ + int ret = 0; +#if (KERNEL_VERSION(3, 13, 0) > LINUX_VERSION_CODE) + ret = request_firmware(fw, filename, dev); +#else + ret = request_firmware_direct(fw, filename, dev); +#endif + if (ret) { + pt_debug(dev, DL_WARN, "%s: ATM - Fail request FW %s load\n", + __func__, filename); + } else { + pt_debug(dev, DL_INFO, "%s: FW %s class file loading\n", + __func__, filename); + } + + return ret; +} + +/******************************************************************************* + * FUNCTION: _search_fw_from_us + * + * SUMMARY: Check the existence of FW from user space by file name defined in + * "pip2_us_file_path". + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int _search_fw_from_us(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int read_size = 16; + int ret = 0; + u8 image[16]; + + if (cd->pip2_us_file_path[0] == '\0') { + pt_debug(dev, DL_WARN, + "%s: US Path not defined, BL from built-in\n", + __func__); + ret = -EINVAL; + } else { + /* Read a few bytes to see if file exists */ + ret = cmd->nonhid_cmd->read_us_file(dev, cd->pip2_us_file_path, + image, &read_size); + if (!ret) { + pt_debug(dev, DL_WARN, "%s: %s Found, BL from US\n", + __func__, cd->pip2_us_file_path); + } else { + pt_debug(dev, DL_WARN, + "%s: ATM - %s NOT Found, BL from built-in\n", + __func__, cd->pip2_us_file_path); + } + } + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_write_file + * + * SUMMARY: Compelete steps to wite a FILE in external SPI flash over PIP2 BL + * commands. It includes: Open->Erase->Write->Close. + * + * NOTE: "write_file_status" is updated from x to 99 in this scope, and higher + * level function is allowed to reset to 0 or report an error status. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *fw - pointer to FW image to load + * file_no - Identifies the files to write + ******************************************************************************/ +static int pt_pip2_write_file(struct device *dev, const struct firmware *fw, + u8 file_no) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_loader_data *ld = pt_get_loader_data(dev); + int ret = 0; + int erase_status = 0; + u32 percent_cmplt; + u32 remain_bytes = 0; + u32 fw_size = 0; + u32 offset = 0; + u32 max_file_size = 0; + u16 packet_size = 0; + u16 sector_num = 0; + u8 *fw_img = NULL; + u8 file_handle = 0; + u8 read_buf[PT_MAX_PIP2_MSG_SIZE]; + + if (!fw || !fw->data || !fw->size) { + pt_debug(dev, DL_ERROR, "%s The FW is invalid\n", __func__); + return -EINVAL; + } + fw_img = (u8 *)&(fw->data[0]); + fw_size = fw->size; + + if (cd->bus_ops->bustype == BUS_I2C) + packet_size = PIP2_BL_I2C_FILE_WRITE_LEN_PER_PACKET; + else + packet_size = PIP2_BL_SPI_FILE_WRITE_LEN_PER_PACKET; + + /* 1. Open file before any file operation */ + pt_debug(dev, DL_INFO, "%s OPEN File %d for write\n", + __func__, file_no); + ret = cmd->nonhid_cmd->pip2_file_open(dev, file_no); + if (ret < 0) { + pt_debug(dev, DL_ERROR, "%s Open file %d failed\n", + __func__, file_no); + _pt_update_write_file_status(dev, UPDATE_FW_FILE_OPEN_ERROR); + goto exit; + } + file_handle = ret; + + /* Write File Status: inc to 11 */ + _pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1); + + /* + * 2. Erase file + * 2.1 Obtain the size of FILE + * 2.2 Confirm content to be written is not too large + * 2.3 Check whether Sector Erase is valid + * 2.4 Log flash information + * 2.5 Do Sector Erase or File Erase + * 2.6 Reset FILE pointer + * NOTE: Regarding to TC3315, the size of RAM_FILE is less than fw + * image, so will not do step 2.2 for PIP2_RAM_FILE. + */ + if (file_no != PIP2_RAM_FILE) { + ret = cmd->nonhid_cmd->pip2_file_get_stats( + dev, file_handle, NULL, &max_file_size); + if (ret) { + pt_debug(dev, DL_ERROR, + "%s: Failed to get_file_state ret=%d\n", + __func__, ret); + goto exit_close_file; + } + if (fw_size > max_file_size) { + pt_debug(dev, DL_ERROR, + "%s: Firmware image(%d) is over size(%d)\n", + __func__, fw_size, max_file_size); + _pt_update_write_file_status(dev, + UPDATE_FW_INVALID_FW_IMAGE); + goto exit_close_file; + } + + /* calculate number of sector */ + sector_num = max_file_size / PT_PIP2_FILE_SECTOR_SIZE; + if (max_file_size % PT_PIP2_FILE_SECTOR_SIZE) { + pt_debug(dev, DL_WARN, + "%s: file size %d misalign, don't use sector erase\n", + __func__, max_file_size); + /* + * TODO: Not sure whether can have this case, and this + * is a workaround to ensure the safety in logic. + * Force sector number to 0 will use the default method + * to do file erase by FILE instead of SECTOR. + */ + sector_num = 0; + } + +#ifdef TTDL_DIAGNOSTICS + /* Log the Flash part info */ + if (cd->debug_level >= DL_DEBUG) + _pt_pip2_get_flash_info(dev, read_buf); +#endif + /* Erase file before loading */ + ret = cmd->nonhid_cmd->pip2_file_erase(dev, + ld->pip2_load_file_no, sector_num, &erase_status); + if (ret < 0) { + pt_debug(dev, DL_ERROR, + "%s: File erase failed rc=%d status=%d\n", + __func__, ret, erase_status); + _pt_update_write_file_status(dev, + UPDATE_FW_ERASE_ERROR); + goto exit_close_file; + } + + /* Write File Status: inc to 12 */ + _pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1); + + /* Reset file pointer as sector erase moved it */ + ret = cmd->nonhid_cmd->pip2_file_seek_offset(dev, + ld->pip2_load_file_no, 0, 0); + if (ret) { + pt_debug(dev, DL_ERROR, + "%s: Failed to seek file offset rc=%d\n", + __func__, ret); + _pt_update_write_file_status(dev, + UPDATE_FW_FILE_SEEK_ERROR); + goto exit_close_file; + } + /* Write File Status: inc to 17 */ + _pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_5); + } + + /* + * 3. Write file + * 3.1* Disable IRQ when using polling method to save BL time + * 3.2 Looping to write file, and the last packet need extra check. + * 3.3* Enable IRQ + */ + pt_debug(dev, DL_WARN, + "%s: ATM - Writing %d bytes of firmware data now\n", + __func__, fw_size); + + /* + * No IRQ function is used to BL to reduce BL time due to any IRQ + * latency. + */ + if (cd->bl_with_no_int) + disable_irq_nosync(cd->irq); + + while (offset < fw_size) { + remain_bytes = fw_size - offset; + /* Don't update BL status on every pass */ + if (remain_bytes % 2000 < packet_size) { + /* Calculate % complete for write_file_status sysfs */ + percent_cmplt = + (fw_size - remain_bytes) * 100 / fw_size + 1; + if (percent_cmplt > UPDATE_FW_ACTIVE_90) + percent_cmplt = UPDATE_FW_ACTIVE_90; + /* + * Write File Status: set from 1 to 90 when + * fw_size is big enough (such as 82033). But the + * function doesn't update "write_file_status" if the + * input value is less than current stored value. + */ + _pt_update_write_file_status(dev, percent_cmplt); +#ifdef TTDL_DIAGNOSTICS + pt_debug(dev, DL_INFO, + "Wrote %d bytes with %d bytes remaining\n", + offset, remain_bytes); +#endif + } + if (remain_bytes > packet_size) { + /* The last para passes in with last_packet=0 (false) */ + ret = _pt_pip2_file_write_packet_and_log_err( + dev, file_handle, &fw_img[offset], packet_size, 0); + offset += packet_size; + if (ret) + break; + } else { + pt_debug(dev, DL_INFO, + "Write last %d bytes to File = 0x%02x\n", + remain_bytes, ld->pip2_load_file_no); + /* The last para passes in with last_packet=1 (true) */ + ret = _pt_pip2_file_write_packet_and_log_err( + dev, file_handle, &fw_img[offset], remain_bytes, 1); + offset += remain_bytes; + break; + } + } + + if (cd->bl_with_no_int) + enable_irq(cd->irq); + + if (!ret) { + /* Write File Status: set to 99 */ + _pt_update_write_file_status(dev, UPDATE_FW_ACTIVE_99); + pt_debug(dev, DL_INFO, + "%s: BIN file write finished successfully\n", __func__); + } else + _pt_update_write_file_status(dev, UPDATE_FW_WRITE_ERROR); + +exit_close_file: + /* 4. Close file */ + ret = cmd->nonhid_cmd->pip2_file_close(dev, file_handle); + if (ret != ld->pip2_load_file_no) { + pt_debug(dev, DL_ERROR, + "%s file close failure\n", __func__); + _pt_update_write_file_status(dev, UPDATE_FW_FILE_CLOSE_ERROR); + ret = -EBADF; + } else + ret = 0; + +exit: + return ret; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_update_fw + * + * SUMMARY: Compelete steps to update FW. It includes following steps: + * 1) Enter bootloader + * 2) Compare IMG version with FW version + * 3) Write IMG to target FILE + * 4) Exit bootloader + * 5) Trigger Enum and check the sentinel + * Also includes exclusive protection, PM function, Watchdog function, and + * complete progress for "update_bl_status". + * + * NOTE: "write_file_status" is updated from 0 to 100 in this scope, and higher + * level function is allowed to reset status to 0 or report an error status. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *fw - pointer to FW image to load + * file_no - Identifies the files to write + ******************************************************************************/ +static int _pt_pip2_update_fw(struct device *dev, const struct firmware *fw, + u8 file_no) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_loader_data *ld = pt_get_loader_data(dev); + int ret = 0; + int t = 0; + bool wait_for_calibration_complete = false; + u8 mode = PT_MODE_UNKNOWN; + u8 status = 0; + u8 read_buf[PT_MAX_PIP2_MSG_SIZE]; + + pt_debug(dev, DL_WARN, "%s: ATM - Begin BL\n", __func__); + /* Write File Status: reset to 0 */ + _pt_update_write_file_status(dev, UPDATE_FW_IDLE); + + if (file_no > PIP2_FW_FILE) { + pt_debug(dev, DL_ERROR, + "%s: Invalid File_no = %d\n", __func__, file_no); + _pt_update_write_file_status(dev, + UPDATE_FW_NOT_SUPPORTED_FILE_NO); + goto exit; + } + + /* Write File Status: set to 0 */ + ret = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT); + if (ret) { + pt_debug(dev, DL_ERROR, + "%s: Failed to aquire exclusive access\n", __func__); + _pt_update_write_file_status(dev, + UPDATE_FW_EXCLUSIVE_ACCESS_ERROR); + goto exit; + } + /* Write File Status: set to 1 */ + _pt_update_write_file_status(dev, UPDATE_FW_REQUEST_EXCLUSIVE); + + cd->fw_updating = true; + wake_up(&cd->wait_q); + + /* Write File Status: inc to 2 */ + _pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1); + + pt_debug(dev, DL_INFO, + "%s: Found file of size: %d bytes\n", __func__, (int)fw->size); + + pm_runtime_get_sync(dev); + /* Write File Status: inc to 3 */ + _pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1); + + cmd->request_stop_wd(dev); + /* Write File Status: inc to 4 */ + _pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1); + + cd->bl_pip_ver_ready = false; + cd->app_pip_ver_ready = false; + + /* + * 'mode' is used below, if DUT was already in BL before attempting to + * enter the BL, there was either no FW to run or the FW was corrupt + * so either way force a BL + */ + ret = cmd->request_pip2_enter_bl(dev, &mode, NULL); + if (ret) { + pt_debug(dev, DL_ERROR, "%s: Failed to enter BL\n", + __func__); + _pt_update_write_file_status(dev, UPDATE_FW_ENTER_BL_ERROR); + goto exit; + } + /* Write File Status: inc to 5 */ + _pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1); + /* Only compare FW ver or previous mode when doing a built-in upgrade */ + if (ld->pip2_load_builtin) { + if (_pt_pip2_need_upgrade_due_to_fw_ver(dev, fw) || + mode == PT_MODE_BOOTLOADER) { + /* Write File Status: inc to 6 */ + _pt_update_write_file_status(dev, + UPDATE_FW_INCREMENT_BY_1); + } else { + _pt_update_write_file_status(dev, + UPDATE_FW_VERSION_ERROR); + goto exit; + } + } + + /* + * Write File Status: set to 10 before pt_pip2_write_file() is called to + * align with _pt_pip2_write_file_cont(). + */ + _pt_update_write_file_status(dev, UPDATE_FW_ACTIVE_10); + /* Write File Status: inc from 10, the max is not bigger than 99 */ + ret = pt_pip2_write_file(dev, fw, file_no); + if (ret) { + pt_debug(dev, DL_ERROR, "%s: Failed to write FILE_%d\n", + __func__, file_no); + goto exit; + } + + if ((file_no == PIP2_RAM_FILE) && + (write_file_status < UPDATE_FW_COMPLETE)) { + /* When writing to RAM don't reset, just launch application */ + pt_debug(dev, DL_INFO, + "%s Sending execute command now...\n", __func__); + cd->startup_status = STARTUP_STATUS_START; + ret = _pt_pip2_execute_app(dev, &status); + if (ret || status) { + pt_debug(dev, DL_ERROR, + "%s Execute command failure\n", __func__); + _pt_update_write_file_status(dev, + UPDATE_FW_EXECUTE_ERROR); + goto exit; + } + } else if (file_no == PIP2_FW_FILE && + write_file_status < UPDATE_FW_COMPLETE) { + pt_debug(dev, DL_INFO, + "%s Toggle TP_XRES now...\n", __func__); + cmd->request_reset(dev, PT_CORE_CMD_UNPROTECTED); + } + pt_debug(dev, DL_INFO, "%s: APP launched\n", __func__); + + /* If any error occurred simply close the file and exit */ + if (write_file_status > UPDATE_FW_COMPLETE) + goto exit; + + /* Wait for FW reset sentinel from reset or execute for up to 500ms */ + t = wait_event_timeout(cd->wait_q, + (cd->startup_status >= STARTUP_STATUS_FW_RESET_SENTINEL), + msecs_to_jiffies(PT_BL_WAIT_FOR_SENTINEL)); + if (IS_TMO(t)) { + pt_debug(dev, DL_WARN, + "%s: 0x%04X Timeout waiting for FW sentinel", + __func__, cd->startup_status); + } + + /* Double verify DUT is alive and well in Application mode */ + if (cd->startup_status & STARTUP_STATUS_FW_RESET_SENTINEL) { + ret = cmd->request_pip2_get_mode_sysmode(dev, + PT_CORE_CMD_UNPROTECTED, &mode, NULL); + pt_debug(dev, DL_WARN, "%s: mode = %d (Expected 2)", + __func__, mode); + if (mode != PT_MODE_OPERATIONAL) { + pt_debug(dev, DL_ERROR, + "%s ERROR: Not in App mode as expected\n", + __func__); + _pt_update_write_file_status(dev, UPDATE_FW_MODE_ERROR); + goto exit; + } + } else { + pt_debug(dev, DL_ERROR, "%s: FW sentinel not seen 0x%04X\n", + __func__, cd->startup_status); + _pt_pip2_log_last_error(dev, read_buf); + _pt_update_write_file_status(dev, UPDATE_FW_SENTINEL_NOT_SEEN); + goto exit; + } + + /* On a Flashless DUT save or restore the CAL data */ + if (cd->cal_cache_in_host == PT_FEATURE_ENABLE) + _pt_calibrate_flashless_dut(dev); + + /* Subscribe calibration task if calibration flag is set */ + if (ld->loader_pdata + && (ld->loader_pdata->flags + & PT_LOADER_FLAG_CALIBRATE_AFTER_FW_UPGRADE) + && (cd->cal_cache_in_host == PT_FEATURE_DISABLE)) { +#if (KERNEL_VERSION(3, 13, 0) <= LINUX_VERSION_CODE) + reinit_completion(&ld->calibration_complete); +#else + INIT_COMPLETION(ld->calibration_complete); +#endif + /* set up call back for startup */ + pt_debug(dev, DL_INFO, "%s: Adding callback for calibration\n", + __func__); + ret = cmd->subscribe_attention(dev, PT_ATTEN_STARTUP, + PT_LOADER_NAME, pt_calibration_attention, 0); + if (ret) { + pt_debug(dev, DL_ERROR, + "%s: Failed adding callback for calibration\n", + __func__); + ret = 0; + } else + wait_for_calibration_complete = true; + } + + pt_debug(dev, DL_INFO, "%s: == PIP2 FW upgrade finished ==\n", + __func__); + +exit: + cd->fw_updating = false; + /* + * For built-in FW update, it should not warn builtin_bin_fw_status + * for each bootup since write_file_status would be + * UPDATE_FW_VERSION_ERROR in most situations because firmware + * should have been up to date. + */ + if (ld->pip2_load_builtin) { + if ((write_file_status == UPDATE_FW_ACTIVE_99) || + (write_file_status == UPDATE_FW_VERSION_ERROR)) + ld->builtin_bin_fw_status = 0; + else + ld->builtin_bin_fw_status = -EINVAL; + } + + cmd->release_exclusive(dev); + pm_runtime_put_sync(dev); + + if ((write_file_status == UPDATE_FW_ACTIVE_99) || + (write_file_status == UPDATE_FW_VERSION_ERROR)) { + pt_debug(dev, DL_WARN, "%s: Queue ENUM\n", __func__); + cmd->request_enum(dev, true); + } + + /* Write File Status: set to 100 */ + if (write_file_status < UPDATE_FW_COMPLETE) + _pt_update_write_file_status(dev, UPDATE_FW_COMPLETE); + + if (wait_for_calibration_complete) + wait_for_completion(&ld->calibration_complete); + + pt_debug(dev, DL_INFO, "%s: Starting watchdog\n", __func__); + cmd->request_start_wd(dev); + /* + * When in No-Flash mode allow auto BL after any BL. + * There is an issue where setting flashless mode via drv_debug + * can happen in the middle of pt_pip2_enter_bl() which will revert + * the flashless_auto_bl value back to what it was when the function + * started. + */ + if (cd->flashless_dut) + cd->flashless_auto_bl = PT_ALLOW_AUTO_BL; + + /* + * ret is not always updated, while builtin_bin_fw_status can reflect + * the result. + */ + return ld->builtin_bin_fw_status; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_write_file_cont + * + * SUMMARY: Write the file to either SRAM or FLASH + * + * NOTE: The DUT must stay in bootloader. This function doesn't try to + * enter/exit bootloader. + * + * PARAMETERS: + * *fw - pointer to the new FW image to load + * *context - pointer to the device + ******************************************************************************/ +static void _pt_pip2_write_file_cont(const struct firmware *fw, void *context) +{ + struct device *dev = context; + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_loader_data *ld = pt_get_loader_data(dev); + int ret = 0; + + if (!fw) { + pt_debug(dev, DL_ERROR, "%s: No FW is provided\n", __func__); + _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); + goto exit; + } + + if (!fw->size) { + pt_debug(dev, DL_ERROR, "%s: Invalid fw or file size=%d\n", + __func__, (int)fw->size); + _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); + goto exit_release; + } + + if (ld->pip2_load_file_no == PIP2_FW_FILE) { + if (fw->data[0] >= (fw->size + 1)) { + pt_debug(dev, DL_ERROR, + "%s: Firmware format is invalid\n", __func__); + _pt_update_write_file_status(dev, + UPDATE_FW_INVALID_FW_IMAGE); + goto exit_release; + } + } + + ret = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT); + if (ret < 0) { + pt_debug(dev, DL_ERROR, + "%s: Failed to aquire exclusive access\n", __func__); + goto exit_release; + } + + /* Write File Status: set to 1 */ + _pt_update_write_file_status(dev, UPDATE_FW_REQUEST_EXCLUSIVE); + + cd->fw_updating = true; + /* Write File Status: inc to 2 */ + _pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1); + + /* + * Write File Status: set to 10 before pt_pip2_write_file() is called to + * align with _pt_pip2_firmware_cont(). + */ + _pt_update_write_file_status(dev, UPDATE_FW_ACTIVE_10); + /* Write File Status: inc from 10, the max is not bigger than 99 */ + ret = pt_pip2_write_file(dev, fw, ld->pip2_load_file_no); + if (ret) { + pt_debug(dev, DL_ERROR, "%s: Failed to write FILE_%d\n", + __func__, ld->pip2_load_file_no); + } + + cd->fw_updating = false; + + /* Write File Status: set to 100 */ + if (write_file_status < UPDATE_FW_COMPLETE) + _pt_update_write_file_status(dev, UPDATE_FW_COMPLETE); + + cmd->release_exclusive(dev); + +exit_release: + if (fw) + release_firmware(fw); +exit: + ld->is_manual_upgrade_enabled = 0; +} + + +/******************************************************************************* + * FUNCTION: _pt_pip2_firmware_cont + * + * SUMMARY: Bootload the DUT with a FW image using the PIP2 protocol. This + * includes getting the DUT into BL mode, writing the file to either SRAM + * or FLASH, and launching the application directly in SRAM or by resetting + * the DUT without the hostmode pin asserted. + * + * NOTE: Special care must be taken to support a DUT communicating in + * PIP2.0 where the length field is defined differently. + * NOTE: The write packet len is set so that the overall packet size is + * less than 255. The overhead is 9 bytes: 2 byte address (0101), + * 4 byte header, 1 byte file no. 2 byte CRC + * + * PARAMETERS: + * *fw - pointer to the new FW image to load + * *context - pointer to the device + ******************************************************************************/ +static void _pt_pip2_firmware_cont(const struct firmware *fw, + void *context) +{ + struct device *dev = context; + struct pt_loader_data *ld = pt_get_loader_data(dev); + + if (!fw) { + pt_debug(dev, DL_ERROR, "%s: No FW is provided\n", __func__); + _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); + goto pt_firmware_cont_exit; + } + + if (!fw->data || !fw->size) { + pt_debug(dev, DL_ERROR, "%s: Invalid fw or file size=%d\n", + __func__, (int)fw->size); + _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); + goto pt_firmware_cont_release_exit; + } + + if (ld->pip2_load_file_no == PIP2_FW_FILE) { + if (fw->data[0] >= (fw->size + 1)) { + pt_debug(dev, DL_ERROR, + "%s: Firmware format is invalid\n", __func__); + _pt_update_write_file_status( + dev, UPDATE_FW_INVALID_FW_IMAGE); + goto pt_firmware_cont_release_exit; + } + } + + _pt_pip2_update_fw(dev, fw, ld->pip2_load_file_no); + +pt_firmware_cont_release_exit: + if (fw) + release_firmware(fw); + +pt_firmware_cont_exit: + ld->is_manual_upgrade_enabled = 0; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_update_fw_from_builtin + * + * SUMMARY: Bootload the DUT with a built in firmware binary image. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to the device structure + ******************************************************************************/ +#define PIP2_MAX_FILE_NAMES 3 +static int _pt_pip2_update_fw_from_builtin(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_loader_data *ld = pt_get_loader_data(dev); + const struct firmware *fw = NULL; + int ret = 0; + int index = 0; + int file_count = 0; + char *filename[PIP2_MAX_FILE_NAMES]; + + /* + * 1. Generate FW Name for builtin kernel + * Load the supported filenames in the correct search order + * 0 - "tt_fw<_PIDX>.bin" + * 1 - "XXXX_tt_fw<_PIDX>.bin" where XXXX = Silicon ID + * 2 - "tt_fw.bin", default FW name + */ + filename[file_count++] = generate_firmware_filename(dev); + filename[file_count++] = generate_silicon_id_firmware_filename(dev); + if (pt_get_panel_id(dev) != PANEL_ID_NOT_ENABLED) { + filename[file_count] = + kzalloc(sizeof(PT_FW_FILE_NAME), GFP_KERNEL); + memcpy(filename[file_count++], PT_FW_FILE_NAME, + sizeof(PT_FW_FILE_NAME)); + } + + for (index = 0; index < file_count; index++) { + if (!filename[index]) + return -ENOMEM; + } + /* 2. Look for any FW file name match */ + mutex_lock(&cd->firmware_class_lock); + index = 0; + while (index < file_count) { + pt_debug(dev, DL_INFO, "%s: Request FW class file: %s\n", + __func__, filename[index]); + if (_search_fw_from_builtin(dev, filename[index], &fw)) + index++; + else + break; + } + + /* No matching file names found */ + if (index == file_count) { + pt_debug(dev, DL_WARN, "%s: No FW is found\n", __func__); + _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); + ret = -EINVAL; + goto exit; + } + + /* 3. Validate the FW */ + if (!fw) { + pt_debug(dev, DL_ERROR, "%s: No FW is provided\n", __func__); + _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); + ret = -EINVAL; + goto exit_release_fw; + } + + if (!fw->size) { + pt_debug(dev, DL_ERROR, "%s: Invalid fw or file size=%d\n", + __func__, (int)fw->size); + _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); + ret = -EINVAL; + goto exit_release_fw; + } + + if (ld->pip2_load_file_no == PIP2_FW_FILE) { + if (fw->data[0] >= (fw->size + 1)) { + pt_debug(dev, DL_ERROR, + "%s: Firmware format is invalid\n", __func__); + _pt_update_write_file_status(dev, + UPDATE_FW_INVALID_FW_IMAGE); + ret = -EINVAL; + goto exit_release_fw; + } + } + + /* 4. update the FW */ + ret = _pt_pip2_update_fw(dev, fw, ld->pip2_load_file_no); + +exit_release_fw: + /* 5. Recycle */ + if (fw) + release_firmware(fw); +exit: + index = 0; + while (index < file_count) + kfree(filename[index++]); + + mutex_unlock(&cd->firmware_class_lock); + return ret; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_update_fw_from_us + * + * SUMMARY: Bootload the DUT with firmware binary image in user space. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to the device structure + ******************************************************************************/ +static int _pt_pip2_update_fw_from_us(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_loader_data *ld = pt_get_loader_data(dev); + struct firmware fw_us; + int ret = 0; + u32 fw_size = 0; + u8 *fw_img = NULL; + + fw_img = kzalloc(PT_PIP2_MAX_FILE_SIZE, GFP_KERNEL); + if (!fw_img) { + _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); + return -ENOMEM; + } + + ret = cmd->nonhid_cmd->read_us_file(dev, cd->pip2_us_file_path, + fw_img, &fw_size); + if (ret) { + pt_debug(dev, DL_ERROR, "%s: No firmware provided to load\n", + __func__); + pt_debug(dev, DL_ERROR, "%s: Exit BL\n", __func__); + _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); + goto exit; + } + + if (!fw_size) { + pt_debug(dev, DL_ERROR, "%s: Invalid fw file size=%d\n", + __func__, (int)fw_size); + _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); + goto exit; + } + + if (ld->pip2_load_file_no == PIP2_FW_FILE) { + if (fw_img[0] >= (fw_size + 1)) { + pt_debug(dev, DL_ERROR, + "%s: Firmware format is invalid\n", __func__); + _pt_update_write_file_status(dev, + UPDATE_FW_INVALID_FW_IMAGE); + goto exit; + } + } + + memset(&fw_us, 0, sizeof(fw_us)); + fw_us.data = fw_img; + fw_us.size = fw_size; + ret = _pt_pip2_update_fw(dev, &fw_us, ld->pip2_load_file_no); + +exit: + kfree(fw_img); + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_upgrade_firmware_from_builtin + * + * SUMMARY: Bootload the DUT with a built in firmware binary image. + * Load either a SRAM image "ttdl_fw_RAM.bin" or a FLASH image + * "ttdl_fw.bin" with the priority being the SRAM image. + * + * PARAMETERS: + * *dev - pointer to the device structure + ******************************************************************************/ +static int pt_pip2_upgrade_firmware_from_builtin(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_loader_data *ld = pt_get_loader_data(dev); + int ret = 0; + + /* 1. Look for FW from us at first */ + if (cd->flashless_dut) { + pt_debug(dev, DL_INFO, "%s: Proceed to BL flashless DUT\n", + __func__); + ld->pip2_load_file_no = PIP2_RAM_FILE; + ret = _search_fw_from_us(dev); + if (!ret) { + ld->pip2_load_builtin = false; + /* 2. Do update with fw from user space */ + ret = _pt_pip2_update_fw_from_us(dev); + return ret; + } else { + ld->pip2_load_builtin = true; + } + } else { + ld->pip2_load_file_no = PIP2_FW_FILE; + ld->pip2_load_builtin = true; + } + + /* 2. Do update with fw from builtin */ + ret = _pt_pip2_update_fw_from_builtin(dev); + return ret; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_update_fw_from_class + * + * SUMMARY: Create the firmware class but don't actually laod any FW to the + * DUT. This creates all the sysfs nodes needed for a user to bootload + * the DUT with their own bin file. + * + * PARAMETERS: + * *pip2_data - pointer to the PIP2 loader data structure + ******************************************************************************/ +static int _pt_pip2_update_fw_from_class(struct device *dev) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + int ret = 0; + + /* + * The file name dev_name(dev) is tied with bus name and usually + * it is "x-0024". This name is wanted to keep consistency + * (e.g. /sys/class/firmware/x-0024/) for the path of fw class + * nodes with different kernel release. Also it is an invalid bin + * file name used intentionally because request_firmware_nowait + * will not find the file which is what we want and then simply + * create the fw class nodes. + */ + ld->pip2_load_builtin = false; + pt_debug(dev, DL_INFO, "%s: Request FW Class", __func__); + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, + dev_name(dev), dev, GFP_KERNEL, dev, + _pt_pip2_firmware_cont); + if (ret) { + pt_debug(dev, DL_ERROR, + "%s: ERROR requesting firmware class\n", __func__); + } + + return ret; +} + +/******************************************************************************* + * FUNCTION: _pt_pip2_write_file_from_class + * + * SUMMARY: Similar function of _pt_pip2_update_fw_from_class() but to call + * function _pt_pip2_write_file_cont() which doesn't perform enter/exit + * bootloader action. + * + * PARAMETERS: + * *pip2_data - pointer to the PIP2 loader data structure + ******************************************************************************/ +static int _pt_pip2_write_file_from_class(struct device *dev) +{ + + int ret = 0; + struct pt_loader_data *ld = pt_get_loader_data(dev); + + /* + * The file name dev_name(dev) is tied with bus name and usually + * it is "x-0024". This name is wanted to keep consistency + * (e.g. /sys/class/firmware/x-0024/) for the path of fw class + * nodes with different kernel release. Also it is an invalid bin + * file name used intentionally because request_firmware_nowait + * will not find the file which is what we want and then simply + * create the fw class nodes. + */ + ld->pip2_load_builtin = false; + pt_debug(dev, DL_INFO, "%s: Request FW Class", __func__); + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, + dev_name(dev), dev, GFP_KERNEL, dev, + _pt_pip2_write_file_cont); + if (ret) { + pt_debug(dev, DL_ERROR, + "%s: ERROR requesting firmware class\n", __func__); + } + + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_bl_from_file_work + * + * SUMMARY: The work function to schedule the BL work for PIP2 only. + * + * PARAMETERS: + * *bl_from_file - pointer to work_struct structure + ******************************************************************************/ +static void pt_pip2_bl_from_file_work(struct work_struct *pip2_bl_from_file) +{ + struct pt_loader_data *ld = container_of(pip2_bl_from_file, + struct pt_loader_data, pip2_bl_from_file); + struct device *dev = ld->dev; + + _pt_pip2_update_fw_from_us(dev); +} + +/******************************************************************************* + * FUNCTION: pt_bl_from_file_work + * + * SUMMARY: The work function to schedule the BL work for PIP2 or PIP1 + * according to the active_dut_generation in core data. + * + * PARAMETERS: + * *bl_from_file - pointer to work_struct structure + ******************************************************************************/ +static void pt_bl_from_file_work(struct work_struct *bl_from_file) +{ + struct pt_loader_data *ld = container_of(bl_from_file, + struct pt_loader_data, bl_from_file); + struct device *dev = ld->dev; + u8 dut_gen = cmd->request_dut_generation(dev); + + if (dut_gen == DUT_PIP2_CAPABLE) + _pt_pip2_update_fw_from_us(dev); + else if (dut_gen == DUT_PIP1_ONLY) + _pt_pip1_bl_from_file(dev); +} + +/******************************************************************************* + * FUNCTION: pt_pip2_bl_from_file_show + * + * SUMMARY: The show method for the "pip2_bl_from_file" sysfs node. The + * scheduled work will perform PIP2 BL. + * + * NOTE: Since this function doesn't set pip2_load_file_no, it will use the + * value what has been stored there. + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes structure + * *buf - pointer to print output buffer + ******************************************************************************/ +static ssize_t pt_pip2_bl_from_file_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + struct pt_core_data *cd = dev_get_drvdata(dev); + int rc = 0; + int read_size = 2; + u8 image[2]; + + /* Write File Status: reset to 0 */ + _pt_update_write_file_status(dev, UPDATE_FW_IDLE); + + mutex_lock(&cd->firmware_class_lock); + ld->pip2_load_builtin = false; + mutex_unlock(&cd->firmware_class_lock); + + /* Read a few bytes to see if file exists */ + rc = cmd->nonhid_cmd->read_us_file(dev, + cd->pip2_us_file_path, image, &read_size); + + if (!rc) { + schedule_work(&ld->pip2_bl_from_file); + + return snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n" + "BL File: %s\n", + rc, cd->pip2_us_file_path); + } else { + _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); + return snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n" + "BL File: '%s' - Does not exist\n", + rc, cd->pip2_us_file_path); + } +} + +/******************************************************************************* + * FUNCTION: pt_bl_from_file_show + * + * SUMMARY: The show method for the "pt_bl_from_file" sysfs node. The scheduled + * work can perform either PIP1 BL and PIP2 BL according to the + * active_dut_generation of core data. + * + * NOTE: Since this function doesn't set pip2_load_file_no, it will use the + * value what has been stored there. + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes structure + * *buf - pointer to print output buffer + ******************************************************************************/ +static ssize_t pt_bl_from_file_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + struct pt_core_data *cd = dev_get_drvdata(dev); + int rc = 0; + int read_size = 2; + u8 dut_gen = cmd->request_dut_generation(dev); + u8 image[2]; + + /* Write File Status: reset to 0 */ + _pt_update_write_file_status(dev, UPDATE_FW_IDLE); + + mutex_lock(&cd->firmware_class_lock); + ld->pip2_load_builtin = false; + mutex_unlock(&cd->firmware_class_lock); + + /* Read a few bytes to see if file exists */ + rc = cmd->nonhid_cmd->read_us_file(dev, + cd->pip2_us_file_path, image, &read_size); + + if (dut_gen == DUT_UNKNOWN) { + _pt_update_write_file_status(dev, UPDATE_FW_MODE_ERROR); + rc = -EINVAL; + return snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n" + "BL File: '%s' - Failed, DUT Generation could not be determined\n", + rc, cd->pip2_us_file_path); + } else if (!rc) { + schedule_work(&ld->bl_from_file); + + return snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n" + "BL File: %s\n", + rc, cd->pip2_us_file_path); + } else { + _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); + return snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n" + "BL File: '%s' - Does not exist\n", + rc, cd->pip2_us_file_path); + } +} + +/******************************************************************************* + * FUNCTION: pt_pip2_bl_from_file_store + * + * SUMMARY: The store method for the "pip2_bl_from_file" and "pt_bl_from_file" + * sysfs node. Used to allow any file path[necessary] and file_no[optional] to + * be used to BL, for example: "echo /data/pt_fw 1 > pip2_bl_from_file", and + * do the "cat" will perform FW loader process. + * + * NOTE: the last char of file path in buf which is a '\n' is not copied. + * NOTE: the default file_no is PIP2_RAM_FILE. + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + * size - size of data in buffer + ******************************************************************************/ +static ssize_t pt_pip2_bl_from_file_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + struct pt_core_data *cd = dev_get_drvdata(dev); + const char *deli_space = " ", *deli_comma = ","; + char name_space[PT_MAX_PATH_SIZE]; + char *ptr_left = NULL, *ptr_right = name_space; + u8 file_no = PIP2_RAM_FILE; + u32 input_data[2]; + int length; + bool file_no_set = false; + + memset(name_space, 0, PT_MAX_PATH_SIZE); + memset(cd->pip2_us_file_path, 0, PT_MAX_PATH_SIZE); + if (size <= PT_MAX_PATH_SIZE) { + memcpy(name_space, buf, size); + ptr_left = strsep(&ptr_right, deli_space); + if (ptr_right == NULL) { + ptr_right = name_space; + ptr_left = strsep(&ptr_right, deli_comma); + } + + if (ptr_right != NULL) { + length = cmd->parse_sysfs_input( + dev, ptr_right, strlen(ptr_right), input_data, + ARRAY_SIZE(input_data)); + if (length <= 0) { + pt_debug(dev, DL_ERROR, + "%s: Input format error!\n", __func__); + return -EINVAL; + } + file_no_set = true; + file_no = input_data[0]; + } + + pt_debug(dev, DL_WARN, "%s:Path=%s, File_no=%s(%d)\n", __func__, + ptr_left, ptr_right, file_no); + + if ((file_no_set) && (file_no > PIP2_FW_FILE)) { + pt_debug(dev, DL_WARN, "%s:Invalid File_no = %d\n", + __func__, file_no); + return -EINVAL; + } + + mutex_lock(&cd->firmware_class_lock); + ld->pip2_load_file_no = file_no; + mutex_unlock(&cd->firmware_class_lock); + + if (ptr_left[strlen(ptr_left) - 1] == '\n') + memcpy(cd->pip2_us_file_path, ptr_left, + strlen(ptr_left) - 1); + else + memcpy(cd->pip2_us_file_path, ptr_left, + strlen(ptr_left)); + } + + return size; +} +static DEVICE_ATTR(pip2_bl_from_file, 0644, + pt_pip2_bl_from_file_show, pt_pip2_bl_from_file_store); + +static DEVICE_ATTR(pt_bl_from_file, 0644, + pt_bl_from_file_show, pt_pip2_bl_from_file_store); + +/******************************************************************************* + * FUNCTION: pt_pip2_manual_upgrade_store + * + * SUMMARY: Store method for the pip2_manual_upgrade sysfs node. Allows + * sysfs control of bootloading a new FW image to FLASH. + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + * size - size of data in buffer + ******************************************************************************/ +static ssize_t pt_pip2_manual_upgrade_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + u32 input_data[2] = {0}; + int length; + int rc = 0; + + length = cmd->parse_sysfs_input(dev, buf, size, input_data, + ARRAY_SIZE(input_data)); + + if (length != 1) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto exit; + } + + if (input_data[0] < 0 || input_data[0] > 1) { + pt_debug(dev, DL_WARN, "%s: Invalid arguments\n", __func__); + rc = -EINVAL; + goto exit; + } + + if (ld->is_manual_upgrade_enabled) { + pt_debug(dev, DL_ERROR, + "%s: ERROR - Manual upgrade busy\n", __func__); + rc = -EBUSY; + goto exit; + } + /* Write File Status: reset to 0 */ + _pt_update_write_file_status(dev, UPDATE_FW_IDLE); + ld->pip2_load_file_no = PIP2_FW_FILE; + pt_debug(dev, DL_DEBUG, "%s: ATM - File number is %d\n", + __func__, ld->pip2_load_file_no); + + ld->is_manual_upgrade_enabled = 1; + rc = _pt_pip2_update_fw_from_class(dev); + ld->is_manual_upgrade_enabled = 0; + if (rc < 0) + pt_debug(dev, DL_ERROR, + "%s: ERROR - FLASH Upgrade failed\n", __func__); + +exit: + if (rc) + return rc; + return size; +} +#ifndef TTDL_KERNEL_SUBMISSION +static DEVICE_ATTR(pip2_manual_upgrade, 0200, + NULL, pt_pip2_manual_upgrade_store); + +/******************************************************************************* + * FUNCTION: pt_pip2_manual_ram_upgrade_store + * + * SUMMARY: Store method for the pip2_manual_ram_upgrade sysfs node. Allows + * sysfs control of bootloading a new FW image to SRAM. + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + * size - size of data in buffer + ******************************************************************************/ +static ssize_t pt_pip2_manual_ram_upgrade_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + u32 input_data[2] = {0}; + int length; + int rc = 0; + + length = cmd->parse_sysfs_input(dev, buf, size, input_data, + ARRAY_SIZE(input_data)); + + if (length != 1) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto exit; + } + + if (input_data[0] < 0 || input_data[0] > 1) { + pt_debug(dev, DL_WARN, "%s: Invalid arguments\n", __func__); + rc = -EINVAL; + goto exit; + } + + if (ld->is_manual_upgrade_enabled) { + pt_debug(dev, DL_ERROR, + "%s: ERROR - Manual upgrade busy\n", __func__); + rc = -EBUSY; + goto exit; + } + + /* Write File Status: reset to 0 */ + _pt_update_write_file_status(dev, UPDATE_FW_IDLE); + ld->pip2_load_file_no = PIP2_RAM_FILE; + pt_debug(dev, DL_DEBUG, "%s: ATM - File number is %d\n", + __func__, ld->pip2_load_file_no); + + ld->is_manual_upgrade_enabled = 1; + rc = _pt_pip2_update_fw_from_class(dev); + ld->is_manual_upgrade_enabled = 0; + if (rc < 0) + pt_debug(dev, DL_ERROR, + "%s: ERROR - RAM Upgrade failed\n", __func__); + +exit: + if (rc) + return rc; + return size; +} +static DEVICE_ATTR(pip2_manual_ram_upgrade, 0200, + NULL, pt_pip2_manual_ram_upgrade_store); + +/******************************************************************************* + * FUNCTION: pt_pip2_file_write_store + * + * SUMMARY: Store method for the pip2_file_write sysfs node. Allows + * sysfs control to "load" data into any file. + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + * size - size of data in buffer + ******************************************************************************/ +static ssize_t pt_pip2_file_write_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_loader_data *ld = pt_get_loader_data(dev); + int rc; + u32 input_data[3]; + int length; + + if (ld->is_manual_upgrade_enabled) { + pt_debug(dev, DL_ERROR, + "%s: ERROR - Manual upgrade busy\n", __func__); + rc = -EBUSY; + goto exit; + } + + length = cmd->parse_sysfs_input(dev, buf, size, input_data, + ARRAY_SIZE(input_data)); + if (length <= 0 || length > 2) { + pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n", + __func__); + ld->pip2_file_data.para_num = 0; + rc = -EINVAL; + goto exit; + } + + if (input_data[0] < PIP2_FW_FILE || input_data[0] > PIP2_FILE_MAX) { + pt_debug(dev, DL_ERROR, "%s: Invalid file handle\n", __func__); + ld->pip2_file_data.para_num = 0; + rc = -EINVAL; + goto exit; + } + + /* This functionality is only available in the BL */ + if (cd->mode != PT_MODE_BOOTLOADER) { + rc = -EPERM; + pt_debug(dev, DL_ERROR, "%s: Invalid DUT mode = %d\n", + __func__, cd->mode); + goto exit; + } + + /* Write File Status: reset to 0 */ + _pt_update_write_file_status(dev, UPDATE_FW_IDLE); + ld->pip2_load_file_no = input_data[0]; + if (length == 2) + ld->pip2_file_data.file_offset = input_data[1]; + + ld->is_manual_upgrade_enabled = 1; + rc = _pt_pip2_write_file_from_class(dev); + ld->is_manual_upgrade_enabled = 0; + if (rc < 0) + pt_debug(dev, DL_ERROR, + "%s: ERROR - RAM Upgrade failed\n", __func__); +exit: + if (rc) + return rc; + return size; +} +static DEVICE_ATTR(pip2_file_write, 0200, NULL, pt_pip2_file_write_store); +#endif /* !TTDL_KERNEL_SUBMISSION */ + +/******************************************************************************* + * FUNCTION: pt_update_fw_store + * + * SUMMARY: Store method for the update_fw sysfs node. This node is required + * by ChromeOS to first determine if loading is available and then perform + * the loading if required. This function is simply a wrapper to call: + * pt_pip2_manual_upgrade_store - for the TC3XXX or TT7XXX parts + * pt_manual_upgrade_store - for the legacy Gen5/6 devices. + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + * size - size of data in buffer + ******************************************************************************/ +static ssize_t pt_update_fw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + u8 dut_gen = cmd->request_dut_generation(dev); + + if (dut_gen == DUT_PIP2_CAPABLE) + size = pt_pip2_manual_upgrade_store(dev, attr, buf, size); + else if (dut_gen == DUT_PIP1_ONLY) + size = pt_manual_upgrade_store(dev, attr, buf, size); + + return size; +} +static DEVICE_ATTR(update_fw, 0200, NULL, pt_update_fw_store); +#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ + +#ifndef TTDL_KERNEL_SUBMISSION +#ifdef TTDL_DIAGNOSTICS +/******************************************************************************* + * FUNCTION: pt_pip2_file_read_show + * + * SUMMARY: The read method for the pip2_file_read sysfs node. Allows to + * perform flash read action according to stored value. This function will + * re-enter always until it returns: + * 0: No data to be written to sysfs node + * <0: Error happens + * (For kernel version large than 3.11(not tested), if the function returns + * non-zero value in previous chunk, to return 0 once can not stop re-enter. + * It needs one more time to stop read action by returned value <= 0. But for + * older version, read action will stop when returned value <= 0 once). + * + * NOTE: Up to PIP2_FILE_WRITE_LEN_PER_PACKET(245) bytes of data are read for + * each enter. When last package is read, pip2_file_data.para_num is assigned + * as negatie value(-1), then next enter can return 0 to indicate the read + * method can be finished. + * + * RETURN: Size of data written to sysfs node + * + * PARAMETERS: + * *filp - pointer to file structure + * *kobj - pointer to kobject structure + * *bin_attr - pointer to bin_attribute structure + * buf - pointer to cmd input buffer + * offset - offset index to store input buffer + * count - size of data in buffer + ******************************************************************************/ +static ssize_t pt_pip2_file_read_show(struct file *filp, + struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t offset, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct pt_loader_data *ld = pt_get_loader_data(dev); + struct pt_core_data *cd = dev_get_drvdata(dev); + int rc = 0; + u8 file_handle = ld->pip2_file_data.file_handle; + u8 *pr_buf = ld->pip2_file_data.file_print_buf; + int print_idx = 0, read_size = 0, i; + u8 read_buf[PT_MAX_PIP2_MSG_SIZE]; + u8 read_len; + u32 address, file_size; + + if (ld->pip2_file_data.file_print_left) { + pt_debug(dev, DL_INFO, "%s: print left=%d, count=%zu\n", + __func__, ld->pip2_file_data.file_print_left, count); + + print_idx = ld->pip2_file_data.file_print_left; + if (count < print_idx) { + memcpy(buf, pr_buf, count); + ld->pip2_file_data.file_print_left = print_idx - count; + for (i = 0; i < ld->pip2_file_data.file_print_left; i++) + pr_buf[i] = pr_buf[i+count]; + print_idx = count; + } else { + memcpy(buf, pr_buf, print_idx); + ld->pip2_file_data.file_print_left = 0; + } + return print_idx; + } + + if (ld->pip2_file_data.para_num == 0) { + /* + * When offset != 0, it means the extra call for splice out, + * don't need a warning. + */ + if (offset != 0) + return 0; + + print_idx += scnprintf(buf, count, "Status: %d\n" + "No input!\n", -EINVAL); + pt_debug(dev, DL_ERROR, "%s: Invalid para_num = %d!\n", + __func__, ld->pip2_file_data.para_num); + return print_idx; + } else if (ld->pip2_file_data.para_num == -1) { + ld->pip2_file_data.para_num = 0; + pt_debug(dev, DL_INFO, "%s: flash read finish!\n", + __func__); + rc = 0; + goto exit_release; + } else if (ld->pip2_file_data.para_num < -1) { + ld->pip2_file_data.para_num = 0; + pt_debug(dev, DL_ERROR, "%s: Exit directly due to errors!\n", + __func__); + return 0; + } else if (ld->pip2_file_data.para_num > 3) { + ld->pip2_file_data.para_num = 0; + pt_debug(dev, DL_ERROR, + "%s: Exit directly due to invalid parameter!\n", + __func__); + return 0; + } + + if (offset == 0) { + ld->pip2_file_data.file_print_buf = kzalloc(PIPE_BUF, + GFP_KERNEL); + if (!ld->pip2_file_data.file_print_buf) { + rc = -ENOMEM; + goto exit; + } + /* This functionality is only available in the BL */ + if (cd->mode != PT_MODE_BOOTLOADER) { + rc = -EPERM; + goto exit_free; + } + + pr_buf = ld->pip2_file_data.file_print_buf; + + rc = cmd->request_exclusive(dev, + PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Failed to request exclusive rc=%d\n", + __func__, rc); + goto exit_free; + } + + rc = cmd->nonhid_cmd->pip2_file_open(dev, file_handle); + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: Failed to file_open rc=%d\n", + __func__, rc); + goto exit_release; + } + + rc = cmd->nonhid_cmd->pip2_file_get_stats(dev, file_handle, + &address, &file_size); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Failed to get_file_state rc=%d\n", + __func__, rc); + goto exit_file_close; + } + + ld->pip2_file_data.file_print_size = 0; + ld->pip2_file_data.file_max_size = file_size; + ld->pip2_file_data.file_print_left = 0; + print_idx += scnprintf(pr_buf, PIPE_BUF, "ROM_DATA:"); + + if (ld->pip2_file_data.para_num == 1) + ld->pip2_file_data.file_read_size = file_size; + else if (ld->pip2_file_data.para_num == 2) { + if (ld->pip2_file_data.file_offset < file_size) + ld->pip2_file_data.file_read_size = file_size - + ld->pip2_file_data.file_offset; + else { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, + "%s: File read out of bounds rc=%d\n", + __func__, rc); + goto exit_file_close; + } + } else if (ld->pip2_file_data.para_num == 3) { + if ((ld->pip2_file_data.file_read_size + + ld->pip2_file_data.file_offset) > file_size) { + rc = -EINVAL; + pt_debug(dev, DL_ERROR, + "%s: File read out of bounds rc=%d\n", + __func__, rc); + goto exit_file_close; + } + } else { + pt_debug(dev, DL_ERROR, + "%s: Invalid number of parameters!\n", + __func__); + goto exit_file_close; + } + } + + offset = ld->pip2_file_data.file_print_size + + ld->pip2_file_data.file_offset; + if ((offset >= ld->pip2_file_data.file_max_size) || + (ld->pip2_file_data.file_print_size >= + ld->pip2_file_data.file_read_size)) + goto exit_file_close; + else if ((ld->pip2_file_data.file_print_size + + PIP2_FILE_WRITE_LEN_PER_PACKET) >= + ld->pip2_file_data.file_read_size) + read_len = ld->pip2_file_data.file_read_size - + ld->pip2_file_data.file_print_size; + else + read_len = PIP2_FILE_WRITE_LEN_PER_PACKET; + + rc = cmd->nonhid_cmd->pip2_file_seek_offset(dev, + file_handle, offset, 0); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Failed to seek file offset rc=%d\n", + __func__, rc); + goto exit_file_close; + } + + read_size = cmd->nonhid_cmd->pip2_file_read(dev, + file_handle, read_len, read_buf); + if (read_size < 0) { + pt_debug(dev, DL_ERROR, "%s: Failed to read file rc=%d\n", + __func__, read_size); + goto exit_file_close; + } + + for (i = 0; i < read_size; i++) + print_idx += scnprintf(pr_buf + print_idx, + PIPE_BUF - print_idx, + "%02X ", read_buf[i + PIP2_RESP_BODY_OFFSET]); + ld->pip2_file_data.file_print_size += read_size; + if (count < print_idx) { + memcpy(buf, pr_buf, count); + ld->pip2_file_data.file_print_left = print_idx - count; + for (i = 0; i < ld->pip2_file_data.file_print_left; i++) + pr_buf[i] = pr_buf[i+count]; + print_idx = count; + } else { + memcpy(buf, pr_buf, print_idx); + } + goto exit_for_next_read; + +exit_file_close: + if (rc) + print_idx += scnprintf(pr_buf + print_idx, + PIPE_BUF - print_idx, "(READ ERROR)\n"); + else if (ld->pip2_file_data.file_print_size) + print_idx += scnprintf(pr_buf + print_idx, + PIPE_BUF - print_idx, + ":(%d bytes)\n", + ld->pip2_file_data.file_print_size); + else + print_idx += scnprintf(pr_buf + print_idx, + PIPE_BUF - print_idx, "No Data\n"); + if (count < print_idx) { + memcpy(buf, pr_buf, count); + ld->pip2_file_data.file_print_left = print_idx - count; + for (i = 0; i < ld->pip2_file_data.file_print_left; i++) + pr_buf[i] = pr_buf[i+count]; + print_idx = count; + } else { + memcpy(buf, pr_buf, print_idx); + } + rc = cmd->nonhid_cmd->pip2_file_close(dev, file_handle); + if (file_handle != rc) + pt_debug(dev, DL_ERROR, + "%s Failed to close file %d, rc = %d\n", __func__, + file_handle, rc); + /* + * Mark para_num as negative value to finish read method during + * next enter. + */ + ld->pip2_file_data.para_num = -1; + +exit_for_next_read: + pt_debug(dev, DL_INFO, + "%s: %s=%d, %s=%d, %s=%d, %s=%d, %s=%d\n", + __func__, + "para_num", ld->pip2_file_data.para_num, + "handle", ld->pip2_file_data.file_handle, + "offset", ld->pip2_file_data.file_offset, + "read", ld->pip2_file_data.file_read_size, + "print", ld->pip2_file_data.file_print_size); + return print_idx; +exit_release: + cmd->release_exclusive(dev); +exit_free: + kfree(ld->pip2_file_data.file_print_buf); +exit: + if (rc) { + ld->pip2_file_data.para_num = -2; + print_idx = scnprintf(buf, count, "Status: %d\n", rc); + return print_idx; + } else + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_file_read_store + * + * SUMMARY: The write method for the pip2_file_read node. The passed + * in data is needed by read method. + * + * RETURN: Size of passed in buffer is success + * + * PARAMETERS: + * *filp - pointer to file structure + * *kobj - pointer to kobject structure + * *bin_attr - pointer to bin_attribute structure + * buf - pointer to cmd input buffer + * offset - offset index to store input buffer + * count - size of data in buffer + ******************************************************************************/ +static ssize_t pt_pip2_file_read_store(struct file *filp, + struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t offset, size_t count) +{ + int rc = 0; + int length; + struct device *dev = container_of(kobj, struct device, kobj); + struct pt_loader_data *ld = pt_get_loader_data(dev); + int ic_buffer[4] = {0}; + + length = cmd->parse_sysfs_input(dev, buf, count, ic_buffer, + ARRAY_SIZE(ic_buffer)); + if (length <= 0 || length > 3) { + pt_debug(dev, DL_ERROR, "%s: Input format error!\n", + __func__); + ld->pip2_file_data.para_num = 0; + rc = -EINVAL; + goto error; + } + + if (ic_buffer[0] < PIP2_FW_FILE || ic_buffer[0] > PIP2_FILE_MAX) { + pt_debug(dev, DL_ERROR, "%s: Invalid file handle!\n", + __func__); + ld->pip2_file_data.para_num = 0; + rc = -EINVAL; + goto error; + } + + switch (length) { + case 1: + ld->pip2_file_data.file_handle = ic_buffer[0]; + ld->pip2_file_data.file_offset = 0; + ld->pip2_file_data.file_read_size = 0; + break; + case 2: + ld->pip2_file_data.file_handle = ic_buffer[0]; + ld->pip2_file_data.file_offset = ic_buffer[1]; + ld->pip2_file_data.file_read_size = 0; + break; + case 3: + ld->pip2_file_data.file_handle = ic_buffer[0]; + ld->pip2_file_data.file_offset = ic_buffer[1]; + ld->pip2_file_data.file_read_size = ic_buffer[2]; + break; + default: + break; + } + + ld->pip2_file_data.para_num = length; +error: + pt_debug(dev, DL_INFO, + "%s: %s=%d, %s=%d, %s=%d, %s=%d, %s=%d\n", + __func__, + "para_num", ld->pip2_file_data.para_num, + "handle", ld->pip2_file_data.file_handle, + "offset", ld->pip2_file_data.file_offset, + "read", ld->pip2_file_data.file_read_size, + "print", ld->pip2_file_data.file_print_size); + + if (rc) + return rc; + return count; +} + +static struct bin_attribute bin_attr_pip2_file_read = { + .attr = { + .name = "pip2_file_read", + .mode = (0644), + }, + .read = pt_pip2_file_read_show, + .write = pt_pip2_file_read_store, +}; + +/****************************************************************************** + * FUNCTION: pt_pip2_file_crc_show + * + * SUMMARY: The show method for the "pip2_file_crc" sysfs node. + * Shows the CRC of a file or portion of the file. + * + * NOTE: If function is called when DUT is already in BL mode, the DUT + * will remain in BL mode when function exits, however if DUT is in + * normal mode when function is called, the DUT will be forced into BL + * mode and then returned to normal mode when function exits. + * + * NOTE: This sysfs node only can be used for BL version 1.8 or greater. + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes structure + * *buf - pointer to print output buffer + ******************************************************************************/ +static ssize_t pt_pip2_file_crc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + struct pt_core_data *cd = dev_get_drvdata(dev); + int rc = 0; + u8 file_handle; + int print_idx = 0; + u8 read_buf[PT_MAX_PIP2_MSG_SIZE]; + u32 address, file_size; + u32 length; + u32 offset; + u16 file_crc; + u16 status; + + if (ld->pip2_fcrc.para_num == 0 || + ld->pip2_fcrc.file_handle == 0 || + ld->pip2_fcrc.file_read_size < 0 || + ld->pip2_fcrc.file_offset < 0) { + pt_debug(dev, DL_ERROR, + "%s: Invalid parameters!\n", + __func__); + print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n" + "Invalid parameters!\n", -EINVAL); + return print_idx; + } + + file_handle = ld->pip2_fcrc.file_handle; + offset = ld->pip2_fcrc.file_offset; + length = ld->pip2_fcrc.file_read_size; + + /* This functionality is only available in the BL */ + if (cd->mode != PT_MODE_BOOTLOADER) { + rc = -EPERM; + print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n", rc); + goto exit; + } + + rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Failed to request exclusive rc=%d\n", + __func__, rc); + print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n", rc); + goto exit; + } + + rc = cmd->nonhid_cmd->pip2_file_open(dev, file_handle); + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: Failed to file_open rc=%d\n", + __func__, rc); + print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n", rc); + goto exit_release; + } + + rc = cmd->nonhid_cmd->pip2_file_get_stats(dev, file_handle, + &address, &file_size); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Failed to get_file_state rc=%d\n", + __func__, rc); + print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n", rc); + goto exit_file_close; + } + + ld->pip2_fcrc.file_max_size = file_size; + + if (ld->pip2_fcrc.file_read_size > file_size + || ld->pip2_fcrc.file_offset > file_size + || ((ld->pip2_fcrc.file_offset + + ld->pip2_fcrc.file_read_size) > file_size)) { + pt_debug(dev, DL_ERROR, + "%s: Invalid parameters!\n", + __func__); + print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n", rc); + goto exit_file_close; + } + + rc = cmd->nonhid_cmd->pip2_file_crc(dev, + file_handle, offset, length, read_buf); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Failed to get file crc, rc=%d\n", + __func__, rc); + print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n", rc); + } else { + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + if (status == PIP2_RSP_ERR_NONE) { + file_crc = get_unaligned_le16(&read_buf[5]); + print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n" + "FILE CRC: %04X\n", + status, file_crc); + } else + print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n" + "FILE CRC: n/a\n", + status); + } + +exit_file_close: + rc = cmd->nonhid_cmd->pip2_file_close(dev, file_handle); + if (file_handle != rc) + pt_debug(dev, DL_ERROR, + "%s Failed to close file %d, rc = %d\n", __func__, + file_handle, rc); + +exit_release: + cmd->release_exclusive(dev); +exit: + ld->pip2_fcrc.para_num = 0; + return print_idx; +} + +/******************************************************************************* + * FUNCTION: pt_pip2_file_crc_store + * + * SUMMARY: The store method for the "pip2_file_crc" sysfs node. The passed in + * data including file_handle, offset and read size are needed by show method. + * + * NOTE: This sysfs node only can be used for BL version 1.8 or greater.. + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes structure + * *buf - pointer to print output buffer + * size - size of buffer + ******************************************************************************/ +static ssize_t pt_pip2_file_crc_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + u32 input_data[4] = {0}; + int length; + int rc = 0; + + ld->pip2_fcrc.file_handle = PIP2_RAM_FILE; + ld->pip2_fcrc.file_offset = -1; + ld->pip2_fcrc.file_read_size = -1; + + length = cmd->parse_sysfs_input(dev, buf, size, input_data, + ARRAY_SIZE(input_data)); + + if (length != 3) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + ld->pip2_fcrc.para_num = 0; + rc = -EINVAL; + goto exit; + } + + if ((input_data[0] < PIP2_FW_FILE) || (input_data[0] > PIP2_FILE_MAX)) { + pt_debug(dev, DL_WARN, "%s: Invalid file_no %d\n", + __func__, input_data[0]); + rc = -EINVAL; + goto exit; + } + + ld->pip2_fcrc.file_handle = input_data[0]; + ld->pip2_fcrc.file_offset = input_data[1]; + ld->pip2_fcrc.file_read_size = input_data[2]; + ld->pip2_fcrc.para_num = 3; + + pt_debug(dev, DL_INFO, + "%s: %s=%d, %s=%d, %s=%d, %s=%d\n", + __func__, + "para_num", ld->pip2_fcrc.para_num, + "handle", ld->pip2_fcrc.file_handle, + "offset", ld->pip2_fcrc.file_offset, + "length", ld->pip2_fcrc.file_read_size); +exit: + if (rc) + return rc; + return size; +} + +static DEVICE_ATTR(pip2_file_crc, 0644, + pt_pip2_file_crc_show, pt_pip2_file_crc_store); +#endif +/******************************************************************************* + * FUNCTION: pt_pip2_file_erase_show + * + * SUMMARY: The show method for the "pip2_file_erase" sysfs node. + * Prints current erase status to output buffer. + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes structure + * *buf - pointer to print output buffer + ******************************************************************************/ +static ssize_t pt_pip2_file_erase_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + struct pt_core_data *cd = dev_get_drvdata(dev); + u8 file_handle; + u8 file = ld->pip2_file_erase_file_no; + u16 sector_num = 0; + u32 max_file_size = 0; + int rc; + + pip2_erase_status = -1; + pip2_erase_rc = 0; + + if (file == PIP2_RAM_FILE) { + rc = -EINVAL; + goto exit; + } + + /* This functionality is only available in the BL */ + if (cd->mode != PT_MODE_BOOTLOADER) { + rc = -EPERM; + goto exit; + } + + rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: Failed to get exclusive access rc=%d\n", + __func__, rc); + goto exit; + } + + file_handle = cmd->nonhid_cmd->pip2_file_open(dev, file); + if (file_handle != file) { + rc = -EBADF; + goto exit_release; + } + + rc = cmd->nonhid_cmd->pip2_file_get_stats(dev, + file, NULL, &max_file_size); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Failed to get_file_state ret=%d\n", + __func__, rc); + goto exit_release; + } + + sector_num = max_file_size / PT_PIP2_FILE_SECTOR_SIZE; + if (max_file_size % PT_PIP2_FILE_SECTOR_SIZE) { + pt_debug(dev, DL_WARN, + "%s: file size %d misalign, don't use sector erase\n", + __func__, max_file_size); + /* + * TODO: Not sure whether can have this case, and this + * is a workaround to ensure the safety in logic. + * Force sector number to 0 will use the default method + * to do file erase by FILE instead of SECTOR. + */ + sector_num = 0; + } + + file_handle = cmd->nonhid_cmd->pip2_file_erase(dev, file, + sector_num, &pip2_erase_status); + if (file_handle < 0) { + rc = file_handle; + pt_debug(dev, DL_INFO, "%s: File erase error rc = %d\n", + __func__, rc); + } else if (file_handle == file) { + pt_debug(dev, DL_INFO, "%s: File %d erased\n", + __func__, file_handle); + } else { + rc = -EBADF; + } + + file_handle = cmd->nonhid_cmd->pip2_file_close(dev, file); + if (file_handle != file && !rc) + rc = -EBADF; + +exit_release: + cmd->release_exclusive(dev); +exit: + pip2_erase_rc = rc; + if (pip2_erase_status == -1) { + return snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n" + "Erase Status: n/a\n", + pip2_erase_rc); + } + return snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n" + "Erase Status: 0x%02X\n", + pip2_erase_rc, pip2_erase_status); +} + +/******************************************************************************* + * FUNCTION: pt_pip2_file_erase_store + * + * SUMMARY: The store method for the "pip2_file_erase" sysfs node. Allows the + * caller to provide the file number to erase in FLASH. + * + * NOTE: The DUT must be in BL mode before calling this function. + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes structure + * *buf - pointer to print output buffer + * size - size of buffer + ******************************************************************************/ +static ssize_t pt_pip2_file_erase_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + u32 input_data[2] = {0}; + int length; + int rc = 0; + + length = cmd->parse_sysfs_input(dev, buf, size, input_data, + ARRAY_SIZE(input_data)); + + if (length != 1) { + pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + __func__); + rc = -EINVAL; + goto exit; + } + + /* Only allow valid files to be erased */ + if (input_data[0] < PIP2_FW_FILE || input_data[0] > PIP2_FILE_MAX) { + pip2_erase_status = PIP2_RSP_ERR_BAD_FILE; + pt_debug(dev, DL_ERROR, "%s: ERROR - Invalid File\n", + __func__); + rc = -EINVAL; + goto exit; + } + + ld->pip2_file_erase_file_no = input_data[0]; + +exit: + if (rc) + return rc; + return size; +} + +static DEVICE_ATTR(pip2_file_erase, 0644, + pt_pip2_file_erase_show, pt_pip2_file_erase_store); + +/******************************************************************************* + * FUNCTION: pt_write_file_status_show + * + * SUMMARY: The show method for the pip2_bl_status sysfs node. + * Shows the percent completion of the current BL or an error message. + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes structure + * *buf - pointer to print output buffer + ******************************************************************************/ +static ssize_t pt_write_file_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + u8 status = write_file_status; + + if (write_file_status <= UPDATE_FW_COMPLETE) { + pt_debug(dev, DL_DEBUG, + "%s BL_STATUS = %d\n", __func__, write_file_status); + return scnprintf(buf, strlen(buf), "%d\n", write_file_status); + } + + switch (status) { + case UPDATE_FW_GENERAL_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - General programming failure\n", status); + break; + case UPDATE_FW_PIP_VERSION_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - Wrong PIP version detected\n", status); + break; + case UPDATE_FW_VERSION_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - FW vervion newer than bin file\n", status); + break; + case UPDATE_FW_ERASE_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - ROM BL failed to erase FW file in FLASH\n", + status); + break; + case UPDATE_FW_FILE_CLOSE_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - ROM BL failed to close FW file in FLASH\n", + status); + break; + case UPDATE_FW_WRITE_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - ROM BL file write failure\n", status); + break; + case UPDATE_FW_EXECUTE_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - ROM BL failed to execute RAM image\n", + status); + break; + case UPDATE_FW_RESET_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - Reset DUT failure\n", + status); + break; + case UPDATE_FW_MODE_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - Program complete, Incorrect BL/APP mode detected after reset sentinel\n", + status); + break; + case UPDATE_FW_ENTER_BL_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - Could not enter the BL\n", status); + break; + case UPDATE_FW_FILE_OPEN_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - ROM BL failed to open FW file in FLASH\n", + status); + break; + case UPDATE_FW_SENTINEL_NOT_SEEN: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - FW Reset Sentinel not seen after XRES\n", + status); + break; + case UPDATE_FW_EXCLUSIVE_ACCESS_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - Failed to get DUT exclusive access\n", + status); + break; + case UPDATE_FW_NO_FW_PROVIDED: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - No FW provided to load\n", status); + break; + case UPDATE_FW_INVALID_FW_IMAGE: + ret = scnprintf(buf, strlen(buf), "ERROR: %d - Invalid FW image\n", status); + break; + case UPDATE_FW_FILE_SEEK_ERROR: + ret = scnprintf(buf, strlen(buf), "ERROR: %d - File seek failure\n", status); + break; + case UPDATE_FW_MISALIGN_FW_IMAGE: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - FW image is misaligned\n", status); + break; + case UPDATE_FW_SYSTEM_NOMEM: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - Failed to alloc memory\n", status); + break; + case UPDATE_FW_INIT_BL_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - Failed to init bootloader\n", status); + break; + case UPDATE_FW_PARSE_ROW_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - Failed to parse row of FW image\n", + status); + break; + case UPDATE_FW_PROGRAM_ROW_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - Failed to progrem FW image\n", status); + break; + case UPDATE_FW_EXIT_BL_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - Failed to exit bootloader\n", status); + break; + case UPDATE_FW_CHECK_SUM_ERROR: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - Failed to verify app checksum\n", status); + break; + case UPDATE_FW_NO_PLATFORM_DATA: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - No platform data\n", status); + break; + case UPDATE_FW_NOT_SUPPORTED_FILE_NO: + ret = scnprintf(buf, strlen(buf), + "ERROR: %d - Not supported file number\n", status); + break; + case UPDATE_FW_UNDEFINED_ERROR: + default: + ret = scnprintf(buf, strlen(buf), "ERROR: %d - Unknown error\n", status); + break; + } + return ret; +} +static DEVICE_ATTR(pip2_bl_status, 0444, pt_write_file_status_show, NULL); +#if PT_FW_UPGRADE +static DEVICE_ATTR(update_fw_status, 0444, pt_write_file_status_show, NULL); +#endif +/******************************************************************************* + * FUNCTION: pt_pip2_get_last_error_show + * + * SUMMARY: The show method for the pip2_get_last_error_show sysfs node. + * Shows the last BL error code. + * + * NOTE: If function is called when DUT is already in BL mode, the DUT + * will remain in BL mode when function exits, however if DUT is in + * normal mode when function is called, the DUT will be forced into BL + * mode and then returned to normal mode when function exits. + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes structure + * *buf - pointer to print output buffer + ******************************************************************************/ +static ssize_t pt_pip2_get_last_error_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + int rc; + u8 read_buf[256]; + + rc = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc) + goto exit; + + cmd->request_stop_wd(dev); + + /* This functionality is only available in the BL */ + if (cd->mode != PT_MODE_BOOTLOADER) { + rc = -EPERM; + goto exit_release; + } + + /* Get and log the last error(s) */ + rc = _pt_pip2_log_last_error(dev, read_buf); + +exit_release: + cmd->release_exclusive(dev); +exit: + if (rc) + return snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", rc); + + if (read_buf[PIP2_RESP_STATUS_OFFSET] == PIP2_RSP_ERR_NONE) { + return snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n" + "Last Error No: 0x%02X\n", + PIP2_RSP_ERR_NONE, + read_buf[PIP2_RESP_BODY_OFFSET]); + } else { + return snprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n" + "Last Error No: n/a\n", + read_buf[PIP2_RESP_STATUS_OFFSET]); + } +} +static DEVICE_ATTR(pip2_get_last_error, 0444, + pt_pip2_get_last_error_show, NULL); +#endif /* !TTDL_KERNEL_SUBMISSION */ + +#if PT_FW_UPGRADE +/******************************************************************************* + * FUNCTION: pt_loader_attention + * + * SUMMARY: Function to be registered to TTDL attention list to set up + * int_running semaphore. + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_loader_attention(struct device *dev) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + + complete(&ld->int_running); + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_fw_upgrade_cb + * + * SUMMARY: Function to be registered to TTDL attention list to allow upgrade + * if host cannot get response from firmware with ping command. + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_fw_upgrade_cb(struct device *dev) +{ + u8 dut_gen = cmd->request_dut_generation(dev); + +#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE + if (dut_gen == DUT_PIP1_ONLY) { + pt_debug(dev, DL_WARN, "%s: Upgrade Platform FW", __func__); + if (!upgrade_firmware_from_platform(dev, false)) + return 0; + } +#endif + +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE + pt_debug(dev, DL_WARN, "%s: Upgrade Builtin FW", __func__); + if (dut_gen == DUT_PIP2_CAPABLE) { + if (!pt_pip2_upgrade_firmware_from_builtin(dev)) + return 0; + pt_debug(dev, DL_WARN, "%s: Builtin FW upgrade failed", + __func__); + } else { + if (!upgrade_firmware_from_builtin(dev)) + return 0; + } +#endif + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_cancel_fw_upgrade_cb + * + * SUMMARY: Function to be registered to TTDL attention list to allow an upgrade + * to be canceled. + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_cancel_fw_upgrade_cb(struct device *dev) +{ + struct pt_loader_data *ld = pt_get_loader_data(dev); + + pt_debug(dev, DL_WARN, + "%s: CANCELLING All Loader work\n", __func__); + cancel_work_sync(&ld->bl_from_file); + cancel_work_sync(&ld->pip2_bl_from_file); + cancel_work_sync(&ld->calibration_work); + cancel_work_sync(&ld->fw_and_config_upgrade); + + return 0; +} +#endif /* PT_FW_UPGRADE */ + +/******************************************************************************* + * FUNCTION: pt_loader_probe + * + * SUMMARY: The probe function for the FW loader. + * + * PARAMETERS: + * *dev - pointer to device structure + * **data - double pointer to the loader data to be created here + ******************************************************************************/ +static int pt_loader_probe(struct device *dev, void **data) +{ + struct pt_loader_data *ld; + struct pip2_loader_data *pip2_data; + struct pt_platform_data *pdata = dev_get_platdata(dev); + int rc; + u8 dut_gen = cmd->request_dut_generation(dev); + +#ifdef TTDL_DIAGNOSTICS + pt_debug(dev, DL_INFO, + "%s: entering %s\n", __func__, __func__); +#endif /* TTDL_DIAGNOSTICS */ + + ld = kzalloc(sizeof(*ld), GFP_KERNEL); + if (!ld) { + rc = -ENOMEM; + goto error_alloc_data_failed; + } + +#if PT_FW_UPGRADE + /* Initialize boot loader status */ + if (write_file_status != UPDATE_FW_COMPLETE) + _pt_update_write_file_status(dev, UPDATE_FW_IDLE); +#endif + + if (dut_gen == DUT_PIP2_CAPABLE) { + pip2_data = kzalloc(sizeof(*pip2_data), GFP_KERNEL); + if (!pip2_data) { + rc = -ENOMEM; + goto error_alloc_data_failed; + } + pip2_data->dev = dev; + ld->pip2_data = pip2_data; + +#if PT_FW_UPGRADE + rc = device_create_file(dev, &dev_attr_update_fw_status); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating update_fw_status\n", + __func__); + goto remove_files; + } +#endif + +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE + rc = device_create_file(dev, &dev_attr_pt_bl_from_file); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating pt_bl_from_file\n", + __func__); + goto remove_files; + } + rc = device_create_file(dev, &dev_attr_update_fw); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating update_fw\n", + __func__); + goto remove_files; + } +#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ + +#ifndef TTDL_KERNEL_SUBMISSION +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE + rc = device_create_file(dev, &dev_attr_pip2_manual_upgrade); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating pip2_manual_upgrade\n", + __func__); + goto remove_files; + } + rc = device_create_file(dev, &dev_attr_pip2_manual_ram_upgrade); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating pip2_manual_ram_upgrade\n", + __func__); + goto remove_files; + } + rc = device_create_file(dev, &dev_attr_pip2_file_write); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating pip2_file_write\n", + __func__); + goto remove_files; + } + rc = device_create_file(dev, &dev_attr_pip2_bl_from_file); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating pip2_bl_from_file\n", + __func__); + goto remove_files; + } + +#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ + rc = device_create_file(dev, &dev_attr_pip2_file_erase); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating pip2_file_erase\n", + __func__); + goto remove_files; + } + rc = device_create_file(dev, &dev_attr_pip2_bl_status); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating pip2_bl_status\n", + __func__); + goto remove_files; + } + +#ifdef TTDL_DIAGNOSTICS + rc = device_create_file(dev, &dev_attr_pip2_get_last_error); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating pip2_get_last_error\n", + __func__); + goto remove_files; + } + rc = device_create_bin_file(dev, &bin_attr_pip2_file_read); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating bin_attr_pip2_file_read\n", + __func__); + goto remove_files; + } + rc = device_create_file(dev, &dev_attr_pip2_file_crc); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating pip2_file_crc\n", + __func__); + goto remove_files; + } +#endif +#endif /* !TTDL_KERNEL_SUBMISSION */ + } else { +#if PT_FW_UPGRADE + rc = device_create_file(dev, &dev_attr_update_fw_status); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating update_fw_status\n", + __func__); + goto remove_files; + } +#endif + +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE + rc = device_create_file(dev, &dev_attr_pt_bl_from_file); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating pt_bl_from_file\n", + __func__); + goto remove_files; + } + rc = device_create_file(dev, &dev_attr_update_fw); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating update_fw\n", + __func__); + goto remove_files; + } +#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ +#ifndef TTDL_KERNEL_SUBMISSION +#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE + rc = device_create_file(dev, &dev_attr_forced_upgrade); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating forced_upgrade\n", + __func__); + goto remove_files; + } +#endif /* CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE */ +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE + rc = device_create_file(dev, &dev_attr_manual_upgrade); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating manual_upgrade\n", + __func__); + goto remove_files; + } +#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ +#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE + rc = device_create_file(dev, &dev_attr_config_loading); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating config_loading\n", + __func__); + goto remove_files; + } + + rc = device_create_bin_file(dev, &bin_attr_config_data); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Error creating config_data\n", + __func__); + goto remove_files; + } +#endif /* CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE */ +#endif /* !TTDL_KERNEL_SUBMISSION */ + } + + if (!pdata || !pdata->loader_pdata) { + pt_debug(dev, DL_ERROR, + "%s: Missing platform data\n", __func__); + rc = -ENODEV; + goto error_no_pdata; + } + + /* Default erase file to an invalid file */ + ld->pip2_file_erase_file_no = PIP2_RAM_FILE; + + ld->loader_pdata = pdata->loader_pdata; + ld->dev = dev; + *data = ld; + +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE + INIT_WORK(&ld->bl_from_file, pt_bl_from_file_work); + INIT_WORK(&ld->pip2_bl_from_file, pt_pip2_bl_from_file_work); +#endif /* CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE */ + +#if PT_FW_UPGRADE + init_completion(&ld->int_running); + + cmd->subscribe_attention(dev, PT_ATTEN_IRQ, PT_LOADER_NAME, + pt_loader_attention, PT_MODE_BOOTLOADER); + + cmd->subscribe_attention(dev, PT_ATTEN_LOADER, PT_LOADER_NAME, + pt_fw_upgrade_cb, PT_MODE_UNKNOWN); + + cmd->subscribe_attention(dev, PT_ATTEN_CANCEL_LOADER, PT_LOADER_NAME, + pt_cancel_fw_upgrade_cb, PT_MODE_UNKNOWN); +#endif +#if PT_FW_UPGRADE || PT_TTCONFIG_UPGRADE + pt_debug(dev, DL_INFO, "%s: INIT_WORK pt_calibrate_idacs\n", + __func__); + init_completion(&ld->calibration_complete); + INIT_WORK(&ld->calibration_work, pt_calibrate_idacs); +#endif + +#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE + if (dut_gen == DUT_PIP1_ONLY) + mutex_init(&ld->config_lock); +#endif + +#ifdef UPGRADE_FW_AND_CONFIG_IN_PROBE + /* Call FW and config upgrade directly in probe */ + pt_fw_and_config_upgrade(&ld->fw_and_config_upgrade); +#else + pt_debug(dev, DL_INFO, "%s: Schedule FW upgrade work\n", __func__); + INIT_WORK(&ld->fw_and_config_upgrade, pt_fw_and_config_upgrade); + schedule_work(&ld->fw_and_config_upgrade); +#endif + + pt_debug(dev, DL_INFO, "%s: Successful probe %s\n", + __func__, dev_name(dev)); + return 0; + + +remove_files: +#ifndef TTDL_KERNEL_SUBMISSION +#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE + device_remove_file(dev, &dev_attr_config_loading); +#endif +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE + device_remove_file(dev, &dev_attr_manual_upgrade); +#endif +#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE + device_remove_file(dev, &dev_attr_forced_upgrade); +#endif +#ifdef TTDL_DIAGNOSTICS + device_remove_file(dev, &dev_attr_pip2_get_last_error); + device_remove_bin_file(dev, &bin_attr_pip2_file_read); + device_remove_file(dev, &dev_attr_pip2_file_crc); +#endif + device_remove_file(dev, &dev_attr_pip2_bl_status); + device_remove_file(dev, &dev_attr_pip2_file_erase); +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE + device_remove_file(dev, &dev_attr_pip2_bl_from_file); + device_remove_file(dev, &dev_attr_pip2_file_write); + device_remove_file(dev, &dev_attr_pt_bl_from_file); + device_remove_file(dev, &dev_attr_pip2_manual_ram_upgrade); + device_remove_file(dev, &dev_attr_pip2_manual_upgrade); + device_remove_file(dev, &dev_attr_update_fw); + device_remove_file(dev, &dev_attr_update_fw_status); +#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ +#endif /* !TTDL_KERNEL_SUBMISSION */ + + kfree(ld->pip2_data); + kfree(ld); +error_alloc_data_failed: +error_no_pdata: + pt_debug(dev, DL_ERROR, "%s failed.\n", __func__); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_loader_release + * + * SUMMARY: Remove function for loader module that does following cleanup: + * - Unsubscibe all registered attention tasks + * - Removes all created sysfs nodes + * - Frees all pointers + * + * PARAMETERS: + * *dev - pointer to device structure + * *data - pointer to the loader data + ******************************************************************************/ +static void pt_loader_release(struct device *dev, void *data) +{ + struct pt_loader_data *ld = (struct pt_loader_data *)data; + u8 dut_gen = cmd->request_dut_generation(dev); + +#if PT_FW_UPGRADE + cmd->unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_LOADER_NAME, + pt_loader_attention, PT_MODE_BOOTLOADER); + + cmd->unsubscribe_attention(dev, PT_ATTEN_LOADER, PT_LOADER_NAME, + pt_fw_upgrade_cb, PT_MODE_UNKNOWN); + + cmd->unsubscribe_attention(dev, PT_ATTEN_CANCEL_LOADER, PT_LOADER_NAME, + pt_cancel_fw_upgrade_cb, PT_MODE_UNKNOWN); +#endif +#if PT_FW_UPGRADE + device_remove_file(dev, &dev_attr_update_fw_status); +#endif + if (dut_gen == DUT_PIP2_CAPABLE) { +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE + device_remove_file(dev, &dev_attr_update_fw); +#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ +#ifndef TTDL_KERNEL_SUBMISSION +#ifdef TTDL_DIAGNOSTICS + device_remove_bin_file(dev, &bin_attr_pip2_file_read); + device_remove_file(dev, &dev_attr_pip2_file_crc); +#endif + device_remove_file(dev, &dev_attr_pip2_get_last_error); + device_remove_file(dev, &dev_attr_pip2_bl_status); + device_remove_file(dev, &dev_attr_pip2_file_erase); +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE + device_remove_file(dev, &dev_attr_pip2_file_write); + device_remove_file(dev, &dev_attr_pip2_bl_from_file); + device_remove_file(dev, &dev_attr_pt_bl_from_file); + device_remove_file(dev, &dev_attr_pip2_manual_ram_upgrade); + device_remove_file(dev, &dev_attr_pip2_manual_upgrade); +#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ +#endif /* !TTDL_KERNEL_SUBMISSION */ + kfree(ld->pip2_data); + } else { +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE + device_remove_file(dev, &dev_attr_update_fw); +#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ +#ifndef TTDL_KERNEL_SUBMISSION +#ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE + device_remove_bin_file(dev, &bin_attr_config_data); + device_remove_file(dev, &dev_attr_config_loading); + if (!ld->config_data) + kfree(ld->config_data); +#endif /* CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE */ +#ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE + device_remove_file(dev, &dev_attr_pt_bl_from_file); + device_remove_file(dev, &dev_attr_manual_upgrade); +#endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ +#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE + device_remove_file(dev, &dev_attr_forced_upgrade); +#endif /* CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE */ +#endif /* !TTDL_KERNEL_SUBMISSION */ + } + kfree(ld); +} + +static struct pt_module loader_module = { + .name = PT_LOADER_NAME, + .probe = pt_loader_probe, + .release = pt_loader_release, +}; + +/******************************************************************************* + * FUNCTION: pt_loader_init + * + * SUMMARY: Initialize function for loader module which to register + * loader_module into TTDL module list. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * *data - pointer to the loader data + ******************************************************************************/ +static int __init pt_loader_init(void) +{ + int rc; + + cmd = pt_get_commands(); + if (!cmd) + return -EINVAL; + + rc = pt_register_module(&loader_module); + if (rc < 0) { + pr_err("%s: Error, failed registering module\n", + __func__); + return rc; + } + + pr_info("%s: Parade FW Loader Driver (Version %s) rc=%d\n", + __func__, PT_DRIVER_VERSION, rc); + return 0; +} +module_init(pt_loader_init); + +/******************************************************************************* + * FUNCTION: pt_loader_exit + * + * SUMMARY: Exit function for loader module which to unregister loader_module + * from TTDL module list. + * + ******************************************************************************/ +static void __exit pt_loader_exit(void) +{ + pt_unregister_module(&loader_module); +} +module_exit(pt_loader_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product FW Loader Driver"); +MODULE_AUTHOR("Parade Technologies "); diff --git a/pt/pt_mt_common.c b/pt/pt_mt_common.c new file mode 100644 index 0000000000..008db5c972 --- /dev/null +++ b/pt/pt_mt_common.c @@ -0,0 +1,970 @@ +/* + * pt_mt_common.c + * Parade TrueTouch(TM) Standard Product Multi-Touch Reports Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.paradetech.com + */ + +#include "pt_regs.h" + +#define MT_PARAM_SIGNAL(md, sig_ost) PARAM_SIGNAL(md->pdata->frmwrk, sig_ost) +#define MT_PARAM_MIN(md, sig_ost) PARAM_MIN(md->pdata->frmwrk, sig_ost) +#define MT_PARAM_MAX(md, sig_ost) PARAM_MAX(md->pdata->frmwrk, sig_ost) +#define MT_PARAM_FUZZ(md, sig_ost) PARAM_FUZZ(md->pdata->frmwrk, sig_ost) +#define MT_PARAM_FLAT(md, sig_ost) PARAM_FLAT(md->pdata->frmwrk, sig_ost) + +/******************************************************************************* + * FUNCTION: pt_mt_lift_all + * + * SUMMARY: Reports touch liftoff action + * + * PARAMETERS: + * *md - pointer to touch data structure + ******************************************************************************/ +static void pt_mt_lift_all(struct pt_mt_data *md) +{ + int max = md->si->tch_abs[PT_TCH_T].max; + + if (md->num_prv_rec != 0) { + if (md->mt_function.report_slot_liftoff) + md->mt_function.report_slot_liftoff(md, max); + input_sync(md->input); + md->num_prv_rec = 0; + } +} + +/******************************************************************************* + * FUNCTION: pt_get_touch_hdr + * + * SUMMARY: Get the header of touch report + * + * PARAMETERS: + * *md - pointer to touch data structure + * *touch - pointer to pt_touch structure + * *xy_mode - pointer to touch mode data + ******************************************************************************/ +static void pt_get_touch_hdr(struct pt_mt_data *md, + struct pt_touch *touch, u8 *xy_mode) +{ + struct device *dev = md->dev; + struct pt_sysinfo *si = md->si; + enum pt_tch_hdr hdr; + + for (hdr = PT_TCH_TIME; hdr < PT_TCH_NUM_HDR; hdr++) { + if (!si->tch_hdr[hdr].report) + continue; + pt_get_touch_field(dev, &touch->hdr[hdr], + si->tch_hdr[hdr].size, + si->tch_hdr[hdr].max, + xy_mode + si->tch_hdr[hdr].ofs, + si->tch_hdr[hdr].bofs); + pt_debug(dev, DL_DEBUG, "%s: get %s=%04X(%d)\n", + __func__, pt_tch_hdr_string[hdr], + touch->hdr[hdr], touch->hdr[hdr]); + } + + pt_debug(dev, DL_INFO, + "%s: time=%X tch_num=%d lo=%d noise=%d counter=%d\n", + __func__, + touch->hdr[PT_TCH_TIME], + touch->hdr[PT_TCH_NUM], + touch->hdr[PT_TCH_LO], + touch->hdr[PT_TCH_NOISE], + touch->hdr[PT_TCH_COUNTER]); +} + +/******************************************************************************* + * FUNCTION: pt_get_touch_record + * + * SUMMARY: Gets axis of touch report + * + * PARAMETERS: + * *md - pointer to touch data structure + * *touch - pointer to pt_touch structure + * *xy_data - pointer to touch data + ******************************************************************************/ +static void pt_get_touch_record(struct pt_mt_data *md, + struct pt_touch *touch, u8 *xy_data) +{ + struct device *dev = md->dev; + struct pt_sysinfo *si = md->si; + enum pt_tch_abs abs; + + for (abs = PT_TCH_X; abs < PT_TCH_NUM_ABS; abs++) { + if (!si->tch_abs[abs].report) + continue; + pt_get_touch_field(dev, &touch->abs[abs], + si->tch_abs[abs].size, + si->tch_abs[abs].max, + xy_data + si->tch_abs[abs].ofs, + si->tch_abs[abs].bofs); + pt_debug(dev, DL_DEBUG, "%s: get %s=%04X(%d)\n", + __func__, pt_tch_abs_string[abs], + touch->abs[abs], touch->abs[abs]); + } +} + +/******************************************************************************* + * FUNCTION: pt_mt_process_touch + * + * SUMMARY: Process touch includes oritation,axis invert and + * convert MAJOR/MINOR from mm to resolution + * + * PARAMETERS: + * *md - pointer to touch data structure + * *touch - pointer to pt_touch structure + ******************************************************************************/ +static void pt_mt_process_touch(struct pt_mt_data *md, + struct pt_touch *touch) +{ + struct device *dev = md->dev; + struct pt_sysinfo *si = md->si; + int tmp; + bool flipped; + + + /* Orientation is signed */ + touch->abs[PT_TCH_OR] = (int8_t)touch->abs[PT_TCH_OR]; + + if (md->pdata->flags & PT_MT_FLAG_FLIP) { + tmp = touch->abs[PT_TCH_X]; + touch->abs[PT_TCH_X] = touch->abs[PT_TCH_Y]; + touch->abs[PT_TCH_Y] = tmp; + if (touch->abs[PT_TCH_OR] > 0) + touch->abs[PT_TCH_OR] = + md->or_max - touch->abs[PT_TCH_OR]; + else + touch->abs[PT_TCH_OR] = + md->or_min - touch->abs[PT_TCH_OR]; + flipped = true; + } else + flipped = false; + + /* + * 1 is subtracted from each touch location to make the location + * 0 based. e.g. If the resolution of touch panel is 1200x1600, + * the FW touch report must be (0~1199,0~1599). The driver + * should register the (min,max) value to Linux input system as + * (0~1199,0~1599). When the host needs to invert the + * coordinates, the driver would incorrectly use the resolution + * to subtract the reported point directly, such as + * 1200-(0~1199). The input system will lose the 0 point report + * and the 1200 point will be ignored. + */ + if (md->pdata->flags & PT_MT_FLAG_INV_X) { + if (flipped) + touch->abs[PT_TCH_X] = si->sensing_conf_data.res_y - + touch->abs[PT_TCH_X] - 1; + else + touch->abs[PT_TCH_X] = si->sensing_conf_data.res_x - + touch->abs[PT_TCH_X] - 1; + touch->abs[PT_TCH_OR] *= -1; + } + if (md->pdata->flags & PT_MT_FLAG_INV_Y) { + if (flipped) + touch->abs[PT_TCH_Y] = si->sensing_conf_data.res_x - + touch->abs[PT_TCH_Y] - 1; + else + touch->abs[PT_TCH_Y] = si->sensing_conf_data.res_y - + touch->abs[PT_TCH_Y] - 1; + touch->abs[PT_TCH_OR] *= -1; + } + + /* Convert MAJOR/MINOR from mm to resolution */ + tmp = touch->abs[PT_TCH_MAJ] * 100 * si->sensing_conf_data.res_x; + touch->abs[PT_TCH_MAJ] = tmp / si->sensing_conf_data.len_x; + tmp = touch->abs[PT_TCH_MIN] * 100 * si->sensing_conf_data.res_x; + touch->abs[PT_TCH_MIN] = tmp / si->sensing_conf_data.len_x; + + pt_debug(dev, DL_INFO, + "%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n", + __func__, flipped ? "true" : "false", + md->pdata->flags & PT_MT_FLAG_INV_X ? "true" : "false", + md->pdata->flags & PT_MT_FLAG_INV_Y ? "true" : "false", + touch->abs[PT_TCH_X], touch->abs[PT_TCH_X], + touch->abs[PT_TCH_Y], touch->abs[PT_TCH_Y]); +} + +/******************************************************************************* + * FUNCTION: pt_report_event + * + * SUMMARY: Reports touch event + * + * PARAMETERS: + * *md - pointer to touch data structure + * event - type of touch event + * value - value of report event + ******************************************************************************/ +static void pt_report_event(struct pt_mt_data *md, int event, + int value) +{ + int sig = MT_PARAM_SIGNAL(md, event); + + if (sig != PT_IGNORE_VALUE) + input_report_abs(md->input, sig, value); +} + +/******************************************************************************* + * FUNCTION: pt_get_mt_touches + * + * SUMMARY: Parse and report touch event + * + * PARAMETERS: + * *md - pointer to touch data structure + * *tch - pointer to touch structure + * num_cur_tch - number of current touch + ******************************************************************************/ +static void pt_get_mt_touches(struct pt_mt_data *md, + struct pt_touch *tch, int num_cur_tch) +{ + struct device *dev = md->dev; + struct pt_sysinfo *si = md->si; + int sig; + int i, j, t = 0; + DECLARE_BITMAP(ids, PT_TOUCH_ID_MAX); + int mt_sync_count = 0; + u8 *tch_addr; + + if (PT_TOUCH_ID_MAX < si->tch_abs[PT_TCH_T].max) { + pt_debug(dev, DL_ERROR, + "%s: Touch ID %d is allocated less than needed %d\n", + __func__, PT_TOUCH_ID_MAX, + (int)si->tch_abs[PT_TCH_T].max); + return; + } + + bitmap_zero(ids, PT_TOUCH_ID_MAX); + memset(tch->abs, 0, sizeof(tch->abs)); + + for (i = 0; i < num_cur_tch; i++) { + tch_addr = si->xy_data + (i * si->desc.tch_record_size); + pt_get_touch_record(md, tch, tch_addr); + + /* Discard proximity event */ + if (tch->abs[PT_TCH_O] == PT_OBJ_PROXIMITY) { + pt_debug(dev, DL_INFO, + "%s: Discarding proximity event\n", + __func__); + continue; + } + + /* Validate track_id */ + t = tch->abs[PT_TCH_T]; + if (t < md->t_min || t > md->t_max) { + pt_debug(dev, DL_INFO, + "%s: tch=%d -> bad trk_id=%d max_id=%d\n", + __func__, i, t, md->t_max); + if (md->mt_function.input_sync) + md->mt_function.input_sync(md->input); + mt_sync_count++; + continue; + } + + /* Lift-off */ + if (tch->abs[PT_TCH_E] == PT_EV_LIFTOFF) { + pt_debug(dev, DL_INFO, "%s: t=%d e=%d lift-off\n", + __func__, t, tch->abs[PT_TCH_E]); + goto pt_get_mt_touches_pr_tch; + } + + /* Process touch */ + pt_mt_process_touch(md, tch); + + /* use 0 based track id's */ + t -= md->t_min; + + sig = MT_PARAM_SIGNAL(md, PT_ABS_ID_OST); + if (sig != PT_IGNORE_VALUE) { + if (md->mt_function.input_report) + md->mt_function.input_report(md->input, sig, + t, tch->abs[PT_TCH_O]); + __set_bit(t, ids); + } + + pt_report_event(md, PT_ABS_D_OST, 0); + + /* all devices: position and pressure fields */ + for (j = 0; j <= PT_ABS_W_OST; j++) { + if (!si->tch_abs[j].report) + continue; + pt_report_event(md, PT_ABS_X_OST + j, + tch->abs[PT_TCH_X + j]); + } + + /* Get the extended touch fields */ + for (j = 0; j < PT_NUM_EXT_TCH_FIELDS; j++) { + if (!si->tch_abs[PT_ABS_MAJ_OST + j].report) + continue; + pt_report_event(md, PT_ABS_MAJ_OST + j, + tch->abs[PT_TCH_MAJ + j]); + } + if (md->mt_function.input_sync) + md->mt_function.input_sync(md->input); + mt_sync_count++; + +pt_get_mt_touches_pr_tch: + pt_debug(dev, DL_INFO, + "%s: t=%d x=%d y=%d z=%d M=%d m=%d o=%d e=%d obj=%d tip=%d\n", + __func__, t, + tch->abs[PT_TCH_X], + tch->abs[PT_TCH_Y], + tch->abs[PT_TCH_P], + tch->abs[PT_TCH_MAJ], + tch->abs[PT_TCH_MIN], + tch->abs[PT_TCH_OR], + tch->abs[PT_TCH_E], + tch->abs[PT_TCH_O], + tch->abs[PT_TCH_TIP]); + } + + if (md->mt_function.final_sync) + md->mt_function.final_sync(md->input, + si->tch_abs[PT_TCH_T].max, mt_sync_count, ids); + + md->num_prv_rec = num_cur_tch; +} + +/******************************************************************************* + * FUNCTION: pt_xy_worker + * + * SUMMARY: Read xy_data for all current touches + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *md - pointer to touch data structure + ******************************************************************************/ +static int pt_xy_worker(struct pt_mt_data *md) +{ + struct device *dev = md->dev; + struct pt_sysinfo *si = md->si; + int max_tch = si->sensing_conf_data.max_tch; + struct pt_touch tch; + u8 num_cur_tch; + int rc = 0; + + pt_get_touch_hdr(md, &tch, si->xy_mode + 3); + + num_cur_tch = tch.hdr[PT_TCH_NUM]; + if (num_cur_tch > max_tch) { + pt_debug(dev, DL_ERROR, "%s: Num touch err detected (n=%d)\n", + __func__, num_cur_tch); + num_cur_tch = max_tch; + } + + if (tch.hdr[PT_TCH_LO]) { + pt_debug(dev, DL_INFO, "%s: Large area detected\n", + __func__); + if (md->pdata->flags & PT_MT_FLAG_NO_TOUCH_ON_LO) + num_cur_tch = 0; + } + + if (num_cur_tch == 0 && md->num_prv_rec == 0) + goto pt_xy_worker_exit; + + /* extract xy_data for all currently reported touches */ + pt_debug(dev, DL_DEBUG, "%s: extract data num_cur_tch=%d\n", + __func__, num_cur_tch); + if (num_cur_tch) + pt_get_mt_touches(md, &tch, num_cur_tch); + else + pt_mt_lift_all(md); + + rc = 0; + +pt_xy_worker_exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_mt_send_dummy_event + * + * SUMMARY: Send dummy/key event to wakeup upper layer of system + * + * PARAMETERS: + * *cd - pointer to core data structure + * *md - pointer to touch data structure + ******************************************************************************/ +static void pt_mt_send_dummy_event(struct pt_core_data *cd, + struct pt_mt_data *md) +{ +#ifndef EASYWAKE_TSG6 + /* TSG5 EasyWake */ + unsigned long ids = 0; + + /* for easy wakeup */ + if (md->mt_function.input_report) + md->mt_function.input_report(md->input, ABS_MT_TRACKING_ID, + 0, PT_OBJ_STANDARD_FINGER); + if (md->mt_function.input_sync) + md->mt_function.input_sync(md->input); + if (md->mt_function.final_sync) + md->mt_function.final_sync(md->input, 0, 1, &ids); + if (md->mt_function.report_slot_liftoff) + md->mt_function.report_slot_liftoff(md, 1); + if (md->mt_function.final_sync) + md->mt_function.final_sync(md->input, 1, 1, &ids); +#else + /* TSG6 FW1.3 and above only. TSG6 FW1.0 - 1.2 does not */ + /* support EasyWake, and this function will not be called */ + u8 key_value = 0; + + switch (cd->gesture_id) { + case GESTURE_DOUBLE_TAP: + key_value = KEY_F1; + break; + case GESTURE_TWO_FINGERS_SLIDE: + key_value = KEY_F2; + break; + case GESTURE_TOUCH_DETECTED: + key_value = KEY_F3; + break; + case GESTURE_PUSH_BUTTON: + key_value = KEY_F4; + break; + case GESTURE_SINGLE_SLIDE_DE_TX: + key_value = KEY_F5; + break; + case GESTURE_SINGLE_SLIDE_IN_TX: + key_value = KEY_F6; + break; + case GESTURE_SINGLE_SLIDE_DE_RX: + key_value = KEY_F7; + break; + case GESTURE_SINGLE_SLIDE_IN_RX: + key_value = KEY_F8; + break; + default: + break; + } + + if (key_value > 0) { + input_report_key(md->input, key_value, 1); + mdelay(10); + input_report_key(md->input, key_value, 0); + input_sync(md->input); + } + + /* + * Caution - this debug print is needed by the TTDL automated + * regression test suite + */ + pt_debug(md->dev, DL_INFO, "%s: report key: %d\n", + __func__, key_value); +#endif +} + +/******************************************************************************* + * FUNCTION: pt_mt_attention + * + * SUMMARY: Wrapper function for pt_xy_worker() that subscribe into the TTDL + * attention list. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_mt_attention(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_mt_data *md = &cd->md; + int rc; + + if (md->si->xy_mode[2] != md->si->desc.tch_report_id) + return 0; + + /* core handles handshake */ + mutex_lock(&md->mt_lock); + rc = pt_xy_worker(md); + mutex_unlock(&md->mt_lock); + if (rc < 0) + pt_debug(dev, DL_ERROR, + "%s: xy_worker error r=%d\n", __func__, rc); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_mt_wake_attention + * + * SUMMARY: Wrapper function for pt_mt_send_dummy_event() that register to + * attention list. + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_mt_wake_attention(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_mt_data *md = &cd->md; + + mutex_lock(&md->mt_lock); + pt_mt_send_dummy_event(cd, md); + mutex_unlock(&md->mt_lock); + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_startup_attention + * + * SUMMARY: Wrapper function for pt_mt_lift_all() that subcribe into the TTDL + * attention list. + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_startup_attention(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_mt_data *md = &cd->md; + + mutex_lock(&md->mt_lock); + pt_mt_lift_all(md); + mutex_unlock(&md->mt_lock); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_mt_suspend_attention + * + * SUMMARY: Function for touch to enter suspend state that as following steps: + * 1) Lift all touch + * 2) Set flag with suspend state + * 3) Decrese pm system count + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_mt_suspend_attention(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_mt_data *md = &cd->md; + + mutex_lock(&md->mt_lock); + pt_mt_lift_all(md); + md->is_suspended = true; + mutex_unlock(&md->mt_lock); + + pm_runtime_put(dev); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_mt_resume_attention + * + * SUMMARY: Function for touch to leave suspend state that as following steps: + * 1) Increse pm system count + * 2) Clear suspend state flag + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_mt_resume_attention(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_mt_data *md = &cd->md; + + pm_runtime_get(dev); + + mutex_lock(&md->mt_lock); + md->is_suspended = false; + mutex_unlock(&md->mt_lock); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_mt_open + * + * SUMMARY: Open method for input device(touch) that sets up call back + * functions to TTDL attention list + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *input - pointer to input_dev structure + ******************************************************************************/ +static int pt_mt_open(struct input_dev *input) +{ + struct device *dev = input->dev.parent; + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_mt_data *md = &cd->md; + + pm_runtime_get_sync(dev); + + mutex_lock(&md->mt_lock); + md->is_suspended = false; + mutex_unlock(&md->mt_lock); + + pt_debug(dev, DL_INFO, "%s: setup subscriptions\n", __func__); + + /* set up touch call back */ + _pt_subscribe_attention(dev, PT_ATTEN_IRQ, PT_MT_NAME, + pt_mt_attention, PT_MODE_OPERATIONAL); + + /* set up startup call back */ + _pt_subscribe_attention(dev, PT_ATTEN_STARTUP, PT_MT_NAME, + pt_startup_attention, 0); + + /* set up wakeup call back */ + _pt_subscribe_attention(dev, PT_ATTEN_WAKE, PT_MT_NAME, + pt_mt_wake_attention, 0); + + /* set up suspend call back */ + _pt_subscribe_attention(dev, PT_ATTEN_SUSPEND, PT_MT_NAME, + pt_mt_suspend_attention, 0); + + /* set up resume call back */ + _pt_subscribe_attention(dev, PT_ATTEN_RESUME, PT_MT_NAME, + pt_mt_resume_attention, 0); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_mt_close + * + * SUMMARY: Close method for input device(touch) that clears call back + * functions from TTDL attention list. + * + * PARAMETERS: + * *input - pointer to input_dev structure + ******************************************************************************/ +static void pt_mt_close(struct input_dev *input) +{ + struct device *dev = input->dev.parent; + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_mt_data *md = &cd->md; + + _pt_unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_MT_NAME, + pt_mt_attention, PT_MODE_OPERATIONAL); + + _pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_MT_NAME, + pt_startup_attention, 0); + + _pt_unsubscribe_attention(dev, PT_ATTEN_WAKE, PT_MT_NAME, + pt_mt_wake_attention, 0); + + _pt_unsubscribe_attention(dev, PT_ATTEN_SUSPEND, PT_MT_NAME, + pt_mt_suspend_attention, 0); + + _pt_unsubscribe_attention(dev, PT_ATTEN_RESUME, PT_MT_NAME, + pt_mt_resume_attention, 0); + + mutex_lock(&md->mt_lock); + if (!md->is_suspended) { + pm_runtime_put(dev); + md->is_suspended = true; + } + mutex_unlock(&md->mt_lock); +} + +/******************************************************************************* + * FUNCTION: pt_setup_input_device + * + * SUMMARY: Set up resolution, event signal capabilities and + * register input device for touch. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_setup_input_device(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_mt_data *md = &cd->md; + int signal = PT_IGNORE_VALUE; + int max_x, max_y, max_p, min, max; + int max_x_tmp, max_y_tmp; + int i; + int rc; + + pt_debug(dev, DL_INFO, "%s: Initialize event signals\n", + __func__); + __set_bit(EV_ABS, md->input->evbit); + __set_bit(EV_REL, md->input->evbit); + __set_bit(EV_KEY, md->input->evbit); +#ifdef INPUT_PROP_DIRECT + __set_bit(INPUT_PROP_DIRECT, md->input->propbit); +#endif + + /* If virtualkeys enabled, don't use all screen */ + if (md->pdata->flags & PT_MT_FLAG_VKEYS) { + max_x_tmp = md->pdata->vkeys_x; + max_y_tmp = md->pdata->vkeys_y; + } else { + max_x_tmp = md->si->sensing_conf_data.res_x; + max_y_tmp = md->si->sensing_conf_data.res_y; + } + + /* get maximum values from the sysinfo data */ + if (md->pdata->flags & PT_MT_FLAG_FLIP) { + max_x = max_y_tmp - 1; + max_y = max_x_tmp - 1; + } else { + max_x = max_x_tmp - 1; + max_y = max_y_tmp - 1; + } + max_p = md->si->sensing_conf_data.max_z; + + /* set event signal capabilities */ + for (i = 0; i < NUM_SIGNALS(md->pdata->frmwrk); i++) { + signal = MT_PARAM_SIGNAL(md, i); + if (signal != PT_IGNORE_VALUE) { + __set_bit(signal, md->input->absbit); + + min = MT_PARAM_MIN(md, i); + max = MT_PARAM_MAX(md, i); + if (i == PT_ABS_ID_OST) { + /* shift track ids down to start at 0 */ + max = max - min; + min = min - min; + } else if (i == PT_ABS_X_OST) + max = max_x; + else if (i == PT_ABS_Y_OST) + max = max_y; + else if (i == PT_ABS_P_OST) + max = max_p; + + input_set_abs_params(md->input, signal, min, max, + MT_PARAM_FUZZ(md, i), MT_PARAM_FLAT(md, i)); + pt_debug(dev, DL_INFO, + "%s: register signal=%02X min=%d max=%d\n", + __func__, signal, min, max); + } + } + + md->or_min = MT_PARAM_MIN(md, PT_ABS_OR_OST); + md->or_max = MT_PARAM_MAX(md, PT_ABS_OR_OST); + + md->t_min = MT_PARAM_MIN(md, PT_ABS_ID_OST); + md->t_max = MT_PARAM_MAX(md, PT_ABS_ID_OST); + + rc = md->mt_function.input_register_device(md->input, + md->si->tch_abs[PT_TCH_T].max); + if (rc < 0) + pt_debug(dev, DL_ERROR, "%s: Error, failed register input device r=%d\n", + __func__, rc); + else + md->input_device_registered = true; + +#ifdef EASYWAKE_TSG6 + input_set_capability(md->input, EV_KEY, KEY_F1); + input_set_capability(md->input, EV_KEY, KEY_F2); + input_set_capability(md->input, EV_KEY, KEY_F3); + input_set_capability(md->input, EV_KEY, KEY_F4); + input_set_capability(md->input, EV_KEY, KEY_F5); + input_set_capability(md->input, EV_KEY, KEY_F6); + input_set_capability(md->input, EV_KEY, KEY_F7); + input_set_capability(md->input, EV_KEY, KEY_F8); +#endif + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_setup_input_attention + * + * SUMMARY: Wrapper function for pt_setup_input_device() register to TTDL + * attention list. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_setup_input_attention(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_mt_data *md = &cd->md; + int rc; + + md->si = _pt_request_sysinfo(dev); + if (!md->si) + return -EINVAL; + + rc = pt_setup_input_device(dev); + + _pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_MT_NAME, + pt_setup_input_attention, 0); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_mt_probe + * + * SUMMARY: The probe function for touch input device + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +int pt_mt_probe(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_mt_data *md = &cd->md; + struct pt_platform_data *pdata = dev_get_platdata(dev); + struct pt_mt_platform_data *mt_pdata; + int rc = 0; + + pt_debug(dev, DL_INFO, + "%s: >>>>>> Register MT <<<<<<\n", __func__); + if (!pdata || !pdata->mt_pdata) { + pt_debug(dev, DL_ERROR, + "%s: Missing platform data\n", __func__); + rc = -ENODEV; + goto error_no_pdata; + } + mt_pdata = pdata->mt_pdata; + + pt_init_function_ptrs(md); + + mutex_init(&md->mt_lock); + md->dev = dev; + md->pdata = mt_pdata; + + /* Create the input device and register it. */ + pt_debug(dev, DL_INFO, + "%s: Create the input device and register it\n", __func__); + md->input = input_allocate_device(); + if (!md->input) { + pt_debug(dev, DL_ERROR, "%s: Error, failed to allocate input device\n", + __func__); + rc = -ENODEV; + goto error_alloc_failed; + } else + md->input_device_allocated = true; + + if (md->pdata->inp_dev_name) + md->input->name = md->pdata->inp_dev_name; + else + md->input->name = PT_MT_NAME; + scnprintf(md->phys, sizeof(md->phys), "%s/input%d", dev_name(dev), + cd->phys_num++); + md->input->phys = md->phys; + md->input->dev.parent = md->dev; + md->input->open = pt_mt_open; + md->input->close = pt_mt_close; + input_set_drvdata(md->input, md); + + /* get sysinfo */ + md->si = _pt_request_sysinfo(dev); + + if (md->si) { + rc = pt_setup_input_device(dev); + if (rc) + goto error_init_input; + } else { + pt_debug(dev, DL_ERROR, "%s: Fail get sysinfo pointer from core p=%p\n", + __func__, md->si); + _pt_subscribe_attention(dev, PT_ATTEN_STARTUP, + PT_MT_NAME, pt_setup_input_attention, 0); + } + + return 0; + +error_init_input: + input_free_device(md->input); + md->input_device_allocated = false; +error_alloc_failed: +error_no_pdata: + pt_debug(dev, DL_ERROR, "%s failed.\n", __func__); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_mt_release + * + * SUMMARY: The release function for touch input device + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +int pt_mt_release(struct device *dev) +{ + struct pt_core_data *cd; + struct pt_mt_data *md; + + /* Ensure valid pointers before de-referencing them */ + if (dev) { + cd = dev_get_drvdata(dev); + if (cd) + md = &cd->md; + else + return 0; + } else { + return 0; + } + + /* + * Second call this function may cause kernel panic if probe fail. + * Use input_device_registered & input_device_allocated variable to + * avoid unregister or free unavailable devive. + */ + if (md && md->input_device_registered) { + md->input_device_registered = false; + input_unregister_device(md->input); + /* Unregistering device will free the device too */ + md->input_device_allocated = false; + } else if (md && md->input_device_allocated) { + md->input_device_allocated = false; + input_free_device(md->input); + _pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, + PT_MT_NAME, pt_setup_input_attention, 0); + } + + return 0; +} diff --git a/pt/pt_mta.c b/pt/pt_mta.c new file mode 100644 index 0000000000..5564816fa5 --- /dev/null +++ b/pt/pt_mta.c @@ -0,0 +1,143 @@ +/* + * pt_mta.c + * Parade TrueTouch(TM) Standard Product Multi-Touch Protocol A Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.paradetech.com + */ + +#include "pt_regs.h" + +/******************************************************************************* + * FUNCTION: pt_final_sync + * + * SUMMARY: Function to create SYN_REPORT + * + * PARAMETERS: + * *input - pointer to input device structure + * max_slots - max support touch number + * mt_sync_count - current valid touch number + * ids - bit map value + ******************************************************************************/ +static void pt_final_sync(struct input_dev *input, int max_slots, + int mt_sync_count, unsigned long *ids) +{ + if (mt_sync_count) + input_sync(input); +} + +/******************************************************************************* + * FUNCTION: pt_input_sync + * + * SUMMARY: Function to create SYN_MT_REPORT + * + * PARAMETERS: + * *input - pointer to input device structure + ******************************************************************************/ +static void pt_input_sync(struct input_dev *input) +{ + input_mt_sync(input); +} + +/******************************************************************************* + * FUNCTION: pt_input_report + * + * SUMMARY: Function to report coordinate information of touch point + * protocol + * + * PARAMETERS: + * *input - pointer to input device structure + * sig - track id to allow tracking of a touch + * t - event id to indicate an event associated with touch instance + * type - indicate the touch object + ******************************************************************************/ +static void pt_input_report(struct input_dev *input, int sig, + int t, int type) +{ + if (type == PT_OBJ_STANDARD_FINGER || type == PT_OBJ_GLOVE) { + input_report_key(input, BTN_TOOL_FINGER, PT_BTN_PRESSED); + input_report_key(input, BTN_TOOL_PEN, PT_BTN_RELEASED); + } else if (type == PT_OBJ_STYLUS) { + input_report_key(input, BTN_TOOL_PEN, PT_BTN_PRESSED); + input_report_key(input, BTN_TOOL_FINGER, PT_BTN_RELEASED); + } + + input_report_key(input, BTN_TOUCH, PT_BTN_PRESSED); + + input_report_abs(input, sig, t); +} + +/******************************************************************************* + * FUNCTION: pt_report_slot_liftoff + * + * SUMMARY: Function to report all touches are lifted + * protocol + * + * PARAMETERS: + * *md - pointer to input device structure + * max_slots - indicate max number of touch id + ******************************************************************************/ +static void pt_report_slot_liftoff(struct pt_mt_data *md, + int max_slots) +{ + input_report_key(md->input, BTN_TOUCH, PT_BTN_RELEASED); + input_report_key(md->input, BTN_TOOL_FINGER, PT_BTN_RELEASED); + input_report_key(md->input, BTN_TOOL_PEN, PT_BTN_RELEASED); + +} + +/******************************************************************************* + * FUNCTION: pt_input_register_device + * + * SUMMARY: Function to register input device + * protocol + * + * PARAMETERS: + * *input - pointer to input device structure + * max_slots - indicate max number of touch id + ******************************************************************************/ +static int pt_input_register_device(struct input_dev *input, int max_slots) +{ + __set_bit(BTN_TOUCH, input->keybit); + __set_bit(BTN_TOOL_FINGER, input->keybit); + __set_bit(BTN_TOOL_PEN, input->keybit); + return input_register_device(input); +} + +/******************************************************************************* + * FUNCTION: pt_init_function_ptrs + * + * SUMMARY: Function to init function pointer + * + * PARAMETERS: + * *md - pointer to touch data structure + ******************************************************************************/ +void pt_init_function_ptrs(struct pt_mt_data *md) +{ + md->mt_function.report_slot_liftoff = pt_report_slot_liftoff; + md->mt_function.final_sync = pt_final_sync; + md->mt_function.input_sync = pt_input_sync; + md->mt_function.input_report = pt_input_report; + md->mt_function.input_register_device = pt_input_register_device; +} diff --git a/pt/pt_mtb.c b/pt/pt_mtb.c new file mode 100644 index 0000000000..d40f817766 --- /dev/null +++ b/pt/pt_mtb.c @@ -0,0 +1,144 @@ +/* + * pt_mtb.c + * Parade TrueTouch(TM) Standard Product Multi-Touch Protocol B Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.paradetech.com + */ + +#include "pt_regs.h" +#include +#include + +/******************************************************************************* + * FUNCTION: pt_final_sync + * + * SUMMARY: Function to create SYN_REPORT + * + * PARAMETERS: + * *input - pointer to input device structure + * max_slots - max support touch number + * mt_sync_count - current valid touch number + * ids - bit map value + ******************************************************************************/ +static void pt_final_sync(struct input_dev *input, int max_slots, + int mt_sync_count, unsigned long *ids) +{ + int t; + + for (t = 0; t < max_slots; t++) { + if (test_bit(t, ids)) + continue; + input_mt_slot(input, t); + input_mt_report_slot_state(input, MT_TOOL_FINGER, false); + } + + input_sync(input); +} + +/******************************************************************************* + * FUNCTION: pt_input_report + * + * SUMMARY: Function to report coordinate information of touch point + * protocol + * + * PARAMETERS: + * *input - pointer to input device structure + * sig - track id to allow tracking of a touch + * t - event id to indicate an event associated with touch instance + * type - indicate the touch object + ******************************************************************************/ +static void pt_input_report(struct input_dev *input, int sig, + int t, int type) +{ + input_mt_slot(input, t); + + if (type == PT_OBJ_STANDARD_FINGER || type == PT_OBJ_GLOVE) + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + else if (type == PT_OBJ_STYLUS) + input_mt_report_slot_state(input, MT_TOOL_PEN, true); +} + +/******************************************************************************* + * FUNCTION: pt_report_slot_liftoff + * + * SUMMARY: Function to report all touches are lifted + * protocol + * + * PARAMETERS: + * *md - pointer to input device structure + * max_slots - indicate max number of touch id + ******************************************************************************/ +static void pt_report_slot_liftoff(struct pt_mt_data *md, + int max_slots) +{ + int t; + + if (md->num_prv_rec == 0) + return; + + for (t = 0; t < max_slots; t++) { + input_mt_slot(md->input, t); + input_mt_report_slot_state(md->input, + MT_TOOL_FINGER, false); + } +} + +/******************************************************************************* + * FUNCTION: pt_input_register_device + * + * SUMMARY: Function to register input device + * protocol + * + * PARAMETERS: + * *input - pointer to input device structure + * max_slots - indicate max number of touch id + ******************************************************************************/ +static int pt_input_register_device(struct input_dev *input, int max_slots) +{ +#if (KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE) + input_mt_init_slots(input, max_slots, 0); +#else + input_mt_init_slots(input, max_slots); +#endif + return input_register_device(input); +} + +/******************************************************************************* + * FUNCTION: pt_init_function_ptrs + * + * SUMMARY: Function to init function pointer + * + * PARAMETERS: + * *md - pointer to touch data structure + ******************************************************************************/ +void pt_init_function_ptrs(struct pt_mt_data *md) +{ + md->mt_function.report_slot_liftoff = pt_report_slot_liftoff; + md->mt_function.final_sync = pt_final_sync; + md->mt_function.input_sync = NULL; + md->mt_function.input_report = pt_input_report; + md->mt_function.input_register_device = pt_input_register_device; +} + diff --git a/pt/pt_pen.c b/pt/pt_pen.c new file mode 100644 index 0000000000..679b7bc190 --- /dev/null +++ b/pt/pt_pen.c @@ -0,0 +1,572 @@ +#ifndef TTDL_KERNEL_SUBMISSION +/* + * pt_pen.c + * Parade TrueTouch(TM) Standard Product CapSense Reports Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2021 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.paradetech.com + */ + +#include "pt_regs.h" + +/******************************************************************************* + * FUNCTION: pt_pen_lift_all + * + * SUMMARY: Reports pen liftoff action + * + * PARAMETERS: + * *pend - pointer to pen data structure + ******************************************************************************/ +static void pt_pen_lift_all(struct pt_pen_data *pend) +{ + input_report_key(pend->input, BTN_STYLUS, 0); + input_report_key(pend->input, BTN_STYLUS2, 0); + input_report_key(pend->input, BTN_TOUCH, 0); + input_report_key(pend->input, BTN_TOOL_PEN, 0); + input_sync(pend->input); +} + +/******************************************************************************* + * FUNCTION: pt_get_pen_data + * + * SUMMARY: Gets axis of pen report + * + * PARAMETERS: + * *pend - pointer to pen data structure + * *xy_data - pointer to touch data + ******************************************************************************/ +static void pt_get_pen(struct pt_pen_data *pend, + struct pt_pen *pen, u8 *xy_data) +{ + struct device *dev = pend->dev; + struct pt_sysinfo *si = pend->si; + enum pt_pen_abs abs; + + for (abs = PT_PEN_X; abs < PT_PEN_NUM_ABS; abs++) { + if (!si->pen_abs[abs].report) + continue; + pt_get_touch_field(dev, &pen->abs[abs], + si->pen_abs[abs].size, + si->pen_abs[abs].max, + xy_data + si->tch_abs[abs].ofs, + si->pen_abs[abs].bofs); + pt_debug(dev, DL_DEBUG, "%s: get %s=%04X(%d)\n", + __func__, pt_pen_abs_string[abs], + pen->abs[abs], pen->abs[abs]); + } +} + + +/******************************************************************************* + * FUNCTION: pt_xy_worker + * + * SUMMARY: Read xy_data for current pen touch + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *pend - pointer to pen data structure + ******************************************************************************/ +static int pt_xy_worker(struct pt_pen_data *pend) +{ + struct pt_sysinfo *si = pend->si; + struct pt_pen pen; + bool tool; + + pt_get_pen(pend, &pen, si->xy_data + 3); + + tool = pen.abs[PT_PEN_IV] ? + BTN_TOOL_RUBBER : BTN_TOOL_PEN; + + input_report_abs(pend->input, ABS_X, pen.abs[PT_PEN_X]); + input_report_abs(pend->input, ABS_Y, pen.abs[PT_PEN_Y]); + input_report_abs(pend->input, ABS_PRESSURE, pen.abs[PT_PEN_P]); + input_report_key(pend->input, BTN_STYLUS, pen.abs[PT_PEN_BS]); + + if (si->pen_abs[PT_PEN_2ND_BS].report) + input_report_key(pend->input, BTN_STYLUS2, + pen.abs[PT_PEN_2ND_BS]); + + input_report_key(pend->input, BTN_TOUCH, pen.abs[PT_PEN_TS]); + input_report_key(pend->input, tool, pen.abs[PT_PEN_IR]); + + if (si->pen_abs[PT_PEN_X_TILT].report) + input_report_abs(pend->input, ABS_TILT_X, + pen.abs[PT_PEN_X_TILT]); + + if (si->pen_abs[PT_PEN_Y_TILT].report) + input_report_abs(pend->input, ABS_TILT_Y, + pen.abs[PT_PEN_Y_TILT]); + + input_sync(pend->input); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pen_attention + * + * SUMMARY: Wrapper function for pt_xy_worker() that register to TTDL attention + * list. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_pen_attention(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_pen_data *pend = &cd->pend; + int rc; + + if (pend->si->xy_mode[2] != pend->si->desc.pen_report_id) + return 0; + + /* core handles handshake */ + mutex_lock(&pend->pen_lock); + rc = pt_xy_worker(pend); + mutex_unlock(&pend->pen_lock); + if (rc < 0) + pt_debug(dev, DL_ERROR, + "%s: xy_worker error r=%d\n", __func__, rc); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_startup_attention + * + * SUMMARY: Wrapper function for pt_pen_lift_all() that register to TTDL + * attention list. + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_startup_attention(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_pen_data *pend = &cd->pend; + + mutex_lock(&pend->pen_lock); + pt_pen_lift_all(pend); + mutex_unlock(&pend->pen_lock); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pen_suspend_attention + * + * SUMMARY: Function for pen to enter suspend state that as following steps: + * 1) Lift pen touch + * 2) Set flag with suspend state + * 3) Decrese pm system count + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_pen_suspend_attention(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_pen_data *pend = &cd->pend; + + mutex_lock(&pend->pen_lock); + pt_pen_lift_all(pend); + pend->is_suspended = true; + mutex_unlock(&pend->pen_lock); + + pm_runtime_put(dev); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pen_resume_attention + * + * SUMMARY: Function for pen to leave suspend state that as following steps: + * 1) Increse pm system count + * 2) Clear suspend state flag + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_pen_resume_attention(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_pen_data *pend = &cd->pend; + + pm_runtime_get(dev); + + mutex_lock(&pend->pen_lock); + pend->is_suspended = false; + mutex_unlock(&pend->pen_lock); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pen_open + * + * SUMMARY: Open method for input device(pen) that sets up call back + * functions to TTDL attention list + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *input - pointer to input_dev structure + ******************************************************************************/ +static int pt_pen_open(struct input_dev *input) +{ + struct device *dev = input->dev.parent; + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_pen_data *pend = &cd->pend; + + pm_runtime_get_sync(dev); + + mutex_lock(&pend->pen_lock); + pend->is_suspended = false; + mutex_unlock(&pend->pen_lock); + + pt_debug(dev, DL_INFO, "%s: setup subscriptions\n", __func__); + + /* set up touch call back */ + _pt_subscribe_attention(dev, PT_ATTEN_IRQ, PT_PEN_NAME, + pt_pen_attention, PT_MODE_OPERATIONAL); + + /* set up startup call back */ + _pt_subscribe_attention(dev, PT_ATTEN_STARTUP, PT_PEN_NAME, + pt_startup_attention, 0); + + /* set up suspend call back */ + _pt_subscribe_attention(dev, PT_ATTEN_SUSPEND, PT_PEN_NAME, + pt_pen_suspend_attention, 0); + + /* set up resume call back */ + _pt_subscribe_attention(dev, PT_ATTEN_RESUME, PT_PEN_NAME, + pt_pen_resume_attention, 0); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_pen_close + * + * SUMMARY: Close method for input device(pen) that clears call back + * functions from TTDL attention list. + * + * PARAMETERS: + * *input - pointer to input_dev structure + ******************************************************************************/ +static void pt_pen_close(struct input_dev *input) +{ + struct device *dev = input->dev.parent; + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_pen_data *pend = &cd->pend; + + _pt_unsubscribe_attention(dev, PT_ATTEN_IRQ, PT_PEN_NAME, + pt_pen_attention, PT_MODE_OPERATIONAL); + + _pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_PEN_NAME, + pt_startup_attention, 0); + + _pt_unsubscribe_attention(dev, PT_ATTEN_SUSPEND, PT_PEN_NAME, + pt_pen_suspend_attention, 0); + + _pt_unsubscribe_attention(dev, PT_ATTEN_RESUME, PT_PEN_NAME, + pt_pen_resume_attention, 0); + + mutex_lock(&pend->pen_lock); + if (!pend->is_suspended) { + pm_runtime_put(dev); + pend->is_suspended = true; + } + mutex_unlock(&pend->pen_lock); +} + +/******************************************************************************* + * FUNCTION: pt_setup_input_device + * + * SUMMARY: Set up resolution, event signal capabilities and register input + * device for pen. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_setup_input_device(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_pen_data *pend = &cd->pend; + int i; + int rc; + u32 usage; + int max_x, max_y, max_p; + + pt_debug(dev, DL_INFO, "%s: Initialize event signals\n", + __func__); + __set_bit(EV_ABS, pend->input->evbit); + __set_bit(EV_KEY, pend->input->evbit); + + for (i = PT_PEN_X; i < PT_PEN_NUM_ABS; i++) { + usage = pt_pen_abs_field_map[i]; + + switch (usage) { + case HID_GD_X: + max_x = pend->si->sensing_conf_data.res_x; + input_set_abs_params(pend->input, + ABS_X, 0, max_x, 0, 0); + break; + case HID_GD_Y: + max_y = pend->si->sensing_conf_data.res_y; + input_set_abs_params(pend->input, + ABS_Y, 0, max_y, 0, 0); + break; + case HID_DG_TIPPRESSURE: + max_p = pend->si->sensing_conf_data.max_z; + input_set_abs_params(pend->input, + ABS_PRESSURE, 0, max_p, 0, 0); + break; + case HID_DG_INRANGE: + input_set_capability(pend->input, + EV_KEY, BTN_TOOL_PEN); + break; + case HID_DG_INVERT: + input_set_capability(pend->input, + EV_KEY, BTN_TOOL_RUBBER); + break; + case HID_DG_TILT_X: + max_x = pend->si->pen_abs[i].max; + input_set_abs_params(pend->input, + ABS_TILT_X, 0, max_x, 0, 0); + break; + case HID_DG_TILT_Y: + max_x = pend->si->pen_abs[i].max; + input_set_abs_params(pend->input, + ABS_TILT_Y, 0, max_x, 0, 0); + break; + case HID_DG_ERASER: + case HID_DG_TIPSWITCH: + input_set_capability(pend->input, + EV_KEY, BTN_TOUCH); + break; + case HID_DG_BARRELSWITCH: + input_set_capability(pend->input, + EV_KEY, BTN_STYLUS); + break; + case HID_DG_BARRELSWITCH2: + input_set_capability(pend->input, + EV_KEY, BTN_STYLUS2); + break; + } + } + + rc = input_register_device(pend->input); + if (rc < 0) + pt_debug(dev, DL_ERROR, + "%s: Error, failed register input device r=%d\n", + __func__, rc); + else + pend->input_device_registered = true; + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_setup_input_attention + * + * SUMMARY: Wrapper function for pt_setup_input_device() register to TTDL + * attention list. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_setup_input_attention(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_pen_data *pend = &cd->pend; + int rc; + + pend->si = _pt_request_sysinfo(dev); + if (!pend->si) + return -1; + + rc = pt_setup_input_device(dev); + + _pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, PT_PEN_NAME, + pt_setup_input_attention, 0); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pen_probe + * + * SUMMARY: The probe function for pen input device + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +int pt_pen_probe(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_pen_data *pend = &cd->pend; + struct pt_platform_data *pdata = dev_get_platdata(dev); + struct pt_pen_platform_data *pen_pdata; + int rc = 0; + + if (!pdata || !pdata->pen_pdata) { + pt_debug(dev, DL_ERROR, + "%s: Missing platform data\n", __func__); + rc = -ENODEV; + goto error_no_pdata; + } + pen_pdata = pdata->pen_pdata; + + mutex_init(&pend->pen_lock); + pend->dev = dev; + pend->pdata = pen_pdata; + + /* Create the input device and register it. */ + pt_debug(dev, DL_INFO, + "%s: Create the input device and register it\n", __func__); + pend->input = input_allocate_device(); + if (!pend->input) { + pt_debug(dev, DL_ERROR, + "%s: Error, failed to allocate input device\n", + __func__); + rc = -ENODEV; + goto error_alloc_failed; + } else + pend->input_device_allocated = true; + + if (pend->pdata->inp_dev_name) + pend->input->name = pend->pdata->inp_dev_name; + else + pend->input->name = PT_PEN_NAME; + scnprintf(pend->phys, sizeof(pend->phys), "%s/input%d", dev_name(dev), + cd->phys_num++); + pend->input->phys = pend->phys; + pend->input->dev.parent = pend->dev; + pend->input->open = pt_pen_open; + pend->input->close = pt_pen_close; + input_set_drvdata(pend->input, pend); + + /* get sysinfo */ + pend->si = _pt_request_sysinfo(dev); + + if (pend->si) { + rc = pt_setup_input_device(dev); + if (rc) + goto error_init_input; + } else { + pt_debug(dev, DL_ERROR, + "%s: Fail get sysinfo pointer from core p=%p\n", + __func__, pend->si); + _pt_subscribe_attention(dev, PT_ATTEN_STARTUP, + PT_PEN_NAME, pt_setup_input_attention, 0); + } + + return 0; + +error_init_input: + input_free_device(pend->input); + pend->input_device_allocated = false; +error_alloc_failed: +error_no_pdata: + pt_debug(dev, DL_ERROR, "%s failed.\n", __func__); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_pen_release + * + * SUMMARY: The release function for pen input device + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +int pt_pen_release(struct device *dev) +{ + struct pt_core_data *cd; + struct pt_pen_data *pend; + + /* Ensure valid pointers before de-referencing them */ + if (dev) { + cd = dev_get_drvdata(dev); + if (cd) + pend = &cd->pend; + else + return 0; + } else { + return 0; + } + + /* + * Second call this function may cause kernel panic if probe fail. + * Use input_device_registered & input_device_allocated variable to + * avoid unregister or free unavailable devive. + */ + if (pend && pend->input_device_registered) { + pend->input_device_registered = false; + input_unregister_device(pend->input); + /* Unregistering device will free the device too */ + pend->input_device_allocated = false; + } else if (pend && pend->input_device_allocated) { + pend->input_device_allocated = false; + input_free_device(pend->input); + _pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, + PT_PEN_NAME, pt_setup_input_attention, 0); + } + + return 0; +} +#endif /*!TTDL_KERNEL_SUBMISSION */ diff --git a/pt/pt_platform.c b/pt/pt_platform.c new file mode 100644 index 0000000000..f623f76c82 --- /dev/null +++ b/pt/pt_platform.c @@ -0,0 +1,1281 @@ +/* + * pt_platform.c + * Parade TrueTouch(TM) Standard Product Platform Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.paradetech.com + */ + +#include "pt_regs.h" +#include +#include +#ifdef PT_PTSBC_SUPPORT +#include +#endif + +#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE +/* FW for Panel ID = 0x00 */ +#include "pt_fw_pid00.h" +static struct pt_touch_firmware pt_firmware_pid00 = { + .img = pt_img_pid00, + .size = ARRAY_SIZE(pt_img_pid00), + .ver = pt_ver_pid00, + .vsize = ARRAY_SIZE(pt_ver_pid00), + .panel_id = 0x00, +}; + +/* FW for Panel ID = 0x01 */ +#include "pt_fw_pid01.h" +static struct pt_touch_firmware pt_firmware_pid01 = { + .img = pt_img_pid01, + .size = ARRAY_SIZE(pt_img_pid01), + .ver = pt_ver_pid01, + .vsize = ARRAY_SIZE(pt_ver_pid01), + .panel_id = 0x01, +}; + +/* FW for Panel ID not enabled (legacy) */ +#include "pt_fw.h" +static struct pt_touch_firmware pt_firmware = { + .img = pt_img, + .size = ARRAY_SIZE(pt_img), + .ver = pt_ver, + .vsize = ARRAY_SIZE(pt_ver), +}; +#else +/* FW for Panel ID not enabled (legacy) */ +static struct pt_touch_firmware pt_firmware = { + .img = NULL, + .size = 0, + .ver = NULL, + .vsize = 0, +}; +#endif + +#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE +/* TT Config for Panel ID = 0x00 */ +#include "pt_params_pid00.h" +static struct touch_settings pt_sett_param_regs_pid00 = { + .data = (uint8_t *)&pt_param_regs_pid00[0], + .size = ARRAY_SIZE(pt_param_regs_pid00), + .tag = 0, +}; + +static struct touch_settings pt_sett_param_size_pid00 = { + .data = (uint8_t *)&pt_param_size_pid00[0], + .size = ARRAY_SIZE(pt_param_size_pid00), + .tag = 0, +}; + +static struct pt_touch_config pt_ttconfig_pid00 = { + .param_regs = &pt_sett_param_regs_pid00, + .param_size = &pt_sett_param_size_pid00, + .fw_ver = ttconfig_fw_ver_pid00, + .fw_vsize = ARRAY_SIZE(ttconfig_fw_ver_pid00), + .panel_id = 0x00, +}; + +/* TT Config for Panel ID = 0x01 */ +#include "pt_params_pid01.h" +static struct touch_settings pt_sett_param_regs_pid01 = { + .data = (uint8_t *)&pt_param_regs_pid01[0], + .size = ARRAY_SIZE(pt_param_regs_pid01), + .tag = 0, +}; + +static struct touch_settings pt_sett_param_size_pid01 = { + .data = (uint8_t *)&pt_param_size_pid01[0], + .size = ARRAY_SIZE(pt_param_size_pid01), + .tag = 0, +}; + +static struct pt_touch_config pt_ttconfig_pid01 = { + .param_regs = &pt_sett_param_regs_pid01, + .param_size = &pt_sett_param_size_pid01, + .fw_ver = ttconfig_fw_ver_pid01, + .fw_vsize = ARRAY_SIZE(ttconfig_fw_ver_pid01), + .panel_id = 0x01, +}; + +/* TT Config for Panel ID not enabled (legacy)*/ +#include "pt_params.h" +static struct touch_settings pt_sett_param_regs = { + .data = (uint8_t *)&pt_param_regs[0], + .size = ARRAY_SIZE(pt_param_regs), + .tag = 0, +}; + +static struct touch_settings pt_sett_param_size = { + .data = (uint8_t *)&pt_param_size[0], + .size = ARRAY_SIZE(pt_param_size), + .tag = 0, +}; + +static struct pt_touch_config pt_ttconfig = { + .param_regs = &pt_sett_param_regs, + .param_size = &pt_sett_param_size, + .fw_ver = ttconfig_fw_ver, + .fw_vsize = ARRAY_SIZE(ttconfig_fw_ver), +}; +#else +/* TT Config for Panel ID not enabled (legacy)*/ +static struct pt_touch_config pt_ttconfig = { + .param_regs = NULL, + .param_size = NULL, + .fw_ver = NULL, + .fw_vsize = 0, +}; +#endif + +static struct pt_touch_firmware *pt_firmwares[] = { +#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE + &pt_firmware_pid00, + &pt_firmware_pid01, +#endif + NULL, /* Last item should always be NULL */ +}; + +static struct pt_touch_config *pt_ttconfigs[] = { +#ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_TTCONFIG_UPGRADE + &pt_ttconfig_pid00, + &pt_ttconfig_pid01, +#endif + NULL, /* Last item should always be NULL */ +}; + +struct pt_loader_platform_data _pt_loader_platform_data = { + .fw = &pt_firmware, + .ttconfig = &pt_ttconfig, + .fws = pt_firmwares, + .ttconfigs = pt_ttconfigs, + .flags = PT_LOADER_FLAG_NONE, +}; + +/******************************************************************************* + * FUNCTION: pt_xres + * + * SUMMARY: Toggles the reset gpio (TP_XRES) to perform a HW reset + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * *dev - pointer to Device structure + ******************************************************************************/ +int pt_xres(struct pt_core_platform_data *pdata, + struct device *dev) +{ + int rst_gpio = pdata->rst_gpio; + int rc = 0; + int ddi_rst_gpio = pdata->ddi_rst_gpio; + + pt_debug(dev, DL_WARN, "%s: 20ms HARD RESET on gpio=%d\n", + __func__, pdata->rst_gpio); + + /* Toggling only TP_XRES as DDI_XRES resets the entire part */ + gpio_set_value(rst_gpio, 1); + if (ddi_rst_gpio) + gpio_set_value(ddi_rst_gpio, 1); + usleep_range(3000, 4000); + gpio_set_value(rst_gpio, 0); + usleep_range(6000, 7000); + gpio_set_value(rst_gpio, 1); + if (ddi_rst_gpio) + gpio_set_value(ddi_rst_gpio, 1); + + /* Sleep to allow the DUT to boot */ + usleep_range(3000, 4000); + return rc; +} + +#ifdef PT_PINCTRL_EN +/******************************************************************************* + * FUNCTION: pt_pinctrl_init + * + * SUMMARY: Pinctrl method to obtain pin state handler for TP_RST, IRQ + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * *dev - pointer to Device structure + ******************************************************************************/ +static int pt_pinctrl_init(struct pt_core_platform_data *pdata, + struct device *dev) +{ + int ret = 0; + + pdata->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(pdata->pinctrl)) { + pt_debug(dev, DL_ERROR, + "Failed to get pinctrl, please check dts"); + ret = PTR_ERR(pdata->pinctrl); + goto err_pinctrl_get; + } + + pdata->pins_active = + pinctrl_lookup_state(pdata->pinctrl, "pmx_ts_active"); + if (IS_ERR_OR_NULL(pdata->pins_active)) { + pt_debug(dev, DL_ERROR, "pmx_ts_active not found"); + ret = PTR_ERR(pdata->pins_active); + goto err_pinctrl_lookup; + } + + pdata->pins_suspend = + pinctrl_lookup_state(pdata->pinctrl, "pmx_ts_suspend"); + if (IS_ERR_OR_NULL(pdata->pins_suspend)) { + pt_debug(dev, DL_ERROR, "pmx_ts_suspend not found"); + ret = PTR_ERR(pdata->pins_suspend); + goto err_pinctrl_lookup; + } + + pdata->pins_release = + pinctrl_lookup_state(pdata->pinctrl, "pmx_ts_release"); + if (IS_ERR_OR_NULL(pdata->pins_release)) { + pt_debug(dev, DL_ERROR, "pmx_ts_release not found"); + ret = PTR_ERR(pdata->pins_release); + goto err_pinctrl_lookup; + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(pdata->pinctrl); + +err_pinctrl_get: + pdata->pinctrl = NULL; + pdata->pins_release = NULL; + pdata->pins_suspend = NULL; + pdata->pins_active = NULL; + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_pinctrl_select_normal + * + * SUMMARY: Pinctrl method to configure drive mode for TP_RST, IRQ - normal + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * *dev - pointer to Device structure + ******************************************************************************/ +static int pt_pinctrl_select_normal(struct pt_core_platform_data *pdata, + struct device *dev) +{ + int ret = 0; + + if (pdata->pinctrl && pdata->pins_active) { + ret = pinctrl_select_state(pdata->pinctrl, pdata->pins_active); + if (ret < 0) { + pt_debug(dev, DL_ERROR, "Set normal pin state error=%d", + ret); + } + } + + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_pinctrl_select_suspend + * + * SUMMARY: Pinctrl method to configure drive mode for TP_RST, IRQ - suspend + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * *dev - pointer to Device structure + ******************************************************************************/ +static int pt_pinctrl_select_suspend(struct pt_core_platform_data *pdata, + struct device *dev) +{ + int ret = 0; + + if (pdata->pinctrl && pdata->pins_suspend) { + ret = pinctrl_select_state(pdata->pinctrl, pdata->pins_suspend); + if (ret < 0) { + pt_debug(dev, DL_ERROR, + "Set suspend pin state error=%d", ret); + } + } + + return ret; +} + +/******************************************************************************* + * FUNCTION: pt_pinctrl_select_release + * + * SUMMARY: Pinctrl method to configure drive mode for TP_RST, IRQ - release + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * *dev - pointer to Device structure + ******************************************************************************/ +static int pt_pinctrl_select_release(struct pt_core_platform_data *pdata, + struct device *dev) +{ + int ret = 0; + + if (pdata->pinctrl) { + if (IS_ERR_OR_NULL(pdata->pins_release)) { + devm_pinctrl_put(pdata->pinctrl); + pdata->pinctrl = NULL; + } else { + ret = pinctrl_select_state(pdata->pinctrl, + pdata->pins_release); + if (ret < 0) + pt_debug(dev, DL_ERROR, + "Set gesture pin state error=%d", ret); + } + } + + return ret; +} +#endif /* PT_PINCTRL_EN */ + +#ifdef PT_REGULATOR_EN +#define PT_VCC_MIN_UV (2800000) +#define PT_VCC_MAX_UV (3300000) +#define PT_VDDI_MIN_UV (1800000) +#define PT_VDDI_MAX_UV (1900000) +/******************************************************************************* + * FUNCTION: pt_regulator_init + * + * SUMMARY: With regulator framework to get regulator handle and setup voltage + * level. + * + * NOTE: The function only contains setup for VCC and VDDI since AVDD AVEE is + * usually used by TDDI products while the power is setup on other driver. + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * *dev - pointer to Device structure + ******************************************************************************/ +static int pt_regulator_init(struct pt_core_platform_data *pdata, + struct device *dev) +{ + int rc = 0; + + pdata->vcc = devm_regulator_get(dev, "vcc"); + if (IS_ERR(pdata->vcc)) { + rc = PTR_ERR(pdata->vcc); + pt_debug(dev, DL_ERROR, "get vcc regulator failed,rc=%d", rc); + return rc; + } + + if (regulator_count_voltages(pdata->vcc) > 0) { + rc = regulator_set_voltage(pdata->vcc, PT_VCC_MIN_UV, + PT_VCC_MAX_UV); + if (rc) { + pt_debug(dev, DL_ERROR, + "set vcc regulator failed rc=%d", rc); + goto error_set_vcc; + } + } + + pdata->vddi = devm_regulator_get(dev, "vddi"); + if (IS_ERR(pdata->vddi)) { + rc = PTR_ERR(pdata->vddi); + pt_debug(dev, DL_ERROR, "get vddi regulator failed,rc=%d", rc); + goto error_get_vcc; + } + + if (regulator_count_voltages(pdata->vddi) > 0) { + rc = regulator_set_voltage(pdata->vddi, PT_VDDI_MIN_UV, + PT_VDDI_MAX_UV); + if (rc) { + pt_debug(dev, DL_ERROR, + "set vddi regulator failed rc=%d", rc); + goto error_set_vddi; + } + } + + return 0; + +error_set_vddi: + devm_regulator_put(pdata->vddi); +error_get_vcc: + /* + * regulator_set_voltage always select minimum legal voltage between + * min_uV and max_uV. To set the minuV to 0 means to restore the default + * value of regulator. Since regulator_set_voltage is the part to + * release regulator source, it's not necessary to check the returned + * value of it. + */ + if (regulator_count_voltages(pdata->vcc) > 0) + regulator_set_voltage(pdata->vcc, 0, PT_VCC_MAX_UV); +error_set_vcc: + devm_regulator_put(pdata->vcc); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_setup_power_by_regulator + * + * SUMMARY: With regulator framework to set power on/off. + * + * NOTE: The function only contains setup for VCC and VDDI since AVDD AVEE is + * usually used by TDDI products while the power is setup on other driver. + * + * NOTE: The timing sequence is the EXAMPLE ONLY for TT7XXX: + * power up order : VDDI, VCC + * power down order : VCC, VDDI + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * on - flag to decide power state,PT_MT_POWER_ON/PT_MT_POWER_OFF + * *dev - pointer to Device structure + ******************************************************************************/ +static int pt_setup_power_by_regulator(struct pt_core_platform_data *pdata, + int on, struct device *dev) +{ + int rc = 0; + + if (IS_ERR(pdata->vddi) || IS_ERR(pdata->vcc)) { + pt_debug(dev, DL_ERROR, "vddi or vcc is not valid\n"); + return -EINVAL; + } + + if (on == PT_MT_POWER_ON) { + rc = regulator_enable(pdata->vddi); + if (rc) { + pt_debug(dev, DL_ERROR, + "enable vddi regulator failed,rc=%d", rc); + } + /* Ensure the power goes stable */ + usleep_range(3000, 4000); + + rc = regulator_enable(pdata->vcc); + if (rc) { + pt_debug(dev, DL_ERROR, + "enable vcc regulator failed,rc=%d", rc); + } + /* Ensure the power goes stable */ + usleep_range(3000, 4000); + } else { + rc = regulator_disable(pdata->vcc); + if (rc) { + pt_debug(dev, DL_ERROR, + "disable vcc regulator failed,rc=%d", rc); + } + rc = regulator_disable(pdata->vddi); + if (rc) { + pt_debug(dev, DL_ERROR, + "disable vddi regulator failed,rc=%d", rc); + } + /* Ensure the power ramp down completely */ + usleep_range(10000, 12000); + } + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_regulator_release + * + * SUMMARY: With regulator framework to release regulator resource + * + * NOTE: The regulator MUST be disabled before this call. + * NOTE: The function only contains setup for VCC and VDDI since AVDD AVEE is + * usually used by TDDI products while the power is setup on other driver. + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * *dev - pointer to Device structure + ******************************************************************************/ +static int pt_regulator_release(struct pt_core_platform_data *pdata, + struct device *dev) +{ + if (IS_ERR(pdata->vddi) || IS_ERR(pdata->vcc)) + return -EINVAL; + + /* + * regulator_set_voltage always select minimum legal voltage between + * min_uV and max_uV. To set the minuV to 0 means to restore the default + * value of regulator. Since regulator_set_voltage is the part to + * release regulator source, it's not necessary to check the returned + * value of it. + */ + if (regulator_count_voltages(pdata->vddi) > 0) + regulator_set_voltage(pdata->vddi, 0, PT_VDDI_MAX_UV); + devm_regulator_put(pdata->vddi); + + if (regulator_count_voltages(pdata->vcc) > 0) + regulator_set_voltage(pdata->vcc, 0, PT_VCC_MAX_UV); + devm_regulator_put(pdata->vcc); + + return 0; +} +#endif /* PT_REGULATOR_EN */ +/******************************************************************************* + * FUNCTION: pt_init + * + * SUMMARY: Set up/free gpios for TP_RST, IRQ, DDI_RST. + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * on - flag to set up or free gpios(0:free; !0:set up) + * *dev - pointer to Device structure + ******************************************************************************/ +int pt_init(struct pt_core_platform_data *pdata, + int on, struct device *dev) +{ + int rst_gpio = pdata->rst_gpio; + int irq_gpio = pdata->irq_gpio; + int ddi_rst_gpio = pdata->ddi_rst_gpio; + int rc = 0; + + if (on) { +#ifdef PT_PINCTRL_EN + rc = pt_pinctrl_init(pdata, dev); + if (!rc) { + pt_pinctrl_select_normal(pdata, dev); + } else { + pt_debug(dev, DL_ERROR, + "%s: Failed to request pinctrl\n", __func__); + } +#endif +#ifdef PT_REGULATOR_EN + rc = pt_regulator_init(pdata, dev); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Failed requesting regulator rc=%d", + __func__, rc); + } +#endif + } + + if (on && rst_gpio) { + /* Configure RST GPIO */ + pt_debug(dev, DL_WARN, "%s: Request RST GPIO %d", + __func__, rst_gpio); + rc = gpio_request(rst_gpio, NULL); + if (rc < 0) { + gpio_free(rst_gpio); + rc = gpio_request(rst_gpio, NULL); + } + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: Failed requesting RST GPIO %d\n", + __func__, rst_gpio); + goto fail_rst_gpio; + } else { + /* + * Set the GPIO direction and the starting level + * The start level is high because the DUT needs + * to stay in reset during power up. + */ + rc = gpio_direction_output(rst_gpio, 1); + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: Output Setup ERROR: RST GPIO %d\n", + __func__, rst_gpio); + goto fail_rst_gpio; + } + } + } + + if (on && irq_gpio) { + /* Configure IRQ GPIO */ + pt_debug(dev, DL_WARN, "%s: Request IRQ GPIO %d", + __func__, irq_gpio); + rc = gpio_request(irq_gpio, NULL); + if (rc < 0) { + gpio_free(irq_gpio); + rc = gpio_request(irq_gpio, NULL); + } + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: Failed requesting IRQ GPIO %d\n", + __func__, irq_gpio); + goto fail_irq_gpio; + } else { + /* Set the GPIO direction */ + rc = gpio_direction_input(irq_gpio); + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: Input Setup ERROR: IRQ GPIO %d\n", + __func__, irq_gpio); + goto fail_irq_gpio; + } + } + } + + if (on && ddi_rst_gpio) { + /* Configure DDI RST GPIO */ + pt_debug(dev, DL_WARN, "%s: Request DDI RST GPIO %d", + __func__, ddi_rst_gpio); + rc = gpio_request(ddi_rst_gpio, NULL); + if (rc < 0) { + gpio_free(ddi_rst_gpio); + rc = gpio_request(ddi_rst_gpio, NULL); + } + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: Failed requesting DDI RST GPIO %d\n", + __func__, ddi_rst_gpio); + goto fail_ddi_rst_gpio; + } else { + /* Set the GPIO direction and the starting level */ + rc = gpio_direction_output(ddi_rst_gpio, 0); + if (rc < 0) { + pt_debug(dev, DL_ERROR, + "%s: Output Setup ERROR: RST GPIO %d\n", + __func__, ddi_rst_gpio); + goto fail_ddi_rst_gpio; + } + } + } + + if (!on) { + /* "on" not set, therefore free all gpio's */ + if (ddi_rst_gpio) + gpio_free(ddi_rst_gpio); + if (irq_gpio) + gpio_free(irq_gpio); + if (rst_gpio) + gpio_free(rst_gpio); +#ifdef PT_REGULATOR_EN + pt_regulator_release(pdata, dev); +#endif +#ifdef PT_PINCTRL_EN + pt_pinctrl_select_release(pdata, dev); +#endif + } + + /* All GPIO's created successfully */ + goto success; + +fail_ddi_rst_gpio: + pt_debug(dev, DL_ERROR, + "%s: ERROR - GPIO setup Failure, freeing DDI_XRES GPIO %d\n", + __func__, ddi_rst_gpio); + gpio_free(ddi_rst_gpio); +fail_irq_gpio: + pt_debug(dev, DL_ERROR, + "%s: ERROR - GPIO setup Failure, freeing IRQ GPIO %d\n", + __func__, irq_gpio); + gpio_free(irq_gpio); +fail_rst_gpio: + pt_debug(dev, DL_ERROR, + "%s: ERROR - GPIO setup Failure, freeing TP_XRES GPIO %d\n", + __func__, rst_gpio); + gpio_free(rst_gpio); + +success: + pt_debug(dev, DL_INFO, + "%s: SUCCESS - Configured DDI_XRES GPIO %d, IRQ GPIO %d, TP_XRES GPIO %d\n", + __func__, ddi_rst_gpio, irq_gpio, rst_gpio); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_irq_stat + * + * SUMMARY: Obtain the level state of IRQ gpio. + * + * RETURN: + * level state of IRQ gpio + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * *dev - pointer to Device structure + ******************************************************************************/ +int pt_irq_stat(struct pt_core_platform_data *pdata, + struct device *dev) +{ + return gpio_get_value(pdata->irq_gpio); +} + +#ifdef PT_DETECT_HW +/******************************************************************************* + * FUNCTION: pt_detect + * + * SUMMARY: Detect the I2C device by reading one byte(FW sentiel) after the + * reset operation. + * + * RETURN: + * 0 - detected + * !0 - undetected + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * *dev - pointer to Device structure + * read - pointer to the function to perform a read operation + ******************************************************************************/ +int pt_detect(struct pt_core_platform_data *pdata, + struct device *dev, pt_platform_read read) +{ + int retry = 3; + int rc; + char buf[1]; + + while (retry--) { + /* Perform reset, wait for 100 ms and perform read */ + pt_debug(dev, DL_WARN, "%s: Performing a reset\n", + __func__); + pdata->xres(pdata, dev); + msleep(100); + rc = read(dev, buf, 1); + if (!rc) + return 0; + + pt_debug(dev, DL_ERROR, "%s: Read unsuccessful, try=%d\n", + __func__, 3 - retry); + } + + return rc; +} +#endif + +#ifndef PT_REGULATOR_EN +/******************************************************************************* + * FUNCTION: pt_setup_power_by_gpio + * + * SUMMARY: With GPIOs to control LDO directly to set power on/off. + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * on - flag to decide power state,PT_MT_POWER_ON/PT_MT_POWER_OFF + * *dev - pointer to Device structure + ******************************************************************************/ +static int pt_setup_power_by_gpio(struct pt_core_platform_data *pdata, + int on, struct device *dev) +{ + int rc = 0; + int en_vcc = pdata->vcc_gpio; + int en_vddi = pdata->vddi_gpio; + int en_avdd = pdata->avdd_gpio; + int en_avee = pdata->avee_gpio; + + if (on == PT_MT_POWER_ON) { + /* + * Enable GPIOs to turn on voltage regulators to pwr up DUT + * - TC device power up order: VDDI, VCC, AVDD, AVEE + * - TT device power up order: VDDI, VCC + * NOTE: VDDI must be stable for >10ms before XRES is released + */ + pt_debug(dev, DL_INFO, + "%s: Enable defined pwr: VDDI, VCC, AVDD, AVEE\n", __func__); + + /* Turn on VDDI [Digital Interface] (+1.8v) */ + if (pdata->vddi_gpio) { + rc = gpio_request(en_vddi, NULL); + if (rc < 0) { + gpio_free(en_vddi); + rc = gpio_request(en_vddi, NULL); + } + if (rc < 0) { + pr_err("%s: Failed requesting VDDI GPIO %d\n", + __func__, en_vddi); + } + rc = gpio_direction_output(en_vddi, 1); + if (rc) + pr_err("%s: setcfg for VDDI GPIO %d failed\n", + __func__, en_vddi); + gpio_free(en_vddi); + usleep_range(3000, 4000); + } + + /* Turn on VCC */ + if (pdata->vcc_gpio) { + rc = gpio_request(en_vcc, NULL); + if (rc < 0) { + gpio_free(en_vcc); + rc = gpio_request(en_vcc, NULL); + } + if (rc < 0) { + pr_err("%s: Failed requesting VCC GPIO %d\n", + __func__, en_vcc); + } + rc = gpio_direction_output(en_vcc, 1); + if (rc) + pr_err("%s: setcfg for VCC GPIO %d failed\n", + __func__, en_vcc); + gpio_free(en_vcc); + usleep_range(3000, 4000); + } + + /* Turn on AVDD (+5.0v) */ + if (pdata->avdd_gpio) { + rc = gpio_request(en_avdd, NULL); + if (rc < 0) { + gpio_free(en_avdd); + rc = gpio_request(en_avdd, NULL); + } + if (rc < 0) { + pr_err("%s: Failed requesting AVDD GPIO %d\n", + __func__, en_avdd); + } + rc = gpio_direction_output(en_avdd, 1); + if (rc) + pr_err("%s: setcfg for AVDD GPIO %d failed\n", + __func__, en_avdd); + gpio_free(en_avdd); + usleep_range(3000, 4000); + } + + /* Turn on AVEE (-5.0v) */ + if (pdata->avee_gpio) { + rc = gpio_request(en_avee, NULL); + if (rc < 0) { + gpio_free(en_avee); + rc = gpio_request(en_avee, NULL); + } + if (rc < 0) { + pr_err("%s: Failed requesting AVEE GPIO %d\n", + __func__, en_avee); + } + rc = gpio_direction_output(en_avee, 1); + if (rc) + pr_err("%s: setcfg for AVEE GPIO %d failed\n", + __func__, en_avee); + gpio_free(en_avee); + usleep_range(3000, 4000); + } + } else { + /* + * Disable GPIOs to turn off voltage regulators to pwr down + * TC device The power down order is: AVEE, AVDD, VDDI + * TT device The power down order is: VCC, VDDI + * + * Note:Turn off some of regulators may effect display + * parts for TDDI chip + */ + pt_debug(dev, DL_INFO, + "%s: Turn off defined pwr: VCC, AVEE, AVDD, VDDI\n", __func__); + + /* Turn off VCC */ + if (pdata->vcc_gpio) { + rc = gpio_request(en_vcc, NULL); + if (rc < 0) { + gpio_free(en_vcc); + rc = gpio_request(en_vcc, NULL); + } + if (rc < 0) { + pr_err("%s: Failed requesting VCC GPIO %d\n", + __func__, en_vcc); + } + rc = gpio_direction_output(en_vcc, 0); + if (rc) + pr_err("%s: setcfg for VCC GPIO %d failed\n", + __func__, en_vcc); + gpio_free(en_vcc); + } + + /* Turn off AVEE (-5.0v) */ + if (pdata->avee_gpio) { + rc = gpio_request(en_avee, NULL); + if (rc < 0) { + gpio_free(en_avee); + rc = gpio_request(en_avee, NULL); + } + if (rc < 0) { + pr_err("%s: Failed requesting AVEE GPIO %d\n", + __func__, en_avee); + } + rc = gpio_direction_output(en_avee, 0); + if (rc) + pr_err("%s: setcfg for AVEE GPIO %d failed\n", + __func__, en_avee); + gpio_free(en_avee); + } + + /* Turn off AVDD (+5.0v) */ + if (pdata->avdd_gpio) { + rc = gpio_request(en_avdd, NULL); + if (rc < 0) { + gpio_free(en_avdd); + rc = gpio_request(en_avdd, NULL); + } + if (rc < 0) { + pr_err("%s: Failed requesting AVDD GPIO %d\n", + __func__, en_avdd); + } + rc = gpio_direction_output(en_avdd, 0); + if (rc) + pr_err("%s: setcfg for AVDD GPIO %d failed\n", + __func__, en_avdd); + gpio_free(en_avdd); + } + + /* Turn off VDDI [Digital Interface] (+1.8v) */ + if (pdata->vddi_gpio) { + rc = gpio_request(en_vddi, NULL); + if (rc < 0) { + gpio_free(en_vddi); + rc = gpio_request(en_vddi, NULL); + } + if (rc < 0) { + pr_err("%s: Failed requesting VDDI GPIO %d\n", + __func__, en_vddi); + } + rc = gpio_direction_output(en_vddi, 0); + if (rc) + pr_err("%s: setcfg for VDDI GPIO %d failed\n", + __func__, en_vddi); + gpio_free(en_vddi); + usleep_range(10000, 12000); + } + } + + return rc; +} +#endif /* PT_REGULATOR_EN */ +/******************************************************************************* + * FUNCTION: pt_setup_power + * + * SUMMARY: Turn on/turn off voltage regulator + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *pdata - pointer to core platform data + * on - flag to decide power state,PT_MT_POWER_ON/PT_MT_POWER_OFF + * *dev - pointer to device + ******************************************************************************/ +int pt_setup_power(struct pt_core_platform_data *pdata, int on, + struct device *dev) +{ + int rc = 0; + + /* + * For TDDI parts, force part into RESET by holding DDI XRES + * while powering it up + */ + if (pdata->ddi_rst_gpio) + gpio_set_value(pdata->ddi_rst_gpio, 0); + + /* + * Force part into RESET by holding XRES#(TP_XRES) + * while powering it up + */ + if (pdata->rst_gpio) + gpio_set_value(pdata->rst_gpio, 0); + +#ifdef PT_REGULATOR_EN + rc = pt_setup_power_by_regulator(pdata, on, dev); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Failed setup power by regulator rc=%d", + __func__, rc); + } +#else /* PT_REGULATOR_EN */ + rc = pt_setup_power_by_gpio(pdata, on, dev); + if (rc) { + pt_debug(dev, DL_ERROR, + "%s: Failed setup power by gpio rc=%d", + __func__, rc); + } +#endif /* PT_REGULATOR_EN */ + + /* Force part out of RESET by releasing XRES#(TP_XRES) */ + if (pdata->rst_gpio) + gpio_set_value(pdata->rst_gpio, 1); + + /* Force part out of RESET by releasing DDI XRES */ + if (pdata->ddi_rst_gpio) + gpio_set_value(pdata->ddi_rst_gpio, 1); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_wakeup + * + * SUMMARY: Resume power for "power on/off" sleep strategy which against to + * "deepsleep" strategy. + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * *dev - pointer to Device structure + * *ignore_irq - pointer to atomic structure to allow the host ignoring false + * IRQ during power up + ******************************************************************************/ +static int pt_wakeup(struct pt_core_platform_data *pdata, + struct device *dev, atomic_t *ignore_irq) +{ + int rc = 0; + +#ifdef PT_PINCTRL_EN + pt_pinctrl_select_normal(pdata, dev); +#endif + rc = pt_setup_power(pdata, PT_MT_POWER_ON, dev); + if (rc) + pt_debug(dev, DL_ERROR, "%s: Failed setup power\n", __func__); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_sleep + * + * SUMMARY: Suspend power for "power on/off" sleep strategy which against to + * "deepsleep" strategy. + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * *dev - pointer to Device structure + * *ignore_irq - pointer to atomic structure to allow the host ignoring false + * IRQ during power down + ******************************************************************************/ +static int pt_sleep(struct pt_core_platform_data *pdata, + struct device *dev, atomic_t *ignore_irq) +{ + int rc = 0; + + rc = pt_setup_power(pdata, PT_MT_POWER_OFF, dev); + if (rc) + pt_debug(dev, DL_ERROR, "%s: Failed setup power\n", __func__); + +#ifdef PT_PINCTRL_EN + pt_pinctrl_select_suspend(pdata, dev); +#endif + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_power + * + * SUMMARY: Wrapper function to resume/suspend power with function + * pt_wakeup()/pt_sleep(). + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * on - flag to remsume/suspend power(0:resume; 1:suspend) + * *dev - pointer to Device structure + * *ignore_irq - pointer to atomic structure to allow the host ignoring false + * IRQ during power up/down + ******************************************************************************/ +int pt_power(struct pt_core_platform_data *pdata, + int on, struct device *dev, atomic_t *ignore_irq) +{ + if (on) + return pt_wakeup(pdata, dev, ignore_irq); + + return pt_sleep(pdata, dev, ignore_irq); +} + +#ifdef PT_PTSBC_SUPPORT + +static struct workqueue_struct *parade_wq; +static u32 int_handle; + +/******************************************************************************* + * FUNCTION: pt_irq_work_function + * + * SUMMARY: Work function for queued IRQ activity + * + * RETURN: Void + * + * PARAMETERS: + * *work - pointer to work structure + ******************************************************************************/ +static void pt_irq_work_function(struct work_struct *work) +{ + struct pt_core_data *cd = container_of(work, + struct pt_core_data, irq_work); + + pt_irq(cd->irq, (void *)cd); +} + +/******************************************************************************* + * FUNCTION: pt_irq_wrapper + * + * SUMMARY: Wrapper function for IRQ to queue the irq_work function + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *handle - void pointer to contain the core_data pointer + ******************************************************************************/ +peint_handle *pt_irq_wrapper(void *handle) +{ + struct pt_core_data *cd = (struct pt_core_data *)handle; + + queue_work(parade_wq, &cd->irq_work); + return 0; +} +#endif /* PT_PTSBC_SUPPORT */ + +/******************************************************************************* + * FUNCTION: pt_setup_irq + * + * SUMMARY: Configure the IRQ GPIO used by the TT DUT + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *pdata - pointer to core platform data + * on - flag to setup interrupt process work(PT_MT_IRQ_FREE/) + * *dev - pointer to device + ******************************************************************************/ +int pt_setup_irq(struct pt_core_platform_data *pdata, int on, + struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + unsigned long irq_flags; + int rc = 0; + + if (on == PT_MT_IRQ_REG) { + /* + * When TTDL has direct access to the GPIO the irq_stat function + * will be defined and the gpio_to_irq conversion must be + * performed. e.g. For CHROMEOS this is not the case, the irq is + * passed in directly. + */ + if (pdata->irq_stat) { + /* Initialize IRQ */ + dev_vdbg(dev, "%s: Value Passed to gpio_to_irq =%d\n", + __func__, pdata->irq_gpio); + cd->irq = gpio_to_irq(pdata->irq_gpio); + dev_vdbg(dev, + "%s: Value Returned from gpio_to_irq =%d\n", + __func__, cd->irq); + } + if (cd->irq < 0) + return -EINVAL; + + cd->irq_enabled = true; + + pt_debug(dev, DL_INFO, "%s: initialize threaded irq=%d\n", + __func__, cd->irq); + + if (pdata->level_irq_udelay > 0) +#ifdef PT_PTSBC_SUPPORT + /* use level triggered interrupts */ + irq_flags = TRIG_LEVL_LOW; + else + /* use edge triggered interrupts */ + irq_flags = TRIG_EDGE_NEGATIVE; +#else + /* use level triggered interrupts */ + irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; + else + /* use edge triggered interrupts */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; +#endif /* PT_PTSBC_SUPPORT */ + +#ifdef PT_PTSBC_SUPPORT + /* Adding new work queue to cd struct */ + INIT_WORK(&cd->irq_work, pt_irq_work_function); + + parade_wq = create_singlethread_workqueue("parade_wq"); + if (!parade_wq) + pt_debug(dev, DL_ERROR, "%s Create workqueue failed.\n", + __func__); + + int_handle = sw_gpio_irq_request(pdata->irq_gpio, irq_flags, + (peint_handle)pt_irq_wrapper, cd); + if (!int_handle) { + pt_debug(dev, DL_ERROR, + "%s: PARADE could not request irq\n", __func__); + rc = -1; + } else { + rc = 0; + /* clk=0: 32Khz; clk=1: 24Mhz*/ + ctp_set_int_port_rate(pdata->irq_gpio, 1); + /* + * Debounce INT Line by clock divider: 2^n. E.g. The + * para:0x03 means the period of interrupt controller is + * 0.33 us = (2^3)/24. It has ability to measure the + * high/low width of the pulse bigger than 1 us. + */ + ctp_set_int_port_deb(pdata->irq_gpio, 0x03); + pt_debug(cd->dev, DL_INFO, + "%s: Parade sw_gpio_irq_request SUCCESS\n", + __func__); + } +#else + rc = request_threaded_irq(cd->irq, NULL, pt_irq, + irq_flags, dev_name(dev), cd); + if (rc < 0) + pt_debug(dev, DL_ERROR, + "%s: Error, could not request irq\n", __func__); +#endif /* PT_PTSBC_SUPPORT */ + } else { + disable_irq_nosync(cd->irq); +#ifndef PT_PTSBC_SUPPORT + free_irq(cd->irq, cd); +#else + sw_gpio_irq_free(int_handle); + cancel_work_sync(&cd->irq_work); + destroy_workqueue(parade_wq); +#endif /* PT_PTSBC_SUPPORT */ + } + return rc; +} diff --git a/pt/pt_proximity.c b/pt/pt_proximity.c new file mode 100644 index 0000000000..ae2998b580 --- /dev/null +++ b/pt/pt_proximity.c @@ -0,0 +1,781 @@ +#ifndef TTDL_KERNEL_SUBMISSION +/* + * pt_proximity.c + * Parade TrueTouch(TM) Standard Product Proximity Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.paradetech.com + * + */ + +#include "pt_regs.h" + +#define PT_PROXIMITY_NAME "pt_proximity" + +/* Timeout value in ms. */ +#define PT_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT 1000 + +#define PT_PROXIMITY_ON 0 +#define PT_PROXIMITY_OFF 1 + +/******************************************************************************* + * FUNCTION: get_prox_data + * + * SUMMARY: Gets pointer of proximity data from core data structure + * + * RETURN: + * pointer of pt_proximity_data structure in core data structure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static inline struct pt_proximity_data *get_prox_data(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + return &cd->pd; +} + +/******************************************************************************* + * FUNCTION: pt_report_proximity + * + * SUMMARY: Reports proximity event + * + * PARAMETERS: + * *pd - pointer to proximity data structure + * on - state of proximity(true:on; false:off) + ******************************************************************************/ +static void pt_report_proximity(struct pt_proximity_data *pd, + bool on) +{ + int val = on ? PT_PROXIMITY_ON : PT_PROXIMITY_OFF; + + input_report_abs(pd->input, ABS_DISTANCE, val); + input_sync(pd->input); +} + +/******************************************************************************* + * FUNCTION: pt_get_touch_hdr + * + * SUMMARY: Gets header of touch report + * + * PARAMETERS: + * *pd - pointer to proximity data structure + * *touch - pointer to pt_touch structure + * *xy_mode - pointer to input mode data + ******************************************************************************/ +static void pt_get_touch_hdr(struct pt_proximity_data *pd, + struct pt_touch *touch, u8 *xy_mode) +{ + struct device *dev = pd->dev; + struct pt_sysinfo *si = pd->si; + enum pt_tch_hdr hdr; + + for (hdr = PT_TCH_TIME; hdr < PT_TCH_NUM_HDR; hdr++) { + if (!si->tch_hdr[hdr].report) + continue; + pt_get_touch_field(dev, &touch->hdr[hdr], + si->tch_hdr[hdr].size, + si->tch_hdr[hdr].max, + xy_mode + si->tch_hdr[hdr].ofs, + si->tch_hdr[hdr].bofs); + pt_debug(dev, DL_INFO, "%s: get %s=%04X(%d)\n", + __func__, pt_tch_hdr_string[hdr], + touch->hdr[hdr], touch->hdr[hdr]); + } +} + +/******************************************************************************* + * FUNCTION: pt_get_touch + * + * SUMMARY: Parse proximity touch event + * + * PARAMETERS: + * *pd - pointer to proximity data structure + * *touch - pointer to touch structure + * xy_data - pointer to touch data + ******************************************************************************/ +static void pt_get_touch(struct pt_proximity_data *pd, + struct pt_touch *touch, u8 *xy_data) +{ + struct device *dev = pd->dev; + struct pt_sysinfo *si = pd->si; + enum pt_tch_abs abs; + + for (abs = PT_TCH_X; abs < PT_TCH_NUM_ABS; abs++) { + if (!si->tch_abs[abs].report) + continue; + pt_get_touch_field(dev, &touch->abs[abs], + si->tch_abs[abs].size, + si->tch_abs[abs].max, + xy_data + si->tch_abs[abs].ofs, + si->tch_abs[abs].bofs); + pt_debug(dev, DL_INFO, "%s: get %s=%04X(%d)\n", + __func__, pt_tch_abs_string[abs], + touch->abs[abs], touch->abs[abs]); + } + + pt_debug(dev, DL_INFO, "%s: x=%04X(%d) y=%04X(%d)\n", + __func__, touch->abs[PT_TCH_X], touch->abs[PT_TCH_X], + touch->abs[PT_TCH_Y], touch->abs[PT_TCH_Y]); +} + +/******************************************************************************* + * FUNCTION: pt_get_proximity_touch + * + * SUMMARY: Parse and report proximity touch event + * + * PARAMETERS: + * *pd - pointer to proximity data structure + * *touch - pointer to pt_touch structure + ******************************************************************************/ +static void pt_get_proximity_touch(struct pt_proximity_data *pd, + struct pt_touch *tch, int num_cur_tch) +{ + struct pt_sysinfo *si = pd->si; + int i; + + for (i = 0; i < num_cur_tch; i++) { + pt_get_touch(pd, tch, si->xy_data + + (i * si->desc.tch_record_size)); + + /* Check for proximity event */ + if (tch->abs[PT_TCH_O] == PT_OBJ_PROXIMITY) { + if (tch->abs[PT_TCH_E] == PT_EV_TOUCHDOWN) + pt_report_proximity(pd, true); + else if (tch->abs[PT_TCH_E] == PT_EV_LIFTOFF) + pt_report_proximity(pd, false); + break; + } + } +} + +/******************************************************************************* + * FUNCTION: pt_xy_worker + * + * SUMMARY: Read xy_data for all current touches + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *pd - pointer to proximity data structure + ******************************************************************************/ +static int pt_xy_worker(struct pt_proximity_data *pd) +{ + struct device *dev = pd->dev; + struct pt_sysinfo *si = pd->si; + struct pt_touch tch; + u8 num_cur_tch; + + pt_get_touch_hdr(pd, &tch, si->xy_mode + 3); + + num_cur_tch = tch.hdr[PT_TCH_NUM]; + if (num_cur_tch > si->sensing_conf_data.max_tch) { + pt_debug(dev, DL_ERROR, "%s: Num touch err detected (n=%d)\n", + __func__, num_cur_tch); + num_cur_tch = si->sensing_conf_data.max_tch; + } + + if (tch.hdr[PT_TCH_LO]) + pt_debug(dev, DL_WARN, "%s: Large area detected\n", + __func__); + + /* extract xy_data for all currently reported touches */ + pt_debug(dev, DL_INFO, "%s: extract data num_cur_rec=%d\n", + __func__, num_cur_tch); + if (num_cur_tch) + pt_get_proximity_touch(pd, &tch, num_cur_tch); + else + pt_report_proximity(pd, false); + + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_mt_attention + * + * SUMMARY: Wrapper function for pt_xy_worker() that subscribe into the TTDL + * attention list. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_proximity_attention(struct device *dev) +{ + struct pt_proximity_data *pd = get_prox_data(dev); + int rc = 0; + + if (pd->si->xy_mode[2] != pd->si->desc.tch_report_id) + return 0; + + mutex_lock(&pd->prox_lock); + rc = pt_xy_worker(pd); + mutex_unlock(&pd->prox_lock); + if (rc < 0) + pt_debug(dev, DL_ERROR, "%s: xy_worker error r=%d\n", + __func__, rc); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_startup_attention + * + * SUMMARY: Wrapper function for pt_report_proximity() that subcribe into the + * TTDL attention list. + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_startup_attention(struct device *dev) +{ + struct pt_proximity_data *pd = get_prox_data(dev); + + mutex_lock(&pd->prox_lock); + pt_report_proximity(pd, false); + mutex_unlock(&pd->prox_lock); + + return 0; +} + +/******************************************************************************* + * FUNCTION: _pt_set_proximity_via_touchmode_enabled + * + * SUMMARY: Enable/Disable proximity via touchmode parameter + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *pd - pointer to proximity data structure + * enable - enable or disable proximity(true:enable; false:disable) + ******************************************************************************/ +static int _pt_set_proximity_via_touchmode_enabled( + struct pt_proximity_data *pd, bool enable) +{ + struct device *dev = pd->dev; + u32 touchmode_enabled; + int rc; + + rc = _pt_request_pip_get_param(dev, 0, + PT_RAM_ID_TOUCHMODE_ENABLED, &touchmode_enabled); + if (rc) + return rc; + + if (enable) + touchmode_enabled |= 0x80; + else + touchmode_enabled &= 0x7F; + + rc = _pt_request_pip_set_param(dev, 0, + PT_RAM_ID_TOUCHMODE_ENABLED, touchmode_enabled, + PT_RAM_ID_TOUCHMODE_ENABLED_SIZE); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_set_proximity_via_proximity_enable + * + * SUMMARY: Enable/Disable proximity via proximity parameter + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *pd - pointer to proximity data structure + * enable - enable or disable proximity(true:enable; false:disable) + ******************************************************************************/ +static int _pt_set_proximity_via_proximity_enable( + struct pt_proximity_data *pd, bool enable) +{ + struct device *dev = pd->dev; + u32 proximity_enable; + int rc; + + rc = _pt_request_pip_get_param(dev, 0, + PT_RAM_ID_PROXIMITY_ENABLE, &proximity_enable); + if (rc) + return rc; + + if (enable) + proximity_enable |= 0x01; + else + proximity_enable &= 0xFE; + + rc = _pt_request_pip_set_param(dev, 0, + PT_RAM_ID_PROXIMITY_ENABLE, proximity_enable, + PT_RAM_ID_PROXIMITY_ENABLE_SIZE); + + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_set_proximity + * + * SUMMARY: Set proximity mode via touchmode parameter or proximity parameter. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *pd - pointer to proximity data structure + * enable - enable or disable proximity(true:enable; false:disable) + ******************************************************************************/ +static int _pt_set_proximity(struct pt_proximity_data *pd, + bool enable) +{ + if (!IS_PIP_VER_GE(pd->si, 1, 4)) + return _pt_set_proximity_via_touchmode_enabled(pd, + enable); + + return _pt_set_proximity_via_proximity_enable(pd, enable); +} + +/******************************************************************************* + * FUNCTION: _pt_set_proximity + * + * SUMMARY: Enable proximity mode and subscribe into IRQ and STARTUP TTDL + * attention list. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *pd - pointer to proximity data structure + ******************************************************************************/ +static int _pt_proximity_enable(struct pt_proximity_data *pd) +{ + struct device *dev = pd->dev; + int rc = 0; + + pm_runtime_get_sync(dev); + + rc = pt_request_exclusive(dev, + PT_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: Error on request exclusive r=%d\n", + __func__, rc); + goto exit; + } + + rc = _pt_set_proximity(pd, true); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: Error on request enable proximity scantype r=%d\n", + __func__, rc); + goto exit_release; + } + + pt_debug(dev, DL_INFO, "%s: setup subscriptions\n", __func__); + + /* set up touch call back */ + _pt_subscribe_attention(dev, PT_ATTEN_IRQ, PT_PROXIMITY_NAME, + pt_proximity_attention, PT_MODE_OPERATIONAL); + + /* set up startup call back */ + _pt_subscribe_attention(dev, PT_ATTEN_STARTUP, + PT_PROXIMITY_NAME, pt_startup_attention, 0); + +exit_release: + pt_release_exclusive(dev); +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: _pt_proximity_disable + * + * SUMMARY: Disable proximity mode and unsubscribe from IRQ and STARTUP TTDL + * attention list. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *pd - pointer to proximity data structure + ******************************************************************************/ +static int _pt_proximity_disable(struct pt_proximity_data *pd, + bool force) +{ + struct device *dev = pd->dev; + int rc = 0; + + rc = pt_request_exclusive(dev, + PT_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: Error on request exclusive r=%d\n", + __func__, rc); + goto exit; + } + + rc = _pt_set_proximity(pd, false); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: Error on request disable proximity scan r=%d\n", + __func__, rc); + goto exit_release; + } + +exit_release: + pt_release_exclusive(dev); + +exit: + if (!rc || force) { + _pt_unsubscribe_attention(dev, PT_ATTEN_IRQ, + PT_PROXIMITY_NAME, pt_proximity_attention, + PT_MODE_OPERATIONAL); + + _pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, + PT_PROXIMITY_NAME, pt_startup_attention, 0); + } + + pm_runtime_put(dev); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_proximity_enable_show + * + * SUMMARY: Show method for the prox_enable sysfs node that will show the + * enable_count of proximity + * + * RETURN: Size of printed buffer + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_proximity_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_proximity_data *pd = get_prox_data(dev); + int val = 0; + + mutex_lock(&pd->sysfs_lock); + val = pd->enable_count; + mutex_unlock(&pd->sysfs_lock); + + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "%d\n", val); +} + +/******************************************************************************* + * FUNCTION: pt_proximity_enable_store + * + * SUMMARY: The store method for the prox_enable sysfs node that allows to + * enable or disable proxmity mode. + * + * RETURN: Size of passed in buffer + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_proximity_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_proximity_data *pd = get_prox_data(dev); + unsigned long value; + int rc; + + rc = kstrtoul(buf, 10, &value); + if (rc < 0 || (value != 0 && value != 1)) { + pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__); + return -EINVAL; + } + + mutex_lock(&pd->sysfs_lock); + if (value) { + if (pd->enable_count++) { + pt_debug(dev, DL_WARN, "%s: '%s' already enabled\n", + __func__, pd->input->name); + } else { + rc = _pt_proximity_enable(pd); + if (rc) + pd->enable_count--; + } + } else { + if (--pd->enable_count) { + if (pd->enable_count < 0) { + pt_debug(dev, DL_ERROR, "%s: '%s' unbalanced disable\n", + __func__, pd->input->name); + pd->enable_count = 0; + } + } else { + rc = _pt_proximity_disable(pd, false); + if (rc) + pd->enable_count++; + } + } + mutex_unlock(&pd->sysfs_lock); + + if (rc) + return rc; + + return size; +} + +static DEVICE_ATTR(prox_enable, 0600, + pt_proximity_enable_show, + pt_proximity_enable_store); + +/******************************************************************************* + * FUNCTION: pt_setup_input_device_and_sysfs + * + * SUMMARY: Create sysnode, set event signal capabilities and register input + * device for proximity. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_setup_input_device_and_sysfs(struct device *dev) +{ + struct pt_proximity_data *pd = get_prox_data(dev); + int signal = PT_IGNORE_VALUE; + int i; + int rc; + + rc = device_create_file(dev, &dev_attr_prox_enable); + if (rc) { + pt_debug(dev, DL_ERROR, "%s: Error, could not create enable\n", + __func__); + goto exit; + } + + pt_debug(dev, DL_INFO, "%s: Initialize event signals\n", + __func__); + + __set_bit(EV_ABS, pd->input->evbit); + + /* set event signal capabilities */ + for (i = 0; i < NUM_SIGNALS(pd->pdata->frmwrk); i++) { + signal = PARAM_SIGNAL(pd->pdata->frmwrk, i); + if (signal != PT_IGNORE_VALUE) { + input_set_abs_params(pd->input, signal, + PARAM_MIN(pd->pdata->frmwrk, i), + PARAM_MAX(pd->pdata->frmwrk, i), + PARAM_FUZZ(pd->pdata->frmwrk, i), + PARAM_FLAT(pd->pdata->frmwrk, i)); + } + } + + rc = input_register_device(pd->input); + if (rc) { + pt_debug(dev, DL_ERROR, "%s: Error, failed register input device r=%d\n", + __func__, rc); + goto unregister_enable; + } + + pd->input_device_registered = true; + return rc; + +unregister_enable: + device_remove_file(dev, &dev_attr_prox_enable); +exit: + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_setup_input_attention + * + * SUMMARY: Wrapper function for pt_setup_input_device_and_sysfs() that + * subscribe into TTDL attention list. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +static int pt_setup_input_attention(struct device *dev) +{ + struct pt_proximity_data *pd = get_prox_data(dev); + int rc; + + pd->si = _pt_request_sysinfo(dev); + if (!pd->si) + return -EINVAL; + + rc = pt_setup_input_device_and_sysfs(dev); + if (!rc) + rc = _pt_set_proximity(pd, false); + + _pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, + PT_PROXIMITY_NAME, pt_setup_input_attention, 0); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_proximity_probe + * + * SUMMARY: The probe function for proximity input device + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +int pt_proximity_probe(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_proximity_data *pd = &cd->pd; + struct pt_platform_data *pdata = dev_get_platdata(dev); + struct pt_proximity_platform_data *prox_pdata; + int rc = 0; + + if (!pdata || !pdata->prox_pdata) { + pt_debug(dev, DL_ERROR, + "%s: Missing platform data\n", __func__); + rc = -ENODEV; + goto error_no_pdata; + } + prox_pdata = pdata->prox_pdata; + + mutex_init(&pd->prox_lock); + mutex_init(&pd->sysfs_lock); + pd->dev = dev; + pd->pdata = prox_pdata; + + /* Create the input device and register it. */ + pt_debug(dev, DL_INFO, + "%s: Create the input device and register it\n", __func__); + pd->input = input_allocate_device(); + if (!pd->input) { + pt_debug(dev, DL_ERROR, "%s: Error, failed to allocate input device\n", + __func__); + rc = -ENODEV; + goto error_alloc_failed; + } else + pd->input_device_allocated = true; + + if (pd->pdata->inp_dev_name) + pd->input->name = pd->pdata->inp_dev_name; + else + pd->input->name = PT_PROXIMITY_NAME; + scnprintf(pd->phys, sizeof(pd->phys), "%s/input%d", dev_name(dev), + cd->phys_num++); + pd->input->phys = pd->phys; + pd->input->dev.parent = pd->dev; + input_set_drvdata(pd->input, pd); + + /* get sysinfo */ + pd->si = _pt_request_sysinfo(dev); + + if (pd->si) { + rc = pt_setup_input_device_and_sysfs(dev); + if (rc) + goto error_init_input; + + rc = _pt_set_proximity(pd, false); + } else { + pt_debug(dev, DL_ERROR, "%s: Fail get sysinfo pointer from core p=%p\n", + __func__, pd->si); + _pt_subscribe_attention(dev, PT_ATTEN_STARTUP, + PT_PROXIMITY_NAME, pt_setup_input_attention, + 0); + } + + return 0; + +error_init_input: + input_free_device(pd->input); + pd->input_device_allocated = false; +error_alloc_failed: +error_no_pdata: + pt_debug(dev, DL_ERROR, "%s failed.\n", __func__); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_proximity_release + * + * SUMMARY: The release function for proximity input device + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *dev - pointer to device structure + ******************************************************************************/ +int pt_proximity_release(struct device *dev) +{ + struct pt_proximity_data *pd; + + /* Ensure valid pointers before de-referencing them */ + if (dev) + pd = get_prox_data(dev); + else + return 0; + + /* + * Second call this function may cause kernel panic if probe fail. + * Use input_device_registered & input_device_allocated variable to + * avoid unregister or free unavailable devive. + */ + if (pd && pd->input_device_registered) { + /* Disable proximity sensing */ + pd->input_device_registered = false; + mutex_lock(&pd->sysfs_lock); + if (pd->enable_count) + _pt_proximity_disable(pd, true); + mutex_unlock(&pd->sysfs_lock); + device_remove_file(dev, &dev_attr_prox_enable); + input_unregister_device(pd->input); + /* Unregistering device will free the device too */ + pd->input_device_allocated = false; + } else if (pd && pd->input_device_allocated) { + pd->input_device_allocated = false; + input_free_device(pd->input); + _pt_unsubscribe_attention(dev, PT_ATTEN_STARTUP, + PT_PROXIMITY_NAME, pt_setup_input_attention, + 0); + } + + return 0; +} +#endif /* !TTDL_KERNEL_SUBMISSION */ diff --git a/pt/pt_regs.h b/pt/pt_regs.h new file mode 100644 index 0000000000..2062a1bbcf --- /dev/null +++ b/pt/pt_regs.h @@ -0,0 +1,1875 @@ +/* + * pt_regs.h + * Parade TrueTouch(TM) Standard Product Registers. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.paradetech.com + * + */ + +#ifndef _PT_REGS_H +#define _PT_REGS_H + +#define PT_PANEL_ID_DEFAULT 0 + +#define PT_MAX_PATH_SIZE 128 +#define PT_PIP2_BIN_FILE_PATH "/data/ttdl/pt_fw.bin" +#define PT_SUPPRESS_AUTO_BL 0 +#define PT_ALLOW_AUTO_BL 1 + +#define PT_PIP2_MAX_FILE_SIZE 0x18000 +#define PT_PIP2_FILE_SECTOR_SIZE 0x1000 + +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#elif defined(CONFIG_FB) +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define STATUS_SUCCESS 0 +#define STATUS_FAIL -1 + +#define PT_FW_FILE_PREFIX "tt_fw" +#define PT_FW_FILE_SUFFIX ".bin" +#define PT_FW_FILE_NAME "tt_fw.bin" +#define PT_FW_RAM_FILE_NAME "tt_fw_ram.bin" +#ifndef TTDL_KERNEL_SUBMISSION +/* Enable special TTDL features */ +#ifndef TTHE_TUNER_SUPPORT +#define TTHE_TUNER_SUPPORT +#endif + +#ifndef TTDL_DIAGNOSTICS +#define TTDL_DIAGNOSTICS +#endif + +#ifndef EASYWAKE_TSG6 +#define EASYWAKE_TSG6 +#endif +#endif /* !TTDL_KERNEL_SUBMISSION */ + +#ifdef TTHE_TUNER_SUPPORT +#define PT_TTHE_TUNER_FILE_NAME "tthe_tuner" +#endif +#define PT_MAX_PRBUF_SIZE PIPE_BUF +#define PT_PR_TRUNCATED " truncated..." + +#define PT_DEFAULT_CORE_ID "pt_core0" +#define PT_MAX_NUM_CORE_DEVS 5 +#define PT_IRQ_ASSERTED_VALUE 0 + +#ifdef PT_ENABLE_MAX_ELEN +#define PT_MAX_ELEN 100 +#endif + +/* + * The largest PIP message is the PIP2 FILE_WRITE which has: + * 2 byte register + * 4 byte header + * 1 byte file handle + * 256 byte payload + * 2 byte CRC + */ +#define PT_MAX_PIP2_MSG_SIZE 265 +#define PT_MAX_PIP1_MSG_SIZE 255 +#define PT_HID_DESC_SIZE 30 + +/* + * The minimun size of PIP2 packet includes: + * 2 byte length + * 1 byte sequence + * 1 byte command ID + * 2 byte CRC + */ +#define PT_MIN_PIP2_PACKET_SIZE 6 + +static const u8 pt_data_block_security_key[] = { + 0xA5, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD, 0x5A +}; + +/* Enum for debug reporting levels */ +enum PT_DEBUG_LEVEL { + DL_QUIET = 0, + DL_ERROR = 1, + DL_WARN = 2, + DL_INFO = 3, + DL_DEBUG = 4, + DL_MAX +}; +#define PT_INITIAL_DEBUG_LEVEL DL_WARN + +/* Startup DUT enum status bitmask */ +enum PT_STARTUP_STATUS { + STARTUP_STATUS_START = 0, + STARTUP_STATUS_BL_RESET_SENTINEL = 0x001, + STARTUP_STATUS_FW_RESET_SENTINEL = 0x002, + STARTUP_STATUS_GET_DESC = 0x004, + STARTUP_STATUS_FW_OUT_OF_BOOT = 0x008, + STARTUP_STATUS_GET_RPT_DESC = 0x010, + STARTUP_STATUS_GET_SYS_INFO = 0x020, + STARTUP_STATUS_GET_CFG_CRC = 0x040, + STARTUP_STATUS_RESTORE_PARM = 0x080, + STARTUP_STATUS_COMPLETE = 0x100, + STARTUP_STATUS_FULL = 0x1FF +}; + +#define SLAVE_DETECT_MASK 0x01 +/* TTDL Built In Self Test selection bit masks */ +enum PT_TTDL_BIST_TESTS { + PT_BIST_BUS_TEST = 0x01, + PT_BIST_IRQ_TEST = 0x02, + PT_BIST_TP_XRES_TEST = 0x04, + PT_BIST_SLAVE_BUS_TEST = 0x08, + PT_BIST_SLAVE_IRQ_TEST = 0x10, + PT_BIST_SLAVE_XRES_TEST = 0x20 +}; + +/* tthe_tuner node format options */ +enum PT_TTHE_TUNER_FORMAT { + PT_TTHE_TUNER_FORMAT_HID_USB = 0x01, + PT_TTHE_TUNER_FORMAT_HID_I2C = 0x02, + PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP = 0x03, + PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP = 0x04, + PT_TTHE_TUNER_FORMAT_RESERVED = 0xFE, +}; + +#define PT_INITIAL_SHOW_TIME_STAMP 0 + +/* + * Print out all debug prints that are less then or equal to set level. + */ +#define pt_debug(dev, dlevel, format, arg...) \ + do { \ + struct pt_core_data *cd_tmp = dev_get_drvdata(dev);\ + if (cd_tmp->debug_level >= dlevel) {\ + if (dlevel == DL_ERROR)\ + dev_err(dev, "[%d] "format, dlevel, ##arg);\ + else\ + dev_info(dev, "[%d] "format, dlevel, ##arg);\ + } \ + } while (0) + +enum PT_PIP_REPORT_ID { + PT_PIP_INVALID_REPORT_ID = 0x00, + PT_PIP_TOUCH_REPORT_ID = 0x01, + PT_PIP_TOUCH_REPORT_WIN8_ID = 0x02, + PT_PIP_CAPSENSE_BTN_REPORT_ID = 0x03, + PT_PIP_WAKEUP_REPORT_ID = 0x04, + PT_PIP_NOISE_METRIC_REPORT_ID = 0x05, + PT_PIP_PUSH_BUTTON_REPORT_ID = 0x06, + PT_PIP_SELFCAP_INPUT_REPORT_ID = 0x0D, + PT_PIP_TRACKING_HEATMAP_REPORT_ID = 0x0E, + PT_PIP_SENSOR_DATA_REPORT_ID = 0x0F, + PT_PIP_NON_HID_RESPONSE_ID = 0x1F, + PT_PIP_NON_HID_COMMAND_ID = 0x2F, + PT_PIP_BL_RESPONSE_REPORT_ID = 0x30, + PT_PIP_BL_COMMAND_REPORT_ID = 0x40 +}; + +enum PT_HID_REPORT_ID { + PT_HID_FINGER_REPORT_ID = 0x01, + PT_HID_PEN_REPORT_ID = 0x02, + PT_HID_VS_FINGER_REPORT_ID = 0x41, /* Vendor Specific ID */ + PT_HID_VS_PEN_REPORT_ID = 0x42 /* Vendor Specific ID */ +}; + + +/* HID IDs and commands */ +#define HID_VENDOR_ID 0x04B4 +#define HID_APP_PRODUCT_ID 0xC101 +#define HID_VERSION 0x0100 +#define HID_REPORT_DESC_ID 0xF6 +#define HID_APP_REPORT_ID 0xF7 +#define HID_BL_REPORT_ID 0xFF +#define HID_RESPONSE_REPORT_ID 0xF0 +#define HID_POWER_ON 0x0 +#define HID_POWER_SLEEP 0x1 +#define HID_POWER_STANDBY 0x2 + +/* PIP1 offsets and masks */ +#define PIP1_RESP_REPORT_ID_OFFSET 2 +#define PIP1_RESP_COMMAND_ID_OFFSET 4 +#define PIP1_RESP_COMMAND_ID_MASK 0x7F +#define PIP1_CMD_COMMAND_ID_OFFSET 6 +#define PIP1_CMD_COMMAND_ID_MASK 0x7F + +#define PIP1_SYSINFO_TTDATA_OFFSET 5 +#define PIP1_SYSINFO_SENSING_OFFSET 33 +#define PIP1_SYSINFO_BTN_OFFSET 48 +#define PIP1_SYSINFO_BTN_MASK 0xFF +#define PIP1_SYSINFO_MAX_BTN 8 + +/* Timeouts in ms */ +#define PT_PTSBC_INIT_WATCHDOG_TIMEOUT 20000 +#define PT_REQUEST_EXCLUSIVE_TIMEOUT 8000 +#define PT_WATCHDOG_TIMEOUT 2000 +#define PT_FW_EXIT_BOOT_MODE_TIMEOUT 1000 +#define PT_BL_WAIT_FOR_SENTINEL 500 +#define PT_REQUEST_ENUM_TIMEOUT 4000 +#define PT_GET_HID_DESCRIPTOR_TIMEOUT 500 +#define PT_HID_GET_REPORT_DESCRIPTOR_TIMEOUT 500 +#define PT_HID_CMD_DEFAULT_TIMEOUT 500 +#define PT_PIP_CMD_DEFAULT_TIMEOUT 2000 +#define PT_PIP1_CMD_DEFAULT_TIMEOUT 1000 +#define PT_PIP1_START_BOOTLOADER_TIMEOUT 2000 +#define PT_PIP1_CMD_GET_SYSINFO_TIMEOUT 500 +#define PT_PIP1_CMD_CALIBRATE_IDAC_TIMEOUT 5000 +#define PT_PIP1_CMD_CALIBRATE_EXT_TIMEOUT 5000 +#define PT_PIP1_CMD_WRITE_CONF_BLOCK_TIMEOUT 400 +#define PT_PIP1_CMD_RUN_SELF_TEST_TIMEOUT 10000 +#define PT_PIP1_CMD_INITIATE_BL_TIMEOUT 20000 +#define PT_PIP1_CMD_PROGRAM_AND_VERIFY_TIMEOUT 400 +#define PT_PIP2_CMD_FILE_ERASE_TIMEOUT 3000 +/* + * BL internal timeout is 500 ms and here it is slightly larger to consider + * the BUS communication cost. (Bugz#92376) + */ +#define PT_PIP2_CMD_FILE_SECTOR_ERASE_TIMEOUT 600 + +/* Max counts */ +#define PT_WATCHDOG_RETRY_COUNT 30 +#define PT_BUS_READ_INPUT_RETRY_COUNT 2 + +#define PT_FLUSH_BUS_BASED_ON_LEN 0 +#define PT_FLUSH_BUS_FULL_256_READ 1 + +/* maximum number of concurrent tracks */ +#define TOUCH_REPORT_SIZE 10 +#define TOUCH_INPUT_HEADER_SIZE 7 +#define TOUCH_COUNT_BYTE_OFFSET 5 +#define BTN_REPORT_SIZE 9 +#define BTN_INPUT_HEADER_SIZE 5 +#define SENSOR_REPORT_SIZE 150 +#define SENSOR_HEADER_SIZE 4 +#define MAX_TOUCH_NUM 6 + +/* helpers */ +#define GET_NUM_TOUCHES(x) ((x) & 0x1F) +#define IS_LARGE_AREA(x) ((x) & 0x20) +#define IS_BAD_PKT(x) ((x) & 0x20) +#define IS_TMO(t) ((t) == 0) +#define HI_BYTE(x) (u8)(((x) >> 8) & 0xFF) +#define LOW_BYTE(x) (u8)((x) & 0xFF) +#define SET_CMD_LOW(byte, bits) \ + ((byte) = (((byte) & 0xF0) | ((bits) & 0x0F))) +#define SET_CMD_HIGH(byte, bits)\ + ((byte) = (((byte) & 0x0F) | ((bits) & 0xF0))) + +#define GET_MASK(length) \ + ((1 << length) - 1) +#define GET_FIELD(name, length, shift) \ + ((name >> shift) & GET_MASK(length)) + +#define _base(x) ((x >= '0' && x <= '9') ? '0' : \ + (x >= 'a' && x <= 'f') ? 'a' - 10 : \ + (x >= 'A' && x <= 'F') ? 'A' - 10 : \ + '\255') +#define HEXOF(x) (x - _base(x)) + +#define HID_ITEM_SIZE_MASK 0x03 +#define HID_ITEM_TYPE_MASK 0x0C +#define HID_ITEM_TAG_MASK 0xF0 + +#define HID_ITEM_SIZE_SHIFT 0 +#define HID_ITEM_TYPE_SHIFT 2 +#define HID_ITEM_TAG_SHIFT 4 + +#define HID_GET_ITEM_SIZE(x) \ + ((x & HID_ITEM_SIZE_MASK) >> HID_ITEM_SIZE_SHIFT) +#define HID_GET_ITEM_TYPE(x) \ + ((x & HID_ITEM_TYPE_MASK) >> HID_ITEM_TYPE_SHIFT) +#define HID_GET_ITEM_TAG(x) \ + ((x & HID_ITEM_TAG_MASK) >> HID_ITEM_TAG_SHIFT) + + +#define IS_EASY_WAKE_CONFIGURED(x) \ + ((x) != 0 && (x) != 0xFF) + +#define IS_PIP_VER_GE(p, maj, min) \ + ((p)->ttdata.pip_ver_major > (maj) ? \ + 1 : \ + (((p)->ttdata.pip_ver_major == (maj) ? \ + ((p)->ttdata.pip_ver_minor >= (min) ? \ + 1 : 0) : \ + 0))) +#define IS_PIP_VER_EQ(p, maj, min) \ + ((p)->ttdata.pip_ver_major == (maj) ? \ + ((p)->ttdata.pip_ver_minor == (min) ? \ + 1 : \ + 0 : \ + 0)) +#define PT_PANEL_ID_BITMASK 0x0000000C +#define PT_PANEL_ID_SHIFT 2 + +#define TTDL_PTVIRTDUT_SUPPORT 1 + +/* DUT Debug commands (dut_debug sysfs) */ +#define PT_DUT_DBG_HID_RESET 50 +#define PT_DUT_DBG_HID_GET_REPORT 51 /* depricated */ +#define PT_DUT_DBG_HID_SET_REPORT 52 /* depricated */ +#define PT_DUT_DBG_HID_SET_POWER_ON 53 +#define PT_DUT_DBG_HID_SET_POWER_SLEEP 54 +#define PT_DUT_DBG_HID_SET_POWER_STANDBY 55 +#define PT_DUT_DBG_PIP_SOFT_RESET 97 +#define PT_DUT_DBG_RESET 98 +#define PT_DUT_DBG_PIP_NULL 100 +#define PT_DUT_DBG_PIP_ENTER_BL 101 +#define PT_DUT_DBG_HID_SYSINFO 102 +#define PT_DUT_DBG_PIP_SUSPEND_SCAN 103 +#define PT_DUT_DBG_PIP_RESUME_SCAN 104 +#define PT_DUT_DBG_HID_DESC 109 + +/* Driver Debug commands (drv_debug sysfs) */ +#define PT_DRV_DBG_SUSPEND 4 +#define PT_DRV_DBG_RESUME 5 +#define PT_DRV_DBG_STOP_WD 105 +#define PT_DRV_DBG_START_WD 106 +#define PT_DRV_DBG_TTHE_TUNER_EXIT 107 +#define PT_DRV_DBG_TTHE_BUF_CLEAN 108 +#define PT_DRV_DBG_CLEAR_PARM_LIST 110 +#define PT_DRV_DBG_FORCE_BUS_READ 111 +#define PT_DRV_DBG_CLEAR_CAL_DATA 112 +#define PT_DUT_DBG_REPORT_DESC 113 + +/* + * Commands that require additional parameters + * will be in the 200 range. Commands that do not + * require additional parameters remain below 200. + */ +#define PT_DRV_DBG_REPORT_LEVEL 200 +#define PT_DRV_DBG_WATCHDOG_INTERVAL 201 +#define PT_DRV_DBG_SHOW_TIMESTAMP 202 +#define PT_DRV_DBG_SET_GENERATION 210 + +#ifdef TTDL_DIAGNOSTICS +#define PT_DRV_DBG_FLUSH_BUS 204 /* deprecated */ +#define PT_DRV_DBG_SETUP_PWR 205 +#define PT_DRV_DBG_GET_PUT_SYNC 206 +#define PT_DRV_DBG_SET_PIP2_LAUNCH_APP 207 /* deprecated */ +#define PT_DRV_DBG_SET_TT_DATA 208 +#define PT_DRV_DBG_SET_RUN_FW_PIN 209 /* deprecated */ +#define PT_DRV_DBG_SET_BRIDGE_MODE 211 +#define PT_DRV_DBG_SET_I2C_ADDRESS 212 +#define PT_DRV_DBG_SET_FLASHLESS_DUT 213 +#define PT_DRV_DBG_SET_FORCE_SEQ 214 +#define PT_DRV_DBG_BL_WITH_NO_INT 215 +#define PT_DRV_DBG_CAL_CACHE_IN_HOST 216 +#define PT_DRV_DBG_NUM_DEVICES 217 +#define PT_DRV_DBG_SET_PANEL_ID_TYPE 218 +#define PT_DRV_DBG_PIP_TIMEOUT 219 +#define PT_DRV_DBG_CORE_PLATFORM_FLAG 220 +#ifdef TTDL_PTVIRTDUT_SUPPORT +#define PT_DRV_DBG_SET_HW_DETECT 298 +#define PT_DRV_DBG_VIRTUAL_I2C_DUT 299 +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + +#define VIRT_MAX_IRQ_RELEASE_TIME_US 500000 +#endif /* TTDL DIAGNOSTICS */ + +/* Recognized usages */ +/* undef them first for possible redefinition in Linux */ +#undef HID_DI_PRESSURE +#undef HID_DI_TIP +#undef HID_DI_CONTACTID +#undef HID_DI_CONTACTCOUNT +#undef HID_DI_SCANTIME +#define HID_DI_PRESSURE 0x000d0030 +#define HID_DI_TIP 0x000d0042 +#define HID_DI_CONTACTID 0x000d0051 +#define HID_DI_CONTACTCOUNT 0x000d0054 +#define HID_DI_SCANTIME 0x000d0056 + +/* Parade vendor specific usages */ +#define HID_PT_UNDEFINED 0xff010000 +#define HID_PT_BOOTLOADER 0xff010001 +#define HID_PT_TOUCHAPPLICATION 0xff010002 +#define HID_PT_BUTTONS 0xff010020 +#define HID_PT_GENERICITEM 0xff010030 +#define HID_PT_LARGEOBJECT 0xff010040 +#define HID_PT_NOISEEFFECTS 0xff010041 +#define HID_PT_REPORTCOUNTER 0xff010042 +#define HID_PT_TOUCHTYPE 0xff010060 +#define HID_PT_EVENTID 0xff010061 +#define HID_PT_MAJORAXISLENGTH 0xff010062 +#define HID_PT_MINORAXISLENGTH 0xff010063 +#define HID_PT_ORIENTATION 0xff010064 +#define HID_PT_BUTTONSIGNAL 0xff010065 +#define HID_PT_MAJOR_CONTACT_AXIS_LENGTH 0xff010066 +#define HID_PT_MINOR_CONTACT_AXIS_LENGTH 0xff010067 +#define HID_PT_TCH_COL_USAGE_PG 0x000D0004 +#define HID_PT_BTN_COL_USAGE_PG 0xFF010020 +#define HID_PT_PEN_COL_USAGE_PG 0x000D0020 + +#define PANEL_ID_NOT_ENABLED 0xFF + +#ifdef EASYWAKE_TSG6 +#define GESTURE_DOUBLE_TAP (1) +#define GESTURE_TWO_FINGERS_SLIDE (2) +#define GESTURE_TOUCH_DETECTED (3) +#define GESTURE_PUSH_BUTTON (4) +#define GESTURE_SINGLE_SLIDE_DE_TX (5) +#define GESTURE_SINGLE_SLIDE_IN_TX (6) +#define GESTURE_SINGLE_SLIDE_DE_RX (7) +#define GESTURE_SINGLE_SLIDE_IN_RX (8) +#endif + +/* FW RAM parameters */ +#define PT_RAM_ID_TOUCHMODE_ENABLED 0x02 +#define PT_RAM_ID_PROXIMITY_ENABLE 0x20 +#define PT_RAM_ID_TOUCHMODE_ENABLED_SIZE 1 +#define PT_RAM_ID_PROXIMITY_ENABLE_SIZE 1 + +/* abs signal capabilities offsets in the frameworks array */ +enum pt_sig_caps { + PT_SIGNAL_OST, + PT_MIN_OST, + PT_MAX_OST, + PT_FUZZ_OST, + PT_FLAT_OST, + PT_NUM_ABS_SET /* number of signal capability fields */ +}; + +/* helpers */ +#define NUM_SIGNALS(frmwrk) ((frmwrk)->size / PT_NUM_ABS_SET) +#define PARAM(frmwrk, sig_ost, cap_ost) \ + ((frmwrk)->abs[((sig_ost) * PT_NUM_ABS_SET) + (cap_ost)]) + +#define PARAM_SIGNAL(frmwrk, sig_ost) PARAM(frmwrk, sig_ost, PT_SIGNAL_OST) +#define PARAM_MIN(frmwrk, sig_ost) PARAM(frmwrk, sig_ost, PT_MIN_OST) +#define PARAM_MAX(frmwrk, sig_ost) PARAM(frmwrk, sig_ost, PT_MAX_OST) +#define PARAM_FUZZ(frmwrk, sig_ost) PARAM(frmwrk, sig_ost, PT_FUZZ_OST) +#define PARAM_FLAT(frmwrk, sig_ost) PARAM(frmwrk, sig_ost, PT_FLAT_OST) + +/* abs axis signal offsets in the framworks array */ +enum pt_sig_ost { + PT_ABS_X_OST, + PT_ABS_Y_OST, + PT_ABS_P_OST, + PT_ABS_W_OST, + PT_ABS_ID_OST, + PT_ABS_MAJ_OST, + PT_ABS_MIN_OST, + PT_ABS_OR_OST, + PT_ABS_TOOL_OST, + PT_ABS_D_OST, + PT_NUM_ABS_OST /* number of abs signals */ +}; + +enum hid_command { + HID_CMD_RESERVED = 0x0, + HID_CMD_RESET = 0x1, + HID_CMD_GET_REPORT = 0x2, + HID_CMD_SET_REPORT = 0x3, + HID_CMD_GET_IDLE = 0x4, + HID_CMD_SET_IDLE = 0x5, + HID_CMD_GET_PROTOCOL = 0x6, + HID_CMD_SET_PROTOCOL = 0x7, + HID_CMD_SET_POWER = 0x8, + HID_CMD_VENDOR = 0xE, +}; + +enum PIP1_cmd_type { + PIP1_CMD_TYPE_FW, + PIP1_CMD_TYPE_BL, +}; + +/* PIP BL cmd IDs and input for dut_debug sysfs */ +enum pip1_bl_cmd_id { + PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY = 0x31, /* 49 */ + PIP1_BL_CMD_ID_GET_INFO = 0x38, /* 56 */ + PIP1_BL_CMD_ID_PROGRAM_AND_VERIFY = 0x39, /* 57 */ + PIP1_BL_CMD_ID_LAUNCH_APP = 0x3B, /* 59 */ + PIP1_BL_CMD_ID_GET_PANEL_ID = 0x3E, /* 62 */ + PIP1_BL_CMD_ID_INITIATE_BL = 0x48, /* 72 */ + PIP1_BL_CMD_ID_LAST, +}; +#define PIP1_BL_SOP 0x1 +#define PIP1_BL_EOP 0x17 + +/* PIP1 Command/Response IDs */ +enum PIP1_CMD_ID { + PIP1_CMD_ID_NULL = 0x00, + PIP1_CMD_ID_START_BOOTLOADER = 0x01, + PIP1_CMD_ID_GET_SYSINFO = 0x02, + PIP1_CMD_ID_SUSPEND_SCANNING = 0x03, + PIP1_CMD_ID_RESUME_SCANNING = 0x04, + PIP1_CMD_ID_GET_PARAM = 0x05, + PIP1_CMD_ID_SET_PARAM = 0x06, + PIP1_CMD_ID_GET_NOISE_METRICS = 0x07, + PIP1_CMD_ID_RESERVED = 0x08, + PIP1_CMD_ID_ENTER_EASYWAKE_STATE = 0x09, + PIP1_CMD_ID_VERIFY_CONFIG_BLOCK_CRC = 0x20, + PIP1_CMD_ID_GET_CONFIG_ROW_SIZE = 0x21, + PIP1_CMD_ID_READ_DATA_BLOCK = 0x22, + PIP1_CMD_ID_WRITE_DATA_BLOCK = 0x23, + PIP1_CMD_ID_GET_DATA_STRUCTURE = 0x24, + PIP1_CMD_ID_LOAD_SELF_TEST_PARAM = 0x25, + PIP1_CMD_ID_RUN_SELF_TEST = 0x26, + PIP1_CMD_ID_GET_SELF_TEST_RESULT = 0x27, + PIP1_CMD_ID_CALIBRATE_IDACS = 0x28, + PIP1_CMD_ID_INITIALIZE_BASELINES = 0x29, + PIP1_CMD_ID_EXEC_PANEL_SCAN = 0x2A, + PIP1_CMD_ID_RETRIEVE_PANEL_SCAN = 0x2B, + PIP1_CMD_ID_START_SENSOR_DATA_MODE = 0x2C, + PIP1_CMD_ID_STOP_SENSOR_DATA_MODE = 0x2D, + PIP1_CMD_ID_START_TRACKING_HEATMAP_MODE = 0x2E, + PIP1_CMD_ID_START_SELF_CAP_RPT_MODE = 0x2F, + PIP1_CMD_ID_CALIBRATE_DEVICE_EXTENDED = 0x30, + PIP1_CMD_ID_INT_PIN_OVERRIDE = 0x40, + PIP1_CMD_ID_STORE_PANEL_SCAN = 0x60, + PIP1_CMD_ID_PROCESS_PANEL_SCAN = 0x61, + PIP1_CMD_ID_DISCARD_INPUT_REPORT, + PIP1_CMD_ID_LAST, + PIP1_CMD_ID_USER_CMD, +}; + +/* PIP2 Command/Response data and structures */ +enum PIP2_CMD_ID { + PIP2_CMD_ID_PING = 0x00, + PIP2_CMD_ID_STATUS = 0x01, + PIP2_CMD_ID_CTRL = 0x02, + PIP2_CMD_ID_CONFIG = 0x03, + PIP2_CMD_ID_RESERVE = 0x04, + PIP2_CMD_ID_CLEAR = 0x05, + PIP2_CMD_ID_RESET = 0x06, + PIP2_CMD_ID_VERSION = 0x07, + PIP2_CMD_ID_FILE_OPEN = 0x10, + PIP2_CMD_ID_FILE_CLOSE = 0x11, + PIP2_CMD_ID_FILE_READ = 0x12, + PIP2_CMD_ID_FILE_WRITE = 0x13, + PIP2_CMD_ID_FILE_IOCTL = 0x14, + PIP2_CMD_ID_FLASH_INFO = 0x15, + PIP2_CMD_ID_EXECUTE = 0x16, + PIP2_CMD_ID_GET_LAST_ERRNO = 0x17, + PIP2_CMD_ID_EXIT_HOST_MODE = 0x18, + PIP2_CMD_ID_READ_GPIO = 0x19, + PIP2_CMD_EXECUTE_SCAN = 0x21, + PIP2_CMD_SET_PARAMETER = 0x40, + PIP2_CMD_GET_PARAMETER = 0x41, + PIP2_CMD_SET_DDI_REG = 0x42, + PIP2_CMD_GET_DDI_REG = 0x43, + PIP2_CMD_ID_END = 0x7F +}; + +enum PIP2_STATUS_EXEC_RUNNING { + PIP2_STATUS_BOOT_EXEC = 0x00, + PIP2_STATUS_APP_EXEC = 0x01, +}; + +/* FW_SYS_MODE_UNDEFINED must be 1 greater than FW_SYS_MODE_MAX */ +enum PIP2_FW_SYSTEM_MODE { + FW_SYS_MODE_BOOT = 0x00, + FW_SYS_MODE_SCANNING = 0x01, + FW_SYS_MODE_DEEP_SLEEP = 0x02, + FW_SYS_MODE_TEST = 0x03, + FW_SYS_MODE_DEEP_STANDBY = 0x04, + FW_SYS_MODE_MAX = FW_SYS_MODE_DEEP_STANDBY, + FW_SYS_MODE_UNDEFINED = FW_SYS_MODE_MAX + 1, +}; + +/* PIP2 Command/Response data and structures */ +enum PIP2_FILE_ID { + PIP2_RAM_FILE = 0x00, + PIP2_FW_FILE = 0x01, + PIP2_CONFIG_FILE = 0x02, + PIP2_FILE_3 = 0x03, + PIP2_FILE_4 = 0x04, + PIP2_FILE_5 = 0x05, + PIP2_FILE_6 = 0x06, + PIP2_FILE_7 = 0x07, + PIP2_FILE_8 = 0x08, + PIP2_FILE_RESERVED = 0x0F, + PIP2_FILE_MAX = PIP2_FILE_RESERVED, +}; + +/* Optimize packet sizes per Allwinner H3 bus drivers */ +#define PIP2_FILE_WRITE_LEN_PER_PACKET (245) +#define PIP2_BL_I2C_FILE_WRITE_LEN_PER_PACKET (245) +#define PIP2_BL_SPI_FILE_WRITE_LEN_PER_PACKET (256) +#define PIP2_FILE_WRITE_MAX_LEN_PER_PACKET \ + PIP2_BL_SPI_FILE_WRITE_LEN_PER_PACKET + +enum DUT_GENERATION { + DUT_UNKNOWN = 0x00, + DUT_PIP1_ONLY = 0x01, + DUT_PIP2_CAPABLE = 0x02, +}; + +enum PIP2_RSP_ERR { + PIP2_RSP_ERR_NONE = 0x00, + PIP2_RSP_ERR_BUSY = 0x01, + PIP2_RSP_ERR_INIT_FAILURE = 0x02, + PIP2_RSP_ERR_ALREADY_OPEN = 0x03, + PIP2_RSP_ERR_NOT_OPEN = 0x04, + PIP2_RSP_ERR_IO_FAILURE = 0x05, + PIP2_RSP_ERR_UNKNOWN_IOCTL = 0x06, + PIP2_RSP_ERR_BAD_ADDRESS = 0x07, + PIP2_RSP_ERR_BAD_FILE = 0x08, + PIP2_RSP_ERR_END_OF_FILE = 0x09, + PIP2_RSP_ERR_TOO_MANY_FILES = 0x0A, + PIP2_RSP_ERR_TIMEOUT = 0x0B, + PIP2_RSP_ERR_ABORTED = 0x0C, + PIP2_RSP_ERR_CRC = 0x0D, + PIP2_RSP_ERR_UNKNOWN_REC_TYPE = 0x0E, + PIP2_RSP_ERR_BAD_FRAME = 0x0F, + PIP2_RSP_ERR_NO_PERMISSION = 0x10, + PIP2_RSP_ERR_UNKNOWN_COMMAND = 0x11, + PIP2_RSP_ERR_INVALID_PARAM = 0x12, + PIP2_RSP_ERR_IO_ALREADY_ACTIVE = 0x13, + PIP2_RSP_ERR_SHUTDOWN = 0x14, + PIP2_RSP_ERR_INVALID_IMAGE = 0x15, + PIP2_RSP_ERR_UNKNOWN_REGISTER = 0x16, + PIP2_RSP_ERR_BAD_LENGTH = 0x17, + PIP2_RSP_ERR_TRIM_FAILURE = 0x18, + PIP2_RSP_ERR_BAD_SEQ = 0x19, + PIP2_RSP_ERR_BUF_TOO_SMALL = 0x1A, + PIP2_RSP_ERR_ASYNC_SEQ = 0x1B, + PIP2_RSP_ERR_EXEC_IMAGE = 0x1C, +}; + +/* + * Extra bytes for PIP2 = 4 + 2: + * 4 byte header - (len_lsb, len_msb, report ID, Tag, Sequence) + * 2 byte footer - (crc_lsb, crc_msb) + */ +#define PIP2_CMD_COMMAND_ID_OFFSET 5 +#define PIP2_CMD_COMMAND_ID_MASK 0x7F +#define PIP2_RESP_COMMAND_ID_OFFSET 3 +#define PIP2_RESP_SEQUENCE_OFFSET 2 +#define PIP2_RESP_SEQUENCE_MASK 0x0F +#define PIP2_RESP_REPORT_ID_OFFSET 3 +#define PIP2_RESP_STATUS_OFFSET 4 +#define PIP2_RESP_BODY_OFFSET 5 +#define PIP2_CRC_SIZE 2 +#define PIP2_LEN_FIELD_SIZE 2 +#define PIP2_VERSION_CHIP_REV_OFFSET 14 +#define PIP2_EXTRA_BYTES_NUM (PIP2_RESP_STATUS_OFFSET + PIP2_CRC_SIZE) + +/* File IOCTL commands */ +#define PIP2_FILE_IOCTL_CODE_ERASE_FILE 0 +#define PIP2_FILE_IOCTL_CODE_SEEK_POINTER 1 +#define PIP2_FILE_IOCTL_CODE_AES_CONTROL 2 +#define PIP2_FILE_IOCTL_CODE_FILE_STATS 3 +#define PIP2_FILE_IOCTL_CODE_FILE_CRC 4 + +struct pip2_cmd_structure { + u8 reg[2]; + u16 len; + u8 id; + u8 seq; + u8 *data; + u8 crc[2]; /* MSB:crc[0], LSB:crc[1] */ +}; + +struct pip2_cmd_response_structure { + u8 id; + u16 response_len; + u32 response_time_min; + u32 response_time_max; +}; + +enum pip1_bl_status { + ERROR_SUCCESS, + ERROR_KEY, + ERROR_VERIFICATION, + ERROR_LENGTH, + ERROR_DATA, + ERROR_COMMAND, + ERROR_CRC = 8, + ERROR_FLASH_ARRAY, + ERROR_FLASH_ROW, + ERROR_FLASH_PROTECTION, + ERROR_UKNOWN = 15, + ERROR_INVALID, +}; + +enum pt_mode { + PT_MODE_UNKNOWN = 0, + PT_MODE_BOOTLOADER = 1, + PT_MODE_OPERATIONAL = 2, + PT_MODE_IGNORE = 255, +}; + +enum pt_protocol_mode { + PT_PROTOCOL_MODE_PIP = 0, + PT_PROTOCOL_MODE_HID = 1, + PT_PROTOCOL_MODE_IGNORE = 255, +}; + +struct pt_dut_status { + enum PIP2_FW_SYSTEM_MODE fw_system_mode; + enum pt_mode mode; + enum pt_protocol_mode protocol_mode; +} __packed; + +enum PT_ENTER_BL_RESULT { + PT_ENTER_BL_PASS = 0, + PT_ENTER_BL_ERROR = 1, + PT_ENTER_BL_RESET_FAIL = 2, + PT_ENTER_BL_HID_START_BL_FAIL = 3, + PT_ENTER_BL_CONFIRM_FAIL = 4, + PT_ENTER_BL_GET_FLASH_INFO_FAIL = 5, +}; + +enum TTDL_EXTENDED_ERROR_CODES { + EX_ERR_FREAD = 400, + EX_ERR_FWRITE = 401, + EX_ERR_FOPEN = 402, + EX_ERR_FCLOSE = 403, + EX_ERR_FLEN = 404, +}; + +enum pt_cmd_status { + PT_CMD_STATUS_SUCCESS = 0, + PT_CMD_STATUS_FAILURE = 1, +}; + +enum { + PT_IC_GRPNUM_RESERVED, + PT_IC_GRPNUM_CMD_REGS, + PT_IC_GRPNUM_TCH_REP, + PT_IC_GRPNUM_DATA_REC, + PT_IC_GRPNUM_TEST_REC, + PT_IC_GRPNUM_PCFG_REC, + PT_IC_GRPNUM_TCH_PARM_VAL, + PT_IC_GRPNUM_TCH_PARM_SIZE, + PT_IC_GRPNUM_RESERVED1, + PT_IC_GRPNUM_RESERVED2, + PT_IC_GRPNUM_OPCFG_REC, + PT_IC_GRPNUM_DDATA_REC, + PT_IC_GRPNUM_MDATA_REC, + PT_IC_GRPNUM_TEST_REGS, + PT_IC_GRPNUM_BTN_KEYS, + PT_IC_GRPNUM_TTHE_REGS, + PT_IC_GRPNUM_SENSING_CONF, + PT_IC_GRPNUM_NUM, +}; + +enum pt_event_id { + PT_EV_NO_EVENT, + PT_EV_TOUCHDOWN, + PT_EV_MOVE, /* significant displacement (> act dist) */ + PT_EV_LIFTOFF, /* record reports last position */ +}; + +enum pt_object_id { + PT_OBJ_STANDARD_FINGER, + PT_OBJ_PROXIMITY, + PT_OBJ_STYLUS, + PT_OBJ_GLOVE, +}; + +enum pt_self_test_result { + PT_ST_RESULT_PASS = 0, + PT_ST_RESULT_FAIL = 1, + PT_ST_RESULT_ABORTED = 2, + PT_ST_RESULT_PARAM_ERR = 3, + PT_ST_RESULT_CFG_ERR = 4, + PT_ST_RESULT_CAL_ERR = 5, + PT_ST_RESULT_DDI_STATE_ERR = 6, + PT_ST_RESULT_HOST_MUST_INTERPRET = 0xFF, +}; +#define PT_ST_PRINT_RESULTS true +#define PT_ST_NOPRINT false + +enum pt_st_get_result { + PT_ST_DONT_GET_RESULTS = 0, + PT_ST_GET_RESULTS = 1, + PT_ST_GET_RESULTS_BASED_ON_DATA = 2, +}; + +/* + * Maximum number of parameters for the fw_self_test sysfs (255 - 12 + 2) + * 255 - Max PIP message size + * 12 - Header size for PIP message 0x25 (Load Self Test Parameters) + * 2 - Additional parameters for fw_self_test for test_id and format + */ +#define PT_FW_SELF_TEST_MAX_PARM 245 + +enum pt_self_test_id { + PT_ST_ID_NULL = 0, + PT_ST_ID_BIST = 1, + PT_ST_ID_SHORTS = 2, + PT_ST_ID_OPENS = 3, + PT_ST_ID_AUTOSHORTS = 4, + PT_ST_ID_CM_PANEL = 5, + PT_ST_ID_CP_PANEL = 6, + PT_ST_ID_CM_BUTTON = 7, + PT_ST_ID_CP_BUTTON = 8, + PT_ST_ID_FORCE = 9, + PT_ST_ID_OPENS_HIZ = 10, + PT_ST_ID_OPENS_GND = 11, + PT_ST_ID_CP_LFT = 12, + PT_ST_ID_SC_NOISE = 13, + PT_ST_ID_LFT_NOISE = 14, + PT_ST_ID_CP_CHIP_ROUTE_PARASITIC_CAP = 15, + PT_ST_ID_NORMALIZED_RAW_CNT_PANEL = 16, + PT_ST_ID_NORMALIZED_RAW_CNT_LFT = 17, + PT_ST_ID_INVALID = 255 +}; + +enum pt_scan_state { + PT_SCAN_STATE_UNKNOWN = 0, + PT_SCAN_STATE_ACTIVE = 1, + PT_SCAN_STATE_INACTIVE = 2, +}; + +#define PT_CAL_DATA_MAX_SIZE 2048 +#define PT_CAL_DATA_ROW_SIZE 128 +#define PT_WAFER_LOT_SIZE 5 +#define PT_UID_SIZE 12 + +enum pt_cal_data_actions { + PT_CAL_DATA_SAVE = 0, + PT_CAL_DATA_RESTORE = 1, + PT_CAL_DATA_CLEAR = 2, + PT_CAL_DATA_INFO = 3 +}; + +enum pt_feature_enable_state { + PT_FEATURE_DISABLE = 0, + PT_FEATURE_ENABLE = 1 +}; + +#define PT_NUM_MFGID 8 +/* System Information interface definitions */ +struct pt_ttdata_dev { + u8 pip_ver_major; + u8 pip_ver_minor; + __le16 fw_pid; + u8 fw_ver_major; + u8 fw_ver_minor; + __le32 revctrl; + __le16 fw_ver_conf; + u8 bl_ver_major; + u8 bl_ver_minor; + __le16 jtag_si_id_l; + __le16 jtag_si_id_h; + u8 mfg_id[PT_NUM_MFGID]; + __le16 post_code; +} __packed; + +/* Struct to cast over PIP2 VERSION response */ +struct pt_pip2_version_full { + u8 status_code; + u8 pip2_version_lsb; + u8 pip2_version_msb; + u8 fw_version_lsb; + u8 fw_version_msb; + u8 bl_version_lsb; + u8 bl_version_msb; + __le16 chip_rev; + __le16 chip_id; + u8 uid[PT_UID_SIZE]; +} __packed; + +struct pt_pip2_version { + u8 status_code; + u8 pip2_version_lsb; + u8 pip2_version_msb; + u8 bl_version_lsb; + u8 bl_version_msb; + u8 fw_version_lsb; + u8 fw_version_msb; + __le16 chip_id; + __le16 chip_rev; +} __packed; + +struct pt_sensing_conf_data_dev { + u8 electrodes_x; + u8 electrodes_y; + __le16 len_x; + __le16 len_y; + __le16 res_x; + __le16 res_y; + __le16 max_z; + u8 origin_x; + u8 origin_y; + u8 panel_id; + u8 btn; + u8 scan_mode; + u8 max_num_of_tch_per_refresh_cycle; +} __packed; + +struct pt_ttdata { + u8 pip_ver_major; + u8 pip_ver_minor; + u8 bl_ver_major; + u8 bl_ver_minor; + u8 fw_ver_major; + u8 fw_ver_minor; + u16 fw_pid; + u16 fw_ver_conf; + u16 post_code; + u32 revctrl; + u16 jtag_id_l; + u16 jtag_id_h; + u8 mfg_id[PT_NUM_MFGID]; + u16 chip_rev; + u16 chip_id; + u8 uid[PT_UID_SIZE]; +}; + +struct pt_sensing_conf_data { + u16 res_x; + u16 res_y; + u16 max_z; + u16 len_x; + u16 len_y; + u8 electrodes_x; + u8 electrodes_y; + u8 origin_x; + u8 origin_y; + u8 panel_id; + u8 btn; + u8 scan_mode; + u8 max_tch; + u8 rx_num; + u8 tx_num; +}; + +enum pt_tch_abs { /* for ordering within the extracted touch data array */ + PT_TCH_X, /* X */ + PT_TCH_Y, /* Y */ + PT_TCH_P, /* P (Z) */ + PT_TCH_T, /* TOUCH ID */ + PT_TCH_E, /* EVENT ID */ + PT_TCH_O, /* OBJECT ID */ + PT_TCH_TIP, /* OBJECT ID */ + PT_TCH_MAJ, /* TOUCH_MAJOR */ + PT_TCH_MIN, /* TOUCH_MINOR */ + PT_TCH_OR, /* ORIENTATION */ + PT_TCH_NUM_ABS, +}; + +enum pt_tch_hdr { + PT_TCH_TIME, /* SCAN TIME */ + PT_TCH_NUM, /* NUMBER OF RECORDS */ + PT_TCH_LO, /* LARGE OBJECT */ + PT_TCH_NOISE, /* NOISE EFFECT */ + PT_TCH_COUNTER, /* REPORT_COUNTER */ + PT_TCH_NUM_HDR, +}; + +static const char * const pt_tch_abs_string[] = { + [PT_TCH_X] = "X", + [PT_TCH_Y] = "Y", + [PT_TCH_P] = "P", + [PT_TCH_T] = "T", + [PT_TCH_E] = "E", + [PT_TCH_O] = "O", + [PT_TCH_TIP] = "TIP", + [PT_TCH_MAJ] = "MAJ", + [PT_TCH_MIN] = "MIN", + [PT_TCH_OR] = "OR", + [PT_TCH_NUM_ABS] = "INVALID", +}; + +static const char * const pt_tch_hdr_string[] = { + [PT_TCH_TIME] = "SCAN TIME", + [PT_TCH_NUM] = "NUMBER OF RECORDS", + [PT_TCH_LO] = "LARGE OBJECT", + [PT_TCH_NOISE] = "NOISE EFFECT", + [PT_TCH_COUNTER] = "REPORT_COUNTER", + [PT_TCH_NUM_HDR] = "INVALID", +}; + +static const int pt_tch_abs_field_map[] = { + [PT_TCH_X] = 0x00010030 /* HID_GD_X */, + [PT_TCH_Y] = 0x00010031 /* HID_GD_Y */, + [PT_TCH_P] = HID_DI_PRESSURE, + [PT_TCH_T] = HID_DI_CONTACTID, + [PT_TCH_E] = HID_PT_EVENTID, + [PT_TCH_O] = HID_PT_TOUCHTYPE, + [PT_TCH_TIP] = HID_DI_TIP, + [PT_TCH_MAJ] = HID_PT_MAJORAXISLENGTH, + [PT_TCH_MIN] = HID_PT_MINORAXISLENGTH, + [PT_TCH_OR] = HID_PT_ORIENTATION, + [PT_TCH_NUM_ABS] = 0, +}; + +static const int pt_tch_hdr_field_map[] = { + [PT_TCH_TIME] = HID_DI_SCANTIME, + [PT_TCH_NUM] = HID_DI_CONTACTCOUNT, + [PT_TCH_LO] = HID_PT_LARGEOBJECT, + [PT_TCH_NOISE] = HID_PT_NOISEEFFECTS, + [PT_TCH_COUNTER] = HID_PT_REPORTCOUNTER, + [PT_TCH_NUM_HDR] = 0, +}; + +#define PT_TOUCH_ID_MAX 32 +#define PT_NUM_EXT_TCH_FIELDS 3 + +struct pt_tch_abs_params { + size_t ofs; /* abs byte offset */ + size_t size; /* size in bits */ + size_t min; /* min value */ + size_t max; /* max value */ + size_t bofs; /* bit offset */ + u8 report; /* non-zero: valid; 0: invalid */ + size_t logical_max; /* logical max value */ +}; + +struct pt_touch { + int hdr[PT_TCH_NUM_HDR]; + int abs[PT_TCH_NUM_ABS]; +}; + +enum pt_pen_abs { /* for ordering within the extracted pen data array */ + PT_PEN_X, /* X */ + PT_PEN_Y, /* Y */ + PT_PEN_P, /* P (Z) */ + PT_PEN_X_TILT, /* X TILT */ + PT_PEN_Y_TILT, /* Y TILT */ + PT_PEN_TS, /* Tip Switch */ + PT_PEN_BS, /* Barrel Switch */ + PT_PEN_IV, /* Invert */ + PT_PEN_ER, /* Eraser */ + PT_PEN_2ND_BS, /* 2nd Barrel Switch */ + PT_PEN_IR, /* In Range */ + PT_PEN_NUM_ABS, +}; + +static const int pt_pen_abs_field_map[] = { + [PT_PEN_X] = 0x00010030 /* HID_GD_X */, + [PT_PEN_Y] = 0x00010031 /* HID_GD_Y */, + [PT_PEN_P] = 0x000D0030, + [PT_PEN_X_TILT] = 0x000D003D, + [PT_PEN_Y_TILT] = 0x000D003E, + [PT_PEN_TS] = 0x000D0042, + [PT_PEN_BS] = 0x000D0044, + [PT_PEN_IV] = 0x000D003C, + [PT_PEN_ER] = 0x000D0045, + [PT_PEN_2ND_BS] = 0x000D005A, + [PT_PEN_IR] = 0x000D0032, + [PT_PEN_NUM_ABS] = 0, +}; + +static const char * const pt_pen_abs_string[] = { + [PT_PEN_X] = "X", + [PT_PEN_Y] = "Y", + [PT_PEN_P] = "P", + [PT_PEN_X_TILT] = "X_TILT", + [PT_PEN_Y_TILT] = "Y_TILT", + [PT_PEN_TS] = "Tip_Switch", + [PT_PEN_BS] = "Barrel_Switch", + [PT_PEN_IV] = "Invert", + [PT_PEN_ER] = "Eraser", + [PT_PEN_2ND_BS] = "2nd Barrel_Switch", + [PT_PEN_IR] = "In_Range", + [PT_PEN_NUM_ABS] = "INVALID", +}; + +struct pt_pen { + int abs[PT_PEN_NUM_ABS]; +}; + +/* button to keycode support */ +#define PT_BITS_PER_BTN 1 +#define PT_NUM_BTN_EVENT_ID ((1 << PT_BITS_PER_BTN) - 1) + +enum pt_btn_state { + PT_BTN_RELEASED = 0, + PT_BTN_PRESSED = 1, + PT_BTN_NUM_STATE +}; + +struct pt_btn { + bool enabled; + int state; /* PT_BTN_PRESSED, PT_BTN_RELEASED */ + int key_code; +}; + +enum pt_ic_ebid { + PT_TCH_PARM_EBID = 0x00, + PT_MDATA_EBID = 0x01, + PT_DDATA_EBID = 0x02, + PT_CAL_EBID = 0xF0, +}; + +/* ttconfig block */ +#define PT_TTCONFIG_VERSION_OFFSET 8 +#define PT_TTCONFIG_VERSION_SIZE 2 +#define PT_TTCONFIG_VERSION_ROW 0 + +struct pt_ttconfig { + u16 version; + u16 crc; +}; + +struct pt_report_desc_data { + u16 tch_report_id; + u16 tch_record_size; + u16 tch_header_size; + u16 btn_report_id; + u16 pen_report_id; + u8 max_touch_num; + u8 max_tch_per_packet; +}; + +struct pt_sysinfo { + bool ready; + struct pt_ttdata ttdata; + struct pt_sensing_conf_data sensing_conf_data; + struct pt_report_desc_data desc; + int num_btns; + struct pt_btn *btn; + struct pt_ttconfig ttconfig; + struct pt_tch_abs_params tch_hdr[PT_TCH_NUM_HDR]; + struct pt_tch_abs_params tch_abs[PT_TCH_NUM_ABS]; + struct pt_tch_abs_params pen_abs[PT_PEN_NUM_ABS]; + u8 *xy_mode; + u8 *xy_data; +}; + +struct pt_bl_info { + bool ready; + u16 chip_id; +}; + +enum pt_atten_type { + PT_ATTEN_IRQ, + PT_ATTEN_STARTUP, + PT_ATTEN_EXCLUSIVE, + PT_ATTEN_WAKE, + PT_ATTEN_LOADER, + PT_ATTEN_SUSPEND, + PT_ATTEN_RESUME, + PT_ATTEN_CANCEL_LOADER, + PT_ATTEN_NUM_ATTEN, +}; + +enum pt_sleep_state { + SS_SLEEP_OFF, + SS_SLEEP_ON, + SS_SLEEPING, + SS_WAKING, +}; + +enum pt_fb_state { + FB_ON, + FB_OFF, +}; + +enum pt_startup_state { + STARTUP_NONE, + STARTUP_QUEUED, + STARTUP_RUNNING, + STARTUP_ILLEGAL, +}; + +struct pt_hid_desc { + __le16 hid_desc_len; + u8 packet_id; + u8 reserved_byte; + __le16 bcd_version; + __le16 report_desc_len; + __le16 report_desc_register; + __le16 input_register; + __le16 max_input_len; + __le16 output_register; + __le16 max_output_len; + __le16 command_register; + __le16 data_register; + __le16 vendor_id; + __le16 product_id; + __le16 version_id; + u8 reserved[4]; +} __packed; + +struct pt_hid_core { + u16 hid_vendor_id; + u16 hid_product_id; + __le16 hid_desc_register; + u16 hid_report_desc_len; + u16 hid_max_input_len; + u16 hid_max_output_len; +}; + +#define PT_HID_MAX_REPORTS 20 +#define PT_HID_MAX_FIELDS 128 +#define PT_HID_MAX_COLLECTIONS 3 +#define PT_HID_MAX_NESTED_COLLECTIONS PT_HID_MAX_COLLECTIONS +#define PT_HID_MAX_CONTINUOUS_USAGES 8 + +/* Max input is for ASCII representation of hex characters */ +#define PT_MAX_INPUT 2048 +#define PT_PIP_1P7_EMPTY_BUF 0xFF00 + +enum pt_module_id { + PT_MODULE_MT, + PT_MODULE_BTN, + PT_MODULE_PROX, + PT_MODULE_LAST, +}; + +struct pt_mt_data; +struct pt_mt_function { + int (*mt_release)(struct device *dev); + int (*mt_probe)(struct device *dev, struct pt_mt_data *md); + void (*report_slot_liftoff)(struct pt_mt_data *md, int max_slots); + void (*input_sync)(struct input_dev *input); + void (*input_report)(struct input_dev *input, int sig, int t, int type); + void (*final_sync)(struct input_dev *input, int max_slots, + int mt_sync_count, unsigned long *ids); + int (*input_register_device)(struct input_dev *input, int max_slots); +}; + +struct pt_mt_data { + struct device *dev; + struct pt_mt_platform_data *pdata; + struct pt_sysinfo *si; + struct input_dev *input; + struct pt_mt_function mt_function; + struct mutex mt_lock; + bool is_suspended; + bool input_device_registered; + bool input_device_allocated; + char phys[NAME_MAX]; + int num_prv_rec; + int or_min; + int or_max; + int t_min; + int t_max; +}; + +struct pt_pen_data { + struct device *dev; + struct pt_pen_platform_data *pdata; + struct pt_sysinfo *si; + struct input_dev *input; + struct mutex pen_lock; + bool is_suspended; + bool input_device_registered; + bool input_device_allocated; + char phys[NAME_MAX]; +}; + +struct pt_btn_data { + struct device *dev; + struct pt_btn_platform_data *pdata; + struct pt_sysinfo *si; + struct input_dev *input; + struct mutex btn_lock; + bool is_suspended; + bool input_device_registered; + bool input_device_allocated; + char phys[NAME_MAX]; +}; + +struct pt_proximity_data { + struct device *dev; + struct pt_proximity_platform_data *pdata; + struct pt_sysinfo *si; + struct input_dev *input; + struct mutex prox_lock; + struct mutex sysfs_lock; + int enable_count; + bool input_device_registered; + bool input_device_allocated; + char phys[NAME_MAX]; +}; + +enum pt_calibrate_idacs_sensing_mode { + PT_CI_SM_MUTCAP_FINE, + PT_CI_SM_MUTCAP_BUTTON, + PT_CI_SM_SELFCAP, +}; + +enum pt_initialize_baselines_sensing_mode { + PT_IB_SM_MUTCAP = 1, + PT_IB_SM_BUTTON = 2, + PT_IB_SM_SELFCAP = 4, + PT_IB_SM_BALANCED = 8, +}; + +/* parameters for extended calibrate command(0x30)*/ +struct pt_cal_ext_data { + u8 mode; + u8 data0; + u8 data1; + u8 data2; +} __packed; +#define PT_CAL_EXT_MODE_UNDEFINED 0xFF + +#define PT_BIN_FILE_MIN_HDR_LENGTH 14 +#define PT_BIN_FILE_MAX_HDR_LENGTH 18 +struct pt_bin_file_hdr { + u8 length; + u16 ttpid; + u8 fw_major; + u8 fw_minor; + u32 fw_rev_ctrl; + u32 fw_crc; + u16 si_rev; + u16 si_id; + u16 config_ver; + u32 hex_file_size; +}; + +struct pt_core_nonhid_cmd { + int (*start_bl)(struct device *dev, int protect); + int (*suspend_scanning)(struct device *dev, int protect); + int (*resume_scanning)(struct device *dev, int protect); + int (*get_param)(struct device *dev, int protect, u8 param_id, + u32 *value); + int (*set_param)(struct device *dev, int protect, u8 param_id, + u32 value, u8 size); + int (*verify_cfg_block_crc)(struct device *dev, int protect, + u8 ebid, u8 *status, u16 *calculated_crc, + u16 *stored_crc); + int (*get_config_row_size)(struct device *dev, int protect, + u16 *row_size); + int (*get_data_structure)(struct device *dev, int protect, + u16 read_offset, u16 read_length, u8 data_id, + u8 *status, u8 *data_format, u16 *actual_read_len, + u8 *data); + int (*run_selftest)(struct device *dev, int protect, u8 test_id, + u8 write_idacs_to_flash, u8 *status, u8 *summary_result, + u8 *results_available); + int (*get_selftest_result)(struct device *dev, int protect, + u16 read_offset, u16 read_length, u8 test_id, u8 *status, + u16 *actual_read_len, u8 *data); + int (*load_self_test_param)(struct device *dev, int protect, + u8 self_test_id, u16 load_offset, u16 load_length, + u8 *parameters, u8 *status, u8 *ret_test_id, u16 *act_load_len); + int (*calibrate_idacs)(struct device *dev, int protect, u8 mode, + u8 *status); + int (*calibrate_ext)(struct device *dev, + int protect, struct pt_cal_ext_data *cal_data, u8 *status); + int (*initialize_baselines)(struct device *dev, int protect, + u8 test_id, u8 *status); + int (*exec_panel_scan)(struct device *dev, int protect, u8 scan_type); + int (*retrieve_panel_scan)(struct device *dev, int protect, + u16 read_offset, u16 read_count, u8 data_id, + u8 *response, u8 *config, u16 *actual_read_len, + u8 *read_buf); + int (*read_data_block)(struct device *dev, u16 row_number, + u16 length, u8 ebid, u16 *actual_read_len, + u8 *read_buf, u16 read_buf_size, u16 *crc); + int (*write_data_block)(struct device *dev, u16 row_number, + u16 write_length, u8 ebid, u8 *write_buf, + u8 *security_key, u16 *actual_write_len); + int (*user_cmd)(struct device *dev, int protect, u16 read_len, + u8 *read_buf, u16 write_len, u8 *write_buf, + u16 *actual_read_len); + int (*get_bl_info)(struct device *dev, int protect, u8 *return_data); + int (*initiate_bl)(struct device *dev, int protect, u16 key_size, + u8 *key_buf, u16 row_size, u8 *metadata_row_buf); + int (*launch_app)(struct device *dev, int protect); + int (*prog_and_verify)(struct device *dev, int protect, u16 data_len, + u8 *data_buf); + int (*verify_app_integrity)(struct device *dev, int protect, + u8 *result); + int (*get_panel_id)(struct device *dev, int protect, u8 *panel_id); + int (*pip2_send_cmd)(struct device *dev, int protect, + u8 id, u8 *data, u16 report_body_len, u8 *read_buf, + u16 *actual_read_len); + int (*pip2_send_cmd_no_int)(struct device *dev, int protect, + u8 id, u8 *data, u16 report_body_len, u8 *read_buf, + u16 *actual_read_len); + int (*get_bl_pip2_version)(struct device *dev); + int (*pip2_file_open)(struct device *dev, u8 file_no); + int (*pip2_file_close)(struct device *dev, u8 file_no); + int (*pip2_file_erase)(struct device *dev, u8 file_no, u16 file_sector, + int *status); + int (*read_us_file)(struct device *dev, u8 *file_path, u8 *buf, + int *size); + int (*pip2_file_read)(struct device *dev, u8 file_no, + u16 num_bytes, u8 *read_buf); + int (*pip2_file_seek_offset)(struct device *dev, u8 file_no, + u32 read_offset, u32 write_offset); + int (*pip2_file_get_stats)(struct device *dev, u8 file_no, + u32 *address, u32 *file_size); + int (*pip2_file_crc)(struct device *dev, u8 file_no, + u32 offset, u32 length, u8 *read_buf); + int (*manage_cal_data)(struct device *dev, u8 action, u16 *size, + unsigned short *crc); + unsigned short (*calc_crc)(unsigned char *q, int len); +}; + +typedef int (*pt_atten_func) (struct device *); + +struct pt_core_commands { + int (*subscribe_attention)(struct device *dev, + enum pt_atten_type type, char *id, + pt_atten_func func, int flags); + int (*unsubscribe_attention)(struct device *dev, + enum pt_atten_type type, char *id, + pt_atten_func func, int flags); + int (*request_exclusive)(struct device *dev, int timeout_ms); + int (*release_exclusive)(struct device *dev); + int (*request_reset)(struct device *dev, int protect); + int (*request_pip2_launch_app)(struct device *dev, int protect); + int (*request_enum)(struct device *dev, bool wait); + struct pt_sysinfo * (*request_sysinfo)(struct device *dev); + struct pt_loader_platform_data + *(*request_loader_pdata)(struct device *dev); + int (*request_stop_wd)(struct device *dev); + int (*request_start_wd)(struct device *dev); + int (*request_get_mode)(struct device *dev, int protect, u8 *mode); + int (*request_pip2_get_mode_sysmode)(struct device *dev, int protect, + u8 *mode, u8 *sys_mode); + int (*request_active_pip_prot)(struct device *dev, int protect, + u8 *pip_version_major, u8 *pip_version_minor); + int (*request_enable_scan_type)(struct device *dev, u8 scan_type); + int (*request_disable_scan_type)(struct device *dev, u8 scan_type); + int (*request_pip2_enter_bl)(struct device *dev, u8 *start_mode, + int *result); + int (*request_pip2_bin_hdr)(struct device *dev, + struct pt_bin_file_hdr *hdr); + int (*request_dut_generation)(struct device *dev); + int (*request_hw_version)(struct device *dev, char *hw_version); +#ifndef TTDL_KERNEL_SUBMISSION + int (*parse_sysfs_input)(struct device *dev, + const char *buf, size_t buf_size, + u32 *out_buf, size_t out_buf_size); +#endif +#ifdef TTHE_TUNER_SUPPORT + int (*request_tthe_print)(struct device *dev, u8 *buf, int buf_len, + const u8 *data_name); +#endif +#ifdef TTDL_DIAGNOSTICS + void (*request_toggle_err_gpio)(struct device *dev, u8 type); +#endif + struct pt_core_nonhid_cmd *nonhid_cmd; + int (*request_get_fw_mode)(struct device *dev, int protect, + u8 *sys_mode, u8 *mode); +}; + +enum core_command_protected_status { + PT_CORE_CMD_UNPROTECTED = 0, + PT_CORE_CMD_PROTECTED = 1 +}; + +enum pt_err_gpio_type { + PT_ERR_GPIO_NONE = 0, + PT_ERR_GPIO_I2C_TRANS = 1, + PT_ERR_GPIO_IRQ_STUCK = 2, + PT_ERR_GPIO_EXCLUSIVE_ACCESS = 3, + PT_ERR_GPIO_EMPTY_PACKET = 4, + PT_ERR_GPIO_BL_RETRY_PACKET = 5, + PT_ERR_GPIO_MAX_TYPE = PT_ERR_GPIO_BL_RETRY_PACKET, +}; + +struct pt_features { + uint8_t easywake; + uint8_t noise_metric; + uint8_t tracking_heatmap; + uint8_t sensor_data; +}; + +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_RUNTIME) +#if (KERNEL_VERSION(3, 3, 0) > LINUX_VERSION_CODE) +#define NEED_SUSPEND_NOTIFIER +#endif /* CONFIG_PM_SLEEP && CONFIG_PM_RUNTIME */ +#endif /* LINUX_VERSION_CODE */ + +struct pt_module { + struct list_head node; + char *name; + int (*probe)(struct device *dev, void **data); + void (*release)(struct device *dev, void *data); +}; + +struct pt_bus_ops { + u16 bustype; + int (*read_default)(struct device *dev, void *buf, int size); + int (*read_default_nosize)(struct device *dev, u8 *buf, u32 max); + int (*write_read_specific)(struct device *dev, u16 write_len, + u8 *write_buf, u8 *read_buf, u16 read_len); +}; + +#define PT_MAX_DEVICES 3 +struct pt_bist_data { + u8 detected; + u8 mask; + u8 boot_err; + u8 bus_toggled; + u8 irq_toggled; + u8 xres_toggled; + char *bus_err_str; + char *irq_err_str; + char *xres_err_str; + char *print_buf; + u16 pr_index; + int status; +}; + +struct pt_core_data { + struct list_head node; + struct list_head module_list; /* List of probed modules */ + char core_id[20]; + struct device *dev; + struct list_head atten_list[PT_ATTEN_NUM_ATTEN]; + struct list_head param_list; + struct mutex module_list_lock; + struct mutex system_lock; + struct mutex sysfs_lock; + struct mutex ttdl_restart_lock; + struct mutex firmware_class_lock; + struct mutex hid_report_lock; + enum pt_mode mode; + spinlock_t spinlock; + struct pt_mt_data md; + struct pt_pen_data pend; + struct pt_btn_data bd; + struct pt_proximity_data pd; + int phys_num; + int pip_cmd_timeout; + int pip_cmd_timeout_default; + void *pt_dynamic_data[PT_MODULE_LAST]; + struct pt_platform_data *pdata; + struct pt_core_platform_data *cpdata; + const struct pt_bus_ops *bus_ops; + wait_queue_head_t wait_q; + enum pt_sleep_state sleep_state; + enum pt_startup_state startup_state; + int irq; + bool irq_enabled; + bool irq_wake; + bool irq_disabled; + bool hw_detected; + u8 easy_wakeup_gesture; +#ifdef EASYWAKE_TSG6 + u8 gesture_id; + u8 gesture_data_length; + u8 gesture_data[80]; +#endif + bool wait_until_wake; + u8 pid_for_loader; + char hw_version[13]; +#ifdef NEED_SUSPEND_NOTIFIER + /* + * This notifier is used to receive suspend prepare events + * When device is PM runtime suspended, pm_generic_suspend() + * does not call our PM suspend callback for kernels with + * version less than 3.3.0. + */ + struct notifier_block pm_notifier; +#endif + struct pt_sysinfo sysinfo; + struct pt_bl_info bl_info; + struct pt_dut_status dut_status; + void *exclusive_dev; + int exclusive_waits; + struct timer_list watchdog_timer; + struct work_struct watchdog_work; + struct work_struct enum_work; + struct work_struct ttdl_restart_work; +#ifdef PT_PTSBC_SUPPORT + struct work_struct irq_work; + struct work_struct probe_work; + struct timer_list probe_timer; +#endif + u16 startup_retry_count; + struct pt_hid_core hid_core; + int hid_cmd_state; + int hid_reset_cmd_state; /* reset can happen any time */ + struct pt_hid_desc hid_desc; + struct pt_hid_report *hid_reports[PT_HID_MAX_REPORTS]; + int num_hid_reports; + struct pt_features features; +#define PT_PREALLOCATED_CMD_BUFFER 32 + u8 cmd_buf[PT_PREALLOCATED_CMD_BUFFER]; + u8 input_buf[PT_MAX_INPUT]; + u8 response_buf[PT_MAX_INPUT]; + u8 cmd_rsp_buf[PT_MAX_INPUT]; + u8 touch_buf[PT_MAX_INPUT]; + u16 cmd_rsp_buf_len; + int raw_cmd_status; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend es; +#elif defined(CONFIG_FB) + struct notifier_block fb_notifier; + enum pt_fb_state fb_state; +#endif +#ifdef TTHE_TUNER_SUPPORT + struct dentry *tthe_debugfs; + u8 *tthe_buf; + u32 tthe_buf_len; + u32 tthe_buf_size; + struct mutex tthe_lock; + u8 tthe_exit; +#endif + u8 debug_level; + u8 watchdog_enabled; + bool watchdog_force_stop; + u32 watchdog_interval; + u8 show_timestamp; + u32 startup_status; + u8 pip2_cmd_tag_seq; + u8 pip2_prot_active; + u8 pip2_send_user_cmd; + u8 get_param_id; + bool bl_pip_ver_ready; + bool app_pip_ver_ready; + u8 core_probe_complete; + u8 active_dut_generation; + bool set_dut_generation; + u8 fw_system_mode; + u8 flashless_dut; + u8 bl_with_no_int; + u8 cal_cache_in_host; + u8 num_devices; + u8 tthe_hid_usb_format; + u8 flashless_auto_bl; + u8 pip2_us_file_path[PT_MAX_PATH_SIZE]; + bool fw_updating; + bool fw_sys_mode_in_standby_state; +#ifdef TTDL_PTVIRTDUT_SUPPORT + u8 route_bus_virt_dut; +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + u8 panel_id_support; +#ifdef TTDL_DIAGNOSTICS + u8 t_refresh_active; + u8 flush_bus_type; + u8 ttdl_bist_select; + u8 force_pip2_seq; + u16 ping_test_size; + u16 pip2_crc_error_count; + u16 t_refresh_count; + u16 t_refresh_total; + u16 wd_xres_count; + u32 watchdog_count; + u32 watchdog_irq_stuck_count; + u32 watchdog_failed_access_count; + u32 bus_transmit_error_count; + u32 irq_count; + u32 bl_retry_packet_count; + u32 file_erase_timeout_count; + unsigned long t_refresh_time; + u16 err_gpio; + u16 err_gpio_type; + bool show_tt_data; + bool bridge_mode; + bool hw_detect_enabled; +#endif +}; + +struct gd_sensor { + int32_t cm_min; + int32_t cm_max; + int32_t cm_ave; + int32_t cm_min_exclude_edge; + int32_t cm_max_exclude_edge; + int32_t cm_ave_exclude_edge; + int32_t gradient_val; +}; + +#ifdef TTHE_TUNER_SUPPORT +#define PT_CMD_RET_PANEL_IN_DATA_OFFSET 0 +#define PT_CMD_RET_PANEL_ELMNT_SZ_MASK 0x07 +#define PT_CMD_RET_PANEL_HDR 0x0A +#define PT_CMD_RET_PANEL_ELMNT_SZ_MAX 0x2 + +enum scan_data_type_list { + PT_MUT_RAW, + PT_MUT_BASE, + PT_MUT_DIFF, + PT_SELF_RAW, + PT_SELF_BASE, + PT_SELF_DIFF, + PT_BAL_RAW, + PT_BAL_BASE, + PT_BAL_DIFF, +}; +#endif + +static inline int pt_adap_read_default(struct pt_core_data *cd, + void *buf, int size) +{ + return cd->bus_ops->read_default(cd->dev, buf, size); +} + +static inline int pt_adap_read_default_nosize(struct pt_core_data *cd, + void *buf, int max) +{ + return cd->bus_ops->read_default_nosize(cd->dev, buf, max); +} + +static inline int pt_adap_write_read_specific(struct pt_core_data *cd, + u16 write_len, u8 *write_buf, u8 *read_buf, u16 read_len) +{ + return cd->bus_ops->write_read_specific(cd->dev, write_len, write_buf, + read_buf, read_len); +} + +static inline void *pt_get_dynamic_data(struct device *dev, int id) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + return cd->pt_dynamic_data[id]; +} + +int request_exclusive(struct pt_core_data *cd, void *ownptr, + int timeout_ms); +int release_exclusive(struct pt_core_data *cd, void *ownptr); +int _pt_request_pip_get_param(struct device *dev, + int protect, u8 param_id, u32 *value); +int _pt_request_pip_set_param(struct device *dev, + int protect, u8 param_id, u32 value, u8 size); + +static inline int pt_request_exclusive(struct device *dev, int timeout_ms) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + return request_exclusive(cd, dev, timeout_ms); +} + +static inline int pt_release_exclusive(struct device *dev) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + return release_exclusive(cd, dev); +} + +static inline int pt_request_nonhid_get_param(struct device *dev, + int protect, u8 param_id, u32 *value) +{ + return _pt_request_pip_get_param(dev, protect, param_id, + value); +} + +static inline int pt_request_nonhid_set_param(struct device *dev, + int protect, u8 param_id, u32 value, u8 size) +{ + return _pt_request_pip_set_param(dev, protect, param_id, + value, size); +} + +void pt_pr_buf(struct device *dev, u8 debug_level, u8 *buf, + u16 buf_len, const char *data_name); + +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT +int pt_devtree_create_and_get_pdata(struct device *adap_dev); +int pt_devtree_clean_pdata(struct device *adap_dev); +#else +static inline int pt_devtree_create_and_get_pdata(struct device *adap_dev) +{ + return 0; +} + +static inline int pt_devtree_clean_pdata(struct device *adap_dev) +{ + return 0; +} +#endif + +int pt_probe(const struct pt_bus_ops *ops, struct device *dev, + u16 irq, size_t xfer_buf_size); +int pt_release(struct pt_core_data *cd); + +struct pt_core_commands *pt_get_commands(void); +struct pt_core_data *pt_get_core_data(char *id); + +#ifdef PT_AUX_BRIDGE_ENABLED +int pt_trigger_ttdl_irq(void); +#endif + +int pt_mt_release(struct device *dev); +int pt_mt_probe(struct device *dev); + +#ifdef CONFIG_TOUCHSCREEN_PARADE_BUTTON +int pt_btn_probe(struct device *dev); +int pt_btn_release(struct device *dev); +#else +static inline int pt_btn_probe(struct device *dev) { return 0; } +static inline int pt_btn_release(struct device *dev) { return 0; } +#endif + +#ifdef CONFIG_TOUCHSCREEN_PARADE_PROXIMITY +int pt_proximity_probe(struct device *dev); +int pt_proximity_release(struct device *dev); +#else +static inline int pt_proximity_probe(struct device *dev) { return 0; } +static inline int pt_proximity_release(struct device *dev) { return 0; } +#endif + +static inline unsigned int pt_get_time_stamp(void) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)) + struct timespec64 ts; + + ktime_get_real_ts64(&ts); + return (ts.tv_sec*1000 + ts.tv_nsec/1000000); +#else + struct timeval tv; + + do_gettimeofday(&tv); + return (tv.tv_sec*1000 + tv.tv_usec/1000); +#endif +} + +void pt_init_function_ptrs(struct pt_mt_data *md); +void pt_get_touch_field(struct device *dev, + int *field, int size, int max, u8 *data, int bofs); +int _pt_subscribe_attention(struct device *dev, + enum pt_atten_type type, char *id, int (*func)(struct device *), + int mode); +int _pt_unsubscribe_attention(struct device *dev, + enum pt_atten_type type, char *id, int (*func)(struct device *), + int mode); +struct pt_sysinfo *_pt_request_sysinfo(struct device *dev); + +extern const struct dev_pm_ops pt_pm_ops; + +int pt_register_module(struct pt_module *module); +void pt_unregister_module(struct pt_module *module); + +void *pt_get_module_data(struct device *dev, + struct pt_module *module); + +#endif /* _PT_REGS_H */ diff --git a/pt/pt_spi.c b/pt/pt_spi.c new file mode 100644 index 0000000000..c921ee8c86 --- /dev/null +++ b/pt/pt_spi.c @@ -0,0 +1,657 @@ +/* + * pt_spi.c + * Parade TrueTouch(TM) Standard Product SPI Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.paradetech.com + */ + +#include "pt_regs.h" + +#include +#include + +/* TC3315 - DUT Address (0x24 & 0x07) << 1 = 0x08 for write and 0x09 for read */ +#define PT_SPI_WR_OP 0x08 /* r/~w */ +#define PT_SPI_RD_OP 0x09 +#define PT_SPI_BITS_PER_WORD 8 +#define PT_SPI_SYNC_ACK 0x62 + +#define PT_SPI_CMD_BYTES 0 +#define PT_SPI_DATA_SIZE (2 * 256) +#define PT_SPI_DATA_BUF_SIZE (PT_SPI_CMD_BYTES + PT_SPI_DATA_SIZE) + +#define PT_SPI_OP_SIZE (1) +#define PT_SPI_DUMMY_READ (1) +#define PT_SPI_BUFFER_SIZE \ + (PT_MAX_PIP2_MSG_SIZE + PT_SPI_OP_SIZE + PT_SPI_DUMMY_READ) + +static u8 *tmp_rbuf; +static u8 *tmp_wbuf; +DEFINE_MUTEX(pt_spi_bus_lock); + +#ifdef TTDL_PTVIRTDUT_SUPPORT +#define VIRT_DUT_BUF_SIZE 300 +static unsigned char pt_dut_cmd_buf[VIRT_DUT_BUF_SIZE]; +static unsigned char pt_dut_out_buf[VIRT_DUT_BUF_SIZE]; +static int pt_dut_cmd_len; +static int pt_dut_out_len; +DEFINE_MUTEX(virt_spi_lock); + +/******************************************************************************* + * FUNCTION: virt_spi_transfer + * + * SUMMARY: Copies the current spi output message to the temporary buffer + * used by the dut_cmd sysfs node + * + * RETURN VALUE: + * Number of messages transferred which in this function will be 1 + * + * PARAMETERS: + * *buf - pointer to spi command + * len - length of command in the buffer + ******************************************************************************/ +static int virt_spi_transfer(u8 *buf, int len) +{ + int rc = 0; + + mutex_lock(&virt_spi_lock); + if (len <= sizeof(pt_dut_cmd_buf)) { + memcpy(pt_dut_cmd_buf, buf, len); + pt_dut_cmd_len = len; + rc = 1; + } else + rc = 0; + mutex_unlock(&virt_spi_lock); + + return rc; +} + +/******************************************************************************* + * FUNCTION: virt_spi_master_recv + * + * SUMMARY: Copies the spi input message from the dut_out sysfs node into a + * temporary buffer. + * + * RETURN VALUE: + * Length of data transferred + * + * PARAMETERS: + * *dev - pointer to device struct + * *buf - pointer to spi incoming report + * size - size to be read + ******************************************************************************/ +static int virt_spi_master_recv(struct device *dev, u8 *buf, int size) +{ +#ifndef PT_POLL_RESP_BY_BUS + struct pt_core_data *cd = dev_get_drvdata(dev); + int i = 0; +#endif + + mutex_lock(&virt_spi_lock); + memcpy(buf, pt_dut_out_buf, size); + + /* Set "empty buffer" */ + pt_dut_out_buf[1] = 0xFF; + pt_dut_out_len = 0; + mutex_unlock(&virt_spi_lock); + +#ifndef PT_POLL_RESP_BY_BUS + if (cd->bl_with_no_int) { + /* + * Wait for IRQ gpio to be released, make read operation + * synchronize with PtVirtDut tool. + * Safety net: Exit after 500ms (50us * 10000 loops = 500ms) + */ + while (i < VIRT_MAX_IRQ_RELEASE_TIME_US && + !gpio_get_value(cd->cpdata->irq_gpio)) { + pt_debug(dev, DL_INFO, "%s: %d IRQ still Enabled\n", + __func__, i); + usleep_range(50, 60); + i += 50; + } + } +#endif + + pt_debug(dev, DL_INFO, + "%s: Copy msg from dut_out to spi buffer, size=%d\n", + __func__, size); + + return size; +} + +/******************************************************************************* + * FUNCTION: pt_dut_cmd_show + * + * SUMMARY: The show function for the dut_cmd sysfs node. Provides read access + * to the pt_dut_cmd_buf and clears it after it has been read. + * + * RETURN VALUE: + * Number of bytes transferred + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to output buffer + ******************************************************************************/ +static ssize_t pt_dut_cmd_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + int index = 0; + + /* Only print to sysfs if the buffer has data */ + mutex_lock(&virt_spi_lock); + if (pt_dut_cmd_len > 0) { + for (i = 0; i < pt_dut_cmd_len; i++) + index += scnprintf(buf + index, strlen(buf), "%02X", + pt_dut_cmd_buf[i]); + index += scnprintf(buf + index, strlen(buf), "\n"); + } + pt_dut_cmd_len = 0; + mutex_unlock(&virt_spi_lock); + return index; +} +static DEVICE_ATTR(dut_cmd, 0444, pt_dut_cmd_show, NULL); + +/******************************************************************************* + * FUNCTION: pt_dut_out_store + * + * SUMMARY: The store function for the dut_out sysfs node. Provides write + * access to the pt_dut_out_buf. The smallest valid PIP response is 2 + * bytes so don't update buffer if only 1 byte passed in. + * + * RETURN VALUE: + * Number of bytes read from virtual DUT + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_dut_out_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int loop_max = ARRAY_SIZE(pt_dut_out_buf); + int hex_str_len = strlen(buf)/2; + int i; + const char *pos = buf; + + /* Clear out the last message */ + mutex_lock(&virt_spi_lock); + memset(pt_dut_out_buf, 0, VIRT_DUT_BUF_SIZE); + pt_dut_out_len = 0; + + /* Only update the dut_out buffer if at least 2 byte payload */ + if (size >= 2 && hex_str_len <= loop_max) { + /* Convert string of hex values to byte array */ + for (i = 0; i < hex_str_len; i++) { + pt_dut_out_buf[i] = ((HEXOF(*pos)) << 4) + + HEXOF(*(pos + 1)); + pos += 2; + } + pt_dut_out_len = get_unaligned_le16(&pt_dut_out_buf[0]); + } else if (size >= PT_PIP_1P7_EMPTY_BUF) { + /* Message too large, set to 'empty buffer' message */ + pt_dut_out_buf[1] = 0xFF; + pt_dut_out_len = 0; + } + mutex_unlock(&virt_spi_lock); + + return size; +} +static DEVICE_ATTR(dut_out, 0200, NULL, pt_dut_out_store); +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + +/******************************************************************************* + * FUNCTION: pt_spi_xfer + * + * SUMMARY: Read or write date for SPI device. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to device structure + * op - flag to write or read data + * *buf - pointer to data buffer + * length - data length + ******************************************************************************/ +static int pt_spi_xfer(struct device *dev, u8 op, u8 *buf, int length) +{ + struct spi_device *spi = to_spi_device(dev); + struct spi_message msg; + struct spi_transfer xfer; + int rc; + + memset(&xfer, 0, sizeof(xfer)); + spi_message_init(&msg); + + switch (op) { + case PT_SPI_RD_OP: + /* Clear tmp_wbuf with additional OP Code and dummy byte */ + memset(tmp_wbuf, 0, length + 2); + tmp_wbuf[0] = op; + /* Total read/write = Read length + Op code + dummy byte */ + xfer.tx_buf = tmp_wbuf; + xfer.rx_buf = tmp_rbuf; + xfer.len = length + 2; + break; + case PT_SPI_WR_OP: + memcpy(&tmp_wbuf[1], buf, length); + tmp_wbuf[0] = op; + /* Write length + size of Op code */ + xfer.tx_buf = tmp_wbuf; + xfer.len = length + 1; + break; + default: + rc = -EIO; + goto exit; + } + + spi_message_add_tail(&xfer, &msg); + rc = spi_sync(spi, &msg); + + /* On reads copy only the data content back into the passed in buf */ + if (op == PT_SPI_RD_OP) + memcpy(buf, &tmp_rbuf[2], length); +exit: + if (rc < 0) + pt_debug(dev, DL_ERROR, "%s: spi_sync() error %d\n", + __func__, rc); + +#if 0 /* TODO TC3315 - need to verify the ACK byte */ + if (tmp_rbuf[0] != PT_SPI_SYNC_ACK) { + pt_debug(dev, DL_ERROR, "%s: r_header = 0x%02X\n", __func__, + r_header[0]); + return -EIO; + } +#endif + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_spi_read_default + * + * SUMMARY: Read a certain number of bytes from the SPI bus + * NOTE: For TC3315 every response includes a "dummy" prefix byte that + * needs to be stipped off before returning buf. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to Device structure + * *buf - pointer to buffer where the data read will be stored + * size - size to be read + ******************************************************************************/ +static int pt_spi_read_default(struct device *dev, void *buf, int size) +{ +#ifdef TTDL_PTVIRTDUT_SUPPORT + struct pt_core_data *cd = dev_get_drvdata(dev); +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + int rc = 0; + + if (!buf || !size || size > PT_MAX_PIP2_MSG_SIZE) + return -EINVAL; + + mutex_lock(&pt_spi_bus_lock); +#ifdef TTDL_PTVIRTDUT_SUPPORT + if (cd->route_bus_virt_dut) + virt_spi_master_recv(dev, buf, size); + else + rc = pt_spi_xfer(dev, PT_SPI_RD_OP, buf, size); +#else + rc = pt_spi_xfer(dev, PT_SPI_RD_OP, buf, size); +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + mutex_unlock(&pt_spi_bus_lock); + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_spi_read_default_nosize + * + * SUMMARY: Read from the SPI bus in two transactions first reading the HID + * packet size (2 bytes) followed by reading the rest of the packet based + * on the size initially read. + * NOTE: The empty buffer 'size' was redefined in PIP version 1.7. + * NOTE: For TC3315 every response includes a "dummy" prefix byte that + * needs to be stipped off before returning buf. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to Device structure + * *buf - pointer to buffer where the data read will be stored + * max - max size that can be read + ******************************************************************************/ +static int pt_spi_read_default_nosize(struct device *dev, u8 *buf, u32 max) +{ + u32 size; + int rc = 0; +#ifdef TTDL_PTVIRTDUT_SUPPORT + struct pt_core_data *cd = dev_get_drvdata(dev); + + if (cd->route_bus_virt_dut) { + mutex_lock(&virt_spi_lock); + size = pt_dut_out_len; + mutex_unlock(&virt_spi_lock); + /* Only copy 2 bytes for "empty buffer" or "FW sentinel" */ + if (!size || size == 2 || size >= PT_PIP_1P7_EMPTY_BUF) + size = 2; + virt_spi_master_recv(dev, buf, size); + return 0; + } + +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + + if (!buf) + return 0; + + mutex_lock(&pt_spi_bus_lock); + + /* Separate transaction to retrieve only the length to read */ + rc = pt_spi_xfer(dev, PT_SPI_RD_OP, buf, 2); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: SPI transfer error rc = %d\n", + __func__, rc); + goto exit; + } + + size = get_unaligned_le16(&buf[0]); + if (!size || size == 2 || size >= PT_PIP_1P7_EMPTY_BUF) + goto exit; + + if (size > max || size > PT_MAX_PIP2_MSG_SIZE) { + pt_debug(dev, DL_ERROR, "%s: Invalid size %d !\n", __func__, + size); + rc = -EINVAL; + goto exit; + } + + rc = pt_spi_xfer(dev, PT_SPI_RD_OP, buf, size); + if (rc) + pt_debug(dev, DL_ERROR, "%s: SPI transfer error rc = %d\n", + __func__, rc); + +exit: + mutex_unlock(&pt_spi_bus_lock); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_spi_write_read_specific + * + * SUMMARY: Write the contents of write_buf to the SPI device and then read + * the response using pt_spi_read_default_nosize() + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to Device structure + * write_len - length of data buffer write_buf + * *write_buf - pointer to buffer to write + * *read_buf - pointer to buffer to read response into + * read_len - length to read, 0 to use pt_spi_read_default_nosize + ******************************************************************************/ +static int pt_spi_write_read_specific(struct device *dev, u16 write_len, + u8 *write_buf, u8 *read_buf, u16 read_len) +{ +#ifdef TTDL_PTVIRTDUT_SUPPORT + struct pt_core_data *cd = dev_get_drvdata(dev); +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + int rc = 0; + + /* Ensure no packet larger than what the PIP spec allows */ + if (write_len > PT_MAX_PIP2_MSG_SIZE) + return -EINVAL; + + if (!write_buf || !write_len) { + if (!write_buf) + pt_debug(dev, DL_ERROR, + "%s write_buf is NULL", __func__); + if (!write_len) + pt_debug(dev, DL_ERROR, + "%s write_len is NULL", __func__); + return -EINVAL; + } + + mutex_lock(&pt_spi_bus_lock); +#ifdef TTDL_PTVIRTDUT_SUPPORT + if (cd->route_bus_virt_dut) { + virt_spi_transfer(write_buf, write_len); + pt_debug(dev, DL_DEBUG, "%s: Virt transfer size = %d", + __func__, write_len); + } else { + rc = pt_spi_xfer(dev, PT_SPI_WR_OP, write_buf, write_len); + if (rc < 0) + goto error; + } +#else + rc = pt_spi_xfer(dev, PT_SPI_WR_OP, write_buf, write_len); + if (rc < 0) + goto error; +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + mutex_unlock(&pt_spi_bus_lock); + + if (read_buf) + rc = pt_spi_read_default_nosize(dev, read_buf, + PT_SPI_DATA_SIZE); + return rc; + +error: + mutex_unlock(&pt_spi_bus_lock); + return rc; +} + +static struct pt_bus_ops pt_spi_bus_ops = { + .bustype = BUS_SPI, + .read_default = pt_spi_read_default, + .read_default_nosize = pt_spi_read_default_nosize, + .write_read_specific = pt_spi_write_read_specific, +}; + +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT +static const struct of_device_id pt_spi_of_match[] = { + { .compatible = "parade,pt_spi_adapter", }, + { } +}; +MODULE_DEVICE_TABLE(of, pt_spi_of_match); +#endif + +/******************************************************************************* + * FUNCTION: pt_spi_probe + * + * SUMMARY: Probe functon for the SPI module + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *spi - pointer to spi device structure + ******************************************************************************/ +static int pt_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT + const struct of_device_id *match; +#endif + int rc; + + /* Set up SPI*/ + spi->bits_per_word = PT_SPI_BITS_PER_WORD; + spi->mode = SPI_MODE_0; + rc = spi_setup(spi); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: SPI setup error %d\n", + __func__, rc); + return rc; + } + +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT + match = of_match_device(of_match_ptr(pt_spi_of_match), dev); + if (match) { + rc = pt_devtree_create_and_get_pdata(dev); + if (rc < 0) + return rc; + } +#endif + + /* Add 2 to the length for the 'OP Code' & 'Dummy' prefix bytes */ + tmp_wbuf = kzalloc(PT_SPI_BUFFER_SIZE, GFP_KERNEL); + tmp_rbuf = kzalloc(PT_SPI_BUFFER_SIZE, GFP_KERNEL); + if (!tmp_wbuf || !tmp_rbuf) + return -ENOMEM; + +#ifdef TTDL_PTVIRTDUT_SUPPORT + device_create_file(dev, &dev_attr_dut_cmd); + device_create_file(dev, &dev_attr_dut_out); +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + + rc = pt_probe(&pt_spi_bus_ops, &spi->dev, spi->irq, + PT_SPI_DATA_BUF_SIZE); + +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT + if (rc && match) + pt_devtree_clean_pdata(dev); +#endif + +#ifdef TTDL_PTVIRTDUT_SUPPORT + if (rc) { + device_remove_file(dev, &dev_attr_dut_cmd); + device_remove_file(dev, &dev_attr_dut_out); + } +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_spi_remove + * + * SUMMARY: Remove functon for the SPI module + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *spi - pointer to spi device structure + ******************************************************************************/ +static int pt_spi_remove(struct spi_device *spi) +{ +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT + const struct of_device_id *match; +#endif + struct device *dev = &spi->dev; + struct pt_core_data *cd = dev_get_drvdata(dev); + + kfree(tmp_rbuf); + kfree(tmp_wbuf); + +#ifdef TTDL_PTVIRTDUT_SUPPORT + device_remove_file(dev, &dev_attr_dut_cmd); + device_remove_file(dev, &dev_attr_dut_out); +#endif /* TTDL_PTVIRTDUT_SUPPORT */ + + pt_release(cd); + +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT + match = of_match_device(of_match_ptr(pt_spi_of_match), dev); + if (match) + pt_devtree_clean_pdata(dev); +#endif + + return 0; +} + +static const struct spi_device_id pt_spi_id[] = { + { PT_SPI_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, pt_spi_id); + +static struct spi_driver pt_spi_driver = { + .driver = { + .name = PT_SPI_NAME, + .bus = &spi_bus_type, + .owner = THIS_MODULE, + .pm = &pt_pm_ops, +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT + .of_match_table = pt_spi_of_match, +#endif + }, + .probe = pt_spi_probe, + .remove = (pt_spi_remove), + .id_table = pt_spi_id, +}; + +#if (KERNEL_VERSION(3, 3, 0) <= LINUX_VERSION_CODE) +module_spi_driver(pt_spi_driver); +#else +/******************************************************************************* + * FUNCTION: pt_spi_init + * + * SUMMARY: Initialize function to register spi module to kernel. + * + * RETURN: + * 0 = success + * !0 = failure + ******************************************************************************/ +static int __init pt_spi_init(void) +{ + int err = spi_register_driver(&pt_spi_driver); + + pr_info("%s: Parade TTDL SPI Driver (Build %s) rc=%d\n", + __func__, PT_DRIVER_VERSION, err); + return err; +} +module_init(pt_spi_init); + +/******************************************************************************* + * FUNCTION: pt_spi_exit + * + * SUMMARY: Exit function to unregister spi module from kernel. + * + ******************************************************************************/ +static void __exit pt_spi_exit(void) +{ + spi_unregister_driver(&pt_spi_driver); +} +module_exit(pt_spi_exit); +#endif + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product SPI Driver"); +MODULE_AUTHOR("Parade Technologies "); From 3fcb581736ecc91f91e6743672b3c8720f6647e7 Mon Sep 17 00:00:00 2001 From: Raviteja Tamatam Date: Tue, 13 Sep 2022 15:47:02 -0700 Subject: [PATCH 035/170] touch: qts: handle errors in touch_abort case Avoid qts_bus_put in case of reclaim failures while handling trusted_touch_abort to prevent external abort crash. Change-Id: I2b76d750b28c64990e045570ea661981a2ed6536 Signed-off-by: Raviteja Tamatam --- qts/qts_core.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/qts/qts_core.c b/qts/qts_core.c index 6272cce3e8..66d9f359fd 100644 --- a/qts/qts_core.c +++ b/qts/qts_core.c @@ -468,7 +468,7 @@ static void qts_trusted_touch_tvm_vm_mode_enable(struct qts_data *qts_data) if (qts_data->vendor_ops.post_le_tui_enable) qts_data->vendor_ops.post_le_tui_enable(qts_data->vendor_data); - pr_debug("trusted touch enabled\n"); + pr_info("Irq, iomem are accepted and trusted touch enabled\n"); mutex_unlock(&qts_data->transition_lock); return; @@ -610,7 +610,7 @@ static void qts_trusted_touch_tvm_vm_mode_disable(struct qts_data *qts_data) if (qts_data->vendor_ops.post_le_tui_disable) qts_data->vendor_ops.post_le_tui_disable(qts_data->vendor_data); - pr_debug("trusted touch disabled\n"); + pr_info("Irq, iomem are released and trusted touch disabled\n"); mutex_unlock(&qts_data->transition_lock); return; error: @@ -726,15 +726,20 @@ static void qts_trusted_touch_abort_pvm(struct qts_data *qts_data) case PVM_IRQ_LENT: case PVM_IRQ_LENT_NOTIFIED: rc = gh_irq_reclaim(qts_data->vm_info->irq_label); - if (rc) + if (rc) { pr_err("failed to reclaim irq on pvm rc:%d\n", rc); + return; + } case PVM_IRQ_RECLAIMED: case PVM_IOMEM_LENT: case PVM_IOMEM_LENT_NOTIFIED: case PVM_IOMEM_RELEASE_NOTIFIED: rc = gh_rm_mem_reclaim(qts_data->vm_info->vm_mem_handle, 0); - if (rc) + if (rc) { pr_err("failed to reclaim iomem on pvm rc:%d\n", rc); + qts_trusted_touch_set_vm_state(qts_data, PVM_IOMEM_RELEASE_NOTIFIED); + return; + } qts_data->vm_info->vm_mem_handle = 0; case PVM_IOMEM_RECLAIMED: case PVM_INTERRUPT_DISABLED: @@ -854,7 +859,7 @@ static void qts_trusted_touch_pvm_vm_mode_disable(struct qts_data *qts_data) } if (qts_trusted_touch_get_vm_state(qts_data) != PVM_ALL_RESOURCES_RELEASE_NOTIFIED) - pr_debug("all release notifications are not received yet\n"); + pr_info("all release notifications are not received yet\n"); if (qts_data->vendor_ops.pre_la_tui_disable) qts_data->vendor_ops.pre_la_tui_disable(qts_data->vendor_data); @@ -889,7 +894,7 @@ static void qts_trusted_touch_pvm_vm_mode_disable(struct qts_data *qts_data) if (qts_data->vendor_ops.post_la_tui_disable) qts_data->vendor_ops.post_la_tui_disable(qts_data->vendor_data); - pr_debug("trusted touch disabled\n"); + pr_info("Irq, iomem are reclaimed and trusted touch disabled\n"); return; error: qts_trusted_touch_abort_handler(qts_data, @@ -1079,7 +1084,7 @@ static int qts_trusted_touch_pvm_vm_mode_enable(struct qts_data *qts_data) mutex_unlock(&qts_data->transition_lock); atomic_set(&qts_data->trusted_touch_transition, 0); atomic_set(&qts_data->trusted_touch_enabled, 1); - pr_debug("trusted touch enabled\n"); + pr_info("Irq, iomem are lent and trusted touch enabled\n"); return rc; abort_handler: @@ -1316,6 +1321,8 @@ static ssize_t trusted_touch_enable_store(struct kobject *kobj, struct kobj_attr if (!atomic_read(&qts_data->trusted_touch_initialized)) return -EIO; + pr_info("TUI trusted_touch_enable:%d\n", value); + #ifdef CONFIG_ARCH_QTI_VM err = qts_handle_trusted_touch_tvm(qts_data, value); if (err) { From 85276af66453891d9530a3d2640cb9b49ab7d781 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Mon, 19 Sep 2022 14:53:16 +0530 Subject: [PATCH 036/170] touch: fix for the errors This change fixes all the errors of dependent change Change-Id: Ic52cfcf8dfbd38188573b64299f0500002579588 Signed-off-by: Surya Teja Kudiri --- pt/Makefile | 4 - pt/pt_btn.c | 4 +- pt/pt_core.c | 3806 +++++++++-------------------------------- pt/pt_device_access.c | 145 +- pt/pt_devtree.c | 70 +- pt/pt_i2c.c | 286 +--- pt/pt_loader.c | 1647 ++++++------------ pt/pt_mt_common.c | 45 +- pt/pt_platform.c | 666 +++---- pt/pt_proximity.c | 42 +- pt/pt_regs.h | 269 +-- pt/pt_spi.c | 238 +-- 12 files changed, 1790 insertions(+), 5432 deletions(-) diff --git a/pt/Makefile b/pt/Makefile index f495e2dbcd..da836e6f10 100644 --- a/pt/Makefile +++ b/pt/Makefile @@ -4,13 +4,11 @@ # # Each configuration option enables a list of files. - obj-$(CONFIG_TOUCHSCREEN_PARADE) += pt.o pt-y := pt_core.o pt_mt_common.o pt-$(CONFIG_TOUCHSCREEN_PARADE_MT_A) += pt_mta.o pt-$(CONFIG_TOUCHSCREEN_PARADE_MT_B) += pt_mtb.o pt-$(CONFIG_TOUCHSCREEN_PARADE_BUTTON) += pt_btn.o -pt-$(CONFIG_TOUCHSCREEN_PARADE_PEN) += pt_pen.o pt-$(CONFIG_TOUCHSCREEN_PARADE_PROXIMITY) += pt_proximity.o obj-$(CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT) += pt_devtree.o ifdef CONFIG_TOUCHSCREEN_PARADE @@ -30,7 +28,6 @@ CFLAGS_pt_mta.o += -DDEBUG CFLAGS_pt_mtb.o += -DDEBUG CFLAGS_pt_mt_common.o += -DDEBUG CFLAGS_pt_btn.o += -DDEBUG -CFLAGS_pt_pen.o += -DDEBUG CFLAGS_pt_proximity.o += -DDEBUG CFLAGS_pt_device_access.o += -DDEBUG CFLAGS_pt_loader.o += -DDEBUG @@ -47,7 +44,6 @@ CFLAGS_pt_mta.o += -DVERBOSE_DEBUG CFLAGS_pt_mtb.o += -DVERBOSE_DEBUG CFLAGS_pt_mt_common.o += -DVERBOSE_DEBUG CFLAGS_pt_btn.o += -DVERBOSE_DEBUG -CFLAGS_pt_pen.o += -DVERBOSE_DEBUG CFLAGS_pt_proximity.o += -DVERBOSE_DEBUG CFLAGS_pt_device_access.o += -DVERBOSE_DEBUG CFLAGS_pt_loader.o += -DVERBOSE_DEBUG diff --git a/pt/pt_btn.c b/pt/pt_btn.c index 28d1df077c..17d2a93b13 100644 --- a/pt/pt_btn.c +++ b/pt/pt_btn.c @@ -1,4 +1,3 @@ -#ifndef TTDL_KERNEL_SUBMISSION /* * pt_btn.c * Parade TrueTouch(TM) Standard Product CapSense Reports Module. @@ -382,7 +381,7 @@ static int pt_setup_input_attention(struct device *dev) bd->si = _pt_request_sysinfo(dev); if (!bd->si) - return -1; + return -EPERM; rc = pt_setup_input_device(dev); @@ -521,4 +520,3 @@ int pt_btn_release(struct device *dev) return 0; } -#endif /*!TTDL_KERNEL_SUBMISSION */ diff --git a/pt/pt_core.c b/pt/pt_core.c index 0ecbe1b537..426b9eeaa2 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -27,17 +27,24 @@ * Contact Parade Technologies at www.paradetech.com */ -#include "pt_regs.h" -#include #include #include +#include +#include "pt_regs.h" -#ifdef PT_PTSBC_SUPPORT -#define PT_CORE_PROBE_STARTUP_DELAY_MS 500 -#endif /* PT_PTSBC_SUPPORT */ +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" + +#define FT_VTG_MIN_UV 2800000 +#define FT_VTG_MAX_UV 2800000 +#define FT_I2C_VTG_MIN_UV 1800000 +#define FT_I2C_VTG_MAX_UV 1800000 #define PT_CORE_STARTUP_RETRY_COUNT 3 +#define PT_STATUS_STR_LEN (50) + MODULE_FIRMWARE(PT_FW_FILE_NAME); static const char *pt_driver_core_name = PT_CORE_NAME; @@ -69,7 +76,6 @@ struct pt_hid_report { int header_size; int record_size; u32 usage_page; - int log_collection_num; }; struct atten_node { @@ -95,7 +101,6 @@ struct module_node { }; struct pt_hid_cmd { - __le16 descriptor; u8 opcode; u8 report_type; union { @@ -104,7 +109,6 @@ struct pt_hid_cmd { }; u8 has_data_register; size_t write_length; - size_t read_length; u8 *write_buf; u8 *read_buf; u8 wait_interrupt; @@ -155,7 +159,7 @@ void pt_pr_buf(struct device *dev, u8 debug_level, u8 *buf, { struct pt_core_data *cd = dev_get_drvdata(dev); int i; - int pr_buf_index = 0; + ssize_t pr_buf_index = 0; int max_size; /* only proceed if valid debug level and there is data to print */ @@ -166,12 +170,15 @@ void pt_pr_buf(struct device *dev, u8 debug_level, u8 *buf, return; /* - * With spaces each printed char takes 3 bytes, subtract - * the length of the data_name and length prefix and divide 3 + * With a space each printed char takes 3 bytes, subtract + * the length of the data_name prefix as well as 11 bytes + * for the " [0..xxx]: " printed before the data. */ + max_size = (PT_MAX_PR_BUF_SIZE - sizeof(data_name) - 11) / 3; + + /* Ensure pr_buf_index stays within the 1018 size */ pr_buf_index += scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE, "%s [0..%d]: ", - data_name, buf_len); - max_size = (PT_MAX_PR_BUF_SIZE - pr_buf_index) / 3; + data_name); for (i = 0; i < buf_len && i < max_size; i++) pr_buf_index += scnprintf(pr_buf + pr_buf_index, PT_MAX_PR_BUF_SIZE, "%02X ", buf[i]); @@ -325,6 +332,7 @@ static int pt_add_parameter(struct pt_core_data *cd, /* Check if parameter already exists in the list */ spin_lock(&cd->spinlock); + list_for_each_entry(param, &cd->param_list, node) { if (param->id == param_id) { /* Update parameter */ @@ -357,7 +365,6 @@ exit_unlock: return 0; } -#ifndef TTDL_KERNEL_SUBMISSION #ifdef TTDL_DIAGNOSTICS /******************************************************************************* * FUNCTION: pt_erase_parameter_list @@ -415,7 +422,6 @@ static int pt_count_parameter_list(struct pt_core_data *cd) return entries; } #endif /* TTDL_DIAGNOSTICS */ -#endif /* !TTDL_KERNEL_SUBMISSION */ /******************************************************************************* * FUNCTION: request_exclusive @@ -523,7 +529,7 @@ int release_exclusive(struct pt_core_data *cd, void *ownptr) } /******************************************************************************* - * FUNCTION: pt_hid_create_cmd_and_send_ + * FUNCTION: pt_hid_exec_cmd_ * * SUMMARY: Send the HID command to the DUT * @@ -535,7 +541,7 @@ int release_exclusive(struct pt_core_data *cd, void *ownptr) * *cd - pointer to core data * *hid_cmd - pointer to the HID command to send ******************************************************************************/ -static int pt_hid_create_cmd_and_send_(struct pt_core_data *cd, +static int pt_hid_exec_cmd_(struct pt_core_data *cd, struct pt_hid_cmd *hid_cmd) { int rc = 0; @@ -543,27 +549,16 @@ static int pt_hid_create_cmd_and_send_(struct pt_core_data *cd, u16 cmd_length; u8 cmd_offset = 0; - if (hid_cmd->descriptor) { - cmd_length = 2; /* hid or report register */ - } else { - cmd_length = - 2 /* command register */ - + 2 /* command */ - + (hid_cmd->report_id >= 0XF ? 1 : 0) /* Report ID */ - + (hid_cmd->has_data_register ? 2 : 0) /* Data register */ - + hid_cmd->write_length; /* Data length */ - } + cmd_length = 2 /* command register */ + + 2 /* command */ + + (hid_cmd->report_id >= 0XF ? 1 : 0) /* Report ID */ + + (hid_cmd->has_data_register ? 2 : 0) /* Data register */ + + hid_cmd->write_length; /* Data length */ cmd = kzalloc(cmd_length, GFP_KERNEL); if (!cmd) return -ENOMEM; - /* hid & report descriptor doesn't require other field */ - if (hid_cmd->descriptor) { - memcpy(&cmd[cmd_offset], &hid_cmd->descriptor, cmd_length); - goto skip_other_field; - } - /* Set Command register */ memcpy(&cmd[cmd_offset], &cd->hid_desc.command_register, sizeof(cd->hid_desc.command_register)); @@ -604,14 +599,12 @@ static int pt_hid_create_cmd_and_send_(struct pt_core_data *cd, ">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n", __func__, cmd_length, hid_cmd->report_id); -skip_other_field: pt_pr_buf(cd->dev, DL_DEBUG, cmd, cmd_length, ">>> CMD"); - - rc = pt_adap_write_read_specific(cd, cmd_length, cmd, hid_cmd->read_buf, - hid_cmd->read_length); + rc = pt_adap_write_read_specific(cd, cmd_length, cmd, + hid_cmd->read_buf); if (rc) pt_debug(cd->dev, DL_ERROR, - "%s: Fail pt_adap_transfer\n", __func__); + "%s: Fail pt_adap_transfer\n", __func__); kfree(cmd); return rc; @@ -685,13 +678,22 @@ static int pt_hid_exec_cmd_and_wait_(struct pt_core_data *cd, else cmd_state = &cd->hid_cmd_state; - mutex_lock(&cd->system_lock); - *cmd_state = 1; - mutex_unlock(&cd->system_lock); + if (hid_cmd->wait_interrupt) { + mutex_lock(&cd->system_lock); + *cmd_state = 1; + mutex_unlock(&cd->system_lock); + } - rc = pt_hid_create_cmd_and_send_(cd, hid_cmd); - if (rc) - goto error; + rc = pt_hid_exec_cmd_(cd, hid_cmd); + if (rc) { + if (hid_cmd->wait_interrupt) + goto error; + + goto exit; + } + + if (!hid_cmd->wait_interrupt) + goto exit; if (hid_cmd->timeout_ms) timeout_ms = hid_cmd->timeout_ms; @@ -723,60 +725,6 @@ exit: return rc; } -/******************************************************************************* - * FUNCTION: pt_hid_exec_cmd_no_wait_ - * - * SUMMARY: The function works to send HID command and can read response - * directly instead of waiting it to be received in interrupt function. It - * assgins the read buffer to receive response in following condition: - * 1) descriptor is assigned to get hid descriptor or report descripter - * 2) output register is assigned for vendor-defined commands (TBD) - * - * NOTE: If no read buffer is assigned, it only perform send action. - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *cd - pointer to core data - * *hid_cmd - pointer to the HID command to send - ******************************************************************************/ -static int pt_hid_exec_cmd_no_wait_(struct pt_core_data *cd, - struct pt_hid_cmd *hid_cmd) -{ - int rc = 0; - - if (hid_cmd->descriptor) - hid_cmd->read_buf = cd->response_buf; - - rc = pt_hid_create_cmd_and_send_(cd, hid_cmd); - return rc; -} - -/******************************************************************************* - * FUNCTION: pt_hid_send_command - * - * SUMMARY: Wrapper function to call pt_hid_exec_cmd_no_wait_() for HID protocol - * and pt_hid_exec_cmd_and_wait_() for PIP protocol. - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *cd - pointer to core data - * *hid_cmd - pointer to the HID command to send - ******************************************************************************/ -static int pt_hid_send_command(struct pt_core_data *cd, - struct pt_hid_cmd *hid_cmd) -{ - if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) - return pt_hid_exec_cmd_no_wait_(cd, hid_cmd); - else - return pt_hid_exec_cmd_and_wait_(cd, hid_cmd); -} - /******************************************************************************* * FUNCTION: pt_hid_cmd_reset_ * @@ -793,11 +741,12 @@ static int pt_hid_cmd_reset_(struct pt_core_data *cd) { struct pt_hid_cmd hid_cmd = { .opcode = HID_CMD_RESET, + .wait_interrupt = 1, .reset_cmd = 1, .timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT, }; - return pt_hid_send_command(cd, &hid_cmd); + return pt_hid_exec_cmd_and_wait_(cd, &hid_cmd); } /******************************************************************************* @@ -854,13 +803,14 @@ static int pt_hid_cmd_set_power_(struct pt_core_data *cd, int rc = 0; struct pt_hid_cmd hid_cmd = { .opcode = HID_CMD_SET_POWER, + .wait_interrupt = 1, .timeout_ms = PT_HID_CMD_DEFAULT_TIMEOUT, }; hid_cmd.power_state = power_state; /* The chip won't give response if goes to Deep Standby */ if (power_state == HID_POWER_STANDBY) { - rc = pt_hid_exec_cmd_no_wait_(cd, &hid_cmd); + rc = pt_hid_exec_cmd_(cd, &hid_cmd); if (rc) pt_debug(cd->dev, DL_ERROR, "%s: Failed to set power to state:%d\n", @@ -871,7 +821,7 @@ static int pt_hid_cmd_set_power_(struct pt_core_data *cd, } cd->fw_sys_mode_in_standby_state = false; - rc = pt_hid_send_command(cd, &hid_cmd); + rc = pt_hid_exec_cmd_and_wait_(cd, &hid_cmd); if (rc) { pt_debug(cd->dev, DL_ERROR, "%s: Failed to set power to state:%d\n", @@ -879,10 +829,6 @@ static int pt_hid_cmd_set_power_(struct pt_core_data *cd, return rc; } - /* HID COMMAND doesn't have a response */ - if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) - return rc; - /* validate */ if ((cd->response_buf[2] != HID_RESPONSE_REPORT_ID) || ((cd->response_buf[3] & 0x3) != power_state) @@ -892,7 +838,6 @@ static int pt_hid_cmd_set_power_(struct pt_core_data *cd, return rc; } -#ifndef TTDL_KERNEL_SUBMISSION /******************************************************************************* * FUNCTION: pt_hid_cmd_set_power * @@ -928,7 +873,6 @@ static int pt_hid_cmd_set_power(struct pt_core_data *cd, return rc; } -#endif /* !TTDL_KERNEL_SUBMISSION */ static const u16 crc_table[16] = { 0x0000, 0x1021, 0x2042, 0x3063, @@ -1229,7 +1173,7 @@ static int pt_pip2_get_cmd_response_len(u8 id) if (p->id != PIP2_CMD_ID_END) return p->response_len; else - return -1; + return -EPERM; } /******************************************************************************* @@ -1627,11 +1571,12 @@ static int pt_hid_send_output_user_(struct pt_core_data *cd, pt_debug(cd->dev, DL_INFO, ">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n", __func__, hid_output->length, cmd); + pt_pr_buf(cd->dev, DL_DEBUG, hid_output->write_buf, hid_output->length, ">>> User CMD"); rc = pt_adap_write_read_specific(cd, hid_output->length, - hid_output->write_buf, NULL, 0); + hid_output->write_buf, NULL); if (rc) pt_debug(cd->dev, DL_ERROR, "%s: Fail pt_adap_transfer\n", __func__); @@ -1679,9 +1624,7 @@ static int pt_hid_send_output_user_and_wait_(struct pt_core_data *cd, rc = -ETIME; goto error; } - pt_check_command(cd, hid_output, true); - goto exit; error: @@ -1764,15 +1707,7 @@ static ssize_t pt_flush_bus(struct pt_core_data *cd, int rc = 0; if (flush_type == PT_FLUSH_BUS_BASED_ON_LEN) { -#ifdef TTDL_PTVIRTDUT_SUPPORT - if (cd->route_bus_virt_dut) - rc = pt_adap_read_default(cd, buf, - PT_MAX_PIP2_MSG_SIZE); - else - rc = pt_adap_read_default(cd, buf, 2); -#else rc = pt_adap_read_default(cd, buf, 2); -#endif /* TTDL_PTVIRTDUT_SUPPORT */ if (rc) { bytes_read = 0; goto exit; @@ -1807,15 +1742,7 @@ static ssize_t pt_flush_bus(struct pt_core_data *cd, "%s: Flush read of %d bytes...\n", __func__, pip_len); -#ifdef TTDL_PTVIRTDUT_SUPPORT - rc = 0; - if (cd->route_bus_virt_dut) - bytes_read = pip_len; - else - rc = pt_adap_read_default(cd, buf, pip_len); -#else rc = pt_adap_read_default(cd, buf, pip_len); -#endif /* TTDL_PTVIRTDUT_SUPPORT */ if (!rc) bytes_read = pip_len; else @@ -1903,15 +1830,6 @@ static int pt_hid_send_output_(struct pt_core_data *cd, u8 cmd_offset = 0; u8 cmd_allocated = 0; -#ifdef FUTURE - /* - * *** TODO - Determine side effects of adding this safety net *** - * If IRQ is already asserted due to a pending report, it must be - * cleared before sending command. - */ - pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN); -#endif - switch (hid_output->cmd_type) { case PIP1_CMD_TYPE_FW: report_id = PT_PIP_NON_HID_COMMAND_ID; @@ -1971,8 +1889,9 @@ static int pt_hid_send_output_(struct pt_core_data *cd, pt_debug(cd->dev, DL_INFO, ">>> %s: Write Buffer Size[%d] Cmd[0x%02X]\n", __func__, length + 2, hid_output->command_code); + pt_pr_buf(cd->dev, DL_DEBUG, cmd, length + 2, ">>> CMD"); - rc = pt_adap_write_read_specific(cd, length + 2, cmd, NULL, 0); + rc = pt_adap_write_read_specific(cd, length + 2, cmd, NULL); if (rc) pt_debug(cd->dev, DL_ERROR, @@ -2072,7 +1991,6 @@ static int pt_hid_output_user_cmd_(struct pt_core_data *cd, .length = write_len, .write_buf = write_buf, }; - #ifdef TTHE_TUNER_SUPPORT if (!cd->pip2_send_user_cmd) { int command_code = 0; @@ -2092,7 +2010,6 @@ static int pt_hid_output_user_cmd_(struct pt_core_data *cd, tthe_print(cd, write_buf, len, "CMD="); } #endif - rc = pt_hid_send_output_user_and_wait_(cd, &hid_output); if (rc) return rc; @@ -2198,7 +2115,6 @@ static int _pt_request_pip2_send_cmd(struct device *dev, u8 extra_bytes; memset(&pip2_cmd, 0, sizeof(pip2_cmd)); - /* Hard coded register for PIP2.x */ pip2_cmd.reg[0] = 0x01; pip2_cmd.reg[1] = 0x01; @@ -2225,6 +2141,7 @@ static int _pt_request_pip2_send_cmd(struct device *dev, pip2_cmd.id = id & PIP2_CMD_COMMAND_ID_MASK; pip2_cmd.seq = pt_pip2_get_next_cmd_seq(cd); pip2_cmd.data = data; + pt_pip2_cmd_calculate_crc(&pip2_cmd, extra_bytes); /* Add the command length to the extra bytes based on PIP version */ @@ -2234,6 +2151,7 @@ static int _pt_request_pip2_send_cmd(struct device *dev, __func__, pip2_cmd.len, write_len); write_buf = kzalloc(write_len, GFP_KERNEL); + if (write_buf == NULL) { rc = -ENOMEM; goto exit; @@ -2254,6 +2172,7 @@ static int _pt_request_pip2_send_cmd(struct device *dev, read_len = pt_pip2_get_cmd_response_len(pip2_cmd.id); if (read_len < 0) read_len = 255; + pt_debug(dev, DL_INFO, "%s cmd_id[0x%02X] expected response length:%d ", __func__, pip2_cmd.id, read_len); @@ -2271,19 +2190,18 @@ static int _pt_request_pip2_send_cmd(struct device *dev, if (protect == PT_CORE_CMD_PROTECTED) rc = pt_hid_output_user_cmd(cd, read_len, read_buf, write_len, write_buf, actual_read_len); - else + else { rc = pt_hid_output_user_cmd_(cd, read_len, read_buf, write_len, write_buf, actual_read_len); + } if (rc) { pt_debug(dev, DL_ERROR, "%s: nonhid_cmd->user_cmd() Error = %d\n", __func__, rc); goto exit; } - rc = pt_pip2_validate_response(cd, &pip2_cmd, read_buf, *actual_read_len); - exit: mutex_lock(&cd->system_lock); cd->pip2_prot_active = false; @@ -2400,7 +2318,7 @@ static int _pt_pip2_send_cmd_no_int(struct device *dev, pt_pr_buf(cd->dev, DL_DEBUG, write_buf, write_len, ">>> NO_INT CMD"); - rc = pt_adap_write_read_specific(cd, write_len, write_buf, NULL, 0); + rc = pt_adap_write_read_specific(cd, write_len, write_buf, NULL); if (rc) { pt_debug(dev, DL_ERROR, "%s: SPI write Error = %d\n", @@ -2533,11 +2451,9 @@ static int pt_pip_null_(struct pt_core_data *cd) struct pt_hid_output hid_output = { CREATE_PIP1_FW_CMD(PIP1_CMD_ID_NULL), }; - return pt_pip1_send_output_and_wait_(cd, &hid_output); } -#ifndef TTDL_KERNEL_SUBMISSION /******************************************************************************* * FUNCTION: pt_pip_null * @@ -2570,13 +2486,12 @@ static int pt_pip_null(struct pt_core_data *cd) return rc; } -#endif /*!TTDL_KERNEL_SUBMISSION */ static void pt_stop_wd_timer(struct pt_core_data *cd); /******************************************************************************* * FUNCTION: pt_pip_start_bootloader_ * - * SUMMARY: Sends the PIP command start_bootloader [PIP cmd 0x01] to the DUT + * SUMMARY: Sends the HID command start_bootloader [PIP cmd 0x01] to the DUT * * NOTE: The WD MUST be stopped/restarted by the calling Function. Having * the WD active could cause this function to fail! @@ -2597,7 +2512,6 @@ static int pt_pip_start_bootloader_(struct pt_core_data *cd) .timeout_ms = PT_PIP1_START_BOOTLOADER_TIMEOUT, .reset_expected = 1, }; - if (cd->watchdog_enabled) { pt_debug(cd->dev, DL_WARN, "%s: watchdog isn't stopped before enter bl\n", @@ -2820,7 +2734,6 @@ static void pt_si_get_sensing_conf_data(struct pt_core_data *cd) scd->tx_num = scd->electrodes_y; scd->rx_num = scd->electrodes_x; } - /* * When the Panel ID is coming from an XY pin and not a dedicated * GPIO, store the PID in pid_for_loader. This cannot be done for all @@ -3120,7 +3033,6 @@ static int pt_hid_output_get_sysinfo_(struct pt_core_data *cd) return rc; } -#ifndef TTDL_KERNEL_SUBMISSION /******************************************************************************* * FUNCTION: pt_hid_output_get_sysinfo * @@ -3153,7 +3065,6 @@ static int pt_hid_output_get_sysinfo(struct pt_core_data *cd) return rc; } -#endif /*!TTDL_KERNEL_SUBMISSION */ /******************************************************************************* * FUNCTION: pt_pip_suspend_scanning_ @@ -3466,7 +3377,6 @@ static int pt_pip_set_param_(struct pt_core_data *cd, CREATE_PIP1_FW_CMD(PIP1_CMD_ID_SET_PARAM), .write_buf = write_buf, }; - write_buf[0] = param_id; write_buf[1] = size; for (i = 0; i < size; i++) { @@ -3576,7 +3486,6 @@ static int pt_hid_output_enter_easywake_state_( .write_length = write_length, .write_buf = param, }; - rc = pt_pip1_send_output_and_wait_(cd, &hid_output); if (rc) return rc; @@ -3614,7 +3523,6 @@ static int pt_pip_verify_config_block_crc_( .write_length = write_length, .write_buf = param, }; - rc = pt_pip1_send_output_and_wait_(cd, &hid_output); if (rc) return rc; @@ -4978,7 +4886,6 @@ static int pt_pip_calibrate_idacs_(struct pt_core_data *cd, .write_buf = write_buf, .timeout_ms = PT_PIP1_CMD_CALIBRATE_IDAC_TIMEOUT, }; - write_buf[cmd_offset++] = mode; rc = pt_pip1_send_output_and_wait_(cd, &hid_output); if (rc) @@ -5083,7 +4990,6 @@ static int pt_hid_output_initialize_baselines_( .write_length = write_length, .write_buf = write_buf, }; - write_buf[cmd_offset++] = test_id; rc = pt_pip1_send_output_and_wait_(cd, &hid_output); @@ -5619,7 +5525,7 @@ static int _pt_request_pip_bl_get_information(struct device *dev, /******************************************************************************* * FUNCTION: pt_hid_output_bl_initiate_bl_ * - * SUMMARY: Sends the PIP "Initiate Bootload" (0x48) command to the + * SUMMARY: Sends the PIP "Get Bootloader Information" (0x48) command to the * DUT to erases the entire TrueTouch application, Configuration Data block, * and Design Data block in flash and enables the host to execute the Program * and Verify Row command to bootload the application image and data. @@ -5742,7 +5648,7 @@ static int _pt_request_pip_bl_initiate_bl(struct device *dev, /******************************************************************************* * FUNCTION: pt_hid_output_bl_program_and_verify_ * - * SUMMARY: Sends the PIP "Program and Verify" (0x39) command to upload + * SUMMARY: Sends the PIP "Get Bootloader Information" (0x39) command to upload * and program a 128-byte row into the flash, and then verifies written data. * * RETURN: @@ -5836,7 +5742,7 @@ static int _pt_request_pip_bl_program_and_verify( /******************************************************************************* * FUNCTION: pt_hid_output_bl_verify_app_integrity_ * - * SUMMARY: Sends the PIP "Verify Application Integrity" (0x31) command to + * SUMMARY: Sends the PIP "Get Bootloader Information" (0x31) command to * perform a full verification of the application integrity by calculating the * CRC of the image in flash and compare it to the expected CRC stored in the * Metadata row. @@ -6112,55 +6018,6 @@ static int _pt_request_pip_bl_get_panel_id( return pt_hid_output_bl_get_panel_id_(cd, panel_id); } -/******************************************************************************* - * FUNCTION: pt_pip2_get_status_ - * - * SUMMARY: Sends a PIP2 STATUS command to the DUT and stores the data in - * cd status_data. - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *cd - pointer to core data structure - ******************************************************************************/ -static int pt_pip2_get_status_(struct pt_core_data *cd) -{ - u16 actual_read_len; - u8 read_buf[12]; - u8 status, boot; - int rc = 0; - - rc = _pt_request_pip2_send_cmd(cd->dev, PT_CORE_CMD_UNPROTECTED, - PIP2_CMD_ID_STATUS, NULL, 0, read_buf, &actual_read_len); - if (rc) { - pt_debug(cd->dev, DL_ERROR, "%s: PIP2 STATUS command rc = %d\n", - __func__, rc); - return rc; - } - - pt_pr_buf(cd->dev, DL_DEBUG, read_buf, actual_read_len, - "PIP2 STATUS"); - - status = read_buf[PIP2_RESP_STATUS_OFFSET]; - boot = read_buf[PIP2_RESP_BODY_OFFSET] & 0x01; - - cd->dut_status.fw_system_mode = - read_buf[PIP2_RESP_BODY_OFFSET + 1]; - - if (status == PIP2_RSP_ERR_NONE && boot == 0x00) - cd->dut_status.mode = PT_MODE_BOOTLOADER; - else if (status == PIP2_RSP_ERR_NONE && boot == 0x01) { - cd->dut_status.mode = PT_MODE_OPERATIONAL; - cd->dut_status.protocol_mode = - read_buf[PIP2_RESP_BODY_OFFSET + 2]; - } else - cd->dut_status.mode = PT_MODE_UNKNOWN; - - return rc; -} - /******************************************************************************* * FUNCTION: pt_pip2_get_mode_sysmode_ * @@ -6179,14 +6036,39 @@ static int pt_pip2_get_status_(struct pt_core_data *cd) static int pt_pip2_get_mode_sysmode_(struct pt_core_data *cd, u8 *mode, u8 *sys_mode) { + u16 actual_read_len; + u8 read_buf[12]; + u8 status, boot; int rc = 0; - rc = pt_pip2_get_status_(cd); + rc = _pt_request_pip2_send_cmd(cd->dev, PT_CORE_CMD_UNPROTECTED, + PIP2_CMD_ID_STATUS, NULL, 0, read_buf, &actual_read_len); + + pt_debug(cd->dev, DL_INFO, "%s: PIP2 STATUS command rc = %d\n", + __func__, rc); + if (!rc) { - if (sys_mode) - *sys_mode = cd->dut_status.fw_system_mode; - if (mode) - *mode = cd->dut_status.mode; + pt_pr_buf(cd->dev, DL_DEBUG, read_buf, actual_read_len, + "PIP2 STATUS"); + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + boot = read_buf[PIP2_RESP_BODY_OFFSET] & 0x01; + if (sys_mode) { + if (status == PIP2_RSP_ERR_NONE && + boot == PIP2_STATUS_APP_EXEC) + *sys_mode = read_buf[PIP2_RESP_BODY_OFFSET + 1]; + else + *sys_mode = FW_SYS_MODE_UNDEFINED; + } + if (mode) { + if (status == PIP2_RSP_ERR_NONE && + boot == PIP2_STATUS_BOOT_EXEC) + *mode = PT_MODE_BOOTLOADER; + else if (status == PIP2_RSP_ERR_NONE && + boot == PIP2_STATUS_APP_EXEC) + *mode = PT_MODE_OPERATIONAL; + else + *mode = PT_MODE_UNKNOWN; + } } else { if (mode) *mode = PT_MODE_UNKNOWN; @@ -6581,12 +6463,9 @@ static int pt_get_hid_descriptor_(struct pt_core_data *cd, struct pt_hid_desc *desc) { struct device *dev = cd->dev; - struct pt_hid_cmd hid_cmd = { - .descriptor = cd->hid_core.hid_desc_register, - .read_length = 0, - }; int rc = 0; - u16 hid_len; + int t; + u8 cmd[2]; /* * During startup the HID descriptor is required for all future @@ -6595,68 +6474,63 @@ static int pt_get_hid_descriptor_(struct pt_core_data *cd, */ pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN); - rc = pt_hid_send_command(cd, &hid_cmd); + /* Read HID descriptor length and version */ + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = 1; + mutex_unlock(&cd->system_lock); + + /* Set HID descriptor register */ + memcpy(cmd, &cd->hid_core.hid_desc_register, + sizeof(cd->hid_core.hid_desc_register)); + + pt_debug(cd->dev, DL_INFO, ">>> %s: Write Buffer [%zu]", + __func__, sizeof(cmd)); + pt_pr_buf(cd->dev, DL_DEBUG, cmd, sizeof(cmd), ">>> Get HID Desc"); + rc = pt_adap_write_read_specific(cd, 2, cmd, NULL); if (rc) { pt_debug(dev, DL_ERROR, "%s: failed to get HID descriptor, rc=%d\n", __func__, rc); - goto exit; + goto error; } - hid_len = get_unaligned_le16(&cd->response_buf[0]); - pt_pr_buf(cd->dev, DL_DEBUG, cd->response_buf, hid_len, "<<< HIDDesc"); + t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0), + msecs_to_jiffies(PT_GET_HID_DESCRIPTOR_TIMEOUT)); - /* - * HID doesn't have packet_id and reserve_byte in struct struct - * pt_hid_desc and assign fixed value to packet_id. - */ - if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) { - if ((hid_len + 2) != sizeof(*desc)) { - pt_debug(dev, DL_ERROR, "%s: Unsupported HID length: %X\n", - __func__, hid_len); - rc = -ENODEV; - goto exit; - } - /* Copy length filed */ - memcpy((u8 *)desc, &cd->response_buf[0], 2); - /* Assign fixed value to packet_id */ - desc->packet_id = HID_APP_REPORT_ID; - /* Skip 2 bytes in desc and Copy to left fields */ - memcpy((u8 *)desc + 4, &cd->response_buf[2], hid_len - 2); + if (IS_TMO(t)) { +#ifdef TTDL_DIAGNOSTICS + cd->bus_transmit_error_count++; + pt_toggle_err_gpio(cd, PT_ERR_GPIO_I2C_TRANS); +#endif /* TTDL_DIAGNOSTICS */ + pt_debug(cd->dev, DL_ERROR, + "%s: HID get descriptor timed out\n", __func__); + rc = -ETIME; + goto error; } else { - if (hid_len != sizeof(*desc)) { - pt_debug(dev, DL_ERROR, "%s: Unsupported HID length: %X\n", - __func__, hid_len); - rc = -ENODEV; - goto exit; - } - - /* Load the HID descriptor including all registers */ - memcpy((u8 *)desc, cd->response_buf, hid_len); + cd->hw_detected = true; } + /* Load the HID descriptor including all registers */ + memcpy((u8 *)desc, cd->response_buf, sizeof(struct pt_hid_desc)); + /* Check HID descriptor length and version */ pt_debug(dev, DL_INFO, "%s: HID len:%X HID ver:%X\n", __func__, le16_to_cpu(desc->hid_desc_len), le16_to_cpu(desc->bcd_version)); - cd->hid_core.hid_report_desc_len = - le16_to_cpu(desc->report_desc_len); - - pt_debug(dev, DL_INFO, "%s: report descriptor len:%d\n", __func__, - cd->hid_core.hid_report_desc_len); - - if (le16_to_cpu(desc->bcd_version) != HID_VERSION) { + if (le16_to_cpu(desc->hid_desc_len) != sizeof(*desc) || + le16_to_cpu(desc->bcd_version) != HID_VERSION) { pt_debug(dev, DL_ERROR, "%s: Unsupported HID version\n", __func__); - rc = -ENODEV; - goto exit; + return -ENODEV; } -#ifdef TTHE_TUNER_SUPPORT - tthe_print(cd, cd->response_buf, hid_len, "HIDDesc="); -#endif + goto exit; +error: + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = 0; + mutex_unlock(&cd->system_lock); exit: return rc; } @@ -6888,15 +6762,9 @@ static int _pt_detect_dut_generation(struct device *dev, struct pt_core_data *cd = dev_get_drvdata(dev); struct pt_hid_desc hid_desc; - /* protocol_mode must be known before dut gen */ - rc = pt_pip2_get_status_(cd); - if (rc) { - /* PIP prot assumed if error */ - cd->dut_status.protocol_mode = PT_PROTOCOL_MODE_PIP; - } - memset(&hid_desc, 0, sizeof(hid_desc)); rc = pt_get_hid_descriptor_(cd, &hid_desc); + while (rc && attempt < 3) { attempt++; usleep_range(2000, 5000); @@ -7460,8 +7328,6 @@ static void pt_init_pip_report_fields(struct pt_core_data *cd) si->desc.tch_record_size = TOUCH_REPORT_SIZE; si->desc.tch_header_size = TOUCH_INPUT_HEADER_SIZE; si->desc.btn_report_id = PT_PIP_CAPSENSE_BTN_REPORT_ID; - si->desc.pen_report_id = PT_HID_PEN_REPORT_ID; - si->desc.max_touch_num = MAX_TOUCH_NUM; cd->features.easywake = 1; cd->features.noise_metric = 1; @@ -7469,989 +7335,6 @@ static void pt_init_pip_report_fields(struct pt_core_data *cd) cd->features.sensor_data = 1; } -/******************************************************************************* - * FUNCTION: pt_get_hid_report_ - * - * SUMMARY: Get or create report. Must be called with cd->hid_report_lock - * acquired. - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *cd - pointer to core data structure - * *index - pointer to report index - * report_type - type of hid report - * report_id - id of hid report - * create - true: will create a new report - * false: will not create a new report - ******************************************************************************/ -static int pt_get_hid_report_(struct pt_core_data *cd, u8 *index, - u8 report_type, u8 report_id, bool create) -{ - struct pt_hid_report *report = NULL; - int i; - int rc = 0; - - /* Look for created reports */ - for (i = 0; i < cd->num_hid_reports; i++) { - if (cd->hid_reports[i]->type == report_type && - cd->hid_reports[i]->id == report_id) { - *index = i; - goto exit; - } - } - - if (create && cd->num_hid_reports >= PT_HID_MAX_REPORTS) { - pt_debug(cd->dev, DL_WARN, - "%s: num_hid_reports=%d max=%d\n", __func__, - cd->num_hid_reports, PT_HID_MAX_REPORTS); - rc = -EINVAL; - } else if (create && cd->num_hid_reports < PT_HID_MAX_REPORTS) { - /* Create a new report */ - report = kzalloc(sizeof(struct pt_hid_report), - GFP_KERNEL); - if (!report) - rc = -ENOMEM; - else { - report->type = report_type; - report->id = report_id; - *index = cd->num_hid_reports; - cd->hid_reports[cd->num_hid_reports++] = report; - } - } - -exit: - return rc; -} - -/******************************************************************************* - * FUNCTION: pt_free_hid_reports_ - * - * SUMMARY: Free HID report. Must be called with cd->hid_report_lock acquired. - * - * PARAMETERS: - * *cd - pointer to core data structure - ******************************************************************************/ -static void pt_free_hid_reports_(struct pt_core_data *cd) -{ - struct pt_hid_report *report; - int i, j; - - for (i = 0; i < cd->num_hid_reports; i++) { - report = cd->hid_reports[i]; - for (j = 0; j < report->num_fields; j++) - kfree(report->fields[j]); - kfree(report); - cd->hid_reports[i] = NULL; - } - - cd->num_hid_reports = 0; -} - -/******************************************************************************* - * FUNCTION: pt_free_hid_reports - * - * SUMMARY: Protected call to pt_free_hid_reports_() by a mutex lock. - * - * PARAMETERS: - * *cd - pointer to core data structure - ******************************************************************************/ -static void pt_free_hid_reports(struct pt_core_data *cd) -{ - mutex_lock(&cd->hid_report_lock); - pt_free_hid_reports_(cd); - mutex_unlock(&cd->hid_report_lock); -} - -/******************************************************************************* - * FUNCTION: pt_create_hid_field_ - * - * SUMMARY: Create field for HID report.Must be called with cd->hid_report_lock - * acquired. - * - * RETURN: - * pointer to hid field structure - * - * PARAMETERS: - * *report - pointer to hid report structure - ******************************************************************************/ -static struct pt_hid_field *pt_create_hid_field_( - struct pt_hid_report *report) -{ - struct pt_hid_field *field; - - if (!report) - return NULL; - - if (report->num_fields == PT_HID_MAX_FIELDS) - return NULL; - - field = kzalloc(sizeof(struct pt_hid_field), GFP_KERNEL); - if (!field) - return NULL; - - field->report = report; - - report->fields[report->num_fields++] = field; - - return field; -} - -/******************************************************************************* - * FUNCTION: get_hid_item_data - * - * SUMMARY: Get hid item data according to the item size. - * - * RETURN: - * 0 = no data - * !0 = data - * - * PARAMETERS: - * *data - pointer to item data - * item_size - the size of the item - ******************************************************************************/ -static inline int get_hid_item_data(u8 *data, int item_size) -{ - if (item_size == 1) - return (int)*data; - else if (item_size == 2) - return (int)get_unaligned_le16(data); - else if (item_size == 4) - return (int)get_unaligned_le32(data); - return 0; -} - -/******************************************************************************* - * FUNCTION: parse_report_descriptor - * - * SUMMARY: Parse report descriptor. - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *cd - pointer to core data structure - * *report_desc - pointer to report descriptor structure - * len - length of the data buffer to be parsed - ******************************************************************************/ -static int parse_report_descriptor(struct pt_core_data *cd, - u8 *report_desc, size_t len) -{ - struct pt_hid_report *report; - struct pt_hid_field *field; - u8 *buf = report_desc; - u8 *end = buf + len; - int rc = 0; - int offset = 0; - int i, j; - u8 report_type; - u32 up_usage; - /* Global items */ - u8 report_id = 0; - u16 usage_page = 0; - int report_count = 0; - int report_size = 0; - int logical_min = 0; - int logical_max = 0; - /* Local items */ - u16 usage = 0; - /* Main items - Collection stack */ - u32 collection_usages[PT_HID_MAX_NESTED_COLLECTIONS] = {0}; - u8 collection_types[PT_HID_MAX_NESTED_COLLECTIONS] = {0}; - u32 usages_pen[PT_HID_MAX_CONTINUOUS_USAGES] = {0}; - /* First collection for header, second for report */ - int logical_collection_count = 0; - int app_collection_count = 0; - int collection_nest = 0; - int usage_cnt = 0; - u8 report_index = 0; - - pt_debug(cd->dev, DL_DEBUG, "%s: Report descriptor length: %u\n", - __func__, (u32)len); - - mutex_lock(&cd->hid_report_lock); - pt_free_hid_reports_(cd); - - while (buf < end) { - int item_type; - int item_size; - int item_tag; - u8 *data; - - /* Get Item */ - item_size = HID_GET_ITEM_SIZE(buf[0]); - if (item_size == 3) - item_size = 4; - item_type = HID_GET_ITEM_TYPE(buf[0]); - item_tag = HID_GET_ITEM_TAG(buf[0]); - - data = ++buf; - buf += item_size; - - /* Process current item */ - switch (item_type) { - case HID_ITEM_TYPE_GLOBAL: /* 1 */ - switch (item_tag) { - case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: /* 0 */ - if (item_size == 0 || item_size == 4) { - rc = -EINVAL; - goto exit; - } - usage_page = (u16)get_hid_item_data(data, - item_size); - break; - case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: /* 1 */ - if (item_size == 0) { - rc = -EINVAL; - goto exit; - } - logical_min = get_hid_item_data(data, - item_size); - break; - case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: /* 2 */ - if (item_size == 0) { - rc = -EINVAL; - goto exit; - } - logical_max = get_hid_item_data(data, - item_size); - break; - case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: /* 3 */ - pt_debug(cd->dev, DL_INFO, - "%s: TAG Ignored - Physical Min\n", - __func__); - break; - case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: /* 4 */ - pt_debug(cd->dev, DL_INFO, - "%s: TAG Ignored - Physical Max\n", - __func__); - break; - case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: /* 5 */ - pt_debug(cd->dev, DL_INFO, - "%s: TAG Ignored - Unit Exponent\n", - __func__); - break; - case HID_GLOBAL_ITEM_TAG_UNIT: /* 6 */ - pt_debug(cd->dev, DL_INFO, - "%s: TAG Ignored - Unit\n", - __func__); - break; - case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: /* 7 */ - if (item_size == 0) { - rc = -EINVAL; - goto exit; - } - report_size = get_hid_item_data(data, - item_size); - break; - case HID_GLOBAL_ITEM_TAG_REPORT_ID: /* 8 */ - if (item_size != 1) { - rc = -EINVAL; - goto exit; - } - report_id = get_hid_item_data(data, item_size); - offset = 0; - logical_collection_count = 0; - break; - case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: /* 9 */ - if (item_size == 0) { - rc = -EINVAL; - goto exit; - } - report_count = get_hid_item_data(data, - item_size); - break; - case HID_GLOBAL_ITEM_TAG_PUSH: /* A */ - pt_debug(cd->dev, DL_INFO, - "%s: TAG Ignored - Push\n", - __func__); - break; - case HID_GLOBAL_ITEM_TAG_POP: /* B */ - pt_debug(cd->dev, DL_INFO, - "%s: TAG Ignored - Pop\n", - __func__); - break; - default: - pt_debug(cd->dev, DL_INFO, - "%s: Unrecognized Global tag 0x%X\n", - __func__, item_tag); - } - break; - case HID_ITEM_TYPE_LOCAL: /* 2 */ - switch (item_tag) { - case HID_LOCAL_ITEM_TAG_USAGE: - if (item_size == 0 || item_size == 4) { - rc = -EINVAL; - goto exit; - } - usage = (u16)get_hid_item_data(data, - item_size); - if (report_id == PT_HID_PEN_REPORT_ID) { - usages_pen[usage_cnt++] = - usage_page << 16 | usage; - } - break; - case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: - if (item_size == 0) { - rc = -EINVAL; - goto exit; - } - /* usage_min = */ - get_hid_item_data(data, item_size); - break; - case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: - if (item_size == 0) { - rc = -EINVAL; - goto exit; - } - /* usage_max = */ - get_hid_item_data(data, item_size); - break; - default: - pt_debug(cd->dev, DL_INFO, - "%s: Unrecognized Local tag %d\n", - __func__, item_tag); - } - break; - case HID_ITEM_TYPE_MAIN: /* 0 */ - switch (item_tag) { - case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: - usage_cnt = 0; - - if (item_size != 1) { - rc = -EINVAL; - goto exit; - } - if (PT_HID_MAX_NESTED_COLLECTIONS == - collection_nest) { - rc = -EINVAL; - goto exit; - } - - up_usage = usage_page << 16 | usage; - - /* Update collection stack */ - collection_usages[collection_nest] = up_usage; - collection_types[collection_nest] = - get_hid_item_data(data, item_size); - - if (collection_types[collection_nest] == - HID_COLLECTION_LOGICAL) { - logical_collection_count++; - app_collection_count = 0; - } - if (collection_types[collection_nest] == - HID_COLLECTION_APPLICATION) - app_collection_count++; - - collection_nest++; - break; - case HID_MAIN_ITEM_TAG_END_COLLECTION: - if (item_size != 0) { - rc = -EINVAL; - goto exit; - } - if (--collection_nest < 0) { - rc = -EINVAL; - goto exit; - } - break; - case HID_MAIN_ITEM_TAG_INPUT: - report_type = HID_INPUT_REPORT; - goto continue_main_item; - case HID_MAIN_ITEM_TAG_OUTPUT: - report_type = HID_OUTPUT_REPORT; - goto continue_main_item; - case HID_MAIN_ITEM_TAG_FEATURE: - report_type = HID_FEATURE_REPORT; -continue_main_item: - if (item_size != 1) { - pt_debug(cd->dev, DL_WARN, - "%s: %s=%d\n", - __func__, - "item_size", item_size); - rc = -EINVAL; - goto exit; - } - - up_usage = usage_page << 16 | usage; - - /* Get or create report */ - rc = pt_get_hid_report_(cd, &report_index, - report_type, report_id, true); - if (rc) { - pt_debug(cd->dev, DL_WARN, - "%s: %s rc=%d\n", - __func__, - "get_hid_report failed", rc); - goto exit; - } else - report = cd->hid_reports[report_index]; - - if (!report->usage_page && collection_nest > 0) - report->usage_page = - collection_usages - [collection_nest - 1]; - - if (report->id == PT_HID_PEN_REPORT_ID - && report_count > 1) - goto continue_pen_usages; - - /* Create field */ - field = pt_create_hid_field_(report); - if (!field) { - pt_debug(cd->dev, DL_WARN, - "%s: %s\n", - __func__, - "create field failed"); - rc = -ENOMEM; - goto exit; - } - - field->report_count = report_count; - field->report_size = report_size; - field->size = report_count - * report_size; - field->offset = offset; - field->data_type = - get_hid_item_data(data, - item_size); - field->logical_min = logical_min; - field->logical_max = logical_max; - field->usage_page = up_usage; - usages_pen[0] = 0; - for (j = 0; j < collection_nest; j++) { - field->collection_usage_pages - [collection_types[j]] = - collection_usages[j]; - } - - goto exit_main_item; -continue_pen_usages: - for (i = 0; i < report_count; i++) { - /* Create field */ - field = pt_create_hid_field_( - report); - if (!field) { - pt_debug(cd->dev, DL_WARN, - "%s: Pen - %s\n", - __func__, - "create field failed"); - rc = -ENOMEM; - goto exit; - } - - field->report_size = report_size; - field->size = report_count * - report_size; - field->report_count = 1; - field->offset = offset + i*report_size; - field->data_type = - get_hid_item_data(data, - item_size); - field->logical_min = logical_min; - field->logical_max = logical_max; - field->usage_page = usages_pen[i]; - usages_pen[i] = 0; - for (j = 0; j < collection_nest; j++) { - field->collection_usage_pages - [collection_types[j]] = - collection_usages[j]; - } - } - -exit_main_item: - /* Update report's header or record size */ - if (app_collection_count == 1) { - report->header_size += field->size; - } else if (logical_collection_count == 1) { - field->record_field = true; - field->offset -= report->header_size; - /* Set record field index */ - if (report->record_field_index == 0) - report->record_field_index = - report->num_fields - 1; - report->record_size += field->size; - } - - report->size += field->size; - report->log_collection_num = - logical_collection_count; - - offset += field->size; - - usage_cnt = 0; - break; - default: - pt_debug(cd->dev, DL_INFO, - "%s: Unrecognized Main tag %d\n", - __func__, item_tag); - } - - /* Reset all local items */ - usage = 0; - break; - } - } - - if (buf != end) { - pt_debug(cd->dev, DL_ERROR, - "%s: Report descriptor length invalid\n", - __func__); - rc = -EINVAL; - goto exit; - } - - if (collection_nest) { - pt_debug(cd->dev, DL_ERROR, - "%s: Unbalanced collection items (%d)\n", - __func__, collection_nest); - rc = -EINVAL; - goto exit; - } - -exit: - if (rc) - pt_free_hid_reports_(cd); - mutex_unlock(&cd->hid_report_lock); - return rc; -} - -/******************************************************************************* - * FUNCTION: find_report_desc_field - * - * SUMMARY: Find the corresponding field from report according to the usage page - * and collection usage page. - * - * RETURN: - * pointer to hid field structure - * - * PARAMETERS: - * *cd - pointer to core data structure - * usage_page - hid usage page - * collection_usage_page - hid collection usage page - * collection_type - hid collection type - ******************************************************************************/ -static struct pt_hid_field *find_report_desc_field( - struct pt_core_data *cd, u32 usage_page, - u32 collection_usage_page, u8 collection_type) -{ - struct pt_hid_report *report = NULL; - struct pt_hid_field *field = NULL; - int i; - int j; - u32 field_cup; - u32 field_up; - - for (i = 0; i < cd->num_hid_reports; i++) { - report = cd->hid_reports[i]; - for (j = 0; j < report->num_fields; j++) { - field = report->fields[j]; - field_cup = field->collection_usage_pages - [collection_type]; - field_up = field->usage_page; - if (field_cup == collection_usage_page - && field_up == usage_page) { - return field; - } - } - } - - return NULL; -} - -/******************************************************************************* - * FUNCTION: fill_tch_abs - * - * SUMMARY: Fill touch abs with hid field. - * - * PARAMETERS: - * *cd - pointer to core data structure - * *field - pointer to hid field structure - ******************************************************************************/ -static void fill_tch_abs(struct pt_tch_abs_params *tch_abs, - struct pt_hid_field *field) -{ - tch_abs->ofs = field->offset / 8; - tch_abs->size = field->report_size / 8; - if (field->report_size % 8) - tch_abs->size += 1; - tch_abs->min = 0; - tch_abs->max = 1 << field->report_size; - tch_abs->bofs = field->offset - (tch_abs->ofs << 3); - tch_abs->logical_max = field->logical_max; -} - -/******************************************************************************* - * FUNCTION: find_report_desc - * - * SUMMARY: Find out the corresponding report based on the usage page. - * - * RETURN: - * pointer to hid report structure - * - * PARAMETERS: - * *cd - pointer to core data structure - * id - report id - * usage_page - hid usage page - ******************************************************************************/ -static struct pt_hid_report *find_report_desc(struct pt_core_data *cd, - u8 id, u32 usage_page) -{ - struct pt_hid_report *report = NULL; - int i; - - for (i = 0; i < cd->num_hid_reports; i++) { - if (cd->hid_reports[i]->usage_page == usage_page && - cd->hid_reports[i]->id == id) { - report = cd->hid_reports[i]; - break; - } - } - - return report; -} - -/******************************************************************************* - * FUNCTION: setup_pen_report_from_report_desc - * - * SUMMARY: Setup values for pen report according to report descriptor. - * - * PARAMETERS: - * *cd - pointer to core data structure - ******************************************************************************/ -static void setup_pen_report_from_report_desc( - struct pt_core_data *cd) -{ - struct pt_sysinfo *si = &cd->sysinfo; - struct pt_hid_report *report; - struct pt_hid_field *field; - int i; - u32 pen_collection_usage_page = HID_PT_PEN_COL_USAGE_PG; - u8 id = PT_HID_PEN_REPORT_ID; - - /* - * Search each pen abs field. If found, fill the values into the - * pen struct.If not, mark this pen field as invalid (report = 0). - */ - for (i = PT_PEN_X; i < PT_PEN_NUM_ABS; i++) { - field = find_report_desc_field(cd, - pt_pen_abs_field_map[i], - pen_collection_usage_page, - HID_COLLECTION_PHYSICAL); - if (field) { - pt_debug(cd->dev, DL_DEBUG, - " Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n", - field, field->report_count, field->report_size, - field->offset, field->data_type, - field->logical_min, field->logical_max, - field->usage_page); - fill_tch_abs(&si->pen_abs[i], field); - si->pen_abs[i].report = 2; - pt_debug(cd->dev, DL_DEBUG, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d", - pt_pen_abs_string[i], - (u32)si->pen_abs[i].ofs, - (u32)si->pen_abs[i].size, - (u32)si->pen_abs[i].min, - (u32)si->pen_abs[i].max, - (u32)si->pen_abs[i].bofs, - si->pen_abs[i].report); - - } else { - si->pen_abs[i].report = 0; - pt_debug(cd->dev, DL_DEBUG, "%s: report:%d\n", - pt_pen_abs_string[i], si->pen_abs[i].report); - } - } - - report = find_report_desc(cd, id, pen_collection_usage_page); - if (report) - si->desc.pen_report_id = report->id; - else - si->desc.pen_report_id = PT_HID_PEN_REPORT_ID; -} - -/******************************************************************************* - * FUNCTION: setup_finger_report_from_report_desc - * - * SUMMARY: Setup values for finger report according to report descriptor. - * - * PARAMETERS: - * *cd - pointer to core data structure - ******************************************************************************/ -static void setup_finger_report_from_report_desc( - struct pt_core_data *cd) -{ - struct pt_sysinfo *si = &cd->sysinfo; - struct pt_hid_report *report; - struct pt_hid_field *field; - u32 tch_collection_usage_page = HID_PT_TCH_COL_USAGE_PG; - u8 id = PT_HID_FINGER_REPORT_ID; - int i; - - /* - * Search each touch abs field. If found, fill the values into the - * abs struct. If not, mark this abs field as invalid (report = 0). - */ - for (i = PT_TCH_X; i < PT_TCH_NUM_ABS; i++) { - field = find_report_desc_field(cd, - pt_tch_abs_field_map[i], - tch_collection_usage_page, - HID_COLLECTION_APPLICATION); - if (field) { - pt_debug(cd->dev, DL_DEBUG, - " Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n", - field, field->report_count, field->report_size, - field->offset, field->data_type, - field->logical_min, field->logical_max, - field->usage_page); - fill_tch_abs(&si->tch_abs[i], field); - si->tch_abs[i].report = 1; - pt_debug(cd->dev, DL_DEBUG, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d", - pt_tch_abs_string[i], - (u32)si->tch_abs[i].ofs, - (u32)si->tch_abs[i].size, - (u32)si->tch_abs[i].min, - (u32)si->tch_abs[i].max, - (u32)si->tch_abs[i].bofs, - si->tch_abs[i].report); - } else { - si->tch_abs[i].report = 0; - } - } - - /* - * Search each touch header field. If found, fill the values into - * the header struct. If not, mark this header field as invalid - * (report = 0). - */ - for (i = PT_TCH_TIME; i < PT_TCH_NUM_HDR; i++) { - field = find_report_desc_field(cd, - pt_tch_hdr_field_map[i], - tch_collection_usage_page, - HID_COLLECTION_APPLICATION); - if (field) { - pt_debug(cd->dev, DL_DEBUG, - " Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n", - field, field->report_count, field->report_size, - field->offset, field->data_type, - field->logical_min, field->logical_max, - field->usage_page); - fill_tch_abs(&si->tch_hdr[i], field); - si->tch_hdr[i].report = 1; - pt_debug(cd->dev, DL_DEBUG, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d", - pt_tch_hdr_string[i], - (u32)si->tch_hdr[i].ofs, - (u32)si->tch_hdr[i].size, - (u32)si->tch_hdr[i].min, - (u32)si->tch_hdr[i].max, - (u32)si->tch_hdr[i].bofs, - si->tch_hdr[i].report); - } else { - si->tch_hdr[i].report = 0; - } - } - - si->desc.max_touch_num = si->tch_hdr[PT_TCH_NUM].max; - - report = find_report_desc(cd, id, tch_collection_usage_page); - if (report) { - si->desc.tch_report_id = report->id; - /* First, report->record_size and report->header_size are based - * on 'BIT', they need to be divided by 8 to get tch_record_size - * and tch_header_size, which are based on 'BYTE'. - * Second, tch_header_size needs to add 3 bytes: 2 bytes length, - * 1 byte report id. - */ - si->desc.tch_record_size = report->record_size / 8; - si->desc.tch_header_size = (report->header_size / 8) + 3; - si->desc.max_tch_per_packet = report->log_collection_num; - } else { - si->desc.tch_report_id = PT_HID_FINGER_REPORT_ID; - si->desc.tch_record_size = TOUCH_REPORT_SIZE; - si->desc.tch_header_size = TOUCH_INPUT_HEADER_SIZE; - si->desc.max_tch_per_packet = MAX_TOUCH_NUM; - } - - pt_debug(cd->dev, DL_DEBUG, - "%s: tch_record_size:%d, tch_header_size:%d, max_touch_num:%d\n", - __func__, - si->desc.tch_record_size, - si->desc.tch_header_size, - si->desc.max_touch_num); -} - -/******************************************************************************* - * FUNCTION: publish_report_desc - * - * SUMMARY: If TTHE_TUNER_SUPPORT is defined print the report descriptor data - * into the tthe_tuner sysfs node under the label "HIDRptDesc". - * - * PARAMETERS: - * *cd - pointer to core data - * len - Report descriptor length - ******************************************************************************/ -static void publish_report_desc(struct pt_core_data *cd, u16 len) -{ - int max_bytes = 300; - int pr_bytes = 0; - int size = len; - -#ifdef TTHE_TUNER_SUPPORT - tthe_print(cd, cd->response_buf, size, - "HIDRptDesc="); -#endif - - while (size > max_bytes) { - pt_pr_buf(cd->dev, DL_DEBUG, - cd->response_buf + pr_bytes, - max_bytes, "<<< HIDRptDesc"); - pr_bytes += max_bytes; - size -= max_bytes; - } - if (size > 0) - pt_pr_buf(cd->dev, DL_DEBUG, - cd->response_buf + pr_bytes, - size, "<<< HIDRptDesc"); -} - -/******************************************************************************* - * FUNCTION: pt_get_report_descriptor_ - * - * SUMMARY: Get and parse report descriptor. - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *cd - pointer to core data - ******************************************************************************/ -static int pt_get_report_descriptor_(struct pt_core_data *cd) -{ - struct device *dev = cd->dev; - struct pt_hid_cmd hid_cmd = { - .descriptor = cd->hid_desc.report_desc_register, - .read_length = cd->hid_core.hid_report_desc_len, - }; - int rc; - int t; - u8 *desc; - u16 desc_len; - - rc = pt_hid_send_command(cd, &hid_cmd); - if (rc) { - pt_debug(dev, DL_ERROR, - "%s: failed to get HID report descriptor length, rc=%d\n", - __func__, rc); - goto exit; - } - - desc = cd->response_buf; - desc_len = cd->hid_core.hid_report_desc_len; - - /* Remove length field and report id to prepare parse work */ - if (cd->dut_status.protocol_mode != PT_PROTOCOL_MODE_HID) { - desc = cd->response_buf + 3; - desc_len -= 3; - } - - publish_report_desc(cd, cd->hid_core.hid_report_desc_len); - - rc = parse_report_descriptor(cd, desc, desc_len); - if (rc) { - pt_debug(cd->dev, DL_ERROR, - "%s: Error parsing report descriptor rc=%d\n", - __func__, rc); - } - - pt_debug(cd->dev, DL_INFO, "%s: %d reports found in descriptor\n", - __func__, cd->num_hid_reports); - - for (t = 0; t < cd->num_hid_reports; t++) { - struct pt_hid_report *report = cd->hid_reports[t]; - int j; - - pt_debug(cd->dev, DL_DEBUG, - "Report %d: type:%d id:%02X size:%d fields:%d rec_fld_index:%d hdr_sz:%d rec_sz:%d usage_page:%08X\n", - t, report->type, report->id, - report->size, report->num_fields, - report->record_field_index, report->header_size, - report->record_size, report->usage_page); - - if (report->id == PT_HID_FINGER_REPORT_ID) - pt_debug(cd->dev, DL_INFO, "%s: logical collection number: %d\n", - __func__, report->log_collection_num); - - for (j = 0; j < report->num_fields; j++) { - struct pt_hid_field *field = report->fields[j]; - - pt_debug(cd->dev, DL_DEBUG, - " Field %d: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n", - j, field->report_count, field->report_size, - field->offset, field->data_type, - field->logical_min, field->logical_max, - field->usage_page); - - pt_debug(cd->dev, DL_DEBUG, " Collections Phys:%08X App:%08X Log:%08X\n", - field->collection_usage_pages - [HID_COLLECTION_PHYSICAL], - field->collection_usage_pages - [HID_COLLECTION_APPLICATION], - field->collection_usage_pages - [HID_COLLECTION_LOGICAL]); - } - } - - setup_pen_report_from_report_desc(cd); - setup_finger_report_from_report_desc(cd); - - /* Free it for now */ - pt_free_hid_reports_(cd); - - pt_debug(cd->dev, DL_INFO, "%s: %d reports found in descriptor\n", - __func__, cd->num_hid_reports); - -exit: - return rc; -} - -/******************************************************************************* - * FUNCTION: pt_get_report_descriptor - * - * SUMMARY: Protected call to pt_get_report_descriptor_() - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *cd - pointer to core data - ******************************************************************************/ -static int pt_get_report_descriptor(struct pt_core_data *cd) -{ - int rc; - - rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); - if (rc < 0) { - pt_debug(cd->dev, DL_ERROR, - "%s: fail get exclusive ex=%p own=%p\n", - __func__, cd->exclusive_dev, cd->dev); - return rc; - } - - rc = pt_get_report_descriptor_(cd); - - if (release_exclusive(cd, cd->dev) < 0) - pt_debug(cd->dev, DL_ERROR, - "%s: fail to release exclusive\n", __func__); - - return rc; -} - /******************************************************************************* * FUNCTION: pt_get_mode * @@ -8498,6 +7381,7 @@ static int _pt_request_get_mode(struct device *dev, int protect, u8 *mode) int rc = 0; memset(&hid_desc, 0, sizeof(hid_desc)); + if (protect) rc = pt_get_hid_descriptor(cd, &hid_desc); else @@ -8764,15 +7648,13 @@ static int pt_core_sleep_(struct pt_core_data *cd) { int rc = 0; - /* - * Do nothing if system already sleeping or in progress of - * entering sleep. Proceed if awake or waking. - */ - if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_SLEEPING) - goto exit; - mutex_lock(&cd->system_lock); - cd->sleep_state = SS_SLEEPING; + if (cd->sleep_state == SS_SLEEP_OFF) { + cd->sleep_state = SS_SLEEPING; + } else { + mutex_unlock(&cd->system_lock); + return 1; + } mutex_unlock(&cd->system_lock); /* Ensure watchdog and startup works stopped */ @@ -8790,15 +7672,9 @@ static int pt_core_sleep_(struct pt_core_data *cd) rc = pt_put_device_into_deep_sleep_(cd); mutex_lock(&cd->system_lock); - if (rc == 0) { - cd->sleep_state = SS_SLEEP_ON; - } else { - cd->sleep_state = SS_SLEEP_OFF; - pt_start_wd_timer(cd); - } + cd->sleep_state = SS_SLEEP_ON; mutex_unlock(&cd->system_lock); -exit: return rc; } @@ -8911,43 +7787,30 @@ exit: } /******************************************************************************* - * FUNCTION: pt_get_touch_field + * FUNCTION: pt_get_touch_axis * - * SUMMARY: Function to calculate touch fields. The field refers to each element - * in the input report, such as "Number of Records", "X-axis". This function - * can calculate the value of element based on the bit offset, size and the - * max value of the element. + * SUMMARY: Function to calculate touch axis * * PARAMETERS: - * *dev - pointer to device structure - * *field - pointer to field calculation result + * *cd - pointer to core data structure + * *axis - pointer to axis calculation result * size - size in bytes * max - max value of result - * *data - pointer to input data to be parsed + * *xy_data - pointer to input data to be parsed * bofs - bit offset ******************************************************************************/ -void pt_get_touch_field(struct device *dev, - int *field, int size, int max, u8 *data, int bofs) +static void pt_get_touch_axis(struct pt_core_data *cd, + int *axis, int size, int max, u8 *data, int bofs) { int nbyte; int next; - for (nbyte = 0, *field = 0, next = 0; nbyte < size; nbyte++) { - pt_debug(dev, DL_DEBUG, - "%s: *field=%02X(%d) size=%d max=%08X data=%p data[%d]=%02X(%d) bofs=%d\n", - __func__, *field, *field, size, max, data, next, - data[next], data[next], bofs); - *field = *field + ((data[next] >> bofs) << (nbyte * 8)); + for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) { + *axis = *axis + ((data[next] >> bofs) << (nbyte * 8)); next++; } - if (max > 0) - *field &= max - 1; - - pt_debug(dev, DL_DEBUG, - "%s: *field=%02X(%d) size=%d max=%08X data=%p data[%d]=%02X(%d)\n", - __func__, *field, *field, size, max, data, next, - data[next], data[next]); + *axis &= max - 1; } /******************************************************************************* @@ -9045,7 +7908,7 @@ static int move_button_data(struct pt_core_data *cd, } /******************************************************************************* - * FUNCTION: move_touch_data_pip + * FUNCTION: move_touch_data * * SUMMARY: Move the valid touch data from the input buffer into the system * information structure, xy_mode and xy_data. @@ -9060,17 +7923,16 @@ static int move_button_data(struct pt_core_data *cd, * *cd - pointer to core data * *si - pointer to the system information structure ******************************************************************************/ -static int move_touch_data_pip(struct pt_core_data *cd, struct pt_sysinfo *si) +static int move_touch_data(struct pt_core_data *cd, struct pt_sysinfo *si) { int max_tch = si->sensing_conf_data.max_tch; - int num_cur_tch = 0; + int num_cur_tch; int length; - int actual_tch_num; struct pt_tch_abs_params *tch = &si->tch_hdr[PT_TCH_NUM]; - - int size = get_unaligned_le16(&cd->input_buf[0]); #ifdef TTHE_TUNER_SUPPORT - if (size > 0) + int size = get_unaligned_le16(&cd->input_buf[0]); + + if (size) tthe_print(cd, cd->input_buf, size, "OpModeData="); #endif @@ -9078,21 +7940,11 @@ static int move_touch_data_pip(struct pt_core_data *cd, struct pt_sysinfo *si) pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_mode, si->desc.tch_header_size, "xy_mode"); - pt_get_touch_field(cd->dev, &num_cur_tch, tch->size, + pt_get_touch_axis(cd, &num_cur_tch, tch->size, tch->max, si->xy_mode + 3 + tch->ofs, tch->bofs); if (unlikely(num_cur_tch > max_tch)) num_cur_tch = max_tch; - actual_tch_num = (size - si->desc.tch_header_size) - / si->desc.tch_record_size; - - if (actual_tch_num < num_cur_tch) { - pt_debug(cd->dev, DL_ERROR, - "%s: ATM - Malformed touch packet. actual_tch_num=%d, num_cur_tch=%d\n", - __func__, actual_tch_num, num_cur_tch); - num_cur_tch = actual_tch_num; - } - length = num_cur_tch * si->desc.tch_record_size; memcpy(si->xy_data, &cd->input_buf[si->desc.tch_header_size], length); @@ -9100,150 +7952,6 @@ static int move_touch_data_pip(struct pt_core_data *cd, struct pt_sysinfo *si) return 0; } -/******************************************************************************* - * FUNCTION: move_touch_data_hid - * - * SUMMARY: Move the valid touch data from the input buffer into the system - * information structure, xy_mode and xy_data. - * - If TTHE_TUNER_SUPPORT is defined print the raw touch data into - * the tthe_tuner sysfs node under the label "HID-USB", "HID-I2C" or - * "OpModeData" - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *cd - pointer to core data - * *si - pointer to the system information structure - ******************************************************************************/ -static int move_touch_data_hid(struct pt_core_data *cd, - struct pt_sysinfo *si) -{ - int max_tch = si->sensing_conf_data.max_tch; - int num_cur_tch = 0; - u16 hdr_sz; - u16 rec_sz; - int max_tch_per_packet; - static u8 remain_tch; - static u8 packet_no; - static u8 input_sz; - int length; - int rc = 0; - struct pt_tch_abs_params *tch = &si->tch_hdr[PT_TCH_NUM]; - int size = get_unaligned_le16(&cd->input_buf[0]); - - hdr_sz = si->desc.tch_header_size; - rec_sz = si->desc.tch_record_size; - max_tch_per_packet = si->desc.max_tch_per_packet; - -#ifdef TTHE_TUNER_SUPPORT - length = hdr_sz + rec_sz * max_tch_per_packet; - pt_debug(cd->dev, DL_DEBUG, - "%s: touch report size=%d, len=%d, record=%d, max_tch=%d\n", - __func__, size, length, rec_sz, max_tch_per_packet); - /* - * HID over USB does not require the two byte length field, so - * this should print from input_buf[2] but to keep both finger - * and pen reports the same the length is included - */ - if (cd->tthe_hid_usb_format == PT_TTHE_TUNER_FORMAT_HID_USB) - tthe_print(cd, &(cd->input_buf[2]), length - 2, - "HID-USB="); - else if (cd->tthe_hid_usb_format == - PT_TTHE_TUNER_FORMAT_HID_I2C) - tthe_print(cd, &(cd->input_buf[0]), length, - "HID-I2C="); -#endif - - memcpy(si->xy_mode, cd->input_buf, hdr_sz); - pt_pr_buf(cd->dev, DL_INFO, (u8 *)si->xy_mode, - hdr_sz, "xy_mode"); - - /* - * The fifth parameter points to the location of "Number of Records": - * Pointer "xy_mode" points to start address of touch report header. - * The byte offset of "Number of Records" is 2 (tch->ofs, retrieved - * from report descriptor). Then the pointer "xy_mode + 3 (skip the - * two bytes length and 1 byte report ID) + tch->ofs" points to the - * location of "Number of Records". - */ - pt_get_touch_field(cd->dev, &num_cur_tch, tch->size, - tch->max, si->xy_mode + 3 + tch->ofs, tch->bofs); - - if (unlikely(num_cur_tch > max_tch)) - num_cur_tch = max_tch; - - /* According to Hybrid Mode touch report defined by Microsoft, - * if the touch number exceeds the max touch per packet (defined - * in report descriptor), the touch packet will be broken up into - * to multiple reports. Timestamp will be consistent across all - * touches in a single frame. The "Number of records" will have the - * total in the first report and be 0 in all subsequent reports that - * belong to the same frame. - */ - pt_debug(cd->dev, DL_INFO, "%s: max_tch=%d, packet_no=%d, num_cur_tch=%d\n", - __func__, max_tch, packet_no, num_cur_tch); - - if (packet_no == 0) { - input_sz = hdr_sz + num_cur_tch * rec_sz; - memset(cd->touch_buf, 0, sizeof(cd->touch_buf)); - memcpy(cd->touch_buf, cd->input_buf, input_sz); - if (num_cur_tch > max_tch_per_packet) { - remain_tch = num_cur_tch - max_tch_per_packet; - packet_no++; - rc = -EINVAL; - goto exit; - } - } else { - if (remain_tch <= max_tch_per_packet) { - memcpy(&cd->touch_buf[hdr_sz + - max_tch_per_packet * packet_no * rec_sz], - &(cd->input_buf[hdr_sz]), - rec_sz * remain_tch); - remain_tch = 0; - packet_no = 0; - rc = 0; - } else { - memcpy(&cd->touch_buf[hdr_sz + - max_tch_per_packet * packet_no * rec_sz], - &(cd->input_buf[hdr_sz]), - rec_sz * max_tch_per_packet); - remain_tch -= max_tch_per_packet; - packet_no++; - rc = -EINVAL; - goto exit; - } - } - -#ifdef TTHE_TUNER_SUPPORT - /* Update pip packet length */ - cd->touch_buf[0] = input_sz & 0xff; - cd->touch_buf[1] = (input_sz & 0xff00) >> 8; - - /* - * For PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP and - * PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP mode, all touches - * should be combined into a single row for the tthe_tuner node. - */ - if (cd->tthe_hid_usb_format == - PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP || - cd->tthe_hid_usb_format == - PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP) - tthe_print(cd, cd->touch_buf, input_sz, - "OpModeData="); -#endif - - memcpy(si->xy_data, &cd->touch_buf[hdr_sz], input_sz - hdr_sz); - pt_pr_buf(cd->dev, DL_INFO, - (u8 *)si->xy_data, input_sz - hdr_sz, "xy_data"); - - input_sz = 0; - -exit: - return rc; -} - /******************************************************************************* * FUNCTION: move_hid_pen_data * @@ -9263,17 +7971,8 @@ exit: ******************************************************************************/ static int move_hid_pen_data(struct pt_core_data *cd, struct pt_sysinfo *si) { - int size = get_unaligned_le16(&cd->input_buf[0]); - #ifdef TTHE_TUNER_SUPPORT - enum pt_pen_abs abs; - struct pt_pen pen; - int packet_len = 17; - int report_id = 1; - static int report_counter; - int event_id = 2; - int touch_id = 0; - int i = 0; + int size = get_unaligned_le16(&cd->input_buf[0]); if (size) { /* @@ -9281,77 +7980,14 @@ static int move_hid_pen_data(struct pt_core_data *cd, struct pt_sysinfo *si) * this should print from input_buf[2] but to keep both finger * and pen reports the same the length is included */ - if (cd->tthe_hid_usb_format == PT_TTHE_TUNER_FORMAT_HID_USB) + if (cd->tthe_hid_usb_format == PT_FEATURE_ENABLE) tthe_print(cd, &(cd->input_buf[2]), size - 2, "HID-USB="); - else if (cd->tthe_hid_usb_format == - PT_TTHE_TUNER_FORMAT_HID_I2C || - cd->tthe_hid_usb_format == - PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP) + else tthe_print(cd, &(cd->input_buf[0]), size, "HID-I2C="); - else if (cd->tthe_hid_usb_format == - PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP) { - memset(cd->touch_buf, 0, sizeof(cd->touch_buf)); - - for (abs = PT_PEN_X; abs < PT_PEN_NUM_ABS; abs++) { - if (!si->pen_abs[abs].report) - continue; - pt_get_touch_field(cd->dev, &pen.abs[abs], - si->pen_abs[abs].size, - si->pen_abs[abs].max, - cd->input_buf + 3 + - si->pen_abs[abs].ofs, - si->pen_abs[abs].bofs); - pt_debug(cd->dev, DL_DEBUG, "%s: get %s=%04X(%d)\n", - __func__, pt_pen_abs_string[abs], - pen.abs[abs], pen.abs[abs]); - } - /* pip packet length: 17 */ - cd->touch_buf[i++] = packet_len & 0xff; - cd->touch_buf[i++] = (packet_len & 0xff00) >> 8; - /* pip finger report id: 1*/ - cd->touch_buf[i++] = report_id; - /* Timestamp: 0 */ - cd->touch_buf[i++] = 0; - cd->touch_buf[i++] = 0; - /* LO: 0; Number of Records: 1*/ - cd->touch_buf[i++] = 1; - /* Report Counter:[0-3]; Noise Effects:0 */ - cd->touch_buf[i++] = (report_counter & 0x03) << 6; - /* Touch Type: Stylus (2)*/ - cd->touch_buf[i++] = PT_OBJ_STYLUS; - /* Tip: pen tip switch; Event ID: 2; Touch ID: 0 */ - cd->touch_buf[i++] = - ((pen.abs[PT_PEN_TS] & 0x01) << 7) | - ((event_id & 0x03) << 5) | - (touch_id & 0x1f); - /* X: Pen X */ - cd->touch_buf[i++] = pen.abs[PT_PEN_X] & 0xff; - cd->touch_buf[i++] = (pen.abs[PT_PEN_X] & 0xff00) >> 8; - /* Y: Pen Y */ - cd->touch_buf[i++] = pen.abs[PT_PEN_Y] & 0xff; - cd->touch_buf[i++] = (pen.abs[PT_PEN_Y] & 0xff00) >> 8; - /* Pressure: Pen pressure drop the least 4 bits */ - cd->touch_buf[i++] = (pen.abs[PT_PEN_P] & 0xff0) >> 4; - /* Major: Pen Tilt_X*/ - cd->touch_buf[i++] = pen.abs[PT_PEN_X_TILT] & 0xff; - /* Minor: Pen Tilt_Y*/ - cd->touch_buf[i++] = pen.abs[PT_PEN_Y_TILT] & 0xff; - /* Orientation: Btn2, Btn1*/ - cd->touch_buf[i++] = - (pen.abs[PT_PEN_BS] & 0x01) | - ((pen.abs[PT_PEN_2ND_BS] & 0x01) << 1); - - if (++report_counter > 3) - report_counter = 0; - - tthe_print(cd, cd->touch_buf, packet_len, - "OpModeData="); - } } #endif - memcpy(si->xy_data, cd->input_buf, size); pt_pr_buf(cd->dev, DL_INFO, (u8 *)&(cd->input_buf[0]), size, "HID Pen"); return 0; } @@ -9388,22 +8024,9 @@ static int parse_touch_input(struct pt_core_data *cd, int size) if (!si->xy_mode || !si->xy_data) return rc; - if (report_id == PT_PIP_TOUCH_REPORT_ID || - report_id == PT_HID_VS_FINGER_REPORT_ID) { - if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_PIP) - rc = move_touch_data_pip(cd, si); - else { - rc = move_touch_data_hid(cd, si); - if (rc) { - pt_debug(cd->dev, DL_INFO, - "%s: Skip report for the first touch packet\n", - __func__); - return 0; - } - } - } else if ((report_id == PT_HID_PEN_REPORT_ID || - report_id == PT_HID_VS_PEN_REPORT_ID) && - cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) + if (report_id == PT_PIP_TOUCH_REPORT_ID) + rc = move_touch_data(cd, si); + else if (report_id == PT_HID_PEN_REPORT_ID) rc = move_hid_pen_data(cd, si); else if (report_id == PT_PIP_CAPSENSE_BTN_REPORT_ID) rc = move_button_data(cd, si); @@ -9515,8 +8138,6 @@ static bool pt_is_touch_report(int id) { if (id == PT_PIP_TOUCH_REPORT_ID || id == PT_HID_PEN_REPORT_ID || - id == PT_HID_VS_FINGER_REPORT_ID || - id == PT_HID_VS_PEN_REPORT_ID || id == PT_PIP_CAPSENSE_BTN_REPORT_ID || id == PT_PIP_SENSOR_DATA_REPORT_ID || id == PT_PIP_TRACKING_HEATMAP_REPORT_ID) @@ -9549,7 +8170,7 @@ static bool pt_is_touch_report(int id) ******************************************************************************/ static int pt_parse_input(struct pt_core_data *cd) { - int report_id = 0; + int report_id; int cmd_id; int is_command = 0; int size; @@ -9566,7 +8187,6 @@ static int pt_parse_input(struct pt_core_data *cd) if (print_size <= PT_MAX_INPUT) pt_pr_buf(cd->dev, DL_DEBUG, cd->input_buf, print_size, "<<< Read buf"); - if (size == 0 || (size == 11 && (cd->input_buf[PIP2_RESP_SEQUENCE_OFFSET] & @@ -9604,7 +8224,6 @@ static int pt_parse_input(struct pt_core_data *cd) STARTUP_STATUS_START; } cd->fw_system_mode = FW_SYS_MODE_UNDEFINED; - pt_debug(cd->dev, DL_INFO, "%s: ATM Gen5/6 %s sentinel received\n", __func__, @@ -9622,7 +8241,6 @@ static int pt_parse_input(struct pt_core_data *cd) __func__); } mutex_unlock(&cd->system_lock); - if (pt_allow_enumeration(cd)) { if (cd->active_dut_generation == DUT_UNKNOWN) { pt_debug(cd->dev, DL_INFO, @@ -9698,11 +8316,9 @@ static int pt_parse_input(struct pt_core_data *cd) /* PIP2 does not have a report id, hard code it */ report_id = 0x00; cmd_id = cd->input_buf[PIP2_RESP_COMMAND_ID_OFFSET]; - calc_crc = crc_ccitt_calculate(cd->input_buf, size - 2); resp_crc = cd->input_buf[size - 2] << 8; resp_crc |= cd->input_buf[size - 1]; - if ((cd->pip2_cmd_tag_seq != (cd->input_buf[PIP2_RESP_SEQUENCE_OFFSET] & 0x0F)) && (resp_crc != calc_crc) && @@ -9710,17 +8326,16 @@ static int pt_parse_input(struct pt_core_data *cd) == PT_PIP_TOUCH_REPORT_ID) || (cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET] == PT_PIP_CAPSENSE_BTN_REPORT_ID))) { - report_id = cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET]; - cmd_id = cd->input_buf[PIP1_RESP_COMMAND_ID_OFFSET]; - pt_debug(cd->dev, DL_INFO, - "%s: %s %d %s\n", __func__, - "Received PIP1 report id =", - report_id, - "when expecting a PIP2 report"); - } else { - is_command = 1; - touch_report = false; + pt_debug(cd->dev, DL_WARN, + "%s: %s %d %s\n", + __func__, + "Received PIP1 report id =", + cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET], + "when expecting a PIP2 report - IGNORE report"); + return 0; } + is_command = 1; + touch_report = false; } else { report_id = cd->input_buf[PIP1_RESP_REPORT_ID_OFFSET]; cmd_id = cd->input_buf[PIP1_RESP_COMMAND_ID_OFFSET]; @@ -9740,7 +8355,6 @@ static int pt_parse_input(struct pt_core_data *cd) #endif return 0; } - mod_timer_pending(&cd->watchdog_timer, jiffies + msecs_to_jiffies(cd->watchdog_interval)); @@ -9752,12 +8366,10 @@ static int pt_parse_input(struct pt_core_data *cd) is_command = 1; touch_report = false; } - if (unlikely(is_command)) { parse_command_input(cd, size); return 0; } - if (touch_report) parse_touch_input(cd, size); @@ -9788,6 +8400,7 @@ static int pt_read_input(struct pt_core_data *cd) * Interrupt for easywake, wait for bus controller to wake */ mutex_lock(&cd->system_lock); + if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) { if (cd->sleep_state == SS_SLEEP_ON) { mutex_unlock(&cd->system_lock); @@ -9815,7 +8428,6 @@ static int pt_read_input(struct pt_core_data *cd) mutex_unlock(&cd->system_lock); read: - memset(cd->input_buf, 0, sizeof(cd->input_buf)); /* Try reading up to 'retry' times */ while (retry-- != 0) { rc = pt_adap_read_default_nosize(cd, cd->input_buf, @@ -9851,15 +8463,8 @@ irqreturn_t pt_irq(int irq, void *handle) struct pt_core_data *cd = handle; int rc = 0; -#ifdef TTDL_PTVIRTDUT_SUPPORT - if (!cd->route_bus_virt_dut) { - if (!pt_check_irq_asserted(cd)) - return IRQ_HANDLED; - } -#else if (!pt_check_irq_asserted(cd)) return IRQ_HANDLED; -#endif /* TTDL_PTVIRTDUT_SUPPORT */ rc = pt_read_input(cd); #ifdef TTDL_DIAGNOSTICS @@ -9886,36 +8491,6 @@ irqreturn_t pt_irq(int irq, void *handle) return IRQ_HANDLED; } -#ifdef PT_AUX_BRIDGE_ENABLED -/******************************************************************************* - * FUNCTION: pt_trigger_ttdl_irq - * - * SUMMARY: API for other kernel drivers to artificially trigger a TTDL IRQ - * - * IMPROVE - dpmux should likely call pt_get_core_data once and make a - * copy of cd so that the function only needs to be called once. - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: none - * - ******************************************************************************/ -int pt_trigger_ttdl_irq(void) -{ - struct pt_core_data *cd; - char *core_name = PT_DEFAULT_CORE_ID; - - cd = pt_get_core_data(core_name); - if (!cd) { - pr_err("%s: No Device\n", __func__); - return -ENODEV; - } - pt_irq(cd->irq, (void *)cd); - return 0; -} -EXPORT_SYMBOL_GPL(pt_trigger_ttdl_irq); -#endif /******************************************************************************* * FUNCTION: _pt_subscribe_attention @@ -10002,11 +8577,11 @@ int _pt_unsubscribe_attention(struct device *dev, if (atten->id == id && atten->mode == mode) { list_del(&atten->node); spin_unlock(&cd->spinlock); - kfree(atten); pt_debug(cd->dev, DL_DEBUG, "%s: %s=%p %s=%d\n", __func__, "unsub for atten->dev", atten->dev, "atten->mode", atten->mode); + kfree(atten); return 0; } } @@ -10520,8 +9095,7 @@ int pt_pip2_exit_bl_(struct pt_core_data *cd, u8 *status_str, int buf_size) rc = _pt_request_wait_for_enum_state(cd->dev, 4000, STARTUP_STATUS_FW_RESET_SENTINEL); if (rc && load_status_str) { - strlcpy(status_str, - "No FW sentinel after BL", + strlcpy(status_str, "No FW sentinel after BL", sizeof(*status_str)*PT_STATUS_STR_LEN); goto exit; } @@ -10550,9 +9124,8 @@ int pt_pip2_exit_bl_(struct pt_core_data *cd, u8 *status_str, int buf_size) cd->startup_status); if (load_status_str && !(cd->startup_status & STARTUP_STATUS_FW_OUT_OF_BOOT)) { - strlcpy(status_str, - "FW Stuck in Boot mode", - sizeof(*status_str)*PT_STATUS_STR_LEN); + strlcpy(status_str, "FW Stuck in Boot mode", + sizeof(*status_str)*PT_STATUS_STR_LEN); goto exit; } } @@ -10569,15 +9142,13 @@ int pt_pip2_exit_bl_(struct pt_core_data *cd, u8 *status_str, int buf_size) } if (load_status_str) { if (rc == PIP2_RSP_ERR_INVALID_IMAGE) - strlcpy(status_str, - "Failed - Invalid image in FLASH", + strlcpy(status_str, "Failed - Invalid image in FLASH", sizeof(*status_str)*PT_STATUS_STR_LEN); else if (!rc) - strlcpy(status_str, - "Entered APP from BL mode", sizeof(*status_str)*PT_STATUS_STR_LEN); + strlcpy(status_str, "Entered APP from BL mode", + sizeof(*status_str)*PT_STATUS_STR_LEN); else - strlcpy(status_str, - "Failed to enter APP from BL mode", + strlcpy(status_str, "Failed to enter APP from BL mode", sizeof(*status_str)*PT_STATUS_STR_LEN); } } else if (mode == PT_MODE_OPERATIONAL) { @@ -10587,12 +9158,10 @@ int pt_pip2_exit_bl_(struct pt_core_data *cd, u8 *status_str, int buf_size) rc = pt_poll_for_fw_exit_boot_mode(cd, 1500, &wait_time); if (load_status_str) { if (!rc) - strlcpy(status_str, - "Already in APP mode", + strlcpy(status_str, "Already in APP mode", sizeof(*status_str)*PT_STATUS_STR_LEN); else - strlcpy(status_str, - "Already in APP mode - FW stuck in Boot mode", + strlcpy(status_str, "Already in APP mode - FW stuck in Boot mode", sizeof(*status_str)*PT_STATUS_STR_LEN); } } else if (rc || mode == PT_MODE_UNKNOWN) { @@ -10600,7 +9169,7 @@ int pt_pip2_exit_bl_(struct pt_core_data *cd, u8 *status_str, int buf_size) cd->mode = mode; mutex_unlock(&cd->system_lock); if (load_status_str) - strlcpy(status_str, "Failed to determine active mode", + strlcpy(status_str, "Failed to determine active mode", sizeof(*status_str)*PT_STATUS_STR_LEN); } @@ -10668,7 +9237,6 @@ static int _fast_startup(struct pt_core_data *cd) int wait_time = 0; memset(&hid_desc, 0, sizeof(hid_desc)); - reset: if (retry != PT_CORE_STARTUP_RETRY_COUNT) pt_debug(cd->dev, DL_INFO, "%s: Retry %d\n", __func__, @@ -10910,15 +9478,13 @@ static int pt_core_wake_(struct pt_core_data *cd) { int rc = 0; - /* - * Do nothing if system already awake or in progress of waking. - * Proceed if sleeping or entering sleep state. - */ - if (cd->sleep_state == SS_SLEEP_OFF || cd->sleep_state == SS_WAKING) - goto exit; - mutex_lock(&cd->system_lock); - cd->sleep_state = SS_WAKING; + if (cd->sleep_state == SS_SLEEP_ON) { + cd->sleep_state = SS_WAKING; + } else { + mutex_unlock(&cd->system_lock); + return 1; + } mutex_unlock(&cd->system_lock); if (!(cd->cpdata->flags & PT_CORE_FLAG_SKIP_RESUME)) { @@ -10933,20 +9499,15 @@ static int pt_core_wake_(struct pt_core_data *cd) } mutex_lock(&cd->system_lock); - if (rc == 0) - cd->sleep_state = SS_SLEEP_OFF; - else - cd->sleep_state = SS_SLEEP_ON; + cd->sleep_state = SS_SLEEP_OFF; mutex_unlock(&cd->system_lock); pt_start_wd_timer(cd); - -exit: return rc; } /******************************************************************************* - * FUNCTION: pt_core_wake + * FUNCTION: pt_core_wake_ * * SUMMARY: Protected call to pt_core_wake_ by exclusive access to the DUT. * @@ -11153,7 +9714,6 @@ static int pt_enum_with_dut_(struct pt_core_data *cd, bool reset, bool detected = false; u8 return_data[8]; u8 mode = PT_MODE_UNKNOWN; - u8 protocol_mode = PT_PROTOCOL_MODE_PIP; u8 pid = PANEL_ID_NOT_ENABLED; u8 sys_mode = FW_SYS_MODE_UNDEFINED; struct pt_hid_desc hid_desc; @@ -11165,13 +9725,11 @@ static int pt_enum_with_dut_(struct pt_core_data *cd, bool reset, pt_debug(cd->dev, DL_INFO, "%s: Start enum... 0x%04X, reset=%d\n", __func__, cd->startup_status, reset); pt_stop_wd_timer(cd); - reset: if (try > 1) pt_debug(cd->dev, DL_WARN, "%s: DUT Enum Attempt %d\n", __func__, try); pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN); - if (cd->active_dut_generation == DUT_PIP1_ONLY) { pt_debug(cd->dev, DL_INFO, "%s: PIP1 Enumeration start\n", __func__); @@ -11196,7 +9754,6 @@ reset: /* sleep to allow FW to be launched if available */ msleep(120); } - rc = pt_get_hid_descriptor_(cd, &hid_desc); if (rc < 0) { pt_debug(cd->dev, DL_ERROR, @@ -11209,7 +9766,6 @@ reset: } detected = true; cd->mode = pt_get_mode(cd, &hid_desc); - /* * Most systems do not use an XY pin as the panel_id and so * the BL is used to retrieve the panel_id, however for @@ -11230,10 +9786,9 @@ reset: goto exit; } cd->mode = PT_MODE_BOOTLOADER; - pt_debug(cd->dev, DL_INFO, - "%s: Bootloader mode\n", __func__); + pt_debug(cd->dev, DL_INFO, "%s: Bootloader mode\n", + __func__); } - rc = pt_hid_output_bl_get_information_(cd, return_data); if (!rc) { cd->bl_info.ready = true; @@ -11246,13 +9801,11 @@ reset: "%s: failed to get chip ID, r=%d\n", __func__, rc); } - rc = pt_hid_output_bl_get_panel_id_(cd, &pid); mutex_lock(&cd->system_lock); if (!rc) { cd->pid_for_loader = pid; - pt_debug(cd->dev, DL_INFO, - "%s: Panel ID: 0x%02X\n", + pt_debug(cd->dev, DL_INFO, "%s: Panel ID: 0x%02X\n", __func__, cd->pid_for_loader); } else { cd->pid_for_loader = PANEL_ID_NOT_ENABLED; @@ -11263,7 +9816,6 @@ reset: mutex_unlock(&cd->system_lock); } - /* Exit BL due to XY_PIN case or any other cause to be in BL */ if (cd->mode == PT_MODE_BOOTLOADER) { rc = pt_hid_output_bl_launch_app_(cd); @@ -11291,6 +9843,7 @@ reset: rc = -EIO; goto exit; } + cd->mode = pt_get_mode(cd, &hid_desc); if (cd->mode == PT_MODE_BOOTLOADER) { pt_debug(cd->dev, DL_WARN, @@ -11310,24 +9863,21 @@ reset: /* Generation is PIP2 Capable */ pt_debug(cd->dev, DL_INFO, "%s: PIP2 Enumeration start\n", __func__); - rc = pt_pip2_get_status_(cd); + + rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL); if (rc) { pt_debug(cd->dev, DL_ERROR, "%s: Get mode failed, mode unknown\n", __func__); mode = PT_MODE_UNKNOWN; - } else { - mode = cd->dut_status.mode; + } else detected = true; - } cd->mode = mode; - switch (cd->mode) { case PT_MODE_OPERATIONAL: pt_debug(cd->dev, DL_INFO, "%s: Operational mode\n", __func__); - protocol_mode = cd->dut_status.protocol_mode; if (cd->app_pip_ver_ready == false) { rc = pt_pip2_get_version_(cd); if (!rc) @@ -11405,20 +9955,19 @@ reset: */ msleep(60); /* Check and update the mode */ - rc = pt_pip2_get_status_(cd); + rc = pt_pip2_get_mode_sysmode_(cd, &mode, NULL); if (rc) { pt_debug(cd->dev, DL_ERROR, "%s: Get mode failed, mode unknown\n", __func__); mode = PT_MODE_UNKNOWN; - } else - mode = cd->dut_status.mode; + } + cd->mode = mode; if (cd->mode == PT_MODE_OPERATIONAL) { pt_debug(cd->dev, DL_INFO, "%s: Launched to Operational mode\n", __func__); - protocol_mode = cd->dut_status.protocol_mode; } else if (cd->mode == PT_MODE_BOOTLOADER) { pt_debug(cd->dev, DL_ERROR, "%s: Launch failed, Bootloader mode\n", @@ -11432,7 +9981,6 @@ reset: goto reset; goto exit; } - if (cd->app_pip_ver_ready == false) { rc = pt_pip2_get_version_(cd); if (!rc) @@ -11451,30 +9999,11 @@ reset: goto reset; goto exit; } - if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) { - rc = pt_get_hid_descriptor_(cd, &hid_desc); - if (!rc) - *enum_status |= STARTUP_STATUS_GET_DESC; - } else { - *enum_status |= STARTUP_STATUS_GET_DESC; - } + *enum_status |= STARTUP_STATUS_GET_DESC; } pt_init_pip_report_fields(cd); - if (protocol_mode == PT_PROTOCOL_MODE_HID) { - rc = pt_get_report_descriptor_(cd); - if (rc < 0) { - pt_debug(cd->dev, DL_ERROR, - "%s: Error on getting report descriptor r=%d\n", - __func__, rc); - if (try++ < PT_CORE_STARTUP_RETRY_COUNT) - goto reset; - goto exit; - } - } - *enum_status |= STARTUP_STATUS_GET_RPT_DESC; - if (!cd->features.easywake) cd->easy_wakeup_gesture = PT_CORE_EWG_NONE; @@ -11488,7 +10017,6 @@ reset: goto exit; } *enum_status |= STARTUP_STATUS_GET_SYS_INFO; - /* FW cannot handle most PIP cmds when it is still in BOOT mode */ rc = _pt_poll_for_fw_exit_boot_mode(cd, 10000, &wait_time); if (!rc) { @@ -11507,7 +10035,6 @@ reset: __func__, cd->sysinfo.ttdata.pip_ver_major, cd->sysinfo.ttdata.pip_ver_minor); - rc = pt_get_ic_crc_(cd, PT_TCH_PARM_EBID); if (rc) { pt_debug(cd->dev, DL_ERROR, @@ -11526,7 +10053,6 @@ reset: } else *enum_status |= STARTUP_STATUS_GET_CFG_CRC; } - rc = pt_restore_parameters_(cd); if (rc) { pt_debug(cd->dev, DL_ERROR, @@ -11534,7 +10060,6 @@ reset: __func__, rc); } else *enum_status |= STARTUP_STATUS_RESTORE_PARM; - call_atten_cb(cd, PT_ATTEN_STARTUP, 0); cd->watchdog_interval = PT_WATCHDOG_TIMEOUT; cd->startup_retry_count = 0; @@ -11600,7 +10125,6 @@ static int pt_enum_with_dut(struct pt_core_data *cd, bool reset, u32 *status) else pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n", __func__); - exit: mutex_lock(&cd->system_lock); /* Clear startup state for any tasks waiting for startup completion */ @@ -11616,11 +10140,9 @@ exit: return rc; } -#ifndef TTDL_KERNEL_SUBMISSION static int add_sysfs_interfaces(struct device *dev); static void remove_sysfs_interfaces(struct device *dev); static void remove_sysfs_and_modules(struct device *dev); -#endif /*!TTDL_KERNEL_SUBMISSION */ static void pt_release_modules(struct pt_core_data *cd); static void pt_probe_modules(struct pt_core_data *cd); /******************************************************************************* @@ -11646,7 +10168,6 @@ static int _pt_ttdl_restart(struct device *dev) struct i2c_client *client = (struct i2c_client *)container_of(dev, struct i2c_client, dev); #endif - /* * Make sure the device is awake, pt_mt_release function will * cause pm sleep function and lead to deadlock. @@ -11892,7 +10413,6 @@ static int pt_core_suspend_(struct device *dev) static int pt_core_suspend(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); - if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) return 0; @@ -11915,7 +10435,6 @@ static int pt_core_suspend(struct device *dev) static int pt_core_resume_(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); - if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) goto exit; @@ -11963,7 +10482,6 @@ exit: static int pt_core_resume(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); - if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) return 0; @@ -11992,7 +10510,6 @@ static int pt_pm_notifier(struct notifier_block *nb, { struct pt_core_data *cd = container_of(nb, struct pt_core_data, pm_notifier); - if (action == PM_SUSPEND_PREPARE) { pt_debug(cd->dev, DL_INFO, "%s: Suspend prepare\n", __func__); @@ -12176,12 +10693,13 @@ _retry: pt_debug(cd->dev, DL_INFO, ">>> %s: Write Buffer Size[%d] Stay in BL\n", __func__, (int)sizeof(host_mode_cmd)); + pt_pr_buf(cd->dev, DL_DEBUG, host_mode_cmd, (int)sizeof(host_mode_cmd), ">>> User CMD"); rc = 1; while (rc && time < 34) { rc = pt_adap_write_read_specific(cd, 4, - host_mode_cmd, NULL, 0); + host_mode_cmd, NULL); usleep_range(1800, 2000); time += 2; } @@ -12274,7 +10792,7 @@ exit: } } - pt_debug(dev, DL_INFO, "%s: Flashless Auto_BL - %s\n", __func__, + pt_debug(dev, DL_INFO, "%s Flashless Auto_BL - %s\n", __func__, saved_flashless_auto_bl_mode == PT_ALLOW_AUTO_BL ? "ALLOW" : "SUPPRESS"); cd->flashless_auto_bl = saved_flashless_auto_bl_mode; @@ -12300,47 +10818,34 @@ exit: ******************************************************************************/ int _pt_pip2_file_open(struct device *dev, u8 file_no) { - int rc = 0; + int ret = 0; u16 status; u16 actual_read_len; u8 file_handle; u8 data[2]; u8 read_buf[10]; - u8 expected_len = pt_pip2_get_cmd_response_len(PIP2_CMD_ID_FILE_OPEN); pt_debug(dev, DL_DEBUG, "%s: OPEN file %d\n", __func__, file_no); data[0] = file_no; - rc = _pt_request_pip2_send_cmd(dev, + ret = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_OPEN, data, 1, read_buf, &actual_read_len); - if (rc) { + if (ret) { pt_debug(dev, DL_ERROR, - "%s: FILE_OPEN timeout for file=%d\n", + "%s ROM BL FILE_OPEN timeout for file = %d\n", __func__, file_no); return -PIP2_RSP_ERR_NOT_OPEN; } status = read_buf[PIP2_RESP_STATUS_OFFSET]; - if (rc || ((status != PIP2_RSP_ERR_NONE) && - (status != PIP2_RSP_ERR_ALREADY_OPEN))) { + file_handle = read_buf[PIP2_RESP_BODY_OFFSET]; + if (ret || ((status != 0x00) && (status != 0x03)) || + (file_handle != file_no)) { pt_debug(dev, DL_ERROR, - "%s: %s 0x%02X for file=%d\n", - __func__, "FILE_OPEN failure:", status, file_no); - return -status; - } else if (actual_read_len == expected_len) { - /* File_open returned a file handle */ - file_handle = read_buf[PIP2_RESP_BODY_OFFSET]; - if (file_handle != file_no) { - pt_debug(dev, DL_ERROR, - "%s: %s 0x%02X file=%d returned handle=%d\n", - __func__, "FILE_OPEN failure:", - status, file_no, file_handle); - return -status; - } - } else { + "%s ROM BL FILE_OPEN failure: 0x%02X for file = %d\n", + __func__, status, file_handle); return -status; } - return file_handle; } @@ -12390,25 +10895,24 @@ int _pt_pip2_file_close(struct device *dev, u8 file_handle) } /******************************************************************************* - * FUNCTION: _pt_pip2_file_erase_by_file_no + * FUNCTION: _pt_pip2_file_erase * - * SUMMARY: Using the BL PIP2 commands erase a file by file number only. + * SUMMARY: Using the BL PIP2 commands erase a file * - * NOTE: The DUT must be in BL mode for this command to work - * NOTE: Some FLASH parts can time out while erasing one or more sectors, - * one retry is attempted for each sector in a file. + * NOTE: The DUT must be in BL mode for this command to work + * NOTE: Some FLASH parts can time out while erasing one or more sectors, + * one retry is attempted for each sector in a file. * * RETURNS: - * <0 = Error - * >0 = file handle closed + * <0 = Error + * >0 = file handle closed * * PARAMETERS: - * *dev - pointer to device structure - * file_handle - handle to the file to be erased - * *status - PIP2 erase status code + * *dev - pointer to device structure + * file_handle - handle to the file to be erased + * *status - PIP2 erase status code ******************************************************************************/ -int _pt_pip2_file_erase_by_file_no(struct device *dev, u8 file_handle, - int *status) +static int _pt_pip2_file_erase(struct device *dev, u8 file_handle, int *status) { int ret = 0; int max_retry = PT_PIP2_MAX_FILE_SIZE/PT_PIP2_FILE_SECTOR_SIZE; @@ -12463,129 +10967,6 @@ int _pt_pip2_file_erase_by_file_no(struct device *dev, u8 file_handle, return file_handle; } -/******************************************************************************* - * FUNCTION: _pt_pip2_file_erase_by_file_sector - * - * SUMMARY: Using the BL PIP2 commands erase a file sector by sector. - * - * NOTE: The DUT must be in BL mode for this command to work - * NOTE: Some FLASH parts can time out while erasing one or more sectors, - * one retry is attempted for each sector in a file. - * - * RETURNS: - * <0 = Error - * >0 = file handle closed - * - * PARAMETERS: - * *dev - pointer to device structure - * file_handle - handle to the file to be erased - * file_sector - Number of sectors to erase - * *status - PIP2 erase status code - ******************************************************************************/ -int _pt_pip2_file_erase_by_file_sector(struct device *dev, u8 file_handle, - u16 file_sector, int *status) -{ - int ret = 0, index = 0; - int max_retry = 3; - int retry = 0; - int tmp_status = PIP2_RSP_ERR_NONE; - u16 sector_to_erase = 1; - u16 actual_read_len; - u8 data[6]; - u8 read_buf[10]; - struct pt_core_data *cd = dev_get_drvdata(dev); - - pt_debug(dev, DL_DEBUG, "%s: ERASE file=%d, sector=%d\n", __func__, - file_handle, file_sector); - data[0] = file_handle; - data[1] = PIP2_FILE_IOCTL_CODE_ERASE_FILE; - /* Set how many sectors to erase*/ - data[4] = LOW_BYTE(sector_to_erase); - data[5] = HI_BYTE(sector_to_erase); - - /* Increase waiting time for large file erase */ - mutex_lock(&cd->system_lock); - cd->pip_cmd_timeout = PT_PIP2_CMD_FILE_SECTOR_ERASE_TIMEOUT; - mutex_unlock(&cd->system_lock); - for (retry = 0; retry < max_retry; retry++) { - for (index = 0; index < file_sector; index++) { - /* Initialize status code */ - tmp_status = PIP2_RSP_ERR_NONE; - /* Set which sector starts to erase */ - data[2] = LOW_BYTE(index); - data[3] = HI_BYTE(index); - ret = _pt_request_pip2_send_cmd(dev, - PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_IOCTL, - data, 6, read_buf, &actual_read_len); - - tmp_status = read_buf[PIP2_RESP_STATUS_OFFSET]; - if (ret || tmp_status != PIP2_RSP_ERR_NONE) - break; - } - /* - * Only retry if response doesn't arrive or the status code is - * PIP2_RSP_ERR_TIMEOUT. - */ - if ((ret != -ETIME) || (tmp_status != PIP2_RSP_ERR_TIMEOUT)) - break; - else { -#ifdef TTDL_DIAGNOSTICS - cd->file_erase_timeout_count++; -#endif - pt_debug(dev, DL_WARN, - "%s: ERASE timeout %d for file=%d, sector=%d\n", - __func__, retry, file_handle, index); - } - } - - mutex_lock(&cd->system_lock); - *status = tmp_status; - cd->pip_cmd_timeout = cd->pip_cmd_timeout_default; - mutex_unlock(&cd->system_lock); - if (ret) { - pt_debug(dev, DL_ERROR, - "%s ROM FILE_ERASE cmd failure: %d for file=%d, sector=%d\n", - __func__, ret, file_handle, index); - return -EIO; - } - - if (*status != 0x00) { - pt_debug(dev, DL_ERROR, - "%s ROM FILE_ERASE failure: 0x%02X for file=%d, sector=%d\n", - __func__, *status, file_handle, index); - return -EIO; - } - return file_handle; -} - -/******************************************************************************* - * FUNCTION: _pt_pip2_file_erase - * - * SUMMARY: Wrapper function to call _pt_pip2_file_erase_by_file_sector() and - * _pt_pip2_file_erase_by_file_no() by checking file_sector. - * - * NOTE: If the file_sector is 0, it will erase the entire file. - * - * RETURNS: - * <0 = Error - * >0 = file handle closed - * - * PARAMETERS: - * *dev - pointer to device structure - * file_handle - handle to the file to be erased - * file_sector - Number of sectors to erase - * *status - PIP2 erase status code - ******************************************************************************/ -int _pt_pip2_file_erase(struct device *dev, u8 file_handle, u16 file_sector, - int *status) -{ - if (file_sector) - return _pt_pip2_file_erase_by_file_sector(dev, file_handle, - file_sector, status); - else - return _pt_pip2_file_erase_by_file_no(dev, file_handle, status); -} - /******************************************************************************* * FUNCTION: _pt_pip2_file_read * @@ -12613,6 +10994,7 @@ int _pt_pip2_file_read(struct device *dev, u8 file_handle, u16 num_bytes, data[0] = file_handle; data[1] = (num_bytes & 0x00FF); data[2] = (num_bytes & 0xFF00) >> 8; + ret = _pt_request_pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_READ, data, 3, read_buf, &actual_read_len); @@ -12621,7 +11003,7 @@ int _pt_pip2_file_read(struct device *dev, u8 file_handle, u16 num_bytes, pt_debug(dev, DL_ERROR, "%s File open failure with error code = %d\n", __func__, status); - return -1; + return -EPERM; } ret = num_bytes; @@ -12657,14 +11039,13 @@ int _pt_read_us_file(struct device *dev, u8 *file_path, u8 *buf, int *size) pt_debug(dev, DL_ERROR, "%s: path || buf is NULL.\n", __func__); return -EINVAL; } - pt_debug(dev, DL_WARN, "%s: path = %s\n", __func__, file_path); oldfs = get_fs(); set_fs(KERNEL_DS); filp = filp_open(file_path, O_RDONLY, 0400); if (IS_ERR(filp)) { - pt_debug(dev, DL_WARN, "%s: Failed to open %s\n", __func__, + pt_debug(dev, DL_ERROR, "%s: Failed to open %s\n", __func__, file_path); rc = -ENOENT; goto err; @@ -12703,12 +11084,7 @@ int _pt_read_us_file(struct device *dev, u8 *file_path, u8 *buf, int *size) goto exit; } filp->private_data = inode->i_private; - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)) - if (kernel_read(filp, buf, read_len, &(filp->f_pos)) != read_len) { -#else if (vfs_read(filp, buf, read_len, &(filp->f_pos)) != read_len) { -#endif pt_debug(dev, DL_ERROR, "%s: file read error.\n", __func__); rc = -EINVAL; goto exit; @@ -12755,7 +11131,7 @@ int _pt_request_pip2_bin_hdr(struct device *dev, struct pt_bin_file_hdr *hdr) read_buf, &read_size); if (rc) { ret = rc; - pt_debug(dev, DL_WARN, + pt_debug(dev, DL_ERROR, "%s Failed to read fw image from US, rc=%d\n", __func__, rc); goto exit; @@ -13098,7 +11474,6 @@ ping_test_exit: return rc; } -#ifndef TTDL_KERNEL_SUBMISSION /******************************************************************************* * FUNCTION: _pt_ic_parse_input_hex * @@ -13301,7 +11676,6 @@ static int _pt_ic_parse_input(struct device *dev, error: return ret; } -#endif /* !TTDL_KERNEL_SUBMISSION */ #ifdef TTHE_TUNER_SUPPORT /******************************************************************************* @@ -13327,7 +11701,6 @@ static int tthe_debugfs_open(struct inode *inode, struct file *filp) u32 buf_size = PT_MAX_PRBUF_SIZE; filp->private_data = inode->i_private; - if (cd->tthe_buf) return -EBUSY; @@ -13363,91 +11736,12 @@ static int tthe_debugfs_close(struct inode *inode, struct file *filp) struct pt_core_data *cd = filp->private_data; filp->private_data = NULL; - kfree(cd->tthe_buf); cd->tthe_buf = NULL; return 0; } -/******************************************************************************* - * FUNCTION: tthe_debugfs_store - * - * SUMMARY: Write method for tthe_tuner debugfs node. This function allows - * user configuration of some output values via a bitmask. - * 0x01 = HID USB vs I2C output - * 0xFE = Reserved - * - * RETURN: Size of debugfs data write - * - * PARAMETERS: - * *filp - file pointer to debugfs file - * *buf - the user space buffer to read to - * count - the maximum number of bytes to read - * *ppos - the current position in the buffer - ******************************************************************************/ -static ssize_t tthe_debugfs_store(struct file *filp, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct pt_core_data *cd = filp->private_data; - ssize_t length; - u32 input_data[2]; - u8 tmp_buf[4]; /* large enough for 1 32bit value */ - int rc = 0; - - mutex_lock(&cd->tthe_lock); - - /* copy data from user space */ - rc = simple_write_to_buffer(tmp_buf, sizeof(tmp_buf), - ppos, buf, count); - if (rc < 0) - goto exit; - - length = _pt_ic_parse_input(cd->dev, tmp_buf, count, - input_data, ARRAY_SIZE(input_data)); - - if (length == 1) { - mutex_lock(&(cd->system_lock)); - cd->tthe_hid_usb_format = input_data[0]; - if (input_data[0] == PT_TTHE_TUNER_FORMAT_HID_USB) - pt_debug(cd->dev, DL_INFO, - "%s: Enable tthe_tuner HID-USB format\n", - __func__); - else if (input_data[0] == PT_TTHE_TUNER_FORMAT_HID_I2C) - pt_debug(cd->dev, DL_INFO, - "%s: Enable tthe_tuner HID-I2C format\n", - __func__); - else if (input_data[0] == - PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP) - pt_debug(cd->dev, DL_INFO, - "%s: Enable tthe_tuner HID-FINGER-TO-PIP format\n", - __func__); - else if (input_data[0] == - PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP) - pt_debug(cd->dev, DL_INFO, - "%s: Enable tthe_tuner HID_FINGER_AND_PEN_TO_PIP format\n", - __func__); - else { - rc = -EINVAL; - pt_debug(cd->dev, DL_ERROR, - "%s: Invalid parameter: %d\n", - __func__, input_data[0]); - } - mutex_unlock(&(cd->system_lock)); - } else { - rc = -EINVAL; - pt_debug(cd->dev, DL_ERROR, - "%s: Invalid number of parameters\n", __func__); - } - -exit: - mutex_unlock(&cd->tthe_lock); - if (rc) - return rc; - else - return count; -} - /******************************************************************************* * FUNCTION: tthe_debugfs_read * @@ -13492,18 +11786,11 @@ static ssize_t tthe_debugfs_read(struct file *filp, char __user *buf, } else { ret = copy_to_user(buf, cd->tthe_buf, size); } - - /* - * When size >= tthe_buf_len setting partial_read will cause NULL - * characters to be printed in the output. - */ - if (size == count && size < cd->tthe_buf_len) + if (size == count) partial_read = count; - if (ret == size) { - mutex_unlock(&cd->tthe_lock); + if (ret == size) return -EFAULT; - } size -= ret; cd->tthe_buf_len -= size; mutex_unlock(&cd->tthe_lock); @@ -13515,7 +11802,6 @@ static const struct file_operations tthe_debugfs_fops = { .open = tthe_debugfs_open, .release = tthe_debugfs_close, .read = tthe_debugfs_read, - .write = tthe_debugfs_store, }; #endif @@ -13580,9 +11866,7 @@ static struct pt_core_commands _pt_core_commands = { .request_pip2_bin_hdr = _pt_request_pip2_bin_hdr, .request_dut_generation = _pt_request_dut_generation, .request_hw_version = _pt_request_hw_version, -#ifndef TTDL_KERNEL_SUBMISSION .parse_sysfs_input = _pt_ic_parse_input, -#endif #ifdef TTHE_TUNER_SUPPORT .request_tthe_print = _pt_request_tthe_print, #endif @@ -13692,7 +11976,7 @@ static void pt_probe_modules(struct pt_core_data *cd) mutex_lock(&module_list_lock); list_for_each_entry(m, &module_list, node) { - pt_debug(cd->dev, DL_WARN, "%s: Probe module %s\n", + pt_debug(cd->dev, DL_ERROR, "%s: Probe module %s\n", __func__, m->name); rc = pt_probe_module(cd, m); if (rc) @@ -13918,12 +12202,11 @@ static void pt_early_suspend(struct early_suspend *h) call_atten_cb(cd, PT_ATTEN_SUSPEND, 0); } - /******************************************************************************* * FUNCTION: pt_late_resume * * SUMMARY: Android PM architecture function that will call "PT_ATTEN_RESUME" - * attention list. + * attention list. * * PARAMETERS: * *h - pointer to early_suspend structure @@ -14018,7 +12301,6 @@ static void pt_setup_fb_notifier(struct pt_core_data *cd) int rc = 0; cd->fb_state = FB_ON; - cd->fb_notifier.notifier_call = fb_notifier_callback; rc = fb_register_client(&cd->fb_notifier); @@ -14054,7 +12336,6 @@ static void pt_watchdog_work(struct work_struct *work) int rc = 0; struct pt_core_data *cd = container_of(work, struct pt_core_data, watchdog_work); - /* * if found the current sleep_state is SS_SLEEPING * then no need to request_exclusive, directly return @@ -14081,7 +12362,7 @@ static void pt_watchdog_work(struct work_struct *work) cd->watchdog_irq_stuck_count++; pt_toggle_err_gpio(cd, PT_ERR_GPIO_IRQ_STUCK); #endif /* TTDL_DIAGNOSTICS */ - pt_debug(cd->dev, DL_WARN, + pt_debug(cd->dev, DL_ERROR, "%s: TTDL WD found IRQ asserted, attempt to clear\n", __func__); pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL); @@ -14145,7 +12426,7 @@ queue_startup: } #ifdef TTDL_DIAGNOSTICS cd->wd_xres_count++; - pt_debug(cd->dev, DL_WARN, + pt_debug(cd->dev, DL_ERROR, "%s: Comm Failed - DUT reset [#%d]\n", __func__, cd->wd_xres_count); #endif /* TTDL_DIAGNOSTICS */ @@ -14154,7 +12435,7 @@ queue_startup: * After trying PT_WATCHDOG_RETRY_COUNT times to * reset the part to regain communications, try to BL */ - pt_debug(cd->dev, DL_WARN, + pt_debug(cd->dev, DL_ERROR, "%s: WD DUT access failure, Start FW Upgrade\n", __func__); #ifdef TTDL_DIAGNOSTICS @@ -14181,7 +12462,7 @@ queue_startup: mutex_unlock(&cd->system_lock); if (cd->active_dut_generation == DUT_UNKNOWN) { - pt_debug(cd->dev, DL_WARN, + pt_debug(cd->dev, DL_ERROR, "%s: Queue Restart\n", __func__); pt_queue_restart(cd); } else @@ -14191,7 +12472,7 @@ queue_startup: cd->hw_detected = true; if (cd->startup_status <= (STARTUP_STATUS_FW_RESET_SENTINEL | STARTUP_STATUS_BL_RESET_SENTINEL)) { - pt_debug(cd->dev, DL_WARN, + pt_debug(cd->dev, DL_ERROR, "%s: HW detected but not enumerated\n", __func__); pt_queue_enum(cd); @@ -14217,7 +12498,6 @@ exit: static void pt_watchdog_timer(unsigned long handle) { struct pt_core_data *cd = (struct pt_core_data *)handle; - if (!cd) return; @@ -14242,7 +12522,6 @@ static void pt_watchdog_timer(unsigned long handle) static void pt_watchdog_timer(struct timer_list *t) { struct pt_core_data *cd = from_timer(cd, t, watchdog_timer); - if (!cd) return; @@ -14254,65 +12533,6 @@ static void pt_watchdog_timer(struct timer_list *t) } #endif -#ifdef PT_PTSBC_SUPPORT -/* Required to support the Parade Techologies Development Platform */ -static int pt_probe_complete(struct pt_core_data *cd); - -/******************************************************************************* - * FUNCTION: pt_probe_work - * - * SUMMARY: For the PtSBC the probe functionality is split into two functions; - * pt_probe() and pt_probe_complete() which is called from here. - * This function is scheduled as a "work" task in order to launch after - * I2C/SPI is up. - * - * RETURN: Void - * - * PARAMETERS: - * *work - pointer to work structure - ******************************************************************************/ -static void pt_probe_work(struct work_struct *work) -{ - struct pt_core_data *cd = - container_of(work, struct pt_core_data, - probe_work); - int rc; - - rc = pt_probe_complete(cd); - - if (rc < 0) - pr_err("%s: Probe_complete returns rc=%d\n", __func__, rc); - else - pt_debug(cd->dev, DL_INFO, - "%s: Probe_complete returns rc=%d\n", __func__, rc); -} - -/******************************************************************************* - * FUNCTION: pt_probe_timer - * - * SUMMARY: For the PtSBC the probe functionality is split into two functions; - * pt_probe() and pt_probe_complete(). This timer shedules the - * probe_work function. - * - * RETURN: Void - * - * PARAMETERS: - * handle - pointer to the core data - ******************************************************************************/ -static void pt_probe_timer(unsigned long handle) -{ - struct pt_core_data *cd = (struct pt_core_data *)handle; - - if (!cd) - return; - - pt_debug(cd->dev, DL_INFO, "%s: Watchdog timer triggered\n", - __func__); - - if (!work_pending(&cd->probe_work)) - schedule_work(&cd->probe_work); -} -#endif /* --- End PT_PTSBC_SUPPORT --- */ @@ -14337,9 +12557,8 @@ static ssize_t pt_hw_version_show(struct device *dev, struct device_attribute *attr, char *buf) { struct pt_core_data *cd = dev_get_drvdata(dev); - _pt_request_hw_version(dev, cd->hw_version); - return snprintf(buf, PT_MAX_PRBUF_SIZE, "%s\n", cd->hw_version); + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "%s\n", cd->hw_version); } static DEVICE_ATTR(hw_version, 0444, pt_hw_version_show, NULL); @@ -14359,7 +12578,7 @@ static DEVICE_ATTR(hw_version, 0444, pt_hw_version_show, NULL); static ssize_t pt_drv_version_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PT_MAX_PRBUF_SIZE, + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Driver: %s\nVersion: %s\nDate: %s\n", pt_driver_core_name, pt_driver_core_version, pt_driver_core_date); @@ -14537,7 +12756,6 @@ static ssize_t pt_sysinfo_show(struct device *dev, } static DEVICE_ATTR(sysinfo, 0444, pt_sysinfo_show, NULL); -#ifndef TTDL_KERNEL_SUBMISSION /******************************************************************************* * FUNCTION: pt_hw_reset_show * @@ -14590,7 +12808,7 @@ static ssize_t pt_hw_reset_show(struct device *dev, t = wait_event_timeout(cd->wait_q, (cd->fw_updating == true), msecs_to_jiffies(200)); if (IS_TMO(t)) { - pt_debug(dev, DL_WARN, + pt_debug(dev, DL_ERROR, "%s: Timeout waiting for FW update", __func__); rc = -ETIME; @@ -14611,7 +12829,7 @@ static ssize_t pt_hw_reset_show(struct device *dev, } } else { /* Wait for any sentinel */ - rc = _pt_request_wait_for_enum_state(dev, 250, + rc = _pt_request_wait_for_enum_state(dev, 150, STARTUP_STATUS_BL_RESET_SENTINEL | STARTUP_STATUS_FW_RESET_SENTINEL); if (rc) { @@ -14726,7 +12944,7 @@ static ssize_t pt_pip2_cmd_rsp_store(struct device *dev, length = _pt_ic_parse_input_hex(dev, buf, size, input_data, PT_MAX_PIP2_MSG_SIZE); if (length <= 0 || length > PT_MAX_PIP2_MSG_SIZE) { - pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n", __func__); rc = -EINVAL; goto exit; @@ -14749,7 +12967,7 @@ static ssize_t pt_pip2_cmd_rsp_store(struct device *dev, } else { cd->cmd_rsp_buf_len = actual_read_len; memcpy(cd->cmd_rsp_buf, read_buf, actual_read_len); - pt_debug(dev, DL_WARN, + pt_debug(dev, DL_ERROR, "%s: PIP2 actual_read_len = %d\n", __func__, actual_read_len); } @@ -14834,9 +13052,6 @@ static ssize_t pt_command_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct pt_core_data *cd = dev_get_drvdata(dev); -#ifdef TTDL_PTVIRTDUT_SUPPORT - struct pt_hid_output hid_output; -#endif unsigned short crc; u16 actual_read_len; u8 input_data[PT_MAX_PIP2_MSG_SIZE + 1]; @@ -14852,45 +13067,12 @@ static ssize_t pt_command_store(struct device *dev, length = _pt_ic_parse_input_hex(dev, buf, size, input_data, PT_MAX_PIP2_MSG_SIZE); if (length <= 0 || length > PT_MAX_PIP2_MSG_SIZE) { - pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n", __func__); rc = -EINVAL; goto pt_command_store_exit; } - /* Get HID Desc */ - if (length == 2 && input_data[0] == 0x01 && input_data[1] == 0x00) { - pm_runtime_get_sync(dev); - rc = pt_get_hid_descriptor(cd, &cd->hid_desc); - mutex_lock(&cd->sysfs_lock); - if (!rc) { - cd->cmd_rsp_buf_len = - get_unaligned_le16(&cd->response_buf[0]); - memcpy(cd->cmd_rsp_buf, cd->response_buf, - cd->cmd_rsp_buf_len); - } - cd->raw_cmd_status = rc; - mutex_unlock(&cd->sysfs_lock); - pm_runtime_put(dev); - goto pt_command_store_exit; - } - - /* Get Report Desc */ - if (length == 2 && input_data[0] == 0x02 && input_data[1] == 0x00) { - pm_runtime_get_sync(dev); - rc = pt_get_report_descriptor(cd); - mutex_lock(&cd->sysfs_lock); - if (!rc) { - cd->cmd_rsp_buf_len = cd->hid_core.hid_report_desc_len; - memcpy(cd->cmd_rsp_buf, cd->response_buf, - cd->cmd_rsp_buf_len); - } - cd->raw_cmd_status = rc; - mutex_unlock(&cd->sysfs_lock); - pm_runtime_put(dev); - goto pt_command_store_exit; - } - /* PIP2 messages begin with 01 01 */ if (length >= 2 && input_data[0] == 0x01 && input_data[1] == 0x01) { cd->pip2_prot_active = 1; @@ -14901,7 +13083,7 @@ static ssize_t pt_command_store(struct device *dev, if (len_field == length && length <= 254) { crc = crc_ccitt_calculate(&input_data[2], length - 2); - pt_debug(dev, DL_WARN, "%s: len=%d crc=0x%02X\n", + pt_debug(dev, DL_ERROR, "%s: len=%d crc=0x%02X\n", __func__, length, crc); input_data[length] = (crc & 0xFF00) >> 8; input_data[length + 1] = crc & 0x00FF; @@ -14914,19 +13096,6 @@ static ssize_t pt_command_store(struct device *dev, pm_runtime_get_sync(dev); -#ifdef TTDL_PTVIRTDUT_SUPPORT - /* Special case for handling Virtual DUT exit */ - if (length >= 3 && - input_data[0] == 0xFF && - input_data[1] == 0xFF && - input_data[2] == 0x00) { - hid_output.length = length, - hid_output.write_buf = input_data, - rc = pt_hid_send_output_user_(cd, &hid_output); - pm_runtime_put(dev); - goto pt_command_store_exit; - } -#endif rc = pt_hid_output_user_cmd(cd, PT_MAX_INPUT, cd->cmd_rsp_buf, length, input_data, &actual_read_len); pm_runtime_put(dev); @@ -14958,105 +13127,38 @@ static DEVICE_ATTR(command, 0220, NULL, pt_command_store); * available to be read here. * * PARAMETERS: - * *filp - pointer to file structure - * *kobj - pointer to kobject structure - * *bin_attr - pointer to bin_attribute structure - * buf - pointer to cmd input buffer - * offset - offset index to store input buffer - * count - size of data in buffer + * *dev - pointer to Device structure + * *attr - pointer to the device attribute structure + * *buf - pointer to buffer to print ******************************************************************************/ -static ssize_t pt_response_show(struct file *filp, - struct kobject *kobj, struct bin_attribute *bin_attr, - char *buf, loff_t offset, size_t count) +static ssize_t pt_response_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct device *dev = container_of(kobj, struct device, kobj); struct pt_core_data *cd = dev_get_drvdata(dev); - static int pr_left; - static int pr_index; - static u8 *pr_buf; - int i = 0; - int index = 0; - int rc = 0; - int print_len = 0; + int i; + ssize_t num_read; + int index; - if (pr_left) { - if (count < pr_left) { - index = count; - memcpy(buf, pr_buf + pr_index, index); - pr_left -= count; - pr_index += count; - } else { - index = pr_left; - memcpy(buf, pr_buf + pr_index, index); - pr_left = 0; - pr_index = 0; - kfree(pr_buf); - } - return index; - } + mutex_lock(&cd->sysfs_lock); + index = scnprintf(buf, PT_MAX_PRBUF_SIZE, + "Status: %d\n", cd->raw_cmd_status); + if (cd->raw_cmd_status) + goto error; - if (offset == 0) { - mutex_lock(&cd->sysfs_lock); - if (cd->raw_cmd_status) { - index = scnprintf(buf, PT_MAX_PRBUF_SIZE, - "Status: %d\n", cd->raw_cmd_status); - mutex_unlock(&cd->sysfs_lock); - return index; - } + num_read = cd->cmd_rsp_buf_len; + for (i = 0; i < num_read; i++) + index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index, + "0x%02X\n", cd->cmd_rsp_buf[i]); - /* - * Allocate memory according to the cost, each byte need 5 - * character, and extra 100 bytes for the header and tail. - */ - if (cd->cmd_rsp_buf_len > PT_MAX_INPUT) { - rc = -EINVAL; - goto exit; - } - print_len = cd->cmd_rsp_buf_len * 5 + 100; - pr_buf = kzalloc(print_len, GFP_KERNEL); - if (!pr_buf) { - rc = -ENOMEM; - goto exit; - } + index += scnprintf(buf + index, PT_MAX_PRBUF_SIZE - index, + "(%zd bytes)\n", num_read); - /* Format all data to pr_buf */ - index = scnprintf(pr_buf, print_len - index, "Status: %d\n", - cd->raw_cmd_status); - for (i = 0; i < cd->cmd_rsp_buf_len; i++) - index += scnprintf(pr_buf + index, - print_len - index, "0x%02X\n", - cd->cmd_rsp_buf[i]); - index += scnprintf(pr_buf + index, print_len - index, - "(%zd bytes)\n", cd->cmd_rsp_buf_len); - - mutex_unlock(&cd->sysfs_lock); - - if (count < index) { - memcpy(buf, pr_buf, count); - pr_left = index - count; - pr_index = count; - index = count; - } else { - memcpy(buf, pr_buf, index); - pr_left = 0; - pr_index = 0; - kfree(pr_buf); - } - return index; - } - -exit: +error: mutex_unlock(&cd->sysfs_lock); - return rc; + return index; } +static DEVICE_ATTR(response, 0444, pt_response_show, NULL); -static struct bin_attribute bin_attr_pt_response = { - .attr = { - .name = "response", - .mode = (0444), - }, - .read = pt_response_show, -}; /******************************************************************************* * FUNCTION: pt_dut_debug_show * @@ -15097,7 +13199,6 @@ static ssize_t pt_dut_debug_show(struct device *dev, "%d %s \t- %s\n" "%d %s \t- %s\n" "%d %s \t- %s\n" - "%d %s \t- %s\n" , PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY, "", "BL Verify APP", PT_DUT_DBG_HID_RESET, "", "HID Reset", @@ -15115,8 +13216,7 @@ static ssize_t pt_dut_debug_show(struct device *dev, PT_DUT_DBG_HID_SYSINFO, "", "HID system info", PT_DUT_DBG_PIP_SUSPEND_SCAN, "", "Suspend Scan", PT_DUT_DBG_PIP_RESUME_SCAN, "", "Resume Scan", - PT_DUT_DBG_HID_DESC, "", "Get HID Desc", - PT_DUT_DBG_REPORT_DESC, "", "Get HID Report Desc" + PT_DUT_DBG_HID_DESC, "", "Get HID Desc" ); return ret; @@ -15171,6 +13271,7 @@ static ssize_t pt_drv_debug_show(struct device *dev, "%d %s \t- %s\n" "%d %s \t- %s\n" "%d %s \t- %s\n" + "%d %s \t- %s\n" #endif /* TTDL_DIAGNOSTICS */ , PT_DRV_DBG_SUSPEND, " ", "Suspend TTDL responding to INT", @@ -15198,8 +13299,10 @@ static ssize_t pt_drv_debug_show(struct device *dev, PT_DRV_DBG_SET_FORCE_SEQ, "[8-15]", "Force PIP2 Sequence #", PT_DRV_DBG_BL_WITH_NO_INT, "[0|1]", "BL with no INT", PT_DRV_DBG_CAL_CACHE_IN_HOST, "[0|1]", "CAL Cache in host", - PT_DRV_DBG_NUM_DEVICES, "[1-255]", "Number of Devices", - PT_DRV_DBG_PIP_TIMEOUT, "[100-7000]", "PIP Resp Timeout (ms)" + PT_DRV_DBG_MULTI_CHIP, "[0|1]", "Multi Chip Support", + PT_DRV_DBG_PIP_TIMEOUT, "[100-7000]", "PIP Resp Timeout (ms)", + PT_DRV_DBG_TTHE_HID_USB_FORMAT, "[0|1]", + "TTHE_TUNER HID USB Format" #endif /* TTDL_DIAGNOSTICS */ ); @@ -15243,7 +13346,6 @@ static ssize_t pt_drv_debug_store(struct device *dev, unsigned short crc = 0; u16 cal_size; #endif - input_data[0] = 0; input_data[1] = 0; @@ -15251,7 +13353,7 @@ static ssize_t pt_drv_debug_store(struct device *dev, length = _pt_ic_parse_input(dev, buf, size, input_data, ARRAY_SIZE(input_data)); if (length < 1 || length > 2) { - pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n", __func__); rc = -EINVAL; goto pt_drv_debug_store_exit; @@ -15432,14 +13534,7 @@ static ssize_t pt_drv_debug_store(struct device *dev, "%s: CAL Cleared, Chip ID=0x%04X size=%d\n", __func__, crc, size); break; - case PT_DUT_DBG_REPORT_DESC: /* 113 */ - rc = pt_get_report_descriptor(cd); - if (rc != 0) { - pt_debug(cd->dev, DL_ERROR, - "%s: Error on getting report descriptor r=%d\n", - __func__, rc); - } - break; + case PT_DRV_DBG_REPORT_LEVEL: /* 200 */ mutex_lock(&cd->system_lock); if (input_data[1] >= 0 && input_data[1] < DL_MAX) { @@ -15510,11 +13605,11 @@ static ssize_t pt_drv_debug_store(struct device *dev, case PT_DRV_DBG_GET_PUT_SYNC: /* 206 */ if (input_data[1] == 0) { pm_runtime_put(dev); - pt_debug(dev, DL_WARN, + pt_debug(dev, DL_ERROR, "%s: Force call pm_runtime_put()\n", __func__); } else if (input_data[1] == 1) { pm_runtime_get_sync(dev); - pt_debug(dev, DL_WARN, + pt_debug(dev, DL_ERROR, "%s: Force call pm_runtime_get_sync()\n", __func__); } else { @@ -15676,25 +13771,18 @@ static ssize_t pt_drv_debug_store(struct device *dev, } mutex_unlock(&(cd->system_lock)); break; - case PT_DRV_DBG_NUM_DEVICES: /* 217 */ + case PT_DRV_DBG_MULTI_CHIP: /* 217 */ mutex_lock(&cd->system_lock); if (input_data[1] == 0) { - /* 0 is not supported */ - rc = -EINVAL; - pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n", - __func__, input_data[1]); - } else if (input_data[1] == 1) { - cd->num_devices = input_data[1]; + cd->multi_chip = PT_FEATURE_DISABLE; cd->ttdl_bist_select = 0x07; pt_debug(dev, DL_INFO, - "%s: Multi-chip support Disabled\n", __func__); - } else if (input_data[1] > 1 && - input_data[1] <= PT_MAX_DEVICES) { - cd->num_devices = input_data[1]; + "%s: Disable Multi-chip support\n", __func__); + } else if (input_data[1] == 1) { + cd->multi_chip = PT_FEATURE_ENABLE; cd->ttdl_bist_select = 0x3F; pt_debug(dev, DL_INFO, - "%s: Multi-chip support Enabled with %d DUTs\n", - __func__, cd->num_devices); + "%s: Enable Multi-chip support\n", __func__); } else { rc = -EINVAL; pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n", @@ -15707,12 +13795,11 @@ static ssize_t pt_drv_debug_store(struct device *dev, if (input_data[1] <= 0x07) { cd->panel_id_support = input_data[1]; pt_debug(dev, DL_INFO, - "%s: ATM - Set panel_id_support to %d\n", + "%s: Set panel_id_support to %d\n", __func__, cd->panel_id_support); } else { rc = -EINVAL; - pt_debug(dev, DL_ERROR, - "%s: ATM - Invalid parameter: %d\n", + pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n", __func__, input_data[1]); } mutex_unlock(&(cd->system_lock)); @@ -15728,73 +13815,34 @@ static ssize_t pt_drv_debug_store(struct device *dev, cd->pip_cmd_timeout_default = input_data[1]; cd->pip_cmd_timeout = input_data[1]; pt_debug(dev, DL_INFO, - "%s: ATM - PIP Timeout = %d\n", __func__, + "%s: PIP Timeout = %d\n", __func__, cd->pip_cmd_timeout_default); } else { rc = -EINVAL; - pt_debug(dev, DL_ERROR, - "%s: ATM - Invalid parameter: %d\n", + pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n", __func__, input_data[1]); } mutex_unlock(&(cd->system_lock)); break; - case PT_DRV_DBG_CORE_PLATFORM_FLAG: /* 220 */ + case PT_DRV_DBG_TTHE_HID_USB_FORMAT: /* 220 */ mutex_lock(&cd->system_lock); - if (cd->cpdata) { - cd->cpdata->flags = input_data[1]; - pt_debug(dev, DL_INFO, - "%s: ATM - Set core platform flag to 0x%02X\n", - __func__, input_data[1]); - } else { - rc = -EINVAL; - pt_debug(dev, DL_ERROR, "%s: No platform data\n", - __func__); - } - mutex_unlock(&cd->system_lock); - break; -#ifdef TTDL_PTVIRTDUT_SUPPORT - case PT_DRV_DBG_SET_HW_DETECT: /* 298 */ - mutex_lock(&cd->system_lock); - if (input_data[1]) - cd->hw_detect_enabled = true; - else - cd->hw_detect_enabled = false; - pt_debug(dev, DL_INFO, - "%s: Set hw_detect_enabled to %d\n", - __func__, cd->hw_detect_enabled); - mutex_unlock(&(cd->system_lock)); - break; - case PT_DRV_DBG_VIRTUAL_I2C_DUT: /* 299 */ if (input_data[1] == 0) { - /* Cancel all work threads when disabling */ - pt_debug(dev, DL_WARN, "%s: Canceling Work", __func__); -#ifdef PT_PTSBC_SUPPORT - cancel_work_sync(&cd->irq_work); - cancel_work_sync(&cd->probe_work); -#endif - cancel_work_sync(&cd->ttdl_restart_work); - cancel_work_sync(&cd->enum_work); - pt_stop_wd_timer(cd); - call_atten_cb(cd, PT_ATTEN_CANCEL_LOADER, 0); - - mutex_lock(&cd->system_lock); - cd->route_bus_virt_dut = 0; - mutex_unlock(&(cd->system_lock)); - pt_debug(dev, DL_WARN, "%s: Enable Virtual DUT mode\n", + cd->tthe_hid_usb_format = PT_FEATURE_DISABLE; + pt_debug(dev, DL_INFO, + "%s: Disable tthe_tuner HID-USB format\n", __func__); } else if (input_data[1] == 1) { - mutex_lock(&cd->system_lock); - cd->route_bus_virt_dut = 1; - mutex_unlock(&(cd->system_lock)); - pt_debug(dev, DL_WARN, "%s: Enable Virtual DUT mode\n", + cd->tthe_hid_usb_format = PT_FEATURE_ENABLE; + pt_debug(dev, DL_INFO, + "%s: Enable tthe_tuner HID-USB format\n", __func__); } else { rc = -EINVAL; pt_debug(dev, DL_ERROR, "%s: Invalid parameter: %d\n", __func__, input_data[1]); } + mutex_unlock(&(cd->system_lock)); break; -#endif /* TTDL_PTVIRTDUT_SUPPORT */ #endif /* TTDL_DIAGNOSTICS */ default: rc = -EINVAL; @@ -15836,9 +13884,9 @@ static ssize_t pt_sleep_status_show(struct device *dev, mutex_lock(&cd->system_lock); if (cd->sleep_state == SS_SLEEP_ON) - ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "off\n"); + ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "off\n"); else - ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "on\n"); + ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "on\n"); mutex_unlock(&cd->system_lock); return ret; @@ -15882,7 +13930,7 @@ static ssize_t pt_panel_id_show(struct device *dev, if (cd->panel_id_support & PT_PANEL_ID_BY_BL) { rc = pt_hid_output_bl_get_panel_id_(cd, &pid); if (rc) { - pt_debug(dev, DL_WARN, "%s: %s %s\n", + pt_debug(dev, DL_ERROR, "%s: %s %s\n", "Failed to retrieve Panel ID. ", "Using cached value\n", __func__); @@ -15896,12 +13944,12 @@ static ssize_t pt_panel_id_show(struct device *dev, if (!rc) pid = cd->sysinfo.sensing_conf_data.panel_id; - pt_debug(dev, DL_WARN, + pt_debug(dev, DL_ERROR, "%s: Gen6 FW mode rc=%d PID=0x%02X\n", __func__, rc, pid); } } else { - pt_debug(dev, DL_WARN, "%s: Active mode unknown\n", + pt_debug(dev, DL_ERROR, "%s: Active mode unknown\n", __func__); rc = -EPERM; } @@ -15922,22 +13970,22 @@ static ssize_t pt_panel_id_show(struct device *dev, if (!rc) pid = cd->sysinfo.sensing_conf_data.panel_id; - pt_debug(dev, DL_WARN, + pt_debug(dev, DL_ERROR, "%s: TT/TC FW mode rc=%d PID=0x%02X\n", __func__, rc, pid); } } else { - pt_debug(dev, DL_WARN, "%s: Active mode unknown\n", + pt_debug(dev, DL_ERROR, "%s: Active mode unknown\n", __func__); rc = -EPERM; } } else { - pt_debug(dev, DL_WARN, "%s: Dut generation is unknown\n", + pt_debug(dev, DL_ERROR, "%s: Dut generation is unknown\n", __func__); rc = -EPERM; } - ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n0x%02X\n", + ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n0x%02X\n", rc, pid); return ret; } @@ -15970,7 +14018,7 @@ static ssize_t pt_get_param_store(struct device *dev, length = _pt_ic_parse_input(dev, buf, size, input_data, ARRAY_SIZE(input_data)); if (length != 1) { - pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n", __func__); rc = -EINVAL; goto exit; @@ -16019,6 +14067,7 @@ static ssize_t pt_get_param_show(struct device *dev, u32 value = 0; status = pt_pip_get_param(cd, cd->get_param_id, &value); + if (status) { pt_debug(dev, DL_ERROR, "%s: %s Failed, status = %d\n", __func__, "pt_get_param", status); @@ -16059,7 +14108,6 @@ static ssize_t pt_ttdl_restart_show(struct device *dev, mutex_lock(&cd->system_lock); cd->startup_state = STARTUP_NONE; mutex_unlock(&(cd->system_lock)); - /* ensure no left over exclusive access is still locked */ release_exclusive(cd, cd->dev); @@ -16109,17 +14157,17 @@ static ssize_t pt_pip2_gpio_read_show(struct device *dev, if (!rc) { if (status == 0) - return snprintf(buf, PT_MAX_PRBUF_SIZE, + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n" "DUT GPIO Reg: 0x%08X\n", rc, gpio_value); else - return snprintf(buf, PT_MAX_PRBUF_SIZE, + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n" "DUT GPIO Reg: n/a\n", status); } else - return snprintf(buf, PT_MAX_PRBUF_SIZE, + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n" "DUT GPIO Reg: n/a\n", rc); @@ -16147,7 +14195,7 @@ static ssize_t pt_pip2_version_show(struct device *dev, rc = pt_pip2_get_version(cd); if (!rc) { - return snprintf(buf, PT_MAX_PRBUF_SIZE, + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "PIP VERSION : %02X.%02X\n" "BL VERSION : %02X.%02X\n" "FW VERSION : %02X.%02X\n" @@ -16167,7 +14215,7 @@ static ssize_t pt_pip2_version_show(struct device *dev, pt_debug(dev, DL_ERROR, "%s: Failed to retriev PIP2 VERSION data\n", __func__); - return snprintf(buf, PT_MAX_PRBUF_SIZE, + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "PIP VERSION : -\n" "BL VERSION : -\n" "FW VERSION : -\n" @@ -16212,17 +14260,14 @@ static ssize_t pt_ttdl_status_show(struct device *dev, "%s: 0x%04X\n" "%s: %d\n" "%s: %s\n" + "%s: %s %s\n" + "%s: %s\n" "%s: 0x%02X\n" "%s: %s\n" "%s: %s\n" + "%s: %s\n" + "%s: %s\n" "%s: %d\n" - "%s: %s %s\n" - "%s: %s\n" - "%s: %s\n" - "%s: %s\n" - "%s: %s\n" - "%s: %s\n" - "%s: %s\n" "%s: %d\n" "%s: %s\n" "%s: %s\n" @@ -16238,22 +14283,19 @@ static ssize_t pt_ttdl_status_show(struct device *dev, "%s: %d\n" "%s: %d\n" "%s: %d\n" - "%s: %d\n" + "%s: %s\n" + "%s: %s\n" "%s: %s\n" "%s: %d\n" "%s: 0x%04X\n" + "%s: %s\n" #endif /* TTDL_DIAGNOSTICS */ , "Startup Status ", cd->startup_status, "TTDL Debug Level ", cd->debug_level, - "Active Bus Module ", - cd->bus_ops->bustype == BUS_I2C ? "I2C" : "SPI", - "I2C Address ", - cd->bus_ops->bustype == BUS_I2C ? client->addr : 0, - "Exclusive Access Lock ", cd->exclusive_dev ? "Set":"Free", - "HW Detected ", - cd->hw_detected ? "True" : "False", - "Number of Devices ", cd->num_devices, + "Mode ", + cd->mode ? (cd->mode == PT_MODE_OPERATIONAL ? + "Operational" : "BL") : "Unknown", "DUT Generation ", cd->active_dut_generation ? (cd->active_dut_generation == DUT_PIP2_CAPABLE ? @@ -16261,17 +14303,14 @@ static ssize_t pt_ttdl_status_show(struct device *dev, cd->active_dut_generation ? (cd->set_dut_generation == true ? "(Set)" : "(Detected)") : "", - "Protocol Mode ", - cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID ? - "Hybrid HID" : "PIP", - "Mode ", - cd->mode ? (cd->mode == PT_MODE_OPERATIONAL ? - "Operational" : "BL") : "Unknown", + "HW Detected ", + cd->hw_detected ? "True" : "False", + "I2C Address ", + cd->bus_ops->bustype == BUS_I2C ? client->addr : 0, + "Active Bus Module ", + cd->bus_ops->bustype == BUS_I2C ? "I2C" : "SPI", "Flashless Mode ", cd->flashless_dut == 1 ? "Yes" : "No", - "Suppress No-Flash Auto BL ", - cd->flashless_auto_bl == PT_SUPPRESS_AUTO_BL ? - "Yes" : "No", "GPIO state - IRQ ", cd->cpdata->irq_stat ? (cd->cpdata->irq_stat(cd->cpdata, dev) ? @@ -16280,30 +14319,35 @@ static ssize_t pt_ttdl_status_show(struct device *dev, pdata->core_pdata->rst_gpio ? (gpio_get_value(pdata->core_pdata->rst_gpio) ? "High" : "Low") : "not defined", - "Error GPIO trigger type ", cd->err_gpio_type, + "RAM Parm restore list ", pt_count_parameter_list(cd), + "Startup Retry Count ", cd->startup_retry_count, "WD - Manual Force Stop ", cd->watchdog_force_stop ? "True" : "False", "WD - Enabled ", cd->watchdog_enabled ? "True" : "False", "WD - Interval (ms) ", cd->watchdog_interval #ifdef TTDL_DIAGNOSTICS - , - "WD - Triggered Count ", cd->watchdog_count, + , "WD - Triggered Count ", cd->watchdog_count, "WD - IRQ Stuck low count ", cd->watchdog_irq_stuck_count, "WD - Device Access Errors ", cd->watchdog_failed_access_count, "WD - XRES Count ", cd->wd_xres_count, - "Startup Retry Count ", cd->startup_retry_count, "IRQ Triggered Count ", cd->irq_count, "BL Packet Retry Count ", cd->bl_retry_packet_count, "PIP2 CRC Error Count ", cd->pip2_crc_error_count, "Bus Transmit Error Count ", cd->bus_transmit_error_count, "File Erase Timeout Count ", cd->file_erase_timeout_count, - "RAM Parm Restore Count ", pt_count_parameter_list(cd), + "Error GPIO trigger type ", cd->err_gpio_type, + "Exclusive Access Lock ", cd->exclusive_dev ? "Set":"Free", + "Suppress No-Flash Auto BL ", + cd->flashless_auto_bl == PT_SUPPRESS_AUTO_BL ? + "Yes" : "No", "Calibration Cache on host ", cd->cal_cache_in_host == PT_FEATURE_ENABLE ? "Yes" : "No", "Calibration Cache size ", cal_size, - "Calibration Cache chip ID ", crc + "Calibration Cache chip ID ", crc, + "Multi-Chip Support ", + cd->multi_chip == PT_FEATURE_ENABLE ? "Yes" : "No" #endif /* TTDL_DIAGNOSTICS */ ); @@ -16346,7 +14390,6 @@ static ssize_t pt_pip2_enter_bl_show(struct device *dev, int result = 0; u8 mode = PT_MODE_UNKNOWN; struct pt_core_data *cd = dev_get_drvdata(dev); - bool current_bridge_mode = cd->bridge_mode; /* Turn off the TTDL WD before enter bootloader */ pt_stop_wd_timer(cd); @@ -16361,45 +14404,42 @@ static ssize_t pt_pip2_enter_bl_show(struct device *dev, rc = _pt_request_pip2_enter_bl(dev, &mode, &result); switch (result) { case PT_ENTER_BL_PASS: - ret = snprintf(buf, PT_MAX_PRBUF_SIZE, + ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\nEntered BL\n", PT_ENTER_BL_PASS); break; case PT_ENTER_BL_ERROR: - ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n", + ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n", rc, " Unknown Error"); break; case PT_ENTER_BL_RESET_FAIL: - ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n", + ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n", rc, " Soft Reset Failed"); break; case PT_ENTER_BL_HID_START_BL_FAIL: - ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n", + ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n", rc, " PIP Start BL Cmd Failed"); break; case PT_ENTER_BL_CONFIRM_FAIL: - ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n", + ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n", rc, " Error confirming DUT entered BL"); break; default: - ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n", + ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n%s\n", rc, " Unknown Error"); break; }; - /* Restore state of allowing enumeration work to be queued again */ - mutex_lock(&cd->system_lock); - cd->bridge_mode = current_bridge_mode; - mutex_unlock(&cd->system_lock); + /* Allow enumeration work to be queued again */ + cd->bridge_mode = false; return ret; } static DEVICE_ATTR(pip2_enter_bl, 0444, pt_pip2_enter_bl_show, NULL); -#define PT_STATUS_STR_LEN (50) /******************************************************************************* * FUNCTION: pt_pip2_exit_bl_show * @@ -16465,7 +14505,7 @@ static ssize_t pt_easy_wakeup_gesture_show(struct device *dev, ssize_t ret; mutex_lock(&cd->system_lock); - ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "0x%02X\n", + ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "0x%02X\n", cd->easy_wakeup_gesture); mutex_unlock(&cd->system_lock); return ret; @@ -16497,7 +14537,7 @@ static ssize_t pt_easy_wakeup_gesture_store(struct device *dev, length = _pt_ic_parse_input(dev, buf, size, input_data, ARRAY_SIZE(input_data)); if (length != 1) { - pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n", __func__); rc = -EINVAL; goto exit; @@ -16554,7 +14594,7 @@ static ssize_t pt_easy_wakeup_gesture_id_show(struct device *dev, ssize_t ret; mutex_lock(&cd->system_lock); - ret = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 0\n0x%02X\n", + ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 0\n0x%02X\n", cd->gesture_id); mutex_unlock(&cd->system_lock); return ret; @@ -16584,13 +14624,12 @@ static ssize_t pt_easy_wakeup_gesture_data_show(struct device *dev, int i; mutex_lock(&cd->system_lock); - - ret += snprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, "Status: %d\n", 0); + ret += scnprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, "Status: %d\n", 0); for (i = 0; i < cd->gesture_data_length; i++) - ret += snprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, + ret += scnprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, "0x%02X\n", cd->gesture_data[i]); - ret += snprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, + ret += scnprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, "(%d bytes)\n", cd->gesture_data_length); mutex_unlock(&cd->system_lock); @@ -16655,12 +14694,11 @@ static ssize_t pt_err_gpio_store(struct device *dev, input_data[0] = 0; input_data[1] = 0; - /* Maximmum input is two elements */ length = _pt_ic_parse_input(dev, buf, size, input_data, ARRAY_SIZE(input_data)); if (length < 1 || length > 2) { - pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n", __func__); rc = -EINVAL; goto exit; @@ -16716,12 +14754,12 @@ static ssize_t pt_drv_irq_show(struct device *dev, ssize_t ret = 0; mutex_lock(&cd->system_lock); - ret += snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", 0); + ret += scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", 0); if (cd->irq_enabled) - ret += snprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, + ret += scnprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, "Driver interrupt: ENABLED\n"); else - ret += snprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, + ret += scnprintf(buf + ret, PT_MAX_PRBUF_SIZE - ret, "Driver interrupt: DISABLED\n"); mutex_unlock(&cd->system_lock); @@ -16754,7 +14792,7 @@ static ssize_t pt_drv_irq_store(struct device *dev, length = _pt_ic_parse_input(dev, buf, size, input_data, ARRAY_SIZE(input_data)); if (length != 1) { - pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n", __func__); rc = -EINVAL; goto exit; @@ -17014,9 +15052,10 @@ static int pt_bist_bus_test(struct device *dev, u8 *net_toggled, u8 *err_str) pt_debug(cd->dev, DL_INFO, ">>> %s: Write Buffer Size[%d] VERSION\n", __func__, (int)sizeof(ver_cmd)); + pt_pr_buf(cd->dev, DL_DEBUG, ver_cmd, (int)sizeof(ver_cmd), ">>> User CMD"); - rc = pt_adap_write_read_specific(cd, sizeof(ver_cmd), ver_cmd, NULL, 0); + rc = pt_adap_write_read_specific(cd, sizeof(ver_cmd), ver_cmd, NULL); if (rc) { pt_debug(dev, DL_ERROR, "%s: BUS Test - Failed to send VER cmd\n", __func__); @@ -17099,9 +15138,6 @@ exit: static int pt_bist_irq_test(struct device *dev, u8 *bus_toggled, u8 *irq_toggled, u8 *xres_toggled, u8 *err_str) { -#ifdef TTDL_PTVIRTDUT_SUPPORT - u8 release_irq[3] = {0xFF, 0xFF, 0x03}; -#endif /* TTDL_PTVIRTDUT_SUPPORT */ struct pt_core_data *cd = dev_get_drvdata(dev); u8 *read_buf = NULL; u8 mode = PT_MODE_UNKNOWN; @@ -17127,14 +15163,6 @@ static int pt_bist_irq_test(struct device *dev, count++; bytes_read += pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL); } -#ifdef TTDL_PTVIRTDUT_SUPPORT - if (pt_check_irq_asserted(cd) && cd->route_bus_virt_dut) { - /* Force virtual DUT to release IRQ */ - pt_pr_buf(cd->dev, DL_DEBUG, release_irq, - (int)sizeof(release_irq), ">>> User CMD"); - pt_adap_write_read_specific(cd, 3, release_irq, NULL, 0); - } -#endif /* TTDL_PTVIRTDUT_SUPPORT */ if (count > 1 && count < 5 && bytes_read > 0) { /* @@ -17291,9 +15319,6 @@ static int pt_bist_xres_test(struct device *dev, { struct pt_core_data *cd = dev_get_drvdata(dev); struct pt_platform_data *pdata = dev_get_platdata(dev); -#ifdef TTDL_PTVIRTDUT_SUPPORT - u8 release_irq[3] = {0xFF, 0xFF, 0x03}; -#endif /* TTDL_PTVIRTDUT_SUPPORT */ u8 *read_buf = NULL; u8 mode = PT_MODE_UNKNOWN; int rc = 0; @@ -17319,14 +15344,6 @@ static int pt_bist_xres_test(struct device *dev, /* Ensure we have nothing pending on active bus */ pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN); -#ifdef TTDL_PTVIRTDUT_SUPPORT - if (pt_check_irq_asserted(cd) && cd->route_bus_virt_dut) { - /* Force virtual DUT to release IRQ */ - pt_pr_buf(cd->dev, DL_DEBUG, release_irq, - (int)sizeof(release_irq), ">>> User CMD"); - pt_adap_write_read_specific(cd, 3, release_irq, NULL, 0); - } -#endif /* TTDL_PTVIRTDUT_SUPPORT */ /* Perform a hard XRES toggle and wait for reset sentinel */ mutex_lock(&cd->system_lock); @@ -17465,11 +15482,16 @@ exit: * !0 = Failure * * PARAMETERS: - * *dev - pointer to device structure - * *slave - pointer to one entry in the slave info array + * *dev - pointer to device structure + * *slave_irq_toggled - pointer to where to store if slave IRQ toggled + * *slave_bus_toggled - pointer to where to store if slave Bus toggled + * *err_str - pointer to error string buffer + * *slave_detect - pointer to slave detect buffer + * *boot_err - pointer to boot_err buffer ******************************************************************************/ static int pt_bist_slave_irq_test(struct device *dev, - struct pt_bist_data *slave) + u8 *slave_irq_toggled, u8 *slave_bus_toggled, u8 *err_str, + u8 *slave_detect, u8 *boot_err) { struct pt_core_data *cd = dev_get_drvdata(dev); u8 mode = PT_MODE_UNKNOWN; @@ -17489,11 +15511,9 @@ static int pt_bist_slave_irq_test(struct device *dev, */ rc = _pt_request_pip2_enter_bl(dev, &mode, &result); if (rc) { - pt_debug(cd->dev, DL_WARN, "%s: Error entering BL rc=%d\n", + pt_debug(cd->dev, DL_ERROR, "%s: Error entering BL rc=%d\n", __func__, rc); - if (slave->irq_err_str) - strlcpy(slave->irq_err_str, - "- State could not be determined.", PT_ERR_STR_SIZE); + strlcpy(err_str, "- State could not be determined.", PT_ERR_STR_SIZE); goto exit; } @@ -17505,7 +15525,7 @@ static int pt_bist_slave_irq_test(struct device *dev, pt_pr_buf(cd->dev, DL_INFO, read_buf, actual_read_len, "PIP2 STATUS"); status = read_buf[PIP2_RESP_STATUS_OFFSET]; - boot = read_buf[PIP2_RESP_BODY_OFFSET] & slave->mask; + boot = read_buf[PIP2_RESP_BODY_OFFSET] & 0x01; /* Slave detect is only valid if status ok and in boot exec */ if (status == PIP2_RSP_ERR_NONE && @@ -17513,17 +15533,13 @@ static int pt_bist_slave_irq_test(struct device *dev, detected = read_buf[PIP2_RESP_BODY_OFFSET + 2] & SLAVE_DETECT_MASK; } else { - if (slave->irq_err_str) - strlcpy(slave->irq_err_str, - "- State could not be determined", PT_ERR_STR_SIZE); + strlcpy(err_str, "- State could not be determined", PT_ERR_STR_SIZE); rc = -EPERM; } } else { - pt_debug(cd->dev, DL_WARN, "%s: STATUS cmd failure\n", + pt_debug(cd->dev, DL_ERROR, "%s: STATUS cmd failure\n", __func__); - if (slave->irq_err_str) - strlcpy(slave->irq_err_str, - "- State could not be determined.", PT_ERR_STR_SIZE); + strlcpy(err_str, "- State could not be determined.", PT_ERR_STR_SIZE); goto exit; } @@ -17540,68 +15556,52 @@ static int pt_bist_slave_irq_test(struct device *dev, status = read_buf[PIP2_RESP_STATUS_OFFSET]; last_err = read_buf[PIP2_RESP_BODY_OFFSET]; if (last_err) { - pt_debug(cd->dev, DL_WARN, + pt_debug(cd->dev, DL_ERROR, "%s: Master Boot Last Err = 0x%02X\n", __func__, last_err); } } else { - pt_debug(cd->dev, DL_WARN, + pt_debug(cd->dev, DL_ERROR, "%s: GET_LAST_ERRNO cmd failure\n", __func__); - if (slave->irq_err_str) - strlcpy(slave->irq_err_str, - "- stuck, likely shorted to GND.", PT_ERR_STR_SIZE); + strlcpy(err_str, "- stuck, likely shorted to GND.", PT_ERR_STR_SIZE); } exit: pt_debug(cd->dev, DL_INFO, "%s: rc=%d detected=0x%02X boot_err=0x%02X\n", __func__, rc, detected, last_err); - - /* - * Clear any possible false positives: - * - An invalid image error as BIST doesn't need valid FW - * - A Flash file too small as BIST doesn't need FLASH - * - FLASH access errors when in no-flash mode - */ - if ((last_err == PIP2_RSP_ERR_INVALID_IMAGE) || - (last_err == PIP2_RSP_ERR_BUF_TOO_SMALL) || - (last_err == PIP2_RSP_ERR_BAD_ADDRESS && cd->flashless_dut) || - (last_err == PIP2_RSP_ERR_BAD_FRAME && cd->flashless_dut)) { - pt_debug(cd->dev, DL_INFO, "%s: Cleared boot error: 0x%02X\n", - __func__, last_err); - last_err = PIP2_RSP_ERR_NONE; - } - - /* Attempt to add a hint based on boot error and detection */ - if (slave->irq_err_str) { - if (last_err && detected) - scnprintf(slave->irq_err_str,PT_ERR_STR_SIZE, "%s 0x%02X", + if (err_str && last_err) { + if (detected) + scnprintf(err_str, PT_ERR_STR_SIZE, "%s 0x%02X", "- Likely stuck low. Boot Error:", last_err); - else if (last_err && !detected) - scnprintf(slave->irq_err_str, PT_ERR_STR_SIZE, "%s 0x%02X", + else + scnprintf(err_str, PT_ERR_STR_SIZE, "%s 0x%02X", "- Likely stuck high. Boot Error:", last_err); - else if (detected) - strlcpy(slave->irq_err_str, - "- Likely stuck low. No Critical Boot Error", PT_ERR_STR_SIZE); - else if (!detected) - strlcpy(slave->irq_err_str, - "- Likely stuck high. No Critical Boot Error.", PT_ERR_STR_SIZE); } - slave->irq_toggled = (detected && !last_err) ? true : false; - /* Leave as UNTEST if slave not detected */ - if (detected) - slave->bus_toggled = !last_err ? true : false; - slave->detected = detected; - slave->boot_err = last_err; + /* Ignore an invalid image error as BIST doesn't need valid FW */ + if (last_err == PIP2_RSP_ERR_INVALID_IMAGE) + last_err = PIP2_RSP_ERR_NONE; + + if (slave_irq_toggled) + *slave_irq_toggled = (detected && !last_err) ? true : false; + if (slave_bus_toggled) { + /* Leave as UNTEST if slave not detected */ + if (detected) + *slave_bus_toggled = !last_err ? true : false; + } + if (slave_detect) + *slave_detect = detected; + if (boot_err) + *boot_err = last_err; pt_debug(cd->dev, DL_INFO, "%s: %s=0x%02X, %s=0x%02X, %s=0x%02X\n", __func__, "Detected", detected, - "slave_irq_toggled", slave->irq_toggled, - "slave_bus_toggled", slave->bus_toggled); + "slave_irq_toggled", *slave_irq_toggled, + "slave_bus_toggled", *slave_bus_toggled); return rc; } @@ -17621,13 +15621,19 @@ exit: * !0 = Failure * * PARAMETERS: - * *dev - pointer to device structure - * *slave - pointer to one entry in the slave info array + * *dev - pointer to device structure + * *slave_irq_toggled - pointer to where to store if slave IRQ toggled + * *slave_bus_toggled - pointer to where to store if slave Bus toggled + * *slave_xres_toggled - pointer to where to store if slave XRES toggled + * *err_str - pointer to error string buffer ******************************************************************************/ static int pt_bist_slave_xres_test(struct device *dev, - struct pt_bist_data *slave) + u8 *slave_irq_toggled, u8 *slave_bus_toggled, u8 *slave_xres_toggled, + u8 *err_str) { struct pt_core_data *cd = dev_get_drvdata(dev); + u8 slave_detect = 0; + u8 boot_err = 0; int rc = 0; /* Force a reset to force the 'slave detect' bits to be re-acquired */ @@ -17638,39 +15644,31 @@ static int pt_bist_slave_xres_test(struct device *dev, pt_hw_hard_reset(cd); msleep(100); - rc = pt_bist_slave_irq_test(dev, slave); + rc = pt_bist_slave_irq_test(dev, slave_irq_toggled, + slave_bus_toggled, err_str, &slave_detect, &boot_err); pt_debug(dev, DL_INFO, "%s: IRQ test rc = %d\n", __func__, rc); - if (!rc && slave->irq_toggled == false) { + if (!rc && *slave_irq_toggled == false) { /* * If the slave IRQ did not toggle, either the slave_detect * bit was not set or we had a boot error. If the slave * detect was not set the slave did not reset causing a boot * error. */ - if (slave->xres_err_str && !slave->detected) { - strlcpy(slave->xres_err_str, slave->irq_err_str, PT_ERR_STR_SIZE); - pt_debug(dev, DL_INFO, - "%s: detected=0 irq_err_str = %s\n", - __func__, slave->irq_err_str); - } else if (slave->xres_err_str && slave->boot_err > 0) { - scnprintf(slave->xres_err_str, PT_ERR_STR_SIZE, "%s 0x%02X", + if (!slave_detect) + strlcpy(err_str, "- likely open.", PT_ERR_STR_SIZE); + else + scnprintf(err_str, PT_ERR_STR_SIZE, "%s 0x%02X", "- likely open or an IRQ issue. Boot Error:", - slave->boot_err); - pt_debug(dev, DL_INFO, - "%s: boot_err=%d xres_err_str = %s\n", - __func__, - slave->boot_err, slave->xres_err_str); - } else { - pt_debug(dev, DL_WARN, "%s: No xres_err_str buffer\n", - __func__); - } + boot_err); } + if (slave_xres_toggled) { + if (!rc) + *slave_xres_toggled = *slave_irq_toggled ? true : false; + else + *slave_xres_toggled = false; - if (!rc) - slave->xres_toggled = slave->irq_toggled ? true : false; - else - slave->xres_toggled = false; + } return rc; } @@ -17691,11 +15689,13 @@ static int pt_bist_slave_xres_test(struct device *dev, * !0 = Failure * * PARAMETERS: - * *dev - pointer to device structure - * *slave - pointer to one entry in the slave info array + * *dev - pointer to device structure + * *slave_irq_toggled - pointer to where to store if slave IRQ toggled + * *slave_bus_toggled - pointer to where to store if slave Bus toggled + * *err_str - pointer to error string buffer ******************************************************************************/ static int pt_bist_slave_bus_test(struct device *dev, - struct pt_bist_data *slave) + u8 *slave_irq_toggled, u8 *slave_bus_toggled, u8 *err_str) { struct pt_core_data *cd = dev_get_drvdata(dev); u8 mode = PT_MODE_UNKNOWN; @@ -17706,11 +15706,9 @@ static int pt_bist_slave_bus_test(struct device *dev, rc = _pt_request_pip2_enter_bl(dev, &mode, &result); if (rc) { - pt_debug(cd->dev, DL_WARN, "%s: Error entering BL rc=%d\n", + pt_debug(cd->dev, DL_ERROR, "%s: Error entering BL rc=%d\n", __func__, rc); - if (slave->bus_err_str) - strlcpy(slave->bus_err_str, - "- State could not be determined.", PT_ERR_STR_SIZE); + strlcpy(err_str, "- State could not be determined.", PT_ERR_STR_SIZE); goto exit; } @@ -17719,16 +15717,14 @@ static int pt_bist_slave_bus_test(struct device *dev, if (file_handle != PIP2_RAM_FILE) { rc = -ENOENT; bus_toggled = false; - pt_debug(dev, DL_WARN, + pt_debug(dev, DL_ERROR, "%s Failed to open bin file\n", __func__); - if (slave->bus_err_str) - strlcpy(slave->bus_err_str, - "- Bus open, shorted or DUT in reset", PT_ERR_STR_SIZE); + strlcpy(err_str, "- Bus open, shorted or DUT in reset", PT_ERR_STR_SIZE); goto exit; } else { bus_toggled = true; if (file_handle != _pt_pip2_file_close(dev, file_handle)) { - pt_debug(dev, DL_WARN, + pt_debug(dev, DL_ERROR, "%s: File Close failed, file_handle=%d\n", __func__, file_handle); } @@ -17736,315 +15732,10 @@ static int pt_bist_slave_bus_test(struct device *dev, exit: /* If the master was able to send/recv a PIP msg, the IRQ must be ok */ - slave->irq_toggled = bus_toggled; - slave->bus_toggled = bus_toggled; - - return rc; -} - -/******************************************************************************* - * FUNCTION: pt_bist_slave_test - * - * SUMMARY: Tests XRES, SPI BUS and IRQ for the slave DUT - * - * RETURN: - * 0 = Success - * !0 = Failure - * - * PARAMETERS: - * *dev - pointer to device structure - * *slave - pointer to one entry in the bist info array - * slave_id - id of the slave chip - ******************************************************************************/ -static int pt_bist_slave_test(struct device *dev, - struct pt_bist_data *slave, int slave_id) -{ - struct pt_core_data *cd = dev_get_drvdata(dev); - char *pr_buf; - int rc = 0; - - slave->mask = 0x01 << slave_id; - slave->bus_toggled = 0x0F; /* untested */ - slave->irq_toggled = 0x0F; /* untested */ - slave->xres_toggled = 0x0F; /* untested */ - - pr_buf = slave->print_buf; - if (!pr_buf) { - rc = -ENOMEM; - goto print_results; - } - - slave->bus_err_str = kzalloc(PT_ERR_STR_SIZE, - GFP_KERNEL); - slave->irq_err_str = kzalloc(PT_ERR_STR_SIZE, - GFP_KERNEL); - slave->xres_err_str = kzalloc(PT_ERR_STR_SIZE, - GFP_KERNEL); - - /* ensure no malloc failure */ - if (!slave->bus_err_str || - !slave->irq_err_str || - !slave->xres_err_str) - goto print_results; - - memset(slave->bus_err_str, 0, PT_ERR_STR_SIZE); - memset(slave->irq_err_str, 0, PT_ERR_STR_SIZE); - memset(slave->xres_err_str, 0, PT_ERR_STR_SIZE); - - /* --------------- SLAVE XRES BIST TEST --------------- */ - if ((cd->ttdl_bist_select & PT_BIST_SLAVE_XRES_TEST) != 0) { - pt_debug(dev, DL_INFO, - "%s: ----- Start Slave %d XRES BIST -----", - __func__, slave_id); - slave->xres_toggled = 0xFF; - rc = pt_bist_slave_xres_test(dev, slave); - if (slave->bus_toggled == 1 && - slave->irq_toggled == 1 && - slave->xres_toggled == 1) - goto print_results; - } - - /* --------------- SLAVE IRQ BIST TEST --------------- */ - if ((cd->ttdl_bist_select & PT_BIST_SLAVE_IRQ_TEST) != 0) { - pt_debug(dev, DL_INFO, - "%s: ----- Start Slave %d IRQ BIST -----", - __func__, slave_id); - slave->irq_toggled = 0xFF; - rc = pt_bist_slave_irq_test(dev, slave); - pt_debug(dev, DL_INFO, - "%s: slave_irq_toggled = 0x%02X\n", - __func__, slave->irq_toggled); - if (slave->irq_toggled == 1) { - slave->bus_toggled = 1; - goto print_results; - } - } - - /* --------------- SLAVE BUS BIST TEST --------------- */ - if ((cd->ttdl_bist_select & PT_BIST_SLAVE_BUS_TEST) != 0) { - pt_debug(dev, DL_INFO, - "%s: ----- Start Slave %d BUS BIST -----", - __func__, slave_id); - slave->bus_toggled = 0xFF; - rc = pt_bist_slave_bus_test(dev, slave); - } - -print_results: - /* --------------- PRINT OUT BIST RESULTS ---------------*/ - pt_debug(dev, DL_INFO, "%s: ----- BIST Print Results ----", __func__); - if (!slave->bus_err_str || - !slave->irq_err_str || - !slave->xres_err_str) { - slave->pr_index = scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE, - "M/S%d SPI (MISO,MOSI,CS,CLK): [UNTEST]\n" - "M/S%d IRQ connection: [UNTEST]\n" - "M/S%d TP_XRES connection: [UNTEST]\n", - slave_id, slave_id, slave_id); - } else { - if (slave->irq_toggled == 1) - memset(slave->irq_err_str, 0, PT_ERR_STR_SIZE); - if (slave->xres_toggled == 1) - memset(slave->xres_err_str, 0, PT_ERR_STR_SIZE); - if (slave->bus_toggled == 1) - memset(slave->bus_err_str, 0, PT_ERR_STR_SIZE); - - slave->status = 0; - if (cd->ttdl_bist_select & PT_BIST_SLAVE_BUS_TEST) - slave->status += slave->bus_toggled; - if (cd->ttdl_bist_select & PT_BIST_SLAVE_IRQ_TEST) - slave->status += slave->irq_toggled; - if (cd->ttdl_bist_select & PT_BIST_SLAVE_XRES_TEST) - slave->status += slave->xres_toggled; - pt_debug(dev, DL_WARN, - "%s: status = %d (Slave %d: %d,%d,%d)\n", - __func__, slave->status, slave_id, - slave->bus_toggled, - slave->irq_toggled, - slave->xres_toggled); - - slave->pr_index = scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE, - "M/S%d SPI (MISO,MOSI,CS,CLK): %s %s\n" - "M/S%d IRQ connection: %s %s\n" - "M/S%d TP_XRES connection: %s %s\n", - slave_id, - slave->bus_toggled == 0x0F ? - "[UNTEST]" : - slave->bus_toggled == 1 ? - "[ OK ]" : - "[FAILED]", - slave->bus_err_str, - slave_id, - slave->irq_toggled == 0x0F ? - "[UNTEST]" : - slave->irq_toggled == 1 ? - "[ OK ]" : - "[FAILED]", - slave->irq_err_str, - slave_id, - slave->xres_toggled == 0x0F ? - "[UNTEST]" : - slave->xres_toggled == 1 ? - "[ OK ]" : - "[FAILED]", - slave->xres_err_str); - } - - kfree(slave->bus_err_str); - kfree(slave->irq_err_str); - kfree(slave->xres_err_str); - - return rc; -} - -/******************************************************************************* - * FUNCTION: pt_bist_host_test - * - * SUMMARY: Tests XRES, SPI BUS and IRQ for the host DUT - * - * RETURN: - * 0 = Success - * !0 = Failure - * - * PARAMETERS: - * *dev - pointer to device structure - * *host - pointer to the entry in the bist info array - ******************************************************************************/ -static int pt_bist_host_test(struct device *dev, - struct pt_bist_data *host) -{ - struct pt_core_data *cd = dev_get_drvdata(dev); - char *pr_buf; - int rc = 0; - - u8 bus_toggled = 0x0F; /* default to untested */ - u8 i2c_toggled = 0x0F; /* default to untested */ - u8 spi_toggled = 0x0F; /* default to untested */ - u8 irq_toggled = 0x0F; /* default to untested */ - u8 xres_toggled = 0x0F; /* default to untested */ - - pr_buf = host->print_buf; - if (!pr_buf) { - rc = -ENOMEM; - goto print_results; - } - - host->bus_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); - host->irq_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); - host->xres_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); - if (!host->bus_err_str || - !host->irq_err_str || - !host->xres_err_str) - goto print_results; - - memset(host->xres_err_str, 0, PT_ERR_STR_SIZE); - memset(host->irq_err_str, 0, PT_ERR_STR_SIZE); - memset(host->bus_err_str, 0, PT_ERR_STR_SIZE); - - /* --------------- TP_XRES BIST TEST --------------- */ - if ((cd->ttdl_bist_select & PT_BIST_TP_XRES_TEST) != 0) { - pt_debug(dev, DL_INFO, - "%s: ----- Start TP_XRES BIST -----", __func__); - rc = pt_bist_xres_test(dev, &bus_toggled, &irq_toggled, - &xres_toggled, host->xres_err_str); - /* Done if the rest of all nets toggled */ - if (bus_toggled == 1 && irq_toggled == 1 && xres_toggled == 1) - goto print_results; - } - - /* Flush bus in case a PIP response is waiting from previous test */ - pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL); - - /* --------------- IRQ BIST TEST --------------- */ - if ((cd->ttdl_bist_select & PT_BIST_IRQ_TEST) != 0) { - pt_debug(dev, DL_INFO, - "%s: ----- Start IRQ BIST -----", __func__); - bus_toggled = 0xFF; - irq_toggled = 0xFF; - rc = pt_bist_irq_test(dev, &bus_toggled, &irq_toggled, - &xres_toggled, host->irq_err_str); - /* If this net failed clear results from previous net */ - if (irq_toggled != 1) { - xres_toggled = 0x0F; - memset(host->xres_err_str, 0, PT_ERR_STR_SIZE); - } - if (bus_toggled == 1 && irq_toggled == 1) - goto print_results; - } - - /* Flush bus in case a PIP response is waiting from previous test */ - pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL); - - /* --------------- BUS BIST TEST --------------- */ - if ((cd->ttdl_bist_select & PT_BIST_BUS_TEST) != 0) { - pt_debug(dev, DL_INFO, - "%s: ----- Start BUS BIST -----", __func__); - bus_toggled = 0xFF; - rc = pt_bist_bus_test(dev, &bus_toggled, host->bus_err_str); - /* If this net failed clear results from previous net */ - if (bus_toggled == 0) { - irq_toggled = 0x0F; - memset(host->irq_err_str, 0, PT_ERR_STR_SIZE); - } - } - -print_results: - /* --------------- PRINT OUT BIST RESULTS ---------------*/ - pt_debug(dev, DL_INFO, "%s: ----- BIST Print Results ----", __func__); - - /* Cannot print if any memory allocation issues */ - if (!host->bus_err_str || !host->irq_err_str || !host->xres_err_str) { - host->pr_index = scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE, - "Status: %d\n" - "I2C (SDA,SCL): [UNTEST]\n" - "SPI (MISO,MOSI,CS,CLK): [UNTEST]\n" - "IRQ connection: [UNTEST]\n" - "TP_XRES connection: [UNTEST]\n", -ENOMEM); - } else { - host->status = 0; - if (bus_toggled == 1) - memset(host->bus_err_str, 0, PT_ERR_STR_SIZE); - if (irq_toggled == 1) - memset(host->irq_err_str, 0, PT_ERR_STR_SIZE); - if (xres_toggled == 1) - memset(host->xres_err_str, 0, PT_ERR_STR_SIZE); - - if (cd->ttdl_bist_select & PT_BIST_BUS_TEST) - host->status += bus_toggled; - if (cd->ttdl_bist_select & PT_BIST_IRQ_TEST) - host->status += irq_toggled; - if (cd->ttdl_bist_select & PT_BIST_TP_XRES_TEST) - host->status += xres_toggled; - pt_debug(dev, DL_WARN, "%s: status = %d (%d,%d,%d)\n", - __func__, host->status, bus_toggled, irq_toggled, - xres_toggled); - - if (cd->bus_ops->bustype == BUS_I2C) - i2c_toggled = bus_toggled; - else - spi_toggled = bus_toggled; - - host->pr_index = scnprintf(pr_buf, PT_MAX_PR_BUF_SIZE, - "I2C (SDA,SCL): %s %s\n" - "SPI (MISO,MOSI,CS,CLK): %s %s\n" - "IRQ connection: %s %s\n" - "TP_XRES connection: %s %s\n", - i2c_toggled == 0x0F ? "[UNTEST]" : - i2c_toggled == 1 ? "[ OK ]" : "[FAILED]", - i2c_toggled == 0x0F ? "" : host->bus_err_str, - spi_toggled == 0x0F ? "[UNTEST]" : - spi_toggled == 1 ? "[ OK ]" : "[FAILED]", - spi_toggled == 0x0F ? "" : host->bus_err_str, - irq_toggled == 0x0F ? "[UNTEST]" : - irq_toggled == 1 ? "[ OK ]" : "[FAILED]", - host->irq_err_str, - xres_toggled == 0x0F ? "[UNTEST]" : - xres_toggled == 1 ? "[ OK ]" : "[FAILED]", - host->xres_err_str); - } - - kfree(host->bus_err_str); - kfree(host->irq_err_str); - kfree(host->xres_err_str); + if (slave_irq_toggled) + *slave_irq_toggled = bus_toggled; + if (slave_bus_toggled) + *slave_bus_toggled = bus_toggled; return rc; } @@ -18075,28 +15766,49 @@ static ssize_t pt_ttdl_bist_show(struct device *dev, struct device_attribute *attr, char *buf) { struct pt_core_data *cd = dev_get_drvdata(dev); - ssize_t ret = 0; + ssize_t ret; + char *bus_err_str = NULL; + char *irq_err_str = NULL; + char *xres_err_str = NULL; + char *slave_bus_err_str = NULL; + char *slave_irq_err_str = NULL; + char *slave_xres_err_str = NULL; u8 tests; - int slave_id = 0; - int num_slaves = 0; int rc = 0; int num_tests = 0; int status = 1; /* 0 = Pass, !0 = fail */ - struct pt_bist_data host; - struct pt_bist_data slaves[PT_MAX_DEVICES]; - int idx_status = 0; + u8 bus_toggled = 0x0F; /* default to untested */ + u8 i2c_toggled = 0x0F; /* default to untested */ + u8 spi_toggled = 0x0F; /* default to untested */ + u8 irq_toggled = 0x0F; /* default to untested */ + u8 xres_toggled = 0x0F; /* default to untested */ + u8 slave_bus_toggled = 0x0F; /* default to untested */ + u8 slave_irq_toggled = 0x0F; /* default to untested */ + u8 slave_xres_toggled = 0x0F; /* default to untested */ - host.print_buf = kzalloc(PT_MAX_PR_BUF_SIZE, GFP_KERNEL); - if (!host.print_buf) { - ret = scnprintf(buf, strlen(buf), - "Status: 1\n" - "Failed to alloc memory"); - return ret; + bus_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); + irq_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); + xres_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); + if (!bus_err_str || !irq_err_str || !xres_err_str) + goto print_results; + + memset(xres_err_str, 0, PT_ERR_STR_SIZE); + memset(irq_err_str, 0, PT_ERR_STR_SIZE); + memset(bus_err_str, 0, PT_ERR_STR_SIZE); + + if (cd->multi_chip) { + slave_bus_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); + slave_irq_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); + slave_xres_err_str = kzalloc(PT_ERR_STR_SIZE, GFP_KERNEL); + if (!slave_bus_err_str || + !slave_irq_err_str || + !slave_xres_err_str) + goto print_results; + memset(slave_bus_err_str, 0, PT_ERR_STR_SIZE); + memset(slave_irq_err_str, 0, PT_ERR_STR_SIZE); + memset(slave_xres_err_str, 0, PT_ERR_STR_SIZE); } - /* Load up slave info array when slaves present */ - num_slaves = cd->num_devices - 1; - /* Turn off the TTDL WD during the test */ pt_stop_wd_timer(cd); @@ -18109,7 +15821,7 @@ static ssize_t pt_ttdl_bist_show(struct device *dev, num_tests += tests & 1; tests >>= 1; } - pt_debug(dev, DL_WARN, "%s: BIST select = 0x%02X, run %d tests\n", + pt_debug(dev, DL_ERROR, "%s: BIST select = 0x%02X, run %d tests\n", __func__, cd->ttdl_bist_select, num_tests); /* Suppress auto BL to avoid loader thread sending PIP during xres */ @@ -18121,43 +15833,95 @@ static ssize_t pt_ttdl_bist_show(struct device *dev, mutex_unlock(&cd->system_lock); } - /* --------------- TP HOST BIST TEST --------------- */ - host.status = 0; - host.pr_index = 0; - rc = pt_bist_host_test(dev, &host); - pt_debug(dev, DL_INFO, "%s print_idx = %d\n", - __func__, host.pr_index); + /* --------------- TP_XRES BIST TEST --------------- */ + if ((cd->ttdl_bist_select & PT_TTDL_BIST_TP_XRES_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start TP_XRES BIST -----", __func__); + rc = pt_bist_xres_test(dev, &bus_toggled, &irq_toggled, + &xres_toggled, xres_err_str); + /* Done if the rest of all nets toggled */ + if (bus_toggled == 1 && irq_toggled == 1 && xres_toggled == 1) + goto host_nets_complete; + } - status = host.status; + /* Flush bus in case a PIP response is waiting from previous test */ + pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL); - /* --------------- TP SLAVE BIST TEST --------------- */ - for (slave_id = 0; slave_id < num_slaves; slave_id++) { - slaves[slave_id].print_buf = kzalloc(PT_MAX_PR_BUF_SIZE, - GFP_KERNEL); - if (!slaves[slave_id].print_buf) { - ret = scnprintf(buf, strlen(buf), - "Status: 1\n" - "Failed to alloc memory"); - goto exit; + /* --------------- IRQ BIST TEST --------------- */ + if ((cd->ttdl_bist_select & PT_TTDL_BIST_IRQ_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start IRQ BIST -----", __func__); + bus_toggled = 0xFF; + irq_toggled = 0xFF; + rc = pt_bist_irq_test(dev, &bus_toggled, &irq_toggled, + &xres_toggled, irq_err_str); + /* If this net failed clear results from previous net */ + if (irq_toggled != 1) { + xres_toggled = 0x0F; + memset(xres_err_str, 0, PT_ERR_STR_SIZE); } - slaves[slave_id].status = 0; - slaves[slave_id].pr_index = 0; - pt_bist_slave_test(dev, &slaves[slave_id], slave_id); - status += slaves[slave_id].status; + if (bus_toggled == 1 && irq_toggled == 1) + goto host_nets_complete; } - idx_status = scnprintf(buf, strlen(buf), "Status: %d\n", - status == num_tests ? 0 : 1); - memcpy(buf + idx_status, host.print_buf, host.pr_index); - ret = idx_status + host.pr_index; + /* Flush bus in case a PIP response is waiting from previous test */ + pt_flush_bus(cd, PT_FLUSH_BUS_BASED_ON_LEN, NULL); - for (slave_id = 0; slave_id < num_slaves; slave_id++) { - memcpy(buf + ret, slaves[slave_id].print_buf, - slaves[slave_id].pr_index); - ret += slaves[slave_id].pr_index; + /* --------------- BUS BIST TEST --------------- */ + if ((cd->ttdl_bist_select & PT_TTDL_BIST_BUS_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start BUS BIST -----", __func__); + bus_toggled = 0xFF; + rc = pt_bist_bus_test(dev, &bus_toggled, bus_err_str); + /* If this net failed clear results from previous net */ + if (bus_toggled == 0) { + irq_toggled = 0x0F; + memset(irq_err_str, 0, PT_ERR_STR_SIZE); + } } -exit: +host_nets_complete: + /* --------------- SLAVE XRES BIST TEST --------------- */ + if (cd->multi_chip && + (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_XRES_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start Slave XRES BIST -----", __func__); + slave_xres_toggled = 0xFF; + rc = pt_bist_slave_xres_test(dev, &slave_irq_toggled, + &slave_bus_toggled, &slave_xres_toggled, + slave_xres_err_str); + if ((slave_bus_toggled == 1 && slave_irq_toggled == 1 && + slave_xres_toggled == 1) || slave_xres_toggled == 0) + goto print_results; + } + + /* --------------- SLAVE IRQ BIST TEST --------------- */ + if (cd->multi_chip && + (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_IRQ_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start Slave IRQ BIST -----", __func__); + slave_irq_toggled = 0xFF; + rc = pt_bist_slave_irq_test(dev, &slave_irq_toggled, + &slave_bus_toggled, slave_irq_err_str, NULL, NULL); + pt_debug(dev, DL_INFO, "%s: slave_irq_toggled = 0x%02X\n", + __func__, slave_irq_toggled); + if (slave_irq_toggled == 1) { + slave_bus_toggled = 1; + goto print_results; + } + } + + /* --------------- SLAVE BUS BIST TEST --------------- */ + if (cd->multi_chip && + (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_BUS_TEST) != 0) { + pt_debug(dev, DL_INFO, + "%s: ----- Start Slave BUS BIST -----", __func__); + slave_bus_toggled = 0xFF; + rc = pt_bist_slave_bus_test(dev, &slave_irq_toggled, + &slave_bus_toggled, slave_bus_err_str); + } + +print_results: /* Restore PIP command timeout */ cd->pip_cmd_timeout = cd->pip_cmd_timeout_default; @@ -18222,14 +15986,112 @@ exit: } msleep(20); + /* --------------- PRINT OUT BIST RESULTS ---------------*/ + pt_debug(dev, DL_INFO, "%s: ----- BIST Print Results ----", __func__); + pt_start_wd_timer(cd); + + /* Canned print if any memory allocation issues */ + if (!bus_err_str || !irq_err_str || !xres_err_str) { + ret = scnprintf(buf, strlen(buf), + "Status: %d\n" + "I2C (SDA,SCL): [UNTEST]\n" + "SPI (MISO,MOSI,CS,CLK): [UNTEST]\n" + "IRQ connection: [UNTEST]\n" + "TP_XRES connection: [UNTEST]\n", -ENOMEM); + if (cd->multi_chip) { + ret += scnprintf(buf + ret, strlen(buf), + "I/P SPI (MISO,MOSI,CS,CLK): [UNTEST]\n" + "I/P IRQ connection: [UNTEST]\n" + "I/P TP_XRES connection: [UNTEST]\n"); + } + } else { + status = 0; + if (bus_toggled == 1) + memset(bus_err_str, 0, PT_ERR_STR_SIZE); + if (irq_toggled == 1) + memset(irq_err_str, 0, PT_ERR_STR_SIZE); + if (xres_toggled == 1) + memset(xres_err_str, 0, PT_ERR_STR_SIZE); + + if (cd->ttdl_bist_select & PT_TTDL_BIST_BUS_TEST) + status += bus_toggled; + if (cd->ttdl_bist_select & PT_TTDL_BIST_IRQ_TEST) + status += irq_toggled; + if (cd->ttdl_bist_select & PT_TTDL_BIST_TP_XRES_TEST) + status += xres_toggled; + pt_debug(dev, DL_ERROR, "%s: status = %d (%d,%d,%d)\n", + __func__, status, bus_toggled, irq_toggled, + xres_toggled); + + if (cd->multi_chip) { + if (slave_irq_toggled == 1) + memset(slave_irq_err_str, 0, PT_ERR_STR_SIZE); + if (slave_xres_toggled == 1) + memset(slave_xres_err_str, 0, PT_ERR_STR_SIZE); + if (slave_bus_toggled == 1) + memset(slave_bus_err_str, 0, PT_ERR_STR_SIZE); + + if (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_BUS_TEST) + status += slave_bus_toggled; + if (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_IRQ_TEST) + status += slave_irq_toggled; + if (cd->ttdl_bist_select & PT_TTDL_BIST_SLAVE_XRES_TEST) + status += slave_xres_toggled; + pt_debug(dev, DL_ERROR, + "%s: status = %d (%d,%d,%d,%d,%d,%d)\n", + __func__, status, bus_toggled, irq_toggled, + xres_toggled, slave_bus_toggled, + slave_irq_toggled, slave_xres_toggled); + } + + if (cd->bus_ops->bustype == BUS_I2C) + i2c_toggled = bus_toggled; + else + spi_toggled = bus_toggled; + + ret = scnprintf(buf, strlen(buf), + "Status: %d\n" + "I2C (SDA,SCL): %s %s\n" + "SPI (MISO,MOSI,CS,CLK): %s %s\n" + "IRQ connection: %s %s\n" + "TP_XRES connection: %s %s\n", + status == num_tests ? 0 : 1, + i2c_toggled == 0x0F ? "[UNTEST]" : + i2c_toggled == 1 ? "[ OK ]" : "[FAILED]", + i2c_toggled == 0x0F ? "" : bus_err_str, + spi_toggled == 0x0F ? "[UNTEST]" : + spi_toggled == 1 ? "[ OK ]" : "[FAILED]", + spi_toggled == 0x0F ? "" : bus_err_str, + irq_toggled == 0x0F ? "[UNTEST]" : + irq_toggled == 1 ? "[ OK ]" : "[FAILED]", + irq_err_str, + xres_toggled == 0x0F ? "[UNTEST]" : + xres_toggled == 1 ? "[ OK ]" : "[FAILED]", + xres_err_str); + + if (cd->multi_chip) { + ret += scnprintf(buf + ret, strlen(buf), + "I/P SPI (MISO,MOSI,CS,CLK): %s %s\n" + "I/P IRQ connection: %s %s\n" + "I/P TP_XRES connection: %s %s\n", + slave_bus_toggled == 0x0F ? "[UNTEST]" : + slave_bus_toggled == 1 ? "[ OK ]" : + "[FAILED]", slave_bus_err_str, + slave_irq_toggled == 0x0F ? "[UNTEST]" : + slave_irq_toggled == 1 ? "[ OK ]" : + "[FAILED]", slave_irq_err_str, + slave_xres_toggled == 0x0F ? "[UNTEST]" : + slave_xres_toggled == 1 ? "[ OK ]" : + "[FAILED]", slave_xres_err_str); + } + } + /* Put TTDL back into a known state, issue a ttdl enum if needed */ pt_debug(dev, DL_INFO, "%s: Startup_status = 0x%04X\n", __func__, cd->startup_status); - - kfree(host.print_buf); - for (slave_id = 0; slave_id < num_slaves; slave_id++) - kfree(slaves[slave_id].print_buf); - + kfree(bus_err_str); + kfree(irq_err_str); + kfree(xres_err_str); return ret; } @@ -18258,7 +16120,7 @@ static ssize_t pt_ttdl_bist_store(struct device *dev, length = _pt_ic_parse_input(dev, buf, size, input_data, ARRAY_SIZE(input_data)); if (length != 1) { - pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n", __func__); rc = -EINVAL; goto exit; @@ -18301,7 +16163,7 @@ static ssize_t pt_flush_bus_store(struct device *dev, length = _pt_ic_parse_input(dev, buf, size, input_data, ARRAY_SIZE(input_data)); if (length != 1) { - pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n", __func__); rc = -EINVAL; goto exit; @@ -18387,7 +16249,7 @@ static ssize_t pt_pip2_ping_test_store(struct device *dev, length = _pt_ic_parse_input(dev, buf, size, input_data, ARRAY_SIZE(input_data)); if (length != 1) { - pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n", __func__); rc = -EINVAL; goto exit; @@ -18474,7 +16336,7 @@ static ssize_t pt_t_refresh_store(struct device *dev, length = _pt_ic_parse_input(dev, buf, size, input_data, ARRAY_SIZE(input_data)); if (length != 1) { - pt_debug(dev, DL_WARN, "%s: Invalid number of arguments\n", + pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n", __func__); rc = -EINVAL; goto exit; @@ -18488,13 +16350,13 @@ static ssize_t pt_t_refresh_store(struct device *dev, cd->t_refresh_count = 0; cd->t_refresh_active = 1; } else { - pt_debug(dev, DL_WARN, "%s: Invalid value\n", __func__); + pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__); rc = -EINVAL; } mutex_unlock(&cd->system_lock); exit: - pt_debug(dev, DL_WARN, "%s: rc = %d\n", __func__, rc); + pt_debug(dev, DL_ERROR, "%s: rc = %d\n", __func__, rc); if (rc) return rc; return size; @@ -18593,7 +16455,7 @@ static ssize_t pt_dut_status_show(struct device *dev, /* Retrieve mode and FW system mode which can only be 0-4 */ rc = pt_get_fw_sys_mode(cd, &sys_mode, &mode); if (rc || mode == PT_MODE_UNKNOWN) { - ret = snprintf(buf, PT_MAX_PRBUF_SIZE, + ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "%s: %d\n" "%s: n/a\n" "%s: n/a\n" @@ -18618,7 +16480,7 @@ static ssize_t pt_dut_status_show(struct device *dev, if (rc) goto print_limited_results; - ret = snprintf(buf, PT_MAX_PRBUF_SIZE, + ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "%s: %d\n" "%s: %s\n" "%s: %s\n" @@ -18637,7 +16499,7 @@ static ssize_t pt_dut_status_show(struct device *dev, } print_limited_results: - ret = snprintf(buf, PT_MAX_PRBUF_SIZE, + ret = scnprintf(buf, PT_MAX_PRBUF_SIZE, "%s: %d\n" "%s: %s\n" "%s: %s\n" @@ -18664,11 +16526,11 @@ static struct attribute *early_attrs[] = { &dev_attr_drv_ver.attr, &dev_attr_fw_version.attr, &dev_attr_sysinfo.attr, -#ifndef TTDL_KERNEL_SUBMISSION &dev_attr_pip2_cmd_rsp.attr, &dev_attr_command.attr, &dev_attr_drv_debug.attr, &dev_attr_hw_reset.attr, + &dev_attr_response.attr, &dev_attr_ttdl_restart.attr, #ifdef TTDL_DIAGNOSTICS &dev_attr_ttdl_status.attr, @@ -18678,7 +16540,6 @@ static struct attribute *early_attrs[] = { &dev_attr_flush_bus.attr, &dev_attr_ttdl_bist.attr, #endif /* TTDL_DIAGNOSTICS */ -#endif /* !TTDL_KERNEL_SUBMISSION */ NULL, }; @@ -18802,8 +16663,125 @@ static void remove_sysfs_and_modules(struct device *dev) pt_mt_release(dev); remove_sysfs_interfaces(dev); } -#endif /* !TTDL_KERNEL_SUBMISSION */ +static int pt_power_init(struct pt_core_data *cd, bool on) +{ + int rc; + + if (!on) + goto pwr_deinit; + + cd->vdd = regulator_get(cd->dev, "vdd"); + if (IS_ERR(cd->vdd)) { + rc = PTR_ERR(cd->vdd); + dev_err(cd->dev, + "Regulator get failed vdd rc=%d\n", rc); + return rc; + } + if (regulator_count_voltages(cd->vdd) > 0) { + rc = regulator_set_voltage(cd->vdd, FT_VTG_MIN_UV, + FT_VTG_MAX_UV); + if (rc) { + dev_err(cd->dev, + "Regulator set_vtg failed vdd rc=%d\n", rc); + goto reg_vdd_put; + } + } + + cd->vcc_i2c = regulator_get(cd->dev, "vcc_i2c"); + if (IS_ERR(cd->vcc_i2c)) { + rc = PTR_ERR(cd->vcc_i2c); + dev_err(cd->dev, + "Regulator get failed vcc_i2c rc=%d\n", rc); + goto reg_vdd_set_vtg; + } + if (regulator_count_voltages(cd->vcc_i2c) > 0) { + rc = regulator_set_voltage(cd->vcc_i2c, FT_I2C_VTG_MIN_UV, + FT_I2C_VTG_MAX_UV); + if (rc) { + dev_err(cd->dev, + "Regulator set_vtg failed vcc_i2c rc=%d\n", rc); + goto reg_vcc_i2c_put; + } + } + + return 0; + +reg_vcc_i2c_put: + regulator_put(cd->vcc_i2c); +reg_vdd_set_vtg: + if (regulator_count_voltages(cd->vdd) > 0) + regulator_set_voltage(cd->vdd, 0, FT_VTG_MAX_UV); +reg_vdd_put: + regulator_put(cd->vdd); + return rc; + +pwr_deinit: + if (regulator_count_voltages(cd->vdd) > 0) + regulator_set_voltage(cd->vdd, 0, FT_VTG_MAX_UV); + + regulator_put(cd->vdd); + + if (regulator_count_voltages(cd->vcc_i2c) > 0) + regulator_set_voltage(cd->vcc_i2c, 0, FT_I2C_VTG_MAX_UV); + + regulator_put(cd->vcc_i2c); + return 0; +} + +static int pt_ts_pinctrl_init(struct pt_core_data *cd) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + cd->ts_pinctrl = devm_pinctrl_get(cd->dev); + if (IS_ERR_OR_NULL(cd->ts_pinctrl)) { + retval = PTR_ERR(cd->ts_pinctrl); + dev_dbg(cd->dev, + "Target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; + } + + cd->pinctrl_state_active + = pinctrl_lookup_state(cd->ts_pinctrl, + PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(cd->pinctrl_state_active)) { + retval = PTR_ERR(cd->pinctrl_state_active); + dev_err(cd->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + cd->pinctrl_state_suspend + = pinctrl_lookup_state(cd->ts_pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(cd->pinctrl_state_suspend)) { + retval = PTR_ERR(cd->pinctrl_state_suspend); + dev_err(cd->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + cd->pinctrl_state_release + = pinctrl_lookup_state(cd->ts_pinctrl, + PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(cd->pinctrl_state_release)) { + retval = PTR_ERR(cd->pinctrl_state_release); + dev_dbg(cd->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(cd->ts_pinctrl); +err_pinctrl_get: + cd->ts_pinctrl = NULL; + return retval; +} /******************************************************************************* ******************************************************************************* @@ -18833,15 +16811,11 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, struct pt_core_data *cd; struct pt_platform_data *pdata = dev_get_platdata(dev); enum pt_atten_type type; + struct i2c_client *client = to_i2c_client(dev); int rc = 0; -#ifndef PT_PTSBC_SUPPORT u8 pip_ver_major; u8 pip_ver_minor; u32 status = STARTUP_STATUS_START; -#endif -#ifdef TTDL_PTVIRTDUT_SUPPORT - int retry = 3; -#endif if (!pdata || !pdata->core_pdata || !pdata->mt_pdata) { pt_debug(dev, DL_ERROR, "%s: Missing platform data\n", @@ -18859,14 +16833,12 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, goto error_no_pdata; } } - /* get context and debug print buffers */ cd = kzalloc(sizeof(*cd), GFP_KERNEL); if (!cd) { rc = -ENOMEM; goto error_alloc_data; } - /* Initialize device info */ cd->dev = dev; cd->pdata = pdata; @@ -18892,8 +16864,8 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, cd->flashless_auto_bl = PT_SUPPRESS_AUTO_BL; cd->bl_with_no_int = 0; cd->cal_cache_in_host = PT_FEATURE_DISABLE; - cd->num_devices = 1; - cd->tthe_hid_usb_format = PT_TTHE_TUNER_FORMAT_HID_I2C; + cd->multi_chip = PT_FEATURE_DISABLE; + cd->tthe_hid_usb_format = PT_FEATURE_DISABLE; if (cd->cpdata->config_dut_generation == CONFIG_DUT_PIP2_CAPABLE) { cd->set_dut_generation = true; @@ -18905,17 +16877,12 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, cd->set_dut_generation = false; cd->active_dut_generation = DUT_UNKNOWN; } - /* Initialize with platform data */ cd->watchdog_force_stop = cd->cpdata->watchdog_force_stop; -#ifdef PT_PTSBC_SUPPORT - /* Extend first WD to allow DDI to complete configuration */ - cd->watchdog_interval = PT_PTSBC_INIT_WATCHDOG_TIMEOUT; -#else cd->watchdog_interval = PT_WATCHDOG_TIMEOUT; -#endif cd->hid_cmd_state = 1; cd->fw_updating = false; + cd->multi_chip = 0; #ifdef TTDL_DIAGNOSTICS cd->t_refresh_active = 0; @@ -18932,20 +16899,9 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, cd->force_pip2_seq = 0; #endif /* TTDL_DIAGNOSTICS */ -#ifdef TTDL_PTVIRTDUT_SUPPORT - /* - * This variable is only used for BATS test. The - * default value "true" has no effect on whether - * the PT_DETECT_HW build flag is enabled or not. - */ - cd->hw_detect_enabled = true; - cd->route_bus_virt_dut = 0; -#endif /* TTDL_PTVIRTDUT_SUPPORT */ - memset(cd->pip2_us_file_path, 0, PT_MAX_PATH_SIZE); memcpy(cd->pip2_us_file_path, PT_PIP2_BIN_FILE_PATH, sizeof(PT_PIP2_BIN_FILE_PATH)); - pt_init_hid_descriptor(&cd->hid_desc); /* Read and store the descriptor lengths */ cd->hid_core.hid_report_desc_len = @@ -18954,14 +16910,12 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, le16_to_cpu(cd->hid_desc.max_input_len); cd->hid_core.hid_max_output_len = le16_to_cpu(cd->hid_desc.max_output_len); - /* Initialize mutexes and spinlocks */ mutex_init(&cd->module_list_lock); mutex_init(&cd->system_lock); mutex_init(&cd->sysfs_lock); mutex_init(&cd->ttdl_restart_lock); mutex_init(&cd->firmware_class_lock); - mutex_init(&cd->hid_report_lock); spin_lock_init(&cd->spinlock); /* Initialize module list */ @@ -18976,6 +16930,33 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, /* Initialize wait queue */ init_waitqueue_head(&cd->wait_q); + rc = pt_ts_pinctrl_init(cd); + if (!rc && cd->ts_pinctrl) { + /* + * Pinctrl handle is optional. If pinctrl handle is found + * let pins to be configured in active state. If not + * found continue further without error. + */ + rc = pinctrl_select_state(cd->ts_pinctrl, + cd->pinctrl_state_active); + if (rc < 0) + dev_err(&client->dev, "failed to select pin to active state\n"); + } + rc = pt_power_init(cd, true); + if (rc < 0) + dev_err(&client->dev, "failed to cyttsp5_power_init\n"); + + rc = regulator_enable(cd->vdd); + if (rc) { + dev_err(dev, "Regulator vdd enable failed rc=%d\n", rc); + goto error_power; + } + rc = regulator_enable(cd->vcc_i2c); + if (rc) { + dev_err(dev, "Regulator vcc_i2c enable failed rc=%d\n", rc); + regulator_disable(cd->vdd); + goto error_power; + } /* Initialize works */ INIT_WORK(&cd->enum_work, pt_enum_work_function); @@ -19006,23 +16987,13 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, snprintf(cd->hw_version, HW_VERSION_LEN_MAX, "FFFF.FFFF.FF"); dev_set_drvdata(dev, cd); -#ifndef PT_PTSBC_SUPPORT /* PtSBC builds will call this function in pt_probe_complete() */ pt_add_core(dev); -#endif rc = sysfs_create_group(&dev->kobj, &early_attr_group); if (rc) - pt_debug(cd->dev, DL_WARN, "%s:create early attrs failed\n", + pt_debug(cd->dev, DL_ERROR, "%s:create early attrs failed\n", __func__); -#ifndef TTDL_KERNEL_SUBMISSION - rc = device_create_bin_file(dev, &bin_attr_pt_response); - if (rc) { - pt_debug(dev, DL_ERROR, - "%s: Error, could not create node response\n", - __func__); - } -#endif /* !TTDL_KERNEL_SUBMISSION */ /* * Save the pointer to a global value, which will be used @@ -19037,74 +17008,15 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, */ if (!cd->cpdata->irq_stat) { cd->irq = irq; - pt_debug(cd->dev, DL_WARN, "%s:No irq_stat, Set cd->irq = %d\n", + pt_debug(cd->dev, DL_ERROR, "%s:No irq_stat, Set cd->irq = %d\n", __func__, cd->irq); } - -#ifdef PT_PTSBC_SUPPORT - /* - * For the PtSBC, on the first bring up, I2C/SPI will not be ready - * in time so complete probe with pt_probe_complete() after work - * probe timer expires. - */ - INIT_WORK(&cd->probe_work, pt_probe_work); -#if (KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE) - setup_timer(&cd->probe_timer, pt_probe_timer, (unsigned long)cd); -#else - timer_setup(&cd->probe_timer, pt_probe_timer, 0); -#endif - - /* Some host i2c/spi busses start late and then run too slow */ - pt_debug(cd->dev, DL_INFO, "%s:start wait for probe timer\n", - __func__); - mod_timer(&cd->probe_timer, jiffies + - msecs_to_jiffies(PT_CORE_PROBE_STARTUP_DELAY_MS)); - return rc; - -error_alloc_data: -error_no_pdata: - pt_debug(dev, DL_ERROR, "%s failed.\n", __func__); - return rc; -} - -/******************************************************************************* - * FUNCTION: pt_probe_complete - * - * SUMMARY: This function is only needed when PT_PTSBC_SUPPORT is enabled. - * For the PtSBC, the probe functionality is split into two functions; - * pt_probe() and pt_probe_complete(). The initial setup is done - * in pt_probe() and the rest is done here after I2C/SPI is up. This - * function also configures all voltage regulators for the PtSBC. - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *cd - pointer to the core data structure - ******************************************************************************/ -static int pt_probe_complete(struct pt_core_data *cd) -{ - int rc = -1; - u32 status = STARTUP_STATUS_START; - struct device *dev = cd->dev; - u8 pip_ver_major; - u8 pip_ver_minor; -#ifdef TTDL_PTVIRTDUT_SUPPORT - int retry = 3; -#endif - - pt_debug(cd->dev, DL_DEBUG, - "%s: PARADE Entering Probe complete function\n", __func__); - pt_add_core(cd->dev); -#endif /* --- End PT_PTSBC_SUPPORT --- */ - /* Call platform init function before setting up the GPIO's */ if (cd->cpdata->init) { pt_debug(cd->dev, DL_INFO, "%s: Init HW\n", __func__); rc = cd->cpdata->init(cd->cpdata, PT_MT_POWER_ON, cd->dev); } else { - pt_debug(cd->dev, DL_WARN, "%s: No HW INIT function\n", + pt_debug(cd->dev, DL_ERROR, "%s: No HW INIT function\n", __func__); rc = 0; } @@ -19112,14 +17024,13 @@ static int pt_probe_complete(struct pt_core_data *cd) pt_debug(cd->dev, DL_ERROR, "%s: HW Init fail r=%d\n", __func__, rc); } - /* Power on any needed regulator(s) */ if (cd->cpdata->setup_power) { pt_debug(cd->dev, DL_INFO, "%s: Device power on!\n", __func__); rc = cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_ON, cd->dev); } else { - pt_debug(cd->dev, DL_WARN, "%s: No setup power function\n", + pt_debug(cd->dev, DL_ERROR, "%s: No setup power function\n", __func__); rc = 0; } @@ -19131,18 +17042,6 @@ static int pt_probe_complete(struct pt_core_data *cd) cd->watchdog_irq_stuck_count = 0; cd->bus_transmit_error_count = 0; #endif /* TTDL_DIAGNOSTICS */ - -#ifdef TTDL_PTVIRTDUT_SUPPORT - /* - * In the case that the variable hw_detect_enabled needs to be set to - * false as the default value, it needs to be set to true through - * drv_debug sysfs node when testing HW detect function. But the probe() - * function runs fast, and drv_debug may not set this variable to - * true in time. This "retry" is to avoid this issue. - */ -retry_hw_detect: - if (cd->hw_detect_enabled) { -#endif if (cd->cpdata->detect) { pt_debug(cd->dev, DL_INFO, "%s: Detect HW\n", __func__); rc = cd->cpdata->detect(cd->cpdata, cd->dev, @@ -19159,7 +17058,7 @@ retry_hw_detect: goto error_detect; } } else { - pt_debug(dev, DL_WARN, + pt_debug(dev, DL_ERROR, "%s: PARADE No HW detect function pointer\n", __func__); /* @@ -19173,14 +17072,6 @@ retry_hw_detect: "%s: FAILED to execute HARD reset\n", __func__); } -#ifdef TTDL_PTVIRTDUT_SUPPORT - } else { - if (retry--) { - msleep(50); - goto retry_hw_detect; - } - } -#endif if (cd->cpdata->setup_irq) { pt_debug(cd->dev, DL_INFO, "%s: setup IRQ\n", __func__); @@ -19204,7 +17095,6 @@ retry_hw_detect: timer_setup(&cd->watchdog_timer, pt_watchdog_timer, 0); #endif pt_stop_wd_timer(cd); - #ifdef TTHE_TUNER_SUPPORT mutex_init(&cd->tthe_lock); cd->tthe_debugfs = debugfs_create_file(PT_TTHE_TUNER_FILE_NAME, @@ -19218,7 +17108,6 @@ retry_hw_detect: pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); - /* If IRQ asserted, read out all from buffer to release INT pin */ if (cd->cpdata->irq_stat) { pt_flush_bus_if_irq_asserted(cd, PT_FLUSH_BUS_BASED_ON_LEN); @@ -19228,10 +17117,8 @@ retry_hw_detect: if (!rc) pt_parse_input(cd); } - /* Without sleep DUT is not ready and will NAK the first write */ msleep(150); - /* Attempt to set the DUT generation if not yet set */ if (cd->active_dut_generation == DUT_UNKNOWN) { if (cd->bl_pip_ver_ready || @@ -19252,18 +17139,17 @@ retry_hw_detect: } } } - _pt_request_active_pip_protocol(cd->dev, PT_CORE_CMD_PROTECTED, &pip_ver_major, &pip_ver_minor); if (pip_ver_major == 2) { cd->bl_pip_ver_ready = true; - pt_debug(dev, DL_WARN, + pt_debug(dev, DL_ERROR, " === PIP2.%d Detected, Attempt to launch APP ===\n", pip_ver_minor); cd->hw_detected = true; } else if (pip_ver_major == 1) { cd->app_pip_ver_ready = true; - pt_debug(dev, DL_WARN, + pt_debug(dev, DL_ERROR, " === PIP1.%d Detected ===\n", pip_ver_minor); cd->hw_detected = true; } else { @@ -19277,23 +17163,18 @@ retry_hw_detect: if (cd->active_dut_generation != DUT_PIP1_ONLY) goto skip_enum; } - rc = pt_enum_with_dut(cd, false, &status); - pt_debug(dev, DL_WARN, "%s: cd->startup_status=0x%04X status=0x%04X\n", + pt_debug(dev, DL_ERROR, "%s: cd->startup_status=0x%04X status=0x%04X\n", __func__, cd->startup_status, status); if (rc == -ENODEV) { pt_debug(cd->dev, DL_ERROR, "%s: Enumeration Failed r=%d\n", __func__, rc); -#ifndef PT_PTSBC_SUPPORT /* For PtSBC don't error out, allow TTDL to stay up */ goto error_after_startup; -#endif } - /* Suspend scanning until probe is complete to avoid asyc touches */ pt_pip_suspend_scanning_(cd); -#ifndef TTDL_KERNEL_SUBMISSION if (cd->hw_detected) { pt_debug(dev, DL_INFO, "%s: Add sysfs interfaces\n", __func__); @@ -19304,12 +17185,10 @@ retry_hw_detect: goto error_after_startup; } } else { - pt_debug(dev, DL_WARN, + pt_debug(dev, DL_ERROR, "%s: No HW detected, sysfs interfaces not added\n", __func__); } -#endif /* !TTDL_KERNEL_SUBMISSION */ - skip_enum: pm_runtime_put_sync(dev); @@ -19320,14 +17199,12 @@ skip_enum: __func__); goto error_after_sysfs_create; } - rc = pt_btn_probe(dev); if (rc < 0) { pt_debug(dev, DL_ERROR, "%s: Error, fail btn probe\n", __func__); goto error_after_startup_mt; } - pt_probe_modules(cd); #ifdef CONFIG_HAS_EARLYSUSPEND @@ -19341,13 +17218,12 @@ skip_enum: register_pm_notifier(&cd->pm_notifier); #endif pt_pip_resume_scanning_(cd); - mutex_lock(&cd->system_lock); cd->startup_status |= status; cd->core_probe_complete = 1; mutex_unlock(&cd->system_lock); - pt_debug(dev, DL_WARN, "%s: TTDL Core Probe Completed Successfully\n", + pt_debug(dev, DL_ERROR, "%s: TTDL Core Probe Completed Successfully\n", __func__); return 0; @@ -19365,9 +17241,7 @@ error_after_sysfs_create: cancel_work_sync(&cd->enum_work); pt_stop_wd_timer(cd); pt_free_si_ptrs(cd); -#ifndef TTDL_KERNEL_SUBMISSION remove_sysfs_interfaces(dev); -#endif /* !TTDL_KERNEL_SUBMISSION */ error_after_startup: pr_err("%s PARADE error_after_startup\n", __func__); @@ -19376,9 +17250,6 @@ error_after_startup: cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_FREE, dev); error_setup_irq: error_detect: -#ifdef PT_PTSBC_SUPPORT - del_timer(&cd->probe_timer); -#endif if (cd->cpdata->init) cd->cpdata->init(cd->cpdata, PT_MT_POWER_OFF, dev); if (cd->cpdata->setup_power) @@ -19386,11 +17257,11 @@ error_detect: sysfs_remove_group(&dev->kobj, &early_attr_group); pt_del_core(dev); dev_set_drvdata(dev, NULL); +error_power: + pt_power_init(cd, false); kfree(cd); -#ifndef PT_PTSBC_SUPPORT error_alloc_data: error_no_pdata: -#endif pr_err("%s failed.\n", __func__); return rc; } @@ -19434,16 +17305,10 @@ int pt_release(struct pt_core_data *cd) * all I2C/SPI communication. */ pt_stop_wd_timer(cd); -#ifdef PT_PTSBC_SUPPORT - cancel_work_sync(&cd->probe_work); -#endif call_atten_cb(cd, PT_ATTEN_CANCEL_LOADER, 0); cancel_work_sync(&cd->ttdl_restart_work); cancel_work_sync(&cd->enum_work); pt_stop_wd_timer(cd); -#ifdef PT_PTSBC_SUPPORT - cancel_work_sync(&cd->irq_work); -#endif pt_release_modules(cd); pt_proximity_release(dev); @@ -19475,9 +17340,7 @@ int pt_release(struct pt_core_data *cd) sysfs_remove_group(&dev->kobj, &early_attr_group); -#ifndef TTDL_KERNEL_SUBMISSION remove_sysfs_interfaces(dev); -#endif /* !TTDL_KERNEL_SUBMISSION */ disable_irq_nosync(cd->irq); if (cd->cpdata->setup_irq) @@ -19489,7 +17352,6 @@ int pt_release(struct pt_core_data *cd) dev_set_drvdata(dev, NULL); pt_del_core(dev); pt_free_si_ptrs(cd); - pt_free_hid_reports(cd); kfree(cd); return 0; } diff --git a/pt/pt_device_access.c b/pt/pt_device_access.c index af9e62b56a..80da0c98d8 100644 --- a/pt/pt_device_access.c +++ b/pt/pt_device_access.c @@ -1,4 +1,3 @@ -#ifndef TTDL_KERNEL_SUBMISSION /* * pt_device_access.c * Parade TrueTouch(TM) Standard Product Device Access Module. @@ -40,7 +39,7 @@ #define CMCP_THRESHOLD_FILE_NAME "ttdl_cmcp_thresholdfile.csv" /* Max test case number */ -#define MAX_CASE_NUM (23) +#define MAX_CASE_NUM (22) /* ASCII */ #define ASCII_LF (0x0A) @@ -52,9 +51,6 @@ /* Max characters of test case name */ #define NAME_SIZE_MAX (50) -/* Max characters of project information */ -#define NAME_PROJECT_INFO_MAX (128) - /* Max sensor and button number */ #define MAX_BUTTONS (PIP1_SYSINFO_MAX_BTN) #define MAX_SENSORS (5120) @@ -85,8 +81,6 @@ enum print_buffer_format { /* cmcp csv file information */ struct configuration { - /* One more space in the arrary below is left for null character */ - char proj_info[NAME_PROJECT_INFO_MAX + 1]; u32 cm_range_limit_row; u32 cm_range_limit_col; u32 cm_min_limit_cal; @@ -146,12 +140,10 @@ enum test_case_type { TEST_CASE_TYPE_ONE, TEST_CASE_TYPE_MUL, TEST_CASE_TYPE_MUL_LINES, - TEST_CASE_TYPE_STRING, }; /* Test case order in test_case_field_array */ enum case_order { - PROJ_VERSION, CM_TEST_INPUTS, CM_EXCLUDING_COL_EDGE, CM_EXCLUDING_ROW_EDGE, @@ -245,7 +237,7 @@ struct pt_device_access_data { struct dentry *cmcp_results_debugfs; struct dentry *base_dentry; struct dentry *mfg_test_dentry; - u8 ic_buf[10 * PT_MAX_PRBUF_SIZE]; + u8 ic_buf[PT_MAX_PRBUF_SIZE]; u8 response_buf[PT_MAX_PRBUF_SIZE]; struct mutex cmcp_threshold_lock; u8 *cmcp_threshold_data; @@ -353,7 +345,7 @@ static struct pt_module device_access_module; static ssize_t pt_run_and_get_selftest_result(struct device *dev, int protect, char *buf, size_t buf_len, u8 test_id, - u16 read_length, u8 get_result_on_pass, + u16 read_length, bool get_result_on_pass, bool print_results, u8 print_format); static int _pt_calibrate_idacs_cmd(struct device *dev, @@ -1820,36 +1812,36 @@ start_testing: if (result->test_summary) { pt_debug(dev, DL_INFO, "%s: Finish Cm/Cp test! All Test Passed\n", __func__); - index = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 1\n"); + index = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 1\n"); } else { pt_debug(dev, DL_INFO, "%s: Finish Cm/Cp test! Range Check Failure\n", __func__); - index = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 6\n"); + index = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 6\n"); } goto cmcp_ready; mismatch: - index = snprintf(buf, PT_MAX_PRBUF_SIZE, + index = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 2\nInput cmcp threshold file mismatches with FW\n"); goto cmcp_ready; invalid_item_btn: - index = snprintf(buf, PT_MAX_PRBUF_SIZE, + index = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 3\nFW doesn't support button!\n"); goto cmcp_ready; invalid_item: - index = snprintf(buf, PT_MAX_PRBUF_SIZE, + index = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 4\nWrong test item or range check input!\nOnly support below items:\n0 - Cm/Cp Panel & Button with Gradient (Typical)\n1 - Cm Panel with Gradient\n2 - Cp Panel\n3 - Cm Button\n4 - Cp Button\nOnly support below range check:\n0 - Full Range Checking (default)\n1 - Basic Range Checking(TSG5 style)\n"); goto cmcp_ready; self_test_failed: - index = snprintf(buf, PT_MAX_PRBUF_SIZE, + index = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 5\nget self test ID not supported!\n"); goto cmcp_ready; cmcp_not_ready: - index = snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 0\n"); + index = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 0\n"); goto cmcp_ready; no_builtin: - index = snprintf(buf, PT_MAX_PRBUF_SIZE, + index = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: 7\nNo cmcp threshold file!\n"); cmcp_ready: mutex_lock(&dad->sysfs_lock); @@ -2030,17 +2022,16 @@ int prepare_print_data(char *out_buf, int32_t *in_buf, int index, int data_num) * index - index in output buffer for appending content * *result - pointer to result structure ******************************************************************************/ -int save_header(char *out_buf, int index, struct configuration *config, - struct result *result) +static int save_header(char *out_buf, int index, struct result *result) { struct rtc_time tm; char time_buf[100] = {0}; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)) - struct timespec64 ts; + struct timespec ts; - ktime_get_real_ts64(&ts); - rtc_time64_to_tm(ts.tv_sec, &tm); + getnstimeofday(&ts); + rtc_time_to_tm(ts.tv_sec, &tm); #else struct timex txc; @@ -2058,12 +2049,6 @@ int save_header(char *out_buf, int index, struct configuration *config, index = prepare_print_string(out_buf, ",SW_VERSION,", index); index = prepare_print_string(out_buf, PT_DRIVER_VERSION, index); index = prepare_print_string(out_buf, ",\n", index); - if (config) { - index = prepare_print_string(out_buf, ",PROJ_VERSION,", index); - index = prepare_print_string(out_buf, config->proj_info, index); - index = prepare_print_string(out_buf, ",\n", index); - } - index = prepare_print_string(out_buf, ",.end,\n", index); index = prepare_print_string(out_buf, ",.engineering data,\n", index); @@ -2972,7 +2957,7 @@ int result_save(struct device *dev, char *buf, if (cmcp_info == NULL) pt_debug(dev, DL_WARN, "cmcp_info is NULL"); - index = save_header(out_buf, index, configuration, result); + index = save_header(out_buf, index, result); index = save_engineering_data(dev, out_buf, index, cmcp_info, configuration, result, test_item, no_builtin_file); @@ -3318,65 +3303,6 @@ exit: return value; } -/******************************************************************************* - * FUNCTION: cmcp_get_one_string - * - * SUMMARY: Parses csv file at a given row and offset and find out one string - * which must start with a comma and should end with a comma. The character - * LF/CR will also be taken as the symbol to end the search. - * - * NOTE: There is a static value to calculate line count inside this function. - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *dev - pointer to devices structure - * *input_buf - pointer to input buffer - * *offset - offset index of input buffer - * *out_buf - pointer to store string - * out_buf_len - size of out_buf - * pFileEnd - pointer to the end of threshold file - ******************************************************************************/ -static int cmcp_get_one_string(struct device *dev, const char *input_buf, - u32 *offset, char *out_buf, u32 out_buf_len, - const char *pFileEnd) -{ - u8 token = ASCII_COMMA; - u32 i; - u32 temp_offset = *offset + 1; /* skip first comma */ - u32 pre_index; /* to store the start index of string */ - u32 next_index; /* to store the index of comma after string */ - u32 total_len = pFileEnd - input_buf; /* valid range of index */ - - memset(out_buf, 0, out_buf_len); - - pre_index = next_index = temp_offset; - for (i = temp_offset; i < total_len; i++) { - if (input_buf[i] == token || input_buf[i] == ASCII_LF || - input_buf[i] == ASCII_CR) { - next_index = i; - /* - * next_index could be equal to pre_index if string is - * null. The max size of string is restricted by macro - * NAME_PROJECT_INFO_MAX. - */ - if (pre_index < next_index) - memcpy(out_buf, input_buf + pre_index, - MIN(next_index - pre_index, - NAME_PROJECT_INFO_MAX)); - *offset = next_index; - return 0; - } - - if (input_buf[i] == ASCII_LF || input_buf[i] == ASCII_CR) - break; - } - - return -ENODATA; -} - /******************************************************************************* * FUNCTION: cmcp_get_configuration_info * @@ -3444,16 +3370,6 @@ void cmcp_get_configuration_info(struct device *dev, field_array[count].data_num = 1; field_array[count].line_num = 1; break; - case TEST_CASE_TYPE_STRING: - value_offset = search_array[sub_count].offset - + search_array[sub_count].name_size; - cmcp_get_one_string( - dev, buf, &value_offset, - (char *)field_array[count].bufptr, - NAME_PROJECT_INFO_MAX, pFileEnd); - field_array[count].data_num = 1; - field_array[count].line_num = 1; - break; case TEST_CASE_TYPE_MUL: case TEST_CASE_TYPE_MUL_LINES: line_num = 1; @@ -3658,8 +3574,6 @@ void cmcp_test_case_field_init(struct test_case_field *test_field_array, struct configuration *configs) { struct test_case_field test_case_field_array[MAX_CASE_NUM] = { - {"PROJ_VERSION", 12, TEST_CASE_TYPE_STRING, - (void *)&configs->proj_info, 0, 0, 0}, {"CM TEST INPUTS", 14, TEST_CASE_TYPE_NO, NULL, 0, 0, 0}, {"CM_EXCLUDING_COL_EDGE", 21, TEST_CASE_TYPE_ONE, @@ -4350,7 +4264,7 @@ exit: ******************************************************************************/ static ssize_t pt_run_and_get_selftest_result(struct device *dev, int protect, char *buf, size_t buf_len, u8 test_id, - u16 read_length, u8 get_result_on_pass, bool print_results, + u16 read_length, bool get_result_on_pass, bool print_results, u8 print_format) { struct pt_device_access_data *dad = pt_get_device_access_data(dev); @@ -4442,23 +4356,13 @@ static ssize_t pt_run_and_get_selftest_result(struct device *dev, length = 2; - /* - * For "auto_shorts" sysfs node, detailed data is - * retrieved only when the summary result is fail. - */ - if (get_result_on_pass == PT_ST_GET_RESULTS_BASED_ON_DATA && - test_id == PT_ST_ID_AUTOSHORTS && - summary_result == PT_ST_RESULT_PASS) - goto resume_scan; - /* * Get data if requested and the cmd status indicates that the test * completed with either a pass or a fail. All other status codes * indicate the test itself was not run so there is no data to retrieve */ if ((cmd_status == PT_ST_RESULT_PASS || - cmd_status == PT_ST_RESULT_FAIL) && - get_result_on_pass != PT_ST_DONT_GET_RESULTS) { + cmd_status == PT_ST_RESULT_FAIL) && get_result_on_pass) { rc = pt_get_selftest_result_cmd_(dev, 0, read_length, test_id, &cmd_status, &act_length, &dad->ic_buf[6]); if (rc) { @@ -5538,7 +5442,7 @@ static ssize_t auto_shorts_debugfs_read(struct file *filp, char __user *buf, data->dad->dev, PT_CORE_CMD_PROTECTED, data->pr_buf, sizeof(data->pr_buf), PT_ST_ID_AUTOSHORTS, PIP_CMD_MAX_LENGTH, - PT_ST_GET_RESULTS_BASED_ON_DATA, PT_ST_PRINT_RESULTS, + PT_ST_DONT_GET_RESULTS, PT_ST_PRINT_RESULTS, PT_PR_FORMAT_DEFAULT); return simple_read_from_buffer(buf, count, ppos, data->pr_buf, @@ -5832,7 +5736,7 @@ exit: /******************************************************************************* * FUNCTION: fw_self_test_debugfs_write * - * SUMMARY: Store the self test ID and output format, the read method will + * SUMMARY: Store the self test ID and ouptut format, the read method will * perform. * * RETURN: Size of debugfs data write @@ -5968,7 +5872,7 @@ static ssize_t tthe_get_panel_data_debugfs_read(struct file *filp, rc = pt_ret_scan_data_cmd_(dev, elem_offset, PT_MAX_ELEN, dad->heatmap.data_type, dad->ic_buf, &config, &actual_read_len, NULL); - } else{ + } else { rc = pt_ret_scan_data_cmd_(dev, elem_offset, elem, dad->heatmap.data_type, dad->ic_buf, &config, &actual_read_len, NULL); @@ -5994,7 +5898,7 @@ static ssize_t tthe_get_panel_data_debugfs_read(struct file *filp, rc = pt_ret_scan_data_cmd_(dev, elem_offset, PT_MAX_ELEN, dad->heatmap.data_type, NULL, &config, &actual_read_len, buf_offset); - } else{ + } else { rc = pt_ret_scan_data_cmd_(dev, elem_offset, elem, dad->heatmap.data_type, NULL, &config, &actual_read_len, buf_offset); @@ -6355,7 +6259,7 @@ static int pt_setup_sysfs(struct device *dev) rc = device_create_file(dev, &dev_attr_cmcp_threshold_loading); if (rc) { pt_debug(dev, DL_ERROR, - "%s: Error, could not create cmcp_thresold_loading\n", + "%s: Error, could not create cmcp_threshold_loading\n", __func__); goto unregister_cmcp_test; } @@ -6363,7 +6267,7 @@ static int pt_setup_sysfs(struct device *dev) rc = device_create_bin_file(dev, &bin_attr_cmcp_threshold_data); if (rc) { pt_debug(dev, DL_ERROR, - "%s: Error, could not create cmcp_thresold_data\n", + "%s: Error, could not create cmcp_threshold_data\n", __func__); goto unregister_cmcp_thresold_loading; } @@ -6827,4 +6731,3 @@ module_exit(pt_device_access_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Device Access Driver"); MODULE_AUTHOR("Parade Technologies "); -#endif /* !TTDL_KERNEL_SUBMISSION */ diff --git a/pt/pt_devtree.c b/pt/pt_devtree.c index b147a7d0d5..3fb3d2f561 100644 --- a/pt/pt_devtree.c +++ b/pt/pt_devtree.c @@ -31,8 +31,8 @@ #include #include #include -#include "pt_regs.h" #include +#include "pt_regs.h" #define MAX_NAME_LENGTH 64 @@ -41,7 +41,6 @@ static bool is_create_and_get_pdata; enum pt_device_type { DEVICE_MT, DEVICE_BTN, - DEVICE_PEN, DEVICE_PROXIMITY, DEVICE_TYPE_MAX, }; @@ -485,58 +484,6 @@ static void free_btn_pdata(void *pdata) kfree(btn_pdata); } -/******************************************************************************* - * FUNCTION: create_and_get_pen_pdata - * - * SUMMARY: Create and get pen platform data from dts. - * - * RETURN: - * success: the pointer of the platform data - * fail : error code with type of error pointer - * - * PARAMETERS: - * *dev_node - pointer to device_node structure - ******************************************************************************/ -static void *create_and_get_pen_pdata(struct device_node *dev_node) -{ - struct pt_pen_platform_data *pdata; - int rc; - - pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - rc = -ENOMEM; - goto fail; - } - - rc = get_inp_dev_name(dev_node, &pdata->inp_dev_name); - if (rc) - goto fail_free_pdata; - - return pdata; - -fail_free_pdata: - kfree(pdata); -fail: - return ERR_PTR(rc); -} - -/******************************************************************************* - * FUNCTION: free_pen_pdata - * - * SUMMARY: Free all pointers for pen platform data. - * - * PARAMETERS: - * *pdata - pointer to virtual key structure - ******************************************************************************/ -static void free_pen_pdata(void *pdata) -{ - struct pt_pen_platform_data *pen_pdata = - (struct pt_pen_platform_data *)pdata; - - kfree(pen_pdata); -} - - /******************************************************************************* * FUNCTION: create_and_get_proximity_pdata * @@ -608,10 +555,6 @@ static struct pt_device_pdata_func device_pdata_funcs[DEVICE_TYPE_MAX] = { .create_and_get_pdata = create_and_get_btn_pdata, .free_pdata = free_btn_pdata, }, - [DEVICE_PEN] = { - .create_and_get_pdata = create_and_get_pen_pdata, - .free_pdata = free_pen_pdata, - }, [DEVICE_PROXIMITY] = { .create_and_get_pdata = create_and_get_proximity_pdata, .free_pdata = free_proximity_pdata, @@ -623,7 +566,6 @@ static struct pt_pdata_ptr pdata_ptr[DEVICE_TYPE_MAX]; static const char *device_names[DEVICE_TYPE_MAX] = { [DEVICE_MT] = "parade,mt", [DEVICE_BTN] = "parade,btn", - [DEVICE_PEN] = "parade,pen", [DEVICE_PROXIMITY] = "parade,proximity", }; @@ -639,7 +581,6 @@ static void set_pdata_ptr(struct pt_platform_data *pdata) { pdata_ptr[DEVICE_MT].pdata = (void **)&pdata->mt_pdata; pdata_ptr[DEVICE_BTN].pdata = (void **)&pdata->btn_pdata; - pdata_ptr[DEVICE_PEN].pdata = (void **)&pdata->pen_pdata; pdata_ptr[DEVICE_PROXIMITY].pdata = (void **)&pdata->prox_pdata; } @@ -832,9 +773,7 @@ static struct pt_core_platform_data *create_and_get_core_pdata( } /* Required fields */ - rc = of_property_read_u32(core_node, "parade,irq_gpio", &value); - if (rc) - goto fail_free; + value = of_get_named_gpio_flags(core_node, "parade,irq_gpio", 0, &pdata->irq_gpio_flags); pdata->irq_gpio = value; rc = of_property_read_u32(core_node, "parade,hid_desc_register", @@ -847,9 +786,8 @@ static struct pt_core_platform_data *create_and_get_core_pdata( /* rst_gpio is optional since a platform may use * power cycling instead of using the XRES pin */ - rc = of_property_read_u32(core_node, "parade,rst_gpio", &value); - if (!rc) - pdata->rst_gpio = value; + value = of_get_named_gpio_flags(core_node, "parade,rst_gpio", 0, &pdata->rst_gpio_flags); + pdata->rst_gpio = value; rc = of_property_read_u32(core_node, "parade,ddi_rst_gpio", &value); if (!rc) diff --git a/pt/pt_i2c.c b/pt/pt_i2c.c index 111834d92d..5ce74720d9 100644 --- a/pt/pt_i2c.c +++ b/pt/pt_i2c.c @@ -33,188 +33,7 @@ #include #define PT_I2C_DATA_SIZE (2 * 256) - -#ifdef TTDL_PTVIRTDUT_SUPPORT -#define VIRT_DUT_BUF_SIZE 2048 -static unsigned char pt_dut_cmd_buf[VIRT_DUT_BUF_SIZE]; -static unsigned char pt_dut_out_buf[VIRT_DUT_BUF_SIZE]; -static int pt_dut_cmd_len; -static int pt_dut_out_len; -DEFINE_MUTEX(virt_i2c_lock); - -/******************************************************************************* - * FUNCTION: virt_i2c_transfer - * - * SUMMARY: Copies the current i2c output message to the temporary buffer - * used by the dut_cmd sysfs node - * - * RETURN VALUE: - * Number of messages transferred which in this function will be 1 - * - * PARAMETERS: - * *buf - pointer to i2c command - * len - length of command in the buffer - ******************************************************************************/ -static int virt_i2c_transfer(u8 *buf, int len) -{ - int rc = 0; - - mutex_lock(&virt_i2c_lock); - if (len <= sizeof(pt_dut_cmd_buf)) { - memcpy(pt_dut_cmd_buf, buf, len); - pt_dut_cmd_len = len; - rc = 1; - } else - rc = 0; - mutex_unlock(&virt_i2c_lock); - - return rc; -} - -/******************************************************************************* - * FUNCTION: virt_i2c_master_recv - * - * SUMMARY: Copies the i2c input message from the dut_out sysfs node into a - * temporary buffer. - * - * RETURN VALUE: - * Length of data transferred - * - * PARAMETERS: - * *dev - pointer to device struct - * *buf - pointer to i2c incoming report - * size - size to be read - ******************************************************************************/ -static int virt_i2c_master_recv(struct device *dev, u8 *buf, int size) -{ -#ifndef PT_POLL_RESP_BY_BUS - struct pt_core_data *cd = dev_get_drvdata(dev); - int i = 0; -#endif - - mutex_lock(&virt_i2c_lock); - memcpy(buf, pt_dut_out_buf, size); - - /* Set "empty buffer" */ - pt_dut_out_buf[1] = 0xFF; - pt_dut_out_len = 0; - mutex_unlock(&virt_i2c_lock); - -#ifndef PT_POLL_RESP_BY_BUS - if (cd->bl_with_no_int) { - /* - * Wait for IRQ gpio to be released, make read operation - * synchronize with PtVirtDut tool. - * Safety net: Exit after 500ms (50us * 10000 loops = 500ms) - */ - while (i < VIRT_MAX_IRQ_RELEASE_TIME_US && - !gpio_get_value(cd->cpdata->irq_gpio)) { - pt_debug(dev, DL_INFO, "%s: %d IRQ still Enabled\n", - __func__, i); - usleep_range(50, 60); - i += 50; - } - } -#endif - - pt_debug(dev, DL_INFO, - "%s: Copy msg from dut_out to i2c buffer, size=%d\n", - __func__, size); - - return size; -} - -/******************************************************************************* - * FUNCTION: pt_dut_cmd_show - * - * SUMMARY: The show function for the dut_cmd sysfs node. Provides read access - * to the pt_dut_cmd_buf and clears it after it has been read. - * - * RETURN VALUE: - * Number of bytes transferred - * - * PARAMETERS: - * *dev - pointer to device structure - * *attr - pointer to device attributes - * *buf - pointer to output buffer - ******************************************************************************/ -static ssize_t pt_dut_cmd_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int i; - int index = 0; - - /* Only print to sysfs if the buffer has data */ - mutex_lock(&virt_i2c_lock); - if (pt_dut_cmd_len > 0) { - for (i = 0; i < pt_dut_cmd_len; i++) - index += scnprintf(buf + index, strlen(buf), "%02X", - pt_dut_cmd_buf[i]); - index += scnprintf(buf + index, strlen(buf), "\n"); - } - pt_dut_cmd_len = 0; - mutex_unlock(&virt_i2c_lock); - return index; -} -static DEVICE_ATTR(dut_cmd, 0444, pt_dut_cmd_show, NULL); - -/******************************************************************************* - * FUNCTION: pt_dut_out_store - * - * SUMMARY: The store function for the dut_out sysfs node. Provides write - * access to the pt_dut_out_buf. The smallest valid PIP response is 2 - * bytes so don't update buffer if only 1 byte passed in. - * - * RETURN VALUE: - * Number of bytes read from virtual DUT - * - * PARAMETERS: - * *dev - pointer to device structure - * *attr - pointer to device attributes - * *buf - pointer to buffer that hold the command parameters - * size - size of buf - ******************************************************************************/ -static ssize_t pt_dut_out_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) -{ - int loop_max = ARRAY_SIZE(pt_dut_out_buf); - int hex_str_len = strlen(buf)/2; - int i; - const char *pos = buf; - struct pt_core_data *cd = dev_get_drvdata(dev); - - /* Clear out the last message */ - mutex_lock(&virt_i2c_lock); - memset(pt_dut_out_buf, 0, VIRT_DUT_BUF_SIZE); - pt_dut_out_len = 0; - - /* Only update the dut_out buffer if at least 2 byte payload */ - if (size >= 2 && hex_str_len <= loop_max) { - /* Convert string of hex values to byte array */ - for (i = 0; i < hex_str_len; i++) { - pt_dut_out_buf[i] = ((HEXOF(*pos)) << 4) + - HEXOF(*(pos + 1)); - pos += 2; - } - /* - * In HID we send the full string, non HID we send based - * on the 2 byte length. - */ - if (cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) - pt_dut_out_len = hex_str_len; - else - pt_dut_out_len = get_unaligned_le16(&pt_dut_out_buf[0]); - } else if (size >= PT_PIP_1P7_EMPTY_BUF) { - /* Message too large, set to 'empty buffer' message */ - pt_dut_out_buf[1] = 0xFF; - pt_dut_out_len = 0; - } - mutex_unlock(&virt_i2c_lock); - - return size; -} -static DEVICE_ATTR(dut_out, 0200, NULL, pt_dut_out_store); -#endif /* TTDL_PTVIRTDUT_SUPPORT */ +#define PT_I2C_NAME "pt_i2c_adapter" /******************************************************************************* * FUNCTION: pt_i2c_read_default @@ -228,24 +47,14 @@ static DEVICE_ATTR(dut_out, 0200, NULL, pt_dut_out_store); ******************************************************************************/ static int pt_i2c_read_default(struct device *dev, void *buf, int size) { -#ifdef TTDL_PTVIRTDUT_SUPPORT - struct pt_core_data *cd = dev_get_drvdata(dev); -#endif /* TTDL_PTVIRTDUT_SUPPORT */ struct i2c_client *client = to_i2c_client(dev); int rc; int read_size = size; - if (!buf || !size || size > VIRT_DUT_BUF_SIZE) + if (!buf || !size || size > PT_I2C_DATA_SIZE) return -EINVAL; -#ifdef TTDL_PTVIRTDUT_SUPPORT - if (cd->route_bus_virt_dut) - rc = virt_i2c_master_recv(dev, buf, read_size); - else - rc = i2c_master_recv(client, buf, read_size); -#else rc = i2c_master_recv(client, buf, read_size); -#endif /* TTDL_PTVIRTDUT_SUPPORT */ return (rc < 0) ? rc : rc != read_size ? -EIO : 0; } @@ -270,22 +79,6 @@ static int pt_i2c_read_default_nosize(struct device *dev, u8 *buf, u32 max) u8 msg_count = 1; int rc; u32 size; -#ifdef TTDL_PTVIRTDUT_SUPPORT - struct pt_core_data *cd = dev_get_drvdata(dev); - - if (cd->route_bus_virt_dut) { - mutex_lock(&virt_i2c_lock); - size = pt_dut_out_len; - mutex_unlock(&virt_i2c_lock); - pt_debug(dev, DL_INFO, "%s: pt_dut_out_len=%d\n", - __func__, pt_dut_out_len); - /* Only copy 2 bytes for "empty buffer" or "FW sentinel" */ - if (!size || size == 2 || size >= PT_PIP_1P7_EMPTY_BUF) - size = 2; - goto skip_read_len; - } - -#endif /* TTDL_PTVIRTDUT_SUPPORT */ if (!buf) return -EINVAL; @@ -309,17 +102,7 @@ static int pt_i2c_read_default_nosize(struct device *dev, u8 *buf, u32 max) if (size > max) return -EINVAL; -#ifdef TTDL_PTVIRTDUT_SUPPORT -skip_read_len: - if (cd->route_bus_virt_dut) - rc = virt_i2c_master_recv(dev, buf, size); - else - rc = i2c_master_recv(client, buf, size); - - pt_debug(dev, DL_INFO, "%s: rc = %d\n", __func__, rc); -#else rc = i2c_master_recv(client, buf, size); -#endif /* TTDL_PTVIRTDUT_SUPPORT */ return (rc < 0) ? rc : rc != (int)size ? -EIO : 0; } @@ -327,22 +110,18 @@ skip_read_len: * FUNCTION: pt_i2c_write_read_specific * * SUMMARY: Write the contents of write_buf to the I2C device and then read - * the response using pt_i2c_read_default_nosize() + * the response using pt_i2c_read_default_nosize() * * PARAMETERS: - * *dev - pointer to Device structure - * write_len - length of data buffer write_buf - * *write_buf - pointer to buffer to write - * *read_buf - pointer to buffer to read response into - * read_len - length to read, 0 to use pt_i2c_read_default_nosize + * *dev - pointer to Device structure + * write_len - length of data buffer write_buf + * *write_buf - pointer to buffer to write + * *read_buf - pointer to buffer to read response into ******************************************************************************/ static int pt_i2c_write_read_specific(struct device *dev, u16 write_len, - u8 *write_buf, u8 *read_buf, u16 read_len) + u8 *write_buf, u8 *read_buf) { struct i2c_client *client = to_i2c_client(dev); -#ifdef TTDL_PTVIRTDUT_SUPPORT - struct pt_core_data *cd = dev_get_drvdata(dev); -#endif /* TTDL_PTVIRTDUT_SUPPORT */ struct i2c_msg msgs[2]; u8 msg_count = 1; int rc; @@ -365,16 +144,7 @@ static int pt_i2c_write_read_specific(struct device *dev, u16 write_len, msgs[0].flags = client->flags & I2C_M_TEN; msgs[0].len = write_len; msgs[0].buf = write_buf; -#ifdef TTDL_PTVIRTDUT_SUPPORT - if (cd->route_bus_virt_dut) { - rc = virt_i2c_transfer(msgs[0].buf, msgs[0].len); - pt_debug(dev, DL_DEBUG, "%s: Virt transfer size = %d", - __func__, msgs[0].len); - } else - rc = i2c_transfer(client->adapter, msgs, msg_count); -#else rc = i2c_transfer(client->adapter, msgs, msg_count); -#endif /* TTDL_PTVIRTDUT_SUPPORT */ if (rc < 0 || rc != msg_count) return (rc < 0) ? rc : -EIO; @@ -382,17 +152,7 @@ static int pt_i2c_write_read_specific(struct device *dev, u16 write_len, rc = 0; if (read_buf) { -#ifdef TTDL_PTVIRTDUT_SUPPORT - if (cd->route_bus_virt_dut && - cd->dut_status.protocol_mode == PT_PROTOCOL_MODE_HID) { - /* Simulate clock stretch */ - usleep_range(3000, 4000); - } -#endif /* TTDL_PTVIRTDUT_SUPPORT */ - if (read_len > 0) - rc = pt_i2c_read_default(dev, read_buf, read_len); - else - rc = pt_i2c_read_default_nosize(dev, read_buf, + rc = pt_i2c_read_default_nosize(dev, read_buf, PT_I2C_DATA_SIZE); } @@ -432,12 +192,10 @@ static int pt_i2c_probe(struct i2c_client *client, const struct of_device_id *match; #endif int rc; - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { pt_debug(dev, DL_ERROR, "I2C functionality not Supported\n"); return -EIO; } - #ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT match = of_match_device(of_match_ptr(pt_i2c_of_match), dev); if (match) { @@ -447,17 +205,6 @@ static int pt_i2c_probe(struct i2c_client *client, } #endif -#ifdef TTDL_PTVIRTDUT_SUPPORT - /* - * When using the virtual DUT these files must be created before - * pt_probe is called. - */ - mutex_lock(&virt_i2c_lock); - device_create_file(dev, &dev_attr_dut_cmd); - device_create_file(dev, &dev_attr_dut_out); - mutex_unlock(&virt_i2c_lock); -#endif /* TTDL_PTVIRTDUT_SUPPORT */ - rc = pt_probe(&pt_i2c_bus_ops, &client->dev, client->irq, PT_I2C_DATA_SIZE); @@ -465,15 +212,6 @@ static int pt_i2c_probe(struct i2c_client *client, if (rc && match) pt_devtree_clean_pdata(dev); #endif -#ifdef TTDL_PTVIRTDUT_SUPPORT - if (rc) { - mutex_lock(&virt_i2c_lock); - device_remove_file(dev, &dev_attr_dut_cmd); - device_remove_file(dev, &dev_attr_dut_out); - mutex_unlock(&virt_i2c_lock); - } -#endif /* TTDL_PTVIRTDUT_SUPPORT */ - return rc; } @@ -494,12 +232,6 @@ static int pt_i2c_remove(struct i2c_client *client) struct pt_core_data *cd = i2c_get_clientdata(client); pt_release(cd); -#ifdef TTDL_PTVIRTDUT_SUPPORT - mutex_lock(&virt_i2c_lock); - device_remove_file(dev, &dev_attr_dut_cmd); - device_remove_file(dev, &dev_attr_dut_out); - mutex_unlock(&virt_i2c_lock); -#endif /* TTDL_PTVIRTDUT_SUPPORT */ #ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT match = of_match_device(of_match_ptr(pt_i2c_of_match), dev); diff --git a/pt/pt_loader.c b/pt/pt_loader.c index c57657e346..ca53aa904b 100644 --- a/pt/pt_loader.c +++ b/pt/pt_loader.c @@ -32,13 +32,6 @@ #define PT_LOADER_NAME "pt_loader" -#ifdef UPGRADE_FW_AND_CONFIG_IN_PROBE -/* Enable UPGRADE_FW_AND_CONFIG_IN_PROBE definition - * to perform FW and config upgrade during probe - * instead of scheduling a work for it - */ -/* #define UPGRADE_FW_AND_CONFIG_IN_PROBE */ -#endif #define PT_AUTO_LOAD_FOR_CORRUPTED_FW 1 #define PT_LOADER_FW_UPGRADE_RETRY_COUNT 3 @@ -133,16 +126,13 @@ struct pt_loader_data { #endif }; -static u8 write_file_status; -#ifndef TTDL_KERNEL_SUBMISSION +static u8 update_fw_status; static int pip2_erase_status; static int pip2_erase_rc; -#endif /* !TTDL_KERNEL_SUBMISSION */ /* PIP2 update fw status codes. 1-99% are "active" */ enum UPDATE_FW_STATUS { UPDATE_FW_IDLE = 0, - UPDATE_FW_REQUEST_EXCLUSIVE = 1, UPDATE_FW_ACTIVE_10 = 10, UPDATE_FW_ACTIVE_90 = 90, UPDATE_FW_ACTIVE_99 = 99, @@ -162,7 +152,6 @@ enum UPDATE_FW_STATUS { UPDATE_FW_EXCLUSIVE_ACCESS_ERROR = 212, UPDATE_FW_NO_FW_PROVIDED = 213, UPDATE_FW_INVALID_FW_IMAGE = 214, - UPDATE_FW_FILE_SEEK_ERROR = 215, UPDATE_FW_MISALIGN_FW_IMAGE = 230, UPDATE_FW_SYSTEM_NOMEM = 231, UPDATE_FW_INIT_BL_ERROR = 232, @@ -171,12 +160,7 @@ enum UPDATE_FW_STATUS { UPDATE_FW_EXIT_BL_ERROR = 235, UPDATE_FW_CHECK_SUM_ERROR = 236, UPDATE_FW_NO_PLATFORM_DATA = 237, - UPDATE_FW_NOT_SUPPORTED_FILE_NO = 238, UPDATE_FW_UNDEFINED_ERROR = 255, - UPDATE_FW_INCREMENT = 300, - UPDATE_FW_INCREMENT_BY_1 = 301, - UPDATE_FW_INCREMENT_BY_5 = 305, - UPDATE_FW_UNDEFINED_INC = 400, }; enum PIP2_FILE_ERASE_STATUS { @@ -380,7 +364,7 @@ static int pt_check_firmware_version(struct device *dev, if (fw_ver_new < fw_ver_img) { pt_debug(dev, DL_WARN, "%s: Image is older, will NOT upgrade\n", __func__); - return -1; + return -EPERM; } fw_revctrl_img = ld->si->ttdata.revctrl; @@ -398,7 +382,7 @@ static int pt_check_firmware_version(struct device *dev, if (fw_revctrl_new < fw_revctrl_img) { pt_debug(dev, DL_WARN, "%s: Image is older, will NOT upgrade\n", __func__); - return -1; + return -EPERM; } return 0; @@ -642,6 +626,7 @@ static int pt_ldr_exit_(struct device *dev) return cmd->nonhid_cmd->launch_app(dev, 0); } +#define PT_NO_INC 0 /******************************************************************************* * FUNCTION: _pt_pip2_update_bl_status * @@ -650,53 +635,43 @@ static int pt_ldr_exit_(struct device *dev) * * PARAMETERS: * *dev - pointer to device structure - * value - Value to force. + * value - Value to force, if 0 then ignore. + * inc - Value to increment, if 0 then ignore. ******************************************************************************/ -static u8 _pt_update_write_file_status(struct device *dev, u16 value) +static u8 _pt_pip2_update_bl_status(struct device *dev, u8 value, u8 inc) { - u8 inc = 0; - pt_debug(dev, DL_DEBUG, - "%s: fw_status = %d, request val=%d\n", - __func__, write_file_status, value); + "%s: fw_status = %d, request val=%d, request inc=%d\n", + __func__, update_fw_status, value, inc); - /* Reset status if value is 0 */ - if (value == UPDATE_FW_IDLE) { - write_file_status = value; + /* Reset status if both value and inc are 0 */ + if (value == UPDATE_FW_IDLE && inc == 0) { + update_fw_status = value; pt_debug(dev, DL_WARN, "%s: ATM - Reset BL Status to %d\n", __func__, value); - return write_file_status; + return update_fw_status; } /* Set to value if valid */ if (value > UPDATE_FW_IDLE && value < UPDATE_FW_UNDEFINED_ERROR) { - if (value <= UPDATE_FW_COMPLETE && value > write_file_status) { - write_file_status = value; - pt_debug(dev, DL_DEBUG, "%s: Set BL Status to %d\n", - __func__, value); + if (value <= UPDATE_FW_COMPLETE && value > update_fw_status) { + update_fw_status = value; } else if (value >= UPDATE_FW_GENERAL_ERROR) { - write_file_status = value; + update_fw_status = value; pt_debug(dev, DL_WARN, "%s: BL Status set to error code %d\n", __func__, value); } - return write_file_status; + return update_fw_status; } - if (value > UPDATE_FW_INCREMENT && value < UPDATE_FW_UNDEFINED_INC) { - inc = value - UPDATE_FW_INCREMENT; - if (write_file_status + inc <= UPDATE_FW_COMPLETE) - write_file_status += inc; - else - pt_debug(dev, DL_ERROR, - "%s: Inc Request out of bounds: status=%d inc=%d\n", - __func__, write_file_status, inc); - } else + if (inc > 0 && update_fw_status + inc <= UPDATE_FW_COMPLETE) + update_fw_status += inc; + else pt_debug(dev, DL_ERROR, - "%s: Value Request out of bounds: status=%d value=%d\n", - __func__, write_file_status, value); - - return write_file_status; + "%s: Inc Request out of bounds: status=%d inc=%d\n", + __func__, update_fw_status, inc); + return update_fw_status; } /******************************************************************************* @@ -936,7 +911,7 @@ static int pt_upgrade_firmware(struct device *dev, const u8 *fw_img, int rc; int t; - _pt_update_write_file_status(dev, UPDATE_FW_IDLE); + _pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC); if (update_status == NULL) { rc = -EINVAL; @@ -1180,7 +1155,8 @@ static int upgrade_firmware_from_platform(struct device *dev, retry_bl: if (!ld->loader_pdata) { - _pt_update_write_file_status(dev, UPDATE_FW_NO_PLATFORM_DATA); + _pt_pip2_update_bl_status(dev, UPDATE_FW_NO_PLATFORM_DATA, + PT_NO_INC); pt_debug(dev, DL_ERROR, "%s: No loader platform data\n", __func__); return rc; @@ -1188,14 +1164,16 @@ retry_bl: fw = pt_get_platform_firmware(dev); if (!fw || !fw->img || !fw->size) { - _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); + _pt_pip2_update_bl_status(dev, UPDATE_FW_NO_FW_PROVIDED, + PT_NO_INC); pt_debug(dev, DL_ERROR, "%s: No platform firmware\n", __func__); return rc; } if (!fw->ver || !fw->vsize) { - _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); + _pt_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE, + PT_NO_INC); pt_debug(dev, DL_ERROR, "%s: No platform firmware version\n", __func__); return rc; @@ -1208,7 +1186,7 @@ retry_bl: if (upgrade) { rc = pt_upgrade_firmware(dev, - fw->img, fw->size, &write_file_status); + fw->img, fw->size, &update_fw_status); /* An extra BL may be needed if default PID was wrong choice */ if ((cd->panel_id_support & PT_PANEL_ID_BY_SYS_INFO) && @@ -1244,11 +1222,12 @@ static int _pt_pip1_bl_from_file(struct device *dev) u8 *fw_img = NULL; u8 header_size = 0; - _pt_update_write_file_status(dev, UPDATE_FW_IDLE); + _pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC); fw_img = kzalloc(PT_PIP2_MAX_FILE_SIZE, GFP_KERNEL); if (!fw_img) { - _pt_update_write_file_status(dev, UPDATE_FW_SYSTEM_NOMEM); + _pt_pip2_update_bl_status(dev, UPDATE_FW_SYSTEM_NOMEM, + PT_NO_INC); rc = -ENOMEM; goto exit; } @@ -1258,14 +1237,16 @@ static int _pt_pip1_bl_from_file(struct device *dev) if (rc) { pt_debug(dev, DL_ERROR, "%s: No firmware provided to load\n", __func__); - _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); + _pt_pip2_update_bl_status(dev, UPDATE_FW_NO_FW_PROVIDED, + PT_NO_INC); goto exit; } if (!fw_img || !fw_size) { pt_debug(dev, DL_ERROR, "%s: No firmware received\n", __func__); - _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); + _pt_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE, + PT_NO_INC); goto exit; } @@ -1273,12 +1254,13 @@ static int _pt_pip1_bl_from_file(struct device *dev) if (header_size >= (fw_size + 1)) { pt_debug(dev, DL_ERROR, "%s: Firmware format is invalid\n", __func__); - _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); + _pt_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE, + PT_NO_INC); goto exit; } pt_upgrade_firmware(dev, &(fw_img[header_size + 1]), - fw_size - (header_size + 1), &write_file_status); + fw_size - (header_size + 1), &update_fw_status); exit: kfree(fw_img); return rc; @@ -1300,17 +1282,19 @@ static void _pt_firmware_cont(const struct firmware *fw, void *context) struct pt_loader_data *ld = pt_get_loader_data(dev); u8 header_size = 0; - _pt_update_write_file_status(dev, UPDATE_FW_IDLE); + _pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC); if (!fw) { pt_debug(dev, DL_ERROR, "%s: No firmware\n", __func__); - _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); + _pt_pip2_update_bl_status(dev, UPDATE_FW_NO_FW_PROVIDED, + PT_NO_INC); goto pt_firmware_cont_exit; } if (!fw->data || !fw->size) { pt_debug(dev, DL_ERROR, "%s: No firmware received\n", __func__); - _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); + _pt_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE, + PT_NO_INC); goto pt_firmware_cont_release_exit; } @@ -1318,12 +1302,13 @@ static void _pt_firmware_cont(const struct firmware *fw, void *context) if (header_size >= (fw->size + 1)) { pt_debug(dev, DL_ERROR, "%s: Firmware format is invalid\n", __func__); - _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); + _pt_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE, + PT_NO_INC); goto pt_firmware_cont_release_exit; } pt_upgrade_firmware(dev, &(fw->data[header_size + 1]), - fw->size - (header_size + 1), &write_file_status); + fw->size - (header_size + 1), &update_fw_status); pt_firmware_cont_release_exit: if (fw) @@ -1374,7 +1359,7 @@ static int pt_check_firmware_config_version(struct device *dev, return 0; } - return -1; + return -EPERM; } /******************************************************************************* @@ -1689,7 +1674,6 @@ retry_bl: } #endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ -#ifndef TTDL_KERNEL_SUBMISSION #if PT_TTCONFIG_UPGRADE /******************************************************************************* * FUNCTION: pt_write_config_row_ @@ -2378,7 +2362,6 @@ exit_free: static DEVICE_ATTR(config_loading, 0200, NULL, pt_config_loading_store); #endif /* CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE */ -#endif /* !TTDL_KERNEL_SUBMISSION */ #ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE /******************************************************************************* @@ -2449,7 +2432,7 @@ static ssize_t pt_manual_upgrade_store(struct device *dev, goto exit; } - _pt_update_write_file_status(dev, UPDATE_FW_IDLE); + _pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC); if (ld->is_manual_upgrade_enabled) { rc = -EBUSY; goto exit; @@ -2467,9 +2450,7 @@ exit: return size; } -#ifndef TTDL_KERNEL_SUBMISSION static DEVICE_ATTR(manual_upgrade, 0200, NULL, pt_manual_upgrade_store); -#endif /* !TTDL_KERNEL_SUBMISSION */ #endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ /******************************************************************************* @@ -2551,7 +2532,8 @@ static void pt_fw_and_config_upgrade( * *dev - pointer to device structure * *read_buf - pointer to the read buffer array to store the response ******************************************************************************/ -static void _pt_pip2_get_flash_info(struct device *dev, u8 *read_buf) +static void _pt_pip2_get_flash_info(struct device *dev, + u8 *read_buf) { u16 actual_read_len; int ret; @@ -2601,85 +2583,6 @@ static void _pt_pip2_get_flash_info(struct device *dev, u8 *read_buf) } #endif /* TTDL_DIAGNOSTICS */ -/******************************************************************************* - * FUNCTION: _pt_pip2_file_write_packet - * - * SUMMARY: Using the BL PIP2 commands to write a file. - * - * NOTE#1: This function support No Interrupt Solution and switch - * "pip2_send_cmd" function according to the global variable "bl_with_no_int". - * NOTE#2: The maximum write len is limited to 256 as well as the total buffer - * size is limited as PT_MAX_PIP2_MSG_SIZE. - * - * RETURNS: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *dev - pointer to device structure - * file_hd - file handle for file operation - * *write_buf - pointer of buffer to write - * write_len - length to write - * *status - pointer to store the STATUS in response - ******************************************************************************/ -static int _pt_pip2_file_write_packet(struct device *dev, u8 file_hd, - u8 *write_buf, u16 write_len, u8 *status) -{ - struct pt_core_data *cd = dev_get_drvdata(dev); - PIP2_SEND_CMD pip2_send_cmd; - int ret = 0; - u16 actual_read_len = 0; - u8 read_buf[PT_MAX_PIP2_MSG_SIZE]; - u8 buf[PT_MAX_PIP2_MSG_SIZE]; - - if (write_len > PIP2_FILE_WRITE_MAX_LEN_PER_PACKET) { - pt_debug(dev, DL_ERROR, "%s write_len (%d) is over size\n", - __func__, write_len); - return -EINVAL; - } - if (cd->bl_with_no_int) - pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd_no_int; - else - pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd; - - buf[0] = file_hd; - memcpy(&buf[1], write_buf, write_len); - ret = pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED, - PIP2_CMD_ID_FILE_WRITE, buf, write_len + 1, - read_buf, &actual_read_len); - if (status) - *status = read_buf[PIP2_RESP_STATUS_OFFSET]; - return ret; -} - -/******************************************************************************* - * FUNCTION: _pt_pip2_execute_app - * - * SUMMARY: Using the BL PIP2 commands to executes an image downloaded into the - * SRAM. - * - * RETURNS: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *dev - pointer to device structure - * *status - pointer to store the STATUS in response - ******************************************************************************/ -static int _pt_pip2_execute_app(struct device *dev, u8 *status) -{ - int ret = 0; - u16 actual_read_len = 0; - u8 read_buf[PT_MAX_PIP2_MSG_SIZE]; - - ret = cmd->nonhid_cmd->pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED, - PIP2_CMD_ID_EXECUTE, NULL, 0, - read_buf, &actual_read_len); - if (status) - *status = read_buf[PIP2_RESP_STATUS_OFFSET]; - return ret; -} - /******************************************************************************* * FUNCTION: _pt_pip2_log_last_error * @@ -2698,7 +2601,8 @@ static int _pt_pip2_execute_app(struct device *dev, u8 *status) * *dev - pointer to device structure * *read_buf - pointer to the read buffer array to store the response ******************************************************************************/ -static int _pt_pip2_log_last_error(struct device *dev, u8 *read_buf) +static int _pt_pip2_log_last_error(struct device *dev, + u8 *read_buf) { u16 actual_read_len; u8 loop = 5; @@ -2757,82 +2661,6 @@ static int _pt_pip2_log_last_error(struct device *dev, u8 *read_buf) return ret; } -/******************************************************************************* - * FUNCTION: _pt_pip2_file_write_packet_and_log_err - * - * SUMMARY: Wrapper function for File Write. If meet failure, it will call - * function _pt_pip2_log_last_error() and do a retry within 3x. - * - * NOTE: This function support No Interrupt Solution and switch "pip2_send_cmd" - * function according to the global variable "bl_with_no_int". - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *dev - pointer to device structure - * file_hd - file handle for file operation - * *write_buf - pointer of buffer to write - * write_len - length to write - * last_write - flag for last_write (1: is last write) - ******************************************************************************/ -#define PIP2_FILE_WRITE_RETRY_MAX (3) -static int _pt_pip2_file_write_packet_and_log_err(struct device *dev, - u8 file_hd, u8 *write_buf, u16 write_len, u8 last_write) -{ - struct pt_core_data *cd = dev_get_drvdata(dev); - int ret = 0; - int retry_packet = 0; - u8 read_buf[PT_MAX_PIP2_MSG_SIZE]; - u8 status = 0; - - do { - ret = _pt_pip2_file_write_packet(dev, file_hd, write_buf, - write_len, &status); - /* Write cmd successful with a fail status */ - if (ret) { - pt_debug(dev, DL_ERROR, "%s %d - Packet cmd error\n", - __func__, ret); - } else if (status) { - /* - * The last time through the loop when remain_bytes = - * write_len, no partial payload will remain, the last - * successful write (when writing to RAM) will respond - * with EOF status, writing to FLASH will respond with a - * standard success status of 0x00 - */ - if (last_write && (file_hd == PIP2_RAM_FILE) && - (status == PIP2_RSP_ERR_END_OF_FILE)) { - pt_debug(dev, DL_WARN, - "%s Last write, ret = 0x%02x\n", - __func__, status); - status = 0; - break; - } - - pt_debug(dev, DL_ERROR, "%s %d - Packet status error\n", - __func__, status); - /* Manually assign ret to negative value */ - ret = -EINVAL; - } else { - /* No issue is seen, and break the loop */ - break; - } - -#ifdef TTDL_DIAGNOSTICS - cd->bl_retry_packet_count++; - cmd->request_toggle_err_gpio(dev, PT_ERR_GPIO_BL_RETRY_PACKET); - pt_debug(dev, DL_WARN, "%s: === Retry Packet #%d ===\n", - __func__, retry_packet); -#endif - /* Get and log the last error(s) */ - _pt_pip2_log_last_error(dev, read_buf); - } while (retry_packet++ < PIP2_FILE_WRITE_RETRY_MAX); - - return ret; -} - /******************************************************************************* * FUNCTION: _pt_pip2_check_fw_ver * @@ -2877,7 +2705,7 @@ static int _pt_pip2_check_fw_ver(struct device *dev, (256 * hdr->fw_major + hdr->fw_minor)) { pt_debug(dev, DL_WARN, "ATM - bin file version < FW, will NOT upgrade FW"); - return -1; + return -EPERM; } if (img_app_rev_ctrl > hdr->fw_rev_ctrl) { @@ -2889,7 +2717,7 @@ static int _pt_pip2_check_fw_ver(struct device *dev, if (img_app_rev_ctrl < hdr->fw_rev_ctrl) { pt_debug(dev, DL_WARN, "bin file rev ctrl > FW, will NOT upgrade FW"); - return -1; + return -EPERM; } return 0; @@ -2932,7 +2760,7 @@ static int _pt_pip2_check_config_ver(struct device *dev, return 1; } else pt_debug(dev, DL_WARN, - "ATM - bin file Config version <= FW, will NOT upgrade FW"); + "ATM - bin file Config version <= FW, will NOT upgrade FW"); return 0; } @@ -3061,386 +2889,136 @@ static int _pt_calibrate_flashless_dut(struct device *dev) } /******************************************************************************* - * FUNCTION: _search_fw_from_builtin + * FUNCTION: _pt_pip2_firmware_cont * - * SUMMARY: Check the existence of builtin FW by filename and set the pointer - * to FW image + * SUMMARY: Bootload the DUT with a FW image using the PIP2 protocol. This + * includes getting the DUT into BL mode, writing the file to either SRAM + * or FLASH, and launching the application directly in SRAM or by resetting + * the DUT without the hostmode pin asserted. * - * RETURN: - * 0 = success - * !0 = failure + * NOTE: Special care must be taken to support a DUT communicating in + * PIP2.0 where the length field is defined differently. + * NOTE: The write packet len is set so that the overall packet size is + * less than 255. The overhead is 9 bytes: 2 byte address (0101), + * 4 byte header, 1 byte file no. 2 byte CRC * * PARAMETERS: - * *dev - pointer to device structure - * *filename - pointer of file name - * **fw - pointer to store the pointer of FW image to load + * *fw - pointer to the new FW image to load + * *context - pointer to the device ******************************************************************************/ -static int _search_fw_from_builtin(struct device *dev, char *filename, - const struct firmware **fw) +static void _pt_pip2_firmware_cont(const struct firmware *fw, + void *context) { - int ret = 0; -#if (KERNEL_VERSION(3, 13, 0) > LINUX_VERSION_CODE) - ret = request_firmware(fw, filename, dev); -#else - ret = request_firmware_direct(fw, filename, dev); -#endif - if (ret) { - pt_debug(dev, DL_WARN, "%s: ATM - Fail request FW %s load\n", - __func__, filename); - } else { - pt_debug(dev, DL_INFO, "%s: FW %s class file loading\n", - __func__, filename); - } - - return ret; -} - -/******************************************************************************* - * FUNCTION: _search_fw_from_us - * - * SUMMARY: Check the existence of FW from user space by file name defined in - * "pip2_us_file_path". - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *dev - pointer to device structure - ******************************************************************************/ -static int _search_fw_from_us(struct device *dev) -{ - struct pt_core_data *cd = dev_get_drvdata(dev); - int read_size = 16; - int ret = 0; - u8 image[16]; - - if (cd->pip2_us_file_path[0] == '\0') { - pt_debug(dev, DL_WARN, - "%s: US Path not defined, BL from built-in\n", - __func__); - ret = -EINVAL; - } else { - /* Read a few bytes to see if file exists */ - ret = cmd->nonhid_cmd->read_us_file(dev, cd->pip2_us_file_path, - image, &read_size); - if (!ret) { - pt_debug(dev, DL_WARN, "%s: %s Found, BL from US\n", - __func__, cd->pip2_us_file_path); - } else { - pt_debug(dev, DL_WARN, - "%s: ATM - %s NOT Found, BL from built-in\n", - __func__, cd->pip2_us_file_path); - } - } - return ret; -} - -/******************************************************************************* - * FUNCTION: pt_pip2_write_file - * - * SUMMARY: Compelete steps to wite a FILE in external SPI flash over PIP2 BL - * commands. It includes: Open->Erase->Write->Close. - * - * NOTE: "write_file_status" is updated from x to 99 in this scope, and higher - * level function is allowed to reset to 0 or report an error status. - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *dev - pointer to device structure - * *fw - pointer to FW image to load - * file_no - Identifies the files to write - ******************************************************************************/ -static int pt_pip2_write_file(struct device *dev, const struct firmware *fw, - u8 file_no) -{ - struct pt_core_data *cd = dev_get_drvdata(dev); - struct pt_loader_data *ld = pt_get_loader_data(dev); - int ret = 0; - int erase_status = 0; - u32 percent_cmplt; - u32 remain_bytes = 0; - u32 fw_size = 0; - u32 offset = 0; - u32 max_file_size = 0; - u16 packet_size = 0; - u16 sector_num = 0; - u8 *fw_img = NULL; - u8 file_handle = 0; u8 read_buf[PT_MAX_PIP2_MSG_SIZE]; + u8 buf[PT_MAX_PIP2_MSG_SIZE]; + u8 *fw_img = NULL; + u16 write_len; + u8 mode = PT_MODE_UNKNOWN; + u8 retry_packet = 0; + u8 us_fw_used = 0; + u16 actual_read_len; + u16 status = 0; + u16 packet_size; + int fw_size = 0; + int remain_bytes; + int ret = 0; + int percent_cmplt; + int t; + int erase_status; + u32 max_file_size; + bool wait_for_calibration_complete = false; + PIP2_SEND_CMD pip2_send_cmd; + struct device *dev = context; + struct pt_loader_data *ld = pt_get_loader_data(dev); + struct pip2_loader_data *pip2_data = ld->pip2_data; + struct pt_core_data *cd = dev_get_drvdata(dev); - if (!fw || !fw->data || !fw->size) { - pt_debug(dev, DL_ERROR, "%s The FW is invalid\n", __func__); - return -EINVAL; - } - fw_img = (u8 *)&(fw->data[0]); - fw_size = fw->size; + pt_debug(dev, DL_WARN, "%s: ATM - Begin BL\n", __func__); + _pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC); if (cd->bus_ops->bustype == BUS_I2C) packet_size = PIP2_BL_I2C_FILE_WRITE_LEN_PER_PACKET; else packet_size = PIP2_BL_SPI_FILE_WRITE_LEN_PER_PACKET; - /* 1. Open file before any file operation */ - pt_debug(dev, DL_INFO, "%s OPEN File %d for write\n", - __func__, file_no); - ret = cmd->nonhid_cmd->pip2_file_open(dev, file_no); - if (ret < 0) { - pt_debug(dev, DL_ERROR, "%s Open file %d failed\n", - __func__, file_no); - _pt_update_write_file_status(dev, UPDATE_FW_FILE_OPEN_ERROR); - goto exit; - } - file_handle = ret; - - /* Write File Status: inc to 11 */ - _pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1); - - /* - * 2. Erase file - * 2.1 Obtain the size of FILE - * 2.2 Confirm content to be written is not too large - * 2.3 Check whether Sector Erase is valid - * 2.4 Log flash information - * 2.5 Do Sector Erase or File Erase - * 2.6 Reset FILE pointer - * NOTE: Regarding to TC3315, the size of RAM_FILE is less than fw - * image, so will not do step 2.2 for PIP2_RAM_FILE. - */ - if (file_no != PIP2_RAM_FILE) { - ret = cmd->nonhid_cmd->pip2_file_get_stats( - dev, file_handle, NULL, &max_file_size); - if (ret) { - pt_debug(dev, DL_ERROR, - "%s: Failed to get_file_state ret=%d\n", - __func__, ret); - goto exit_close_file; - } - if (fw_size > max_file_size) { - pt_debug(dev, DL_ERROR, - "%s: Firmware image(%d) is over size(%d)\n", - __func__, fw_size, max_file_size); - _pt_update_write_file_status(dev, - UPDATE_FW_INVALID_FW_IMAGE); - goto exit_close_file; - } - - /* calculate number of sector */ - sector_num = max_file_size / PT_PIP2_FILE_SECTOR_SIZE; - if (max_file_size % PT_PIP2_FILE_SECTOR_SIZE) { - pt_debug(dev, DL_WARN, - "%s: file size %d misalign, don't use sector erase\n", - __func__, max_file_size); - /* - * TODO: Not sure whether can have this case, and this - * is a workaround to ensure the safety in logic. - * Force sector number to 0 will use the default method - * to do file erase by FILE instead of SECTOR. - */ - sector_num = 0; - } - -#ifdef TTDL_DIAGNOSTICS - /* Log the Flash part info */ - if (cd->debug_level >= DL_DEBUG) - _pt_pip2_get_flash_info(dev, read_buf); -#endif - /* Erase file before loading */ - ret = cmd->nonhid_cmd->pip2_file_erase(dev, - ld->pip2_load_file_no, sector_num, &erase_status); - if (ret < 0) { - pt_debug(dev, DL_ERROR, - "%s: File erase failed rc=%d status=%d\n", - __func__, ret, erase_status); - _pt_update_write_file_status(dev, - UPDATE_FW_ERASE_ERROR); - goto exit_close_file; - } - - /* Write File Status: inc to 12 */ - _pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1); - - /* Reset file pointer as sector erase moved it */ - ret = cmd->nonhid_cmd->pip2_file_seek_offset(dev, - ld->pip2_load_file_no, 0, 0); - if (ret) { - pt_debug(dev, DL_ERROR, - "%s: Failed to seek file offset rc=%d\n", - __func__, ret); - _pt_update_write_file_status(dev, - UPDATE_FW_FILE_SEEK_ERROR); - goto exit_close_file; - } - /* Write File Status: inc to 17 */ - _pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_5); - } - - /* - * 3. Write file - * 3.1* Disable IRQ when using polling method to save BL time - * 3.2 Looping to write file, and the last packet need extra check. - * 3.3* Enable IRQ - */ - pt_debug(dev, DL_WARN, - "%s: ATM - Writing %d bytes of firmware data now\n", - __func__, fw_size); - - /* - * No IRQ function is used to BL to reduce BL time due to any IRQ - * latency. - */ - if (cd->bl_with_no_int) - disable_irq_nosync(cd->irq); - - while (offset < fw_size) { - remain_bytes = fw_size - offset; - /* Don't update BL status on every pass */ - if (remain_bytes % 2000 < packet_size) { - /* Calculate % complete for write_file_status sysfs */ - percent_cmplt = - (fw_size - remain_bytes) * 100 / fw_size + 1; - if (percent_cmplt > UPDATE_FW_ACTIVE_90) - percent_cmplt = UPDATE_FW_ACTIVE_90; - /* - * Write File Status: set from 1 to 90 when - * fw_size is big enough (such as 82033). But the - * function doesn't update "write_file_status" if the - * input value is less than current stored value. - */ - _pt_update_write_file_status(dev, percent_cmplt); -#ifdef TTDL_DIAGNOSTICS - pt_debug(dev, DL_INFO, - "Wrote %d bytes with %d bytes remaining\n", - offset, remain_bytes); -#endif - } - if (remain_bytes > packet_size) { - /* The last para passes in with last_packet=0 (false) */ - ret = _pt_pip2_file_write_packet_and_log_err( - dev, file_handle, &fw_img[offset], packet_size, 0); - offset += packet_size; - if (ret) - break; - } else { - pt_debug(dev, DL_INFO, - "Write last %d bytes to File = 0x%02x\n", - remain_bytes, ld->pip2_load_file_no); - /* The last para passes in with last_packet=1 (true) */ - ret = _pt_pip2_file_write_packet_and_log_err( - dev, file_handle, &fw_img[offset], remain_bytes, 1); - offset += remain_bytes; - break; - } - } - - if (cd->bl_with_no_int) - enable_irq(cd->irq); - - if (!ret) { - /* Write File Status: set to 99 */ - _pt_update_write_file_status(dev, UPDATE_FW_ACTIVE_99); - pt_debug(dev, DL_INFO, - "%s: BIN file write finished successfully\n", __func__); - } else - _pt_update_write_file_status(dev, UPDATE_FW_WRITE_ERROR); - -exit_close_file: - /* 4. Close file */ - ret = cmd->nonhid_cmd->pip2_file_close(dev, file_handle); - if (ret != ld->pip2_load_file_no) { - pt_debug(dev, DL_ERROR, - "%s file close failure\n", __func__); - _pt_update_write_file_status(dev, UPDATE_FW_FILE_CLOSE_ERROR); - ret = -EBADF; - } else - ret = 0; - -exit: - return ret; -} - -/******************************************************************************* - * FUNCTION: _pt_pip2_update_fw - * - * SUMMARY: Compelete steps to update FW. It includes following steps: - * 1) Enter bootloader - * 2) Compare IMG version with FW version - * 3) Write IMG to target FILE - * 4) Exit bootloader - * 5) Trigger Enum and check the sentinel - * Also includes exclusive protection, PM function, Watchdog function, and - * complete progress for "update_bl_status". - * - * NOTE: "write_file_status" is updated from 0 to 100 in this scope, and higher - * level function is allowed to reset status to 0 or report an error status. - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *dev - pointer to device structure - * *fw - pointer to FW image to load - * file_no - Identifies the files to write - ******************************************************************************/ -static int _pt_pip2_update_fw(struct device *dev, const struct firmware *fw, - u8 file_no) -{ - struct pt_core_data *cd = dev_get_drvdata(dev); - struct pt_loader_data *ld = pt_get_loader_data(dev); - int ret = 0; - int t = 0; - bool wait_for_calibration_complete = false; - u8 mode = PT_MODE_UNKNOWN; - u8 status = 0; - u8 read_buf[PT_MAX_PIP2_MSG_SIZE]; - - pt_debug(dev, DL_WARN, "%s: ATM - Begin BL\n", __func__); - /* Write File Status: reset to 0 */ - _pt_update_write_file_status(dev, UPDATE_FW_IDLE); - - if (file_no > PIP2_FW_FILE) { - pt_debug(dev, DL_ERROR, - "%s: Invalid File_no = %d\n", __func__, file_no); - _pt_update_write_file_status(dev, - UPDATE_FW_NOT_SUPPORTED_FILE_NO); - goto exit; - } - - /* Write File Status: set to 0 */ ret = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT); - if (ret) { + if (ret < 0) { pt_debug(dev, DL_ERROR, "%s: Failed to aquire exclusive access\n", __func__); - _pt_update_write_file_status(dev, - UPDATE_FW_EXCLUSIVE_ACCESS_ERROR); + update_fw_status = UPDATE_FW_EXCLUSIVE_ACCESS_ERROR; goto exit; } - /* Write File Status: set to 1 */ - _pt_update_write_file_status(dev, UPDATE_FW_REQUEST_EXCLUSIVE); + _pt_pip2_update_bl_status(dev, 0, 1); + + if (!fw) { + if (ld->pip2_load_builtin) { + pt_debug(dev, DL_ERROR, + "%s: No builtin firmware\n", __func__); + ld->builtin_bin_fw_status = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Exit BL\n", __func__); + _pt_pip2_update_bl_status(dev, + UPDATE_FW_NO_FW_PROVIDED, PT_NO_INC); + goto exit; + } else { + fw_img = kzalloc(PT_PIP2_MAX_FILE_SIZE, GFP_KERNEL); + us_fw_used = 1; + ret = cmd->nonhid_cmd->read_us_file(dev, + cd->pip2_us_file_path, fw_img, &fw_size); + if (ret) { + pt_debug(dev, DL_ERROR, + "%s: No firmware provided to load\n", + __func__); + pt_debug(dev, DL_ERROR, "%s: Exit BL\n", + __func__); + _pt_pip2_update_bl_status(dev, + UPDATE_FW_NO_FW_PROVIDED, PT_NO_INC); + goto exit; + } + } + } else { + fw_img = (u8 *)&(fw->data[0]); + fw_size = fw->size; + } + + if (!fw_img || !fw_size) { + pt_debug(dev, DL_ERROR, + "%s: Invalid fw or file size=%d\n", __func__, + (int)fw_size); + _pt_pip2_update_bl_status(dev, UPDATE_FW_INVALID_FW_IMAGE, + PT_NO_INC); + goto exit; + } + if (ld->pip2_load_file_no == PIP2_FW_FILE) { + if (fw_img[0] >= (fw_size + 1)) { + pt_debug(dev, DL_ERROR, + "%s: Firmware format is invalid\n", __func__); + _pt_pip2_update_bl_status(dev, + UPDATE_FW_INVALID_FW_IMAGE, PT_NO_INC); + goto exit; + } + } cd->fw_updating = true; wake_up(&cd->wait_q); - /* Write File Status: inc to 2 */ - _pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1); - + _pt_pip2_update_bl_status(dev, 0, 1); pt_debug(dev, DL_INFO, - "%s: Found file of size: %d bytes\n", __func__, (int)fw->size); - + "%s: Found file of size: %d bytes\n", __func__, (int)fw_size); pm_runtime_get_sync(dev); - /* Write File Status: inc to 3 */ - _pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1); + _pt_pip2_update_bl_status(dev, 0, 1); + + /* Wait for completion of FW upgrade thread before continuing */ + if (!ld->pip2_load_builtin) + init_completion(&pip2_data->pip2_fw_upgrade_complete); + + _pt_pip2_update_bl_status(dev, 0, 1); cmd->request_stop_wd(dev); - /* Write File Status: inc to 4 */ - _pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1); - cd->bl_pip_ver_ready = false; - cd->app_pip_ver_ready = false; + if (cd->flashless_dut) { + cd->bl_pip_ver_ready = false; + cd->app_pip_ver_ready = false; + } /* * 'mode' is used below, if DUT was already in BL before attempting to @@ -3451,54 +3029,278 @@ static int _pt_pip2_update_fw(struct device *dev, const struct firmware *fw, if (ret) { pt_debug(dev, DL_ERROR, "%s: Failed to enter BL\n", __func__); - _pt_update_write_file_status(dev, UPDATE_FW_ENTER_BL_ERROR); + _pt_pip2_update_bl_status(dev, UPDATE_FW_ENTER_BL_ERROR, + PT_NO_INC); goto exit; } - /* Write File Status: inc to 5 */ - _pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1); + _pt_pip2_update_bl_status(dev, 0, 1); + /* Only compare FW ver or previous mode when doing a built-in upgrade */ if (ld->pip2_load_builtin) { if (_pt_pip2_need_upgrade_due_to_fw_ver(dev, fw) || mode == PT_MODE_BOOTLOADER) { - /* Write File Status: inc to 6 */ - _pt_update_write_file_status(dev, - UPDATE_FW_INCREMENT_BY_1); + _pt_pip2_update_bl_status(dev, 0, 1); } else { - _pt_update_write_file_status(dev, - UPDATE_FW_VERSION_ERROR); + _pt_pip2_update_bl_status(dev, + UPDATE_FW_VERSION_ERROR, PT_NO_INC); goto exit; } } + pt_debug(dev, DL_INFO, "%s OPEN File %d for write\n", + __func__, ld->pip2_load_file_no); + ret = cmd->nonhid_cmd->pip2_file_open(dev, ld->pip2_load_file_no); + if (ret < 0) { + pt_debug(dev, DL_ERROR, "%s Open file %d failed\n", + __func__, ld->pip2_load_file_no); + _pt_pip2_update_bl_status(dev, UPDATE_FW_FILE_OPEN_ERROR, + PT_NO_INC); + goto exit; + } + pip2_data->pip2_file_handle = ret; + _pt_pip2_update_bl_status(dev, 0, 1); + + /* Regarding to TC3315, the size of RAM_FILE is less than fw image */ + if (ld->pip2_load_file_no != PIP2_RAM_FILE) { + ret = cmd->nonhid_cmd->pip2_file_get_stats(dev, + ld->pip2_load_file_no, NULL, &max_file_size); + if (ret) { + pt_debug(dev, DL_ERROR, + "%s: Failed to get_file_state ret=%d\n", + __func__, ret); + goto exit_close_file; + } + if (fw_size > max_file_size) { + pt_debug(dev, DL_ERROR, + "%s: Firmware image(%d) is over size(%d)\n", + __func__, fw_size, max_file_size); + _pt_pip2_update_bl_status(dev, + UPDATE_FW_INVALID_FW_IMAGE, PT_NO_INC); + goto exit_close_file; + } +#ifdef TTDL_DIAGNOSTICS + /* Log the Flash part info */ + if (cd->debug_level >= DL_DEBUG) + _pt_pip2_get_flash_info(dev, read_buf); +#endif + /* Erase file before loading */ + ret = cmd->nonhid_cmd->pip2_file_erase(dev, + ld->pip2_load_file_no, &erase_status); + if (ret < 0) { + pt_debug(dev, DL_ERROR, + "%s: File erase failed rc=%d status=%d\n", + __func__, ret, erase_status); + _pt_pip2_update_bl_status(dev, + UPDATE_FW_ERASE_ERROR, PT_NO_INC); + goto exit_close_file; + } + _pt_pip2_update_bl_status(dev, 0, 5); + } + + remain_bytes = fw_size; + buf[0] = pip2_data->pip2_file_handle; + pt_debug(dev, DL_WARN, + "%s: ATM - Writing %d bytes of firmware data now\n", + __func__, fw_size); + /* - * Write File Status: set to 10 before pt_pip2_write_file() is called to - * align with _pt_pip2_write_file_cont(). + * No IRQ function is used to BL to reduce BL time due to any IRQ + * latency. */ - _pt_update_write_file_status(dev, UPDATE_FW_ACTIVE_10); - /* Write File Status: inc from 10, the max is not bigger than 99 */ - ret = pt_pip2_write_file(dev, fw, file_no); - if (ret) { - pt_debug(dev, DL_ERROR, "%s: Failed to write FILE_%d\n", - __func__, file_no); + if (cd->bl_with_no_int) { + pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd_no_int; + disable_irq_nosync(cd->irq); + } else + pip2_send_cmd = cmd->nonhid_cmd->pip2_send_cmd; + + /* Continue writing while data remains */ + while (remain_bytes > packet_size) { + write_len = packet_size; + + /* Don't update BL status on every pass */ + if (remain_bytes % 2000 < packet_size) { + /* Calculate % complete for update_fw_status sysfs */ + percent_cmplt = (fw_size - remain_bytes) * + 100 / fw_size; + if (percent_cmplt > 0 && + percent_cmplt > UPDATE_FW_ACTIVE_90) + percent_cmplt = UPDATE_FW_ACTIVE_90; + _pt_pip2_update_bl_status(dev, percent_cmplt, + PT_NO_INC); + +#ifdef TTDL_DIAGNOSTICS + pt_debug(dev, DL_INFO, + "Wrote %d bytes with %d bytes remaining\n", + fw_size - remain_bytes - write_len, + remain_bytes); +#endif + } + if (retry_packet > 0) { +#ifdef TTDL_DIAGNOSTICS + cd->bl_retry_packet_count++; + cmd->request_toggle_err_gpio(dev, + PT_ERR_GPIO_BL_RETRY_PACKET); + pt_debug(dev, DL_WARN, + "%s: === Retry Packet #%d ===\n", + __func__, retry_packet); +#endif + /* Get and log the last error(s) */ + _pt_pip2_log_last_error(dev, read_buf); + } + + memcpy(&buf[1], fw_img, write_len); + ret = pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_FILE_WRITE, + buf, write_len + 1, read_buf, &actual_read_len); + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + + /* Write cmd successful with a fail status */ + if (!ret && status) { + /* + * The last time through the loop when remain_bytes = + * write_len, no partial payload will remain, the last + * successful write (when writing to RAM) will respond + * with EOF status, writing to FLASH will respond with a + * standard success status of 0x00 + */ + if ((ld->pip2_load_file_no == PIP2_RAM_FILE) && + (status == PIP2_RSP_ERR_END_OF_FILE) && + (remain_bytes == write_len)) { + pt_debug(dev, DL_WARN, + "%s Last write, ret = 0x%02x\n", + __func__, status); + /* Drop out of the while loop */ + break; + } + _pt_pip2_update_bl_status(dev, + UPDATE_FW_WRITE_ERROR, PT_NO_INC); + pt_debug(dev, DL_ERROR, + "%s file write failure, status = 0x%02x\n", + __func__, status); + if (retry_packet >= 3) { + /* Tripple retry error - break */ + remain_bytes = 0; + pt_debug(dev, DL_ERROR, + "%s %d - Packet status error - Break\n", + __func__, status); + } else { + retry_packet++; + } + } else if (ret) { + /* Packet write failed - retry 3x */ + if (retry_packet >= 3) { + remain_bytes = 0; + pt_debug(dev, DL_ERROR, + "%s %d - Packet cmd error - Break\n", + __func__, ret); + } + retry_packet++; + } else { + /* Cmd success and status success */ + retry_packet = 0; + } + + if (retry_packet == 0) { + fw_img += write_len; + remain_bytes -= write_len; + } + } + /* Write the remaining bytes if any remain */ + if (remain_bytes > 0 && retry_packet == 0) { + pt_debug(dev, DL_INFO, + "Write last %d bytes to File = 0x%02x\n", + remain_bytes, pip2_data->pip2_file_handle); + memcpy(&buf[1], fw_img, remain_bytes); + while (remain_bytes > 0 && retry_packet <= 3) { + ret = pip2_send_cmd(dev, PT_CORE_CMD_UNPROTECTED, + PIP2_CMD_ID_FILE_WRITE, buf, remain_bytes + 1, + read_buf, &actual_read_len); + status = read_buf[PIP2_RESP_STATUS_OFFSET]; + if (ret || status) { + /* + * Any non zero status when writing to FLASH is + * an error. Only when writing to RAM, the last + * packet must respond with an EOF status + */ + if ((ld->pip2_load_file_no >= PIP2_FW_FILE) || + (ld->pip2_load_file_no == PIP2_RAM_FILE && + status != PIP2_RSP_ERR_END_OF_FILE)) { + _pt_pip2_update_bl_status(dev, + UPDATE_FW_WRITE_ERROR, + PT_NO_INC); + pt_debug(dev, DL_ERROR, + "%s Write Fail-status=0x%02x\n", + __func__, status); + retry_packet++; + } else if (ld->pip2_load_file_no == + PIP2_RAM_FILE && + status == PIP2_RSP_ERR_END_OF_FILE) { + /* Special case EOF writing to SRAM */ + remain_bytes = 0; + status = 0; + } + } else { + remain_bytes = 0; + } + } + } + + if (cd->bl_with_no_int) + enable_irq(cd->irq); + + if (remain_bytes == 0 && retry_packet == 0) + _pt_pip2_update_bl_status(dev, UPDATE_FW_ACTIVE_99, PT_NO_INC); + + if (retry_packet >= 3) { + /* A packet write failure occurred 3x */ + pt_debug(dev, DL_ERROR, + "%s: BL terminated due to consecutive write errors\n", + __func__); + _pt_pip2_update_bl_status(dev, UPDATE_FW_WRITE_ERROR, + PT_NO_INC); + } else if (status) { + ret = status; + pt_debug(dev, DL_ERROR, + "%s: File write failed with status=%d\n", + __func__, status); + _pt_pip2_update_bl_status(dev, UPDATE_FW_WRITE_ERROR, + PT_NO_INC); + } else + pt_debug(dev, DL_INFO, + "%s: BIN file write finished successfully\n", __func__); + + ret = cmd->nonhid_cmd->pip2_file_close(dev, ld->pip2_load_file_no); + if (ret != ld->pip2_load_file_no) { + pt_debug(dev, DL_ERROR, + "%s file close failure\n", __func__); + _pt_pip2_update_bl_status(dev, UPDATE_FW_FILE_CLOSE_ERROR, + PT_NO_INC); goto exit; } - if ((file_no == PIP2_RAM_FILE) && - (write_file_status < UPDATE_FW_COMPLETE)) { + /* When updating non FW files, stay in BL */ + if (ld->pip2_load_file_no >= PIP2_CONFIG_FILE) + goto exit; + + if ((ld->pip2_load_file_no == PIP2_RAM_FILE) && + (update_fw_status < UPDATE_FW_COMPLETE)) { /* When writing to RAM don't reset, just launch application */ pt_debug(dev, DL_INFO, "%s Sending execute command now...\n", __func__); cd->startup_status = STARTUP_STATUS_START; - ret = _pt_pip2_execute_app(dev, &status); + ret = cmd->nonhid_cmd->pip2_send_cmd(dev, + PT_CORE_CMD_UNPROTECTED, PIP2_CMD_ID_EXECUTE, + NULL, 0, read_buf, &actual_read_len); + status = read_buf[PIP2_RESP_STATUS_OFFSET]; if (ret || status) { pt_debug(dev, DL_ERROR, "%s Execute command failure\n", __func__); - _pt_update_write_file_status(dev, - UPDATE_FW_EXECUTE_ERROR); + _pt_pip2_update_bl_status(dev, + UPDATE_FW_EXECUTE_ERROR, PT_NO_INC); goto exit; } - } else if (file_no == PIP2_FW_FILE && - write_file_status < UPDATE_FW_COMPLETE) { + } else if (ld->pip2_load_file_no == PIP2_FW_FILE && + update_fw_status < UPDATE_FW_COMPLETE) { pt_debug(dev, DL_INFO, "%s Toggle TP_XRES now...\n", __func__); cmd->request_reset(dev, PT_CORE_CMD_UNPROTECTED); @@ -3506,8 +3308,8 @@ static int _pt_pip2_update_fw(struct device *dev, const struct firmware *fw, pt_debug(dev, DL_INFO, "%s: APP launched\n", __func__); /* If any error occurred simply close the file and exit */ - if (write_file_status > UPDATE_FW_COMPLETE) - goto exit; + if (update_fw_status > UPDATE_FW_COMPLETE) + goto exit_close_file; /* Wait for FW reset sentinel from reset or execute for up to 500ms */ t = wait_event_timeout(cd->wait_q, @@ -3529,14 +3331,16 @@ static int _pt_pip2_update_fw(struct device *dev, const struct firmware *fw, pt_debug(dev, DL_ERROR, "%s ERROR: Not in App mode as expected\n", __func__); - _pt_update_write_file_status(dev, UPDATE_FW_MODE_ERROR); + _pt_pip2_update_bl_status(dev, UPDATE_FW_MODE_ERROR, + PT_NO_INC); goto exit; } } else { pt_debug(dev, DL_ERROR, "%s: FW sentinel not seen 0x%04X\n", __func__, cd->startup_status); _pt_pip2_log_last_error(dev, read_buf); - _pt_update_write_file_status(dev, UPDATE_FW_SENTINEL_NOT_SEEN); + _pt_pip2_update_bl_status(dev, UPDATE_FW_SENTINEL_NOT_SEEN, + PT_NO_INC); goto exit; } @@ -3570,41 +3374,58 @@ static int _pt_pip2_update_fw(struct device *dev, const struct firmware *fw, pt_debug(dev, DL_INFO, "%s: == PIP2 FW upgrade finished ==\n", __func__); + goto exit; + +exit_close_file: + ret = cmd->nonhid_cmd->pip2_file_close(dev, ld->pip2_load_file_no); + if (ret < 0) { + pt_debug(dev, DL_ERROR, + "%s file close failure\n", __func__); + _pt_pip2_update_bl_status(dev, UPDATE_FW_FILE_CLOSE_ERROR, + PT_NO_INC); + } exit: cd->fw_updating = false; + if (us_fw_used) + kfree(fw_img); + if (ld->pip2_load_file_no > PIP2_FW_FILE) + goto exit_staying_in_bl; + + cmd->release_exclusive(dev); + + pm_runtime_put_sync(dev); + if (fw) + release_firmware(fw); + /* * For built-in FW update, it should not warn builtin_bin_fw_status - * for each bootup since write_file_status would be + * for each bootup since update_fw_status would be * UPDATE_FW_VERSION_ERROR in most situations because firmware * should have been up to date. */ if (ld->pip2_load_builtin) { - if ((write_file_status == UPDATE_FW_ACTIVE_99) || - (write_file_status == UPDATE_FW_VERSION_ERROR)) + if ((update_fw_status == UPDATE_FW_ACTIVE_99) || + (update_fw_status == UPDATE_FW_VERSION_ERROR)) ld->builtin_bin_fw_status = 0; else ld->builtin_bin_fw_status = -EINVAL; } - cmd->release_exclusive(dev); - pm_runtime_put_sync(dev); - - if ((write_file_status == UPDATE_FW_ACTIVE_99) || - (write_file_status == UPDATE_FW_VERSION_ERROR)) { + if ((update_fw_status == UPDATE_FW_ACTIVE_99) || + (update_fw_status == UPDATE_FW_VERSION_ERROR)) { pt_debug(dev, DL_WARN, "%s: Queue ENUM\n", __func__); cmd->request_enum(dev, true); } - - /* Write File Status: set to 100 */ - if (write_file_status < UPDATE_FW_COMPLETE) - _pt_update_write_file_status(dev, UPDATE_FW_COMPLETE); + if (update_fw_status < UPDATE_FW_COMPLETE) + _pt_pip2_update_bl_status(dev, UPDATE_FW_COMPLETE, PT_NO_INC); if (wait_for_calibration_complete) wait_for_completion(&ld->calibration_complete); pt_debug(dev, DL_INFO, "%s: Starting watchdog\n", __func__); - cmd->request_start_wd(dev); + ret = cmd->request_start_wd(dev); + /* * When in No-Flash mode allow auto BL after any BL. * There is an issue where setting flashless mode via drv_debug @@ -3615,184 +3436,48 @@ exit: if (cd->flashless_dut) cd->flashless_auto_bl = PT_ALLOW_AUTO_BL; - /* - * ret is not always updated, while builtin_bin_fw_status can reflect - * the result. - */ - return ld->builtin_bin_fw_status; -} - -/******************************************************************************* - * FUNCTION: _pt_pip2_write_file_cont - * - * SUMMARY: Write the file to either SRAM or FLASH - * - * NOTE: The DUT must stay in bootloader. This function doesn't try to - * enter/exit bootloader. - * - * PARAMETERS: - * *fw - pointer to the new FW image to load - * *context - pointer to the device - ******************************************************************************/ -static void _pt_pip2_write_file_cont(const struct firmware *fw, void *context) -{ - struct device *dev = context; - struct pt_core_data *cd = dev_get_drvdata(dev); - struct pt_loader_data *ld = pt_get_loader_data(dev); - int ret = 0; - - if (!fw) { - pt_debug(dev, DL_ERROR, "%s: No FW is provided\n", __func__); - _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); - goto exit; - } - - if (!fw->size) { - pt_debug(dev, DL_ERROR, "%s: Invalid fw or file size=%d\n", - __func__, (int)fw->size); - _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); - goto exit_release; - } - - if (ld->pip2_load_file_no == PIP2_FW_FILE) { - if (fw->data[0] >= (fw->size + 1)) { - pt_debug(dev, DL_ERROR, - "%s: Firmware format is invalid\n", __func__); - _pt_update_write_file_status(dev, - UPDATE_FW_INVALID_FW_IMAGE); - goto exit_release; - } - } - - ret = cmd->request_exclusive(dev, PT_LDR_REQUEST_EXCLUSIVE_TIMEOUT); - if (ret < 0) { - pt_debug(dev, DL_ERROR, - "%s: Failed to aquire exclusive access\n", __func__); - goto exit_release; - } - - /* Write File Status: set to 1 */ - _pt_update_write_file_status(dev, UPDATE_FW_REQUEST_EXCLUSIVE); - - cd->fw_updating = true; - /* Write File Status: inc to 2 */ - _pt_update_write_file_status(dev, UPDATE_FW_INCREMENT_BY_1); - - /* - * Write File Status: set to 10 before pt_pip2_write_file() is called to - * align with _pt_pip2_firmware_cont(). - */ - _pt_update_write_file_status(dev, UPDATE_FW_ACTIVE_10); - /* Write File Status: inc from 10, the max is not bigger than 99 */ - ret = pt_pip2_write_file(dev, fw, ld->pip2_load_file_no); - if (ret) { - pt_debug(dev, DL_ERROR, "%s: Failed to write FILE_%d\n", - __func__, ld->pip2_load_file_no); - } - - cd->fw_updating = false; - - /* Write File Status: set to 100 */ - if (write_file_status < UPDATE_FW_COMPLETE) - _pt_update_write_file_status(dev, UPDATE_FW_COMPLETE); + return; +exit_staying_in_bl: + /* When updating a non FW file, a restart is not wanted. Stay in BL */ + _pt_pip2_update_bl_status(dev, UPDATE_FW_COMPLETE, PT_NO_INC); cmd->release_exclusive(dev); - -exit_release: + pm_runtime_put_sync(dev); if (fw) release_firmware(fw); -exit: - ld->is_manual_upgrade_enabled = 0; } - +#define PIP2_MAX_FILE_NAMES 3 /******************************************************************************* - * FUNCTION: _pt_pip2_firmware_cont - * - * SUMMARY: Bootload the DUT with a FW image using the PIP2 protocol. This - * includes getting the DUT into BL mode, writing the file to either SRAM - * or FLASH, and launching the application directly in SRAM or by resetting - * the DUT without the hostmode pin asserted. - * - * NOTE: Special care must be taken to support a DUT communicating in - * PIP2.0 where the length field is defined differently. - * NOTE: The write packet len is set so that the overall packet size is - * less than 255. The overhead is 9 bytes: 2 byte address (0101), - * 4 byte header, 1 byte file no. 2 byte CRC - * - * PARAMETERS: - * *fw - pointer to the new FW image to load - * *context - pointer to the device - ******************************************************************************/ -static void _pt_pip2_firmware_cont(const struct firmware *fw, - void *context) -{ - struct device *dev = context; - struct pt_loader_data *ld = pt_get_loader_data(dev); - - if (!fw) { - pt_debug(dev, DL_ERROR, "%s: No FW is provided\n", __func__); - _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); - goto pt_firmware_cont_exit; - } - - if (!fw->data || !fw->size) { - pt_debug(dev, DL_ERROR, "%s: Invalid fw or file size=%d\n", - __func__, (int)fw->size); - _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); - goto pt_firmware_cont_release_exit; - } - - if (ld->pip2_load_file_no == PIP2_FW_FILE) { - if (fw->data[0] >= (fw->size + 1)) { - pt_debug(dev, DL_ERROR, - "%s: Firmware format is invalid\n", __func__); - _pt_update_write_file_status( - dev, UPDATE_FW_INVALID_FW_IMAGE); - goto pt_firmware_cont_release_exit; - } - } - - _pt_pip2_update_fw(dev, fw, ld->pip2_load_file_no); - -pt_firmware_cont_release_exit: - if (fw) - release_firmware(fw); - -pt_firmware_cont_exit: - ld->is_manual_upgrade_enabled = 0; -} - -/******************************************************************************* - * FUNCTION: _pt_pip2_update_fw_from_builtin + * FUNCTION: pt_pip2_upgrade_firmware_from_builtin * * SUMMARY: Bootload the DUT with a built in firmware binary image. - * - * RETURN: - * 0 = success - * !0 = failure + * Load either a SRAM image "ttdl_fw_RAM.bin" or a FLASH image + * "ttdl_fw.bin" with the priority being the SRAM image. * * PARAMETERS: * *dev - pointer to the device structure ******************************************************************************/ -#define PIP2_MAX_FILE_NAMES 3 -static int _pt_pip2_update_fw_from_builtin(struct device *dev) +static int pt_pip2_upgrade_firmware_from_builtin(struct device *dev) { - struct pt_core_data *cd = dev_get_drvdata(dev); struct pt_loader_data *ld = pt_get_loader_data(dev); - const struct firmware *fw = NULL; - int ret = 0; + struct pt_core_data *cd = dev_get_drvdata(dev); + const struct firmware *fw_entry = NULL; + int retval; + int rc = 0; + int read_size = 16; + u8 image[16]; int index = 0; int file_count = 0; char *filename[PIP2_MAX_FILE_NAMES]; /* - * 1. Generate FW Name for builtin kernel * Load the supported filenames in the correct search order * 0 - "tt_fw<_PIDX>.bin" * 1 - "XXXX_tt_fw<_PIDX>.bin" where XXXX = Silicon ID * 2 - "tt_fw.bin", default FW name */ + filename[file_count++] = generate_firmware_filename(dev); filename[file_count++] = generate_silicon_id_firmware_filename(dev); if (pt_get_panel_id(dev) != PANEL_ID_NOT_ENABLED) { @@ -3806,175 +3491,77 @@ static int _pt_pip2_update_fw_from_builtin(struct device *dev) if (!filename[index]) return -ENOMEM; } - /* 2. Look for any FW file name match */ - mutex_lock(&cd->firmware_class_lock); - index = 0; - while (index < file_count) { - pt_debug(dev, DL_INFO, "%s: Request FW class file: %s\n", - __func__, filename[index]); - if (_search_fw_from_builtin(dev, filename[index], &fw)) - index++; - else - break; - } - /* No matching file names found */ - if (index == file_count) { - pt_debug(dev, DL_WARN, "%s: No FW is found\n", __func__); - _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); - ret = -EINVAL; - goto exit; - } - - /* 3. Validate the FW */ - if (!fw) { - pt_debug(dev, DL_ERROR, "%s: No FW is provided\n", __func__); - _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); - ret = -EINVAL; - goto exit_release_fw; - } - - if (!fw->size) { - pt_debug(dev, DL_ERROR, "%s: Invalid fw or file size=%d\n", - __func__, (int)fw->size); - _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); - ret = -EINVAL; - goto exit_release_fw; - } - - if (ld->pip2_load_file_no == PIP2_FW_FILE) { - if (fw->data[0] >= (fw->size + 1)) { - pt_debug(dev, DL_ERROR, - "%s: Firmware format is invalid\n", __func__); - _pt_update_write_file_status(dev, - UPDATE_FW_INVALID_FW_IMAGE); - ret = -EINVAL; - goto exit_release_fw; - } - } - - /* 4. update the FW */ - ret = _pt_pip2_update_fw(dev, fw, ld->pip2_load_file_no); - -exit_release_fw: - /* 5. Recycle */ - if (fw) - release_firmware(fw); -exit: - index = 0; - while (index < file_count) - kfree(filename[index++]); - - mutex_unlock(&cd->firmware_class_lock); - return ret; -} - -/******************************************************************************* - * FUNCTION: _pt_pip2_update_fw_from_us - * - * SUMMARY: Bootload the DUT with firmware binary image in user space. - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *dev - pointer to the device structure - ******************************************************************************/ -static int _pt_pip2_update_fw_from_us(struct device *dev) -{ - struct pt_core_data *cd = dev_get_drvdata(dev); - struct pt_loader_data *ld = pt_get_loader_data(dev); - struct firmware fw_us; - int ret = 0; - u32 fw_size = 0; - u8 *fw_img = NULL; - - fw_img = kzalloc(PT_PIP2_MAX_FILE_SIZE, GFP_KERNEL); - if (!fw_img) { - _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); - return -ENOMEM; - } - - ret = cmd->nonhid_cmd->read_us_file(dev, cd->pip2_us_file_path, - fw_img, &fw_size); - if (ret) { - pt_debug(dev, DL_ERROR, "%s: No firmware provided to load\n", - __func__); - pt_debug(dev, DL_ERROR, "%s: Exit BL\n", __func__); - _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); - goto exit; - } - - if (!fw_size) { - pt_debug(dev, DL_ERROR, "%s: Invalid fw file size=%d\n", - __func__, (int)fw_size); - _pt_update_write_file_status(dev, UPDATE_FW_INVALID_FW_IMAGE); - goto exit; - } - - if (ld->pip2_load_file_no == PIP2_FW_FILE) { - if (fw_img[0] >= (fw_size + 1)) { - pt_debug(dev, DL_ERROR, - "%s: Firmware format is invalid\n", __func__); - _pt_update_write_file_status(dev, - UPDATE_FW_INVALID_FW_IMAGE); - goto exit; - } - } - - memset(&fw_us, 0, sizeof(fw_us)); - fw_us.data = fw_img; - fw_us.size = fw_size; - ret = _pt_pip2_update_fw(dev, &fw_us, ld->pip2_load_file_no); - -exit: - kfree(fw_img); - return ret; -} - -/******************************************************************************* - * FUNCTION: pt_pip2_upgrade_firmware_from_builtin - * - * SUMMARY: Bootload the DUT with a built in firmware binary image. - * Load either a SRAM image "ttdl_fw_RAM.bin" or a FLASH image - * "ttdl_fw.bin" with the priority being the SRAM image. - * - * PARAMETERS: - * *dev - pointer to the device structure - ******************************************************************************/ -static int pt_pip2_upgrade_firmware_from_builtin(struct device *dev) -{ - struct pt_core_data *cd = dev_get_drvdata(dev); - struct pt_loader_data *ld = pt_get_loader_data(dev); - int ret = 0; - - /* 1. Look for FW from us at first */ if (cd->flashless_dut) { - pt_debug(dev, DL_INFO, "%s: Proceed to BL flashless DUT\n", - __func__); + pt_debug(dev, DL_INFO, + "%s: Proceed to BL flashless DUT\n", __func__); ld->pip2_load_file_no = PIP2_RAM_FILE; - ret = _search_fw_from_us(dev); - if (!ret) { - ld->pip2_load_builtin = false; - /* 2. Do update with fw from user space */ - ret = _pt_pip2_update_fw_from_us(dev); - return ret; - } else { + if (cd->pip2_us_file_path[0] == '\0') { ld->pip2_load_builtin = true; + pt_debug(dev, DL_WARN, + "%s: US Path not defined, BL from built-in\n", + __func__); + } else { + /* Read a few bytes to see if file exists */ + rc = cmd->nonhid_cmd->read_us_file(dev, + cd->pip2_us_file_path, image, &read_size); + if (!rc) { + ld->pip2_load_builtin = false; + pt_debug(dev, DL_WARN, + "%s: %s Found, BL from US\n", + __func__, cd->pip2_us_file_path); + goto ready; + } else { + ld->pip2_load_builtin = true; + pt_debug(dev, DL_WARN, + "%s: ATM - %s NOT Found, BL from built-in\n", + __func__, cd->pip2_us_file_path); + } } } else { ld->pip2_load_file_no = PIP2_FW_FILE; ld->pip2_load_builtin = true; } - /* 2. Do update with fw from builtin */ - ret = _pt_pip2_update_fw_from_builtin(dev); - return ret; + /* Look for any FW file name match and request the FW */ + mutex_lock(&cd->firmware_class_lock); + index = 0; + while (index < file_count) { + pt_debug(dev, DL_INFO, "%s: Request FW class file: %s\n", + __func__, filename[index]); + retval = request_firmware_direct(&fw_entry, + filename[index], dev); + if (retval < 0) { + pt_debug(dev, DL_WARN, "%s: ATM - Fail request FW %s load\n", + __func__, filename[index]); + } else { + pt_debug(dev, DL_INFO, "%s: FW %s class file loading\n", + __func__, filename[index]); + break; + } + index++; + } + + /* No matching file names found */ + if (index == file_count) { + pt_debug(dev, DL_WARN, "%s: No FW is found\n", __func__); + goto exit; + } + +ready: + _pt_pip2_firmware_cont(fw_entry, dev); + retval = ld->builtin_bin_fw_status; +exit: + mutex_unlock(&cd->firmware_class_lock); + index = 0; + while (index < file_count) + kfree(filename[index++]); + + return retval; } /******************************************************************************* - * FUNCTION: _pt_pip2_update_fw_from_class + * FUNCTION: pt_pip2_create_fw_class * * SUMMARY: Create the firmware class but don't actually laod any FW to the * DUT. This creates all the sysfs nodes needed for a user to bootload @@ -3983,10 +3570,12 @@ static int pt_pip2_upgrade_firmware_from_builtin(struct device *dev) * PARAMETERS: * *pip2_data - pointer to the PIP2 loader data structure ******************************************************************************/ -static int _pt_pip2_update_fw_from_class(struct device *dev) +static int pt_pip2_create_fw_class(struct pip2_loader_data *pip2_data) { - struct pt_loader_data *ld = pt_get_loader_data(dev); + int ret = 0; + struct device *dev = pip2_data->dev; + struct pt_loader_data *ld = pt_get_loader_data(dev); /* * The file name dev_name(dev) is tied with bus name and usually @@ -4010,44 +3599,6 @@ static int _pt_pip2_update_fw_from_class(struct device *dev) return ret; } -/******************************************************************************* - * FUNCTION: _pt_pip2_write_file_from_class - * - * SUMMARY: Similar function of _pt_pip2_update_fw_from_class() but to call - * function _pt_pip2_write_file_cont() which doesn't perform enter/exit - * bootloader action. - * - * PARAMETERS: - * *pip2_data - pointer to the PIP2 loader data structure - ******************************************************************************/ -static int _pt_pip2_write_file_from_class(struct device *dev) -{ - - int ret = 0; - struct pt_loader_data *ld = pt_get_loader_data(dev); - - /* - * The file name dev_name(dev) is tied with bus name and usually - * it is "x-0024". This name is wanted to keep consistency - * (e.g. /sys/class/firmware/x-0024/) for the path of fw class - * nodes with different kernel release. Also it is an invalid bin - * file name used intentionally because request_firmware_nowait - * will not find the file which is what we want and then simply - * create the fw class nodes. - */ - ld->pip2_load_builtin = false; - pt_debug(dev, DL_INFO, "%s: Request FW Class", __func__); - ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, - dev_name(dev), dev, GFP_KERNEL, dev, - _pt_pip2_write_file_cont); - if (ret) { - pt_debug(dev, DL_ERROR, - "%s: ERROR requesting firmware class\n", __func__); - } - - return ret; -} - /******************************************************************************* * FUNCTION: pt_pip2_bl_from_file_work * @@ -4061,8 +3612,9 @@ static void pt_pip2_bl_from_file_work(struct work_struct *pip2_bl_from_file) struct pt_loader_data *ld = container_of(pip2_bl_from_file, struct pt_loader_data, pip2_bl_from_file); struct device *dev = ld->dev; + const struct firmware *fw_entry = NULL; - _pt_pip2_update_fw_from_us(dev); + _pt_pip2_firmware_cont(fw_entry, dev); } /******************************************************************************* @@ -4079,10 +3631,11 @@ static void pt_bl_from_file_work(struct work_struct *bl_from_file) struct pt_loader_data *ld = container_of(bl_from_file, struct pt_loader_data, bl_from_file); struct device *dev = ld->dev; + const struct firmware *fw_entry = NULL; u8 dut_gen = cmd->request_dut_generation(dev); if (dut_gen == DUT_PIP2_CAPABLE) - _pt_pip2_update_fw_from_us(dev); + _pt_pip2_firmware_cont(fw_entry, dev); else if (dut_gen == DUT_PIP1_ONLY) _pt_pip1_bl_from_file(dev); } @@ -4110,9 +3663,6 @@ static ssize_t pt_pip2_bl_from_file_show(struct device *dev, int read_size = 2; u8 image[2]; - /* Write File Status: reset to 0 */ - _pt_update_write_file_status(dev, UPDATE_FW_IDLE); - mutex_lock(&cd->firmware_class_lock); ld->pip2_load_builtin = false; mutex_unlock(&cd->firmware_class_lock); @@ -4124,13 +3674,12 @@ static ssize_t pt_pip2_bl_from_file_show(struct device *dev, if (!rc) { schedule_work(&ld->pip2_bl_from_file); - return snprintf(buf, PT_MAX_PRBUF_SIZE, + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n" "BL File: %s\n", rc, cd->pip2_us_file_path); } else { - _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); - return snprintf(buf, PT_MAX_PRBUF_SIZE, + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n" "BL File: '%s' - Does not exist\n", rc, cd->pip2_us_file_path); @@ -4162,9 +3711,6 @@ static ssize_t pt_bl_from_file_show(struct device *dev, u8 dut_gen = cmd->request_dut_generation(dev); u8 image[2]; - /* Write File Status: reset to 0 */ - _pt_update_write_file_status(dev, UPDATE_FW_IDLE); - mutex_lock(&cd->firmware_class_lock); ld->pip2_load_builtin = false; mutex_unlock(&cd->firmware_class_lock); @@ -4174,22 +3720,20 @@ static ssize_t pt_bl_from_file_show(struct device *dev, cd->pip2_us_file_path, image, &read_size); if (dut_gen == DUT_UNKNOWN) { - _pt_update_write_file_status(dev, UPDATE_FW_MODE_ERROR); rc = -EINVAL; - return snprintf(buf, PT_MAX_PRBUF_SIZE, + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n" "BL File: '%s' - Failed, DUT Generation could not be determined\n", rc, cd->pip2_us_file_path); } else if (!rc) { schedule_work(&ld->bl_from_file); - return snprintf(buf, PT_MAX_PRBUF_SIZE, + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n" "BL File: %s\n", rc, cd->pip2_us_file_path); } else { - _pt_update_write_file_status(dev, UPDATE_FW_NO_FW_PROVIDED); - return snprintf(buf, PT_MAX_PRBUF_SIZE, + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n" "BL File: '%s' - Does not exist\n", rc, cd->pip2_us_file_path); @@ -4252,7 +3796,8 @@ static ssize_t pt_pip2_bl_from_file_store(struct device *dev, pt_debug(dev, DL_WARN, "%s:Path=%s, File_no=%s(%d)\n", __func__, ptr_left, ptr_right, file_no); - if ((file_no_set) && (file_no > PIP2_FW_FILE)) { + if ((file_no_set) && + ((file_no < PIP2_RAM_FILE) || (file_no > PIP2_FILE_MAX))) { pt_debug(dev, DL_WARN, "%s:Invalid File_no = %d\n", __func__, file_no); return -EINVAL; @@ -4314,20 +3859,19 @@ static ssize_t pt_pip2_manual_upgrade_store(struct device *dev, goto exit; } + _pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC); if (ld->is_manual_upgrade_enabled) { pt_debug(dev, DL_ERROR, "%s: ERROR - Manual upgrade busy\n", __func__); rc = -EBUSY; goto exit; } - /* Write File Status: reset to 0 */ - _pt_update_write_file_status(dev, UPDATE_FW_IDLE); ld->pip2_load_file_no = PIP2_FW_FILE; pt_debug(dev, DL_DEBUG, "%s: ATM - File number is %d\n", __func__, ld->pip2_load_file_no); ld->is_manual_upgrade_enabled = 1; - rc = _pt_pip2_update_fw_from_class(dev); + rc = pt_pip2_create_fw_class(ld->pip2_data); ld->is_manual_upgrade_enabled = 0; if (rc < 0) pt_debug(dev, DL_ERROR, @@ -4338,7 +3882,6 @@ exit: return rc; return size; } -#ifndef TTDL_KERNEL_SUBMISSION static DEVICE_ATTR(pip2_manual_upgrade, 0200, NULL, pt_pip2_manual_upgrade_store); @@ -4378,6 +3921,7 @@ static ssize_t pt_pip2_manual_ram_upgrade_store(struct device *dev, goto exit; } + _pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC); if (ld->is_manual_upgrade_enabled) { pt_debug(dev, DL_ERROR, "%s: ERROR - Manual upgrade busy\n", __func__); @@ -4385,14 +3929,12 @@ static ssize_t pt_pip2_manual_ram_upgrade_store(struct device *dev, goto exit; } - /* Write File Status: reset to 0 */ - _pt_update_write_file_status(dev, UPDATE_FW_IDLE); ld->pip2_load_file_no = PIP2_RAM_FILE; pt_debug(dev, DL_DEBUG, "%s: ATM - File number is %d\n", __func__, ld->pip2_load_file_no); ld->is_manual_upgrade_enabled = 1; - rc = _pt_pip2_update_fw_from_class(dev); + rc = pt_pip2_create_fw_class(ld->pip2_data); ld->is_manual_upgrade_enabled = 0; if (rc < 0) pt_debug(dev, DL_ERROR, @@ -4421,7 +3963,6 @@ static DEVICE_ATTR(pip2_manual_ram_upgrade, 0200, static ssize_t pt_pip2_file_write_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct pt_core_data *cd = dev_get_drvdata(dev); struct pt_loader_data *ld = pt_get_loader_data(dev); int rc; u32 input_data[3]; @@ -4451,22 +3992,12 @@ static ssize_t pt_pip2_file_write_store(struct device *dev, goto exit; } - /* This functionality is only available in the BL */ - if (cd->mode != PT_MODE_BOOTLOADER) { - rc = -EPERM; - pt_debug(dev, DL_ERROR, "%s: Invalid DUT mode = %d\n", - __func__, cd->mode); - goto exit; - } - - /* Write File Status: reset to 0 */ - _pt_update_write_file_status(dev, UPDATE_FW_IDLE); ld->pip2_load_file_no = input_data[0]; if (length == 2) ld->pip2_file_data.file_offset = input_data[1]; ld->is_manual_upgrade_enabled = 1; - rc = _pt_pip2_write_file_from_class(dev); + rc = pt_pip2_create_fw_class(ld->pip2_data); ld->is_manual_upgrade_enabled = 0; if (rc < 0) pt_debug(dev, DL_ERROR, @@ -4477,7 +4008,6 @@ exit: return size; } static DEVICE_ATTR(pip2_file_write, 0200, NULL, pt_pip2_file_write_store); -#endif /* !TTDL_KERNEL_SUBMISSION */ /******************************************************************************* * FUNCTION: pt_update_fw_store @@ -4509,7 +4039,6 @@ static ssize_t pt_update_fw_store(struct device *dev, static DEVICE_ATTR(update_fw, 0200, NULL, pt_update_fw_store); #endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ -#ifndef TTDL_KERNEL_SUBMISSION #ifdef TTDL_DIAGNOSTICS /******************************************************************************* * FUNCTION: pt_pip2_file_read_show @@ -4818,7 +4347,7 @@ static ssize_t pt_pip2_file_read_store(struct file *filp, goto error; } - if (ic_buffer[0] < PIP2_FW_FILE || ic_buffer[0] > PIP2_FILE_MAX) { + if (ic_buffer[0] < PIP2_FW_FILE || ic_buffer[0] > PIP2_FILE_7) { pt_debug(dev, DL_ERROR, "%s: Invalid file handle!\n", __func__); ld->pip2_file_data.para_num = 0; @@ -4911,7 +4440,7 @@ static ssize_t pt_pip2_file_crc_show(struct device *dev, pt_debug(dev, DL_ERROR, "%s: Invalid parameters!\n", __func__); - print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n" "Invalid parameters!\n", -EINVAL); return print_idx; @@ -4924,7 +4453,7 @@ static ssize_t pt_pip2_file_crc_show(struct device *dev, /* This functionality is only available in the BL */ if (cd->mode != PT_MODE_BOOTLOADER) { rc = -EPERM; - print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", rc); goto exit; } @@ -4934,7 +4463,7 @@ static ssize_t pt_pip2_file_crc_show(struct device *dev, pt_debug(dev, DL_ERROR, "%s: Failed to request exclusive rc=%d\n", __func__, rc); - print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", rc); goto exit; } @@ -4944,7 +4473,7 @@ static ssize_t pt_pip2_file_crc_show(struct device *dev, pt_debug(dev, DL_ERROR, "%s: Failed to file_open rc=%d\n", __func__, rc); - print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", rc); goto exit_release; } @@ -4955,7 +4484,7 @@ static ssize_t pt_pip2_file_crc_show(struct device *dev, pt_debug(dev, DL_ERROR, "%s: Failed to get_file_state rc=%d\n", __func__, rc); - print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", rc); goto exit_file_close; } @@ -4969,7 +4498,7 @@ static ssize_t pt_pip2_file_crc_show(struct device *dev, pt_debug(dev, DL_ERROR, "%s: Invalid parameters!\n", __func__); - print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", rc); goto exit_file_close; } @@ -4980,18 +4509,18 @@ static ssize_t pt_pip2_file_crc_show(struct device *dev, pt_debug(dev, DL_ERROR, "%s: Failed to get file crc, rc=%d\n", __func__, rc); - print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", rc); } else { status = read_buf[PIP2_RESP_STATUS_OFFSET]; if (status == PIP2_RSP_ERR_NONE) { file_crc = get_unaligned_le16(&read_buf[5]); - print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n" "FILE CRC: %04X\n", status, file_crc); } else - print_idx = snprintf(buf, PT_MAX_PRBUF_SIZE, + print_idx = scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n" "FILE CRC: n/a\n", status); @@ -5094,8 +4623,6 @@ static ssize_t pt_pip2_file_erase_show(struct device *dev, struct pt_core_data *cd = dev_get_drvdata(dev); u8 file_handle; u8 file = ld->pip2_file_erase_file_no; - u16 sector_num = 0; - u32 max_file_size = 0; int rc; pip2_erase_status = -1; @@ -5126,31 +4653,8 @@ static ssize_t pt_pip2_file_erase_show(struct device *dev, goto exit_release; } - rc = cmd->nonhid_cmd->pip2_file_get_stats(dev, - file, NULL, &max_file_size); - if (rc) { - pt_debug(dev, DL_ERROR, - "%s: Failed to get_file_state ret=%d\n", - __func__, rc); - goto exit_release; - } - - sector_num = max_file_size / PT_PIP2_FILE_SECTOR_SIZE; - if (max_file_size % PT_PIP2_FILE_SECTOR_SIZE) { - pt_debug(dev, DL_WARN, - "%s: file size %d misalign, don't use sector erase\n", - __func__, max_file_size); - /* - * TODO: Not sure whether can have this case, and this - * is a workaround to ensure the safety in logic. - * Force sector number to 0 will use the default method - * to do file erase by FILE instead of SECTOR. - */ - sector_num = 0; - } - file_handle = cmd->nonhid_cmd->pip2_file_erase(dev, file, - sector_num, &pip2_erase_status); + &pip2_erase_status); if (file_handle < 0) { rc = file_handle; pt_debug(dev, DL_INFO, "%s: File erase error rc = %d\n", @@ -5171,12 +4675,12 @@ exit_release: exit: pip2_erase_rc = rc; if (pip2_erase_status == -1) { - return snprintf(buf, PT_MAX_PRBUF_SIZE, + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n" "Erase Status: n/a\n", pip2_erase_rc); } - return snprintf(buf, PT_MAX_PRBUF_SIZE, + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n" "Erase Status: 0x%02X\n", pip2_erase_rc, pip2_erase_status); @@ -5235,7 +4739,7 @@ static DEVICE_ATTR(pip2_file_erase, 0644, pt_pip2_file_erase_show, pt_pip2_file_erase_store); /******************************************************************************* - * FUNCTION: pt_write_file_status_show + * FUNCTION: pt_pip2_bl_status_show * * SUMMARY: The show method for the pip2_bl_status sysfs node. * Shows the percent completion of the current BL or an error message. @@ -5245,16 +4749,16 @@ static DEVICE_ATTR(pip2_file_erase, 0644, * *attr - pointer to device attributes structure * *buf - pointer to print output buffer ******************************************************************************/ -static ssize_t pt_write_file_status_show(struct device *dev, +static ssize_t pt_pip2_bl_status_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t ret; - u8 status = write_file_status; + u8 status = update_fw_status; - if (write_file_status <= UPDATE_FW_COMPLETE) { + if (update_fw_status <= UPDATE_FW_COMPLETE) { pt_debug(dev, DL_DEBUG, - "%s BL_STATUS = %d\n", __func__, write_file_status); - return scnprintf(buf, strlen(buf), "%d\n", write_file_status); + "%s BL_STATUS = %d\n", __func__, update_fw_status); + return scnprintf(buf, strlen(buf), "%d\n", update_fw_status); } switch (status) { @@ -5325,9 +4829,6 @@ static ssize_t pt_write_file_status_show(struct device *dev, case UPDATE_FW_INVALID_FW_IMAGE: ret = scnprintf(buf, strlen(buf), "ERROR: %d - Invalid FW image\n", status); break; - case UPDATE_FW_FILE_SEEK_ERROR: - ret = scnprintf(buf, strlen(buf), "ERROR: %d - File seek failure\n", status); - break; case UPDATE_FW_MISALIGN_FW_IMAGE: ret = scnprintf(buf, strlen(buf), "ERROR: %d - FW image is misaligned\n", status); @@ -5361,10 +4862,6 @@ static ssize_t pt_write_file_status_show(struct device *dev, ret = scnprintf(buf, strlen(buf), "ERROR: %d - No platform data\n", status); break; - case UPDATE_FW_NOT_SUPPORTED_FILE_NO: - ret = scnprintf(buf, strlen(buf), - "ERROR: %d - Not supported file number\n", status); - break; case UPDATE_FW_UNDEFINED_ERROR: default: ret = scnprintf(buf, strlen(buf), "ERROR: %d - Unknown error\n", status); @@ -5372,9 +4869,9 @@ static ssize_t pt_write_file_status_show(struct device *dev, } return ret; } -static DEVICE_ATTR(pip2_bl_status, 0444, pt_write_file_status_show, NULL); +static DEVICE_ATTR(pip2_bl_status, 0444, pt_pip2_bl_status_show, NULL); #if PT_FW_UPGRADE -static DEVICE_ATTR(update_fw_status, 0444, pt_write_file_status_show, NULL); +static DEVICE_ATTR(update_fw_status, 0444, pt_pip2_bl_status_show, NULL); #endif /******************************************************************************* * FUNCTION: pt_pip2_get_last_error_show @@ -5418,16 +4915,16 @@ exit_release: cmd->release_exclusive(dev); exit: if (rc) - return snprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", rc); + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n", rc); if (read_buf[PIP2_RESP_STATUS_OFFSET] == PIP2_RSP_ERR_NONE) { - return snprintf(buf, PT_MAX_PRBUF_SIZE, + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n" "Last Error No: 0x%02X\n", PIP2_RSP_ERR_NONE, read_buf[PIP2_RESP_BODY_OFFSET]); } else { - return snprintf(buf, PT_MAX_PRBUF_SIZE, + return scnprintf(buf, PT_MAX_PRBUF_SIZE, "Status: %d\n" "Last Error No: n/a\n", read_buf[PIP2_RESP_STATUS_OFFSET]); @@ -5435,7 +4932,6 @@ exit: } static DEVICE_ATTR(pip2_get_last_error, 0444, pt_pip2_get_last_error_show, NULL); -#endif /* !TTDL_KERNEL_SUBMISSION */ #if PT_FW_UPGRADE /******************************************************************************* @@ -5552,8 +5048,8 @@ static int pt_loader_probe(struct device *dev, void **data) #if PT_FW_UPGRADE /* Initialize boot loader status */ - if (write_file_status != UPDATE_FW_COMPLETE) - _pt_update_write_file_status(dev, UPDATE_FW_IDLE); + if (update_fw_status != UPDATE_FW_COMPLETE) + _pt_pip2_update_bl_status(dev, UPDATE_FW_IDLE, PT_NO_INC); #endif if (dut_gen == DUT_PIP2_CAPABLE) { @@ -5592,7 +5088,6 @@ static int pt_loader_probe(struct device *dev, void **data) } #endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ -#ifndef TTDL_KERNEL_SUBMISSION #ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE rc = device_create_file(dev, &dev_attr_pip2_manual_upgrade); if (rc) { @@ -5662,7 +5157,6 @@ static int pt_loader_probe(struct device *dev, void **data) goto remove_files; } #endif -#endif /* !TTDL_KERNEL_SUBMISSION */ } else { #if PT_FW_UPGRADE rc = device_create_file(dev, &dev_attr_update_fw_status); @@ -5690,7 +5184,6 @@ static int pt_loader_probe(struct device *dev, void **data) goto remove_files; } #endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ -#ifndef TTDL_KERNEL_SUBMISSION #ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE rc = device_create_file(dev, &dev_attr_forced_upgrade); if (rc) { @@ -5726,7 +5219,6 @@ static int pt_loader_probe(struct device *dev, void **data) goto remove_files; } #endif /* CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE */ -#endif /* !TTDL_KERNEL_SUBMISSION */ } if (!pdata || !pdata->loader_pdata) { @@ -5772,14 +5264,9 @@ static int pt_loader_probe(struct device *dev, void **data) mutex_init(&ld->config_lock); #endif -#ifdef UPGRADE_FW_AND_CONFIG_IN_PROBE - /* Call FW and config upgrade directly in probe */ - pt_fw_and_config_upgrade(&ld->fw_and_config_upgrade); -#else pt_debug(dev, DL_INFO, "%s: Schedule FW upgrade work\n", __func__); INIT_WORK(&ld->fw_and_config_upgrade, pt_fw_and_config_upgrade); schedule_work(&ld->fw_and_config_upgrade); -#endif pt_debug(dev, DL_INFO, "%s: Successful probe %s\n", __func__, dev_name(dev)); @@ -5787,7 +5274,6 @@ static int pt_loader_probe(struct device *dev, void **data) remove_files: -#ifndef TTDL_KERNEL_SUBMISSION #ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE device_remove_file(dev, &dev_attr_config_loading); #endif @@ -5813,7 +5299,6 @@ remove_files: device_remove_file(dev, &dev_attr_update_fw); device_remove_file(dev, &dev_attr_update_fw_status); #endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ -#endif /* !TTDL_KERNEL_SUBMISSION */ kfree(ld->pip2_data); kfree(ld); @@ -5857,7 +5342,6 @@ static void pt_loader_release(struct device *dev, void *data) #ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE device_remove_file(dev, &dev_attr_update_fw); #endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ -#ifndef TTDL_KERNEL_SUBMISSION #ifdef TTDL_DIAGNOSTICS device_remove_bin_file(dev, &bin_attr_pip2_file_read); device_remove_file(dev, &dev_attr_pip2_file_crc); @@ -5872,13 +5356,11 @@ static void pt_loader_release(struct device *dev, void *data) device_remove_file(dev, &dev_attr_pip2_manual_ram_upgrade); device_remove_file(dev, &dev_attr_pip2_manual_upgrade); #endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ -#endif /* !TTDL_KERNEL_SUBMISSION */ kfree(ld->pip2_data); } else { #ifdef CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE device_remove_file(dev, &dev_attr_update_fw); #endif /* CONFIG_TOUCHSCREEN_PARADE_BINARY_FW_UPGRADE */ -#ifndef TTDL_KERNEL_SUBMISSION #ifdef CONFIG_TOUCHSCREEN_PARADE_MANUAL_TTCONFIG_UPGRADE device_remove_bin_file(dev, &bin_attr_config_data); device_remove_file(dev, &dev_attr_config_loading); @@ -5892,7 +5374,6 @@ static void pt_loader_release(struct device *dev, void *data) #ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE device_remove_file(dev, &dev_attr_forced_upgrade); #endif /* CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE */ -#endif /* !TTDL_KERNEL_SUBMISSION */ } kfree(ld); } diff --git a/pt/pt_mt_common.c b/pt/pt_mt_common.c index 008db5c972..d1a9ade148 100644 --- a/pt/pt_mt_common.c +++ b/pt/pt_mt_common.c @@ -55,6 +55,42 @@ static void pt_mt_lift_all(struct pt_mt_data *md) } } +/******************************************************************************* + * FUNCTION: pt_get_touch_axis + * + * SUMMARY: Calculates touch axis + * + * PARAMETERS: + * *md - pointer to touch data structure + * *axis - pointer to axis calculation result + * size - size in byte + * max - max value of result + * *xy_data - pointer to input data to be parsed + * bofs - bit offset + ******************************************************************************/ +static void pt_get_touch_axis(struct pt_mt_data *md, + int *axis, int size, int max, u8 *xy_data, int bofs) +{ + int nbyte; + int next; + + for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) { + pt_debug(md->dev, DL_DEBUG, + "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d) bofs=%d\n", + __func__, *axis, *axis, size, max, xy_data, next, + xy_data[next], xy_data[next], bofs); + *axis = *axis + ((xy_data[next] >> bofs) << (nbyte * 8)); + next++; + } + + *axis &= max - 1; + + pt_debug(md->dev, DL_DEBUG, + "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d)\n", + __func__, *axis, *axis, size, max, xy_data, next, + xy_data[next], xy_data[next]); +} + /******************************************************************************* * FUNCTION: pt_get_touch_hdr * @@ -75,7 +111,7 @@ static void pt_get_touch_hdr(struct pt_mt_data *md, for (hdr = PT_TCH_TIME; hdr < PT_TCH_NUM_HDR; hdr++) { if (!si->tch_hdr[hdr].report) continue; - pt_get_touch_field(dev, &touch->hdr[hdr], + pt_get_touch_axis(md, &touch->hdr[hdr], si->tch_hdr[hdr].size, si->tch_hdr[hdr].max, xy_mode + si->tch_hdr[hdr].ofs, @@ -115,7 +151,7 @@ static void pt_get_touch_record(struct pt_mt_data *md, for (abs = PT_TCH_X; abs < PT_TCH_NUM_ABS; abs++) { if (!si->tch_abs[abs].report) continue; - pt_get_touch_field(dev, &touch->abs[abs], + pt_get_touch_axis(md, &touch->abs[abs], si->tch_abs[abs].size, si->tch_abs[abs].max, xy_data + si->tch_abs[abs].ofs, @@ -249,9 +285,8 @@ static void pt_get_mt_touches(struct pt_mt_data *md, if (PT_TOUCH_ID_MAX < si->tch_abs[PT_TCH_T].max) { pt_debug(dev, DL_ERROR, - "%s: Touch ID %d is allocated less than needed %d\n", - __func__, PT_TOUCH_ID_MAX, - (int)si->tch_abs[PT_TCH_T].max); + "%s: Touch ID num %d is allocated less than needed %d\n", + __func__, PT_TOUCH_ID_MAX, si->tch_abs[PT_TCH_T].max); return; } diff --git a/pt/pt_platform.c b/pt/pt_platform.c index f623f76c82..c4bd2b039a 100644 --- a/pt/pt_platform.c +++ b/pt/pt_platform.c @@ -29,10 +29,6 @@ #include "pt_regs.h" #include -#include -#ifdef PT_PTSBC_SUPPORT -#include -#endif #ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE /* FW for Panel ID = 0x00 */ @@ -368,193 +364,6 @@ static int pt_pinctrl_select_release(struct pt_core_platform_data *pdata, } #endif /* PT_PINCTRL_EN */ -#ifdef PT_REGULATOR_EN -#define PT_VCC_MIN_UV (2800000) -#define PT_VCC_MAX_UV (3300000) -#define PT_VDDI_MIN_UV (1800000) -#define PT_VDDI_MAX_UV (1900000) -/******************************************************************************* - * FUNCTION: pt_regulator_init - * - * SUMMARY: With regulator framework to get regulator handle and setup voltage - * level. - * - * NOTE: The function only contains setup for VCC and VDDI since AVDD AVEE is - * usually used by TDDI products while the power is setup on other driver. - * - * RETURN: - * 0 = success - * !0 = fail - * - * PARAMETERS: - * *pdata - pointer to the platform data structure - * *dev - pointer to Device structure - ******************************************************************************/ -static int pt_regulator_init(struct pt_core_platform_data *pdata, - struct device *dev) -{ - int rc = 0; - - pdata->vcc = devm_regulator_get(dev, "vcc"); - if (IS_ERR(pdata->vcc)) { - rc = PTR_ERR(pdata->vcc); - pt_debug(dev, DL_ERROR, "get vcc regulator failed,rc=%d", rc); - return rc; - } - - if (regulator_count_voltages(pdata->vcc) > 0) { - rc = regulator_set_voltage(pdata->vcc, PT_VCC_MIN_UV, - PT_VCC_MAX_UV); - if (rc) { - pt_debug(dev, DL_ERROR, - "set vcc regulator failed rc=%d", rc); - goto error_set_vcc; - } - } - - pdata->vddi = devm_regulator_get(dev, "vddi"); - if (IS_ERR(pdata->vddi)) { - rc = PTR_ERR(pdata->vddi); - pt_debug(dev, DL_ERROR, "get vddi regulator failed,rc=%d", rc); - goto error_get_vcc; - } - - if (regulator_count_voltages(pdata->vddi) > 0) { - rc = regulator_set_voltage(pdata->vddi, PT_VDDI_MIN_UV, - PT_VDDI_MAX_UV); - if (rc) { - pt_debug(dev, DL_ERROR, - "set vddi regulator failed rc=%d", rc); - goto error_set_vddi; - } - } - - return 0; - -error_set_vddi: - devm_regulator_put(pdata->vddi); -error_get_vcc: - /* - * regulator_set_voltage always select minimum legal voltage between - * min_uV and max_uV. To set the minuV to 0 means to restore the default - * value of regulator. Since regulator_set_voltage is the part to - * release regulator source, it's not necessary to check the returned - * value of it. - */ - if (regulator_count_voltages(pdata->vcc) > 0) - regulator_set_voltage(pdata->vcc, 0, PT_VCC_MAX_UV); -error_set_vcc: - devm_regulator_put(pdata->vcc); - - return rc; -} - -/******************************************************************************* - * FUNCTION: pt_setup_power_by_regulator - * - * SUMMARY: With regulator framework to set power on/off. - * - * NOTE: The function only contains setup for VCC and VDDI since AVDD AVEE is - * usually used by TDDI products while the power is setup on other driver. - * - * NOTE: The timing sequence is the EXAMPLE ONLY for TT7XXX: - * power up order : VDDI, VCC - * power down order : VCC, VDDI - * - * RETURN: - * 0 = success - * !0 = fail - * - * PARAMETERS: - * *pdata - pointer to the platform data structure - * on - flag to decide power state,PT_MT_POWER_ON/PT_MT_POWER_OFF - * *dev - pointer to Device structure - ******************************************************************************/ -static int pt_setup_power_by_regulator(struct pt_core_platform_data *pdata, - int on, struct device *dev) -{ - int rc = 0; - - if (IS_ERR(pdata->vddi) || IS_ERR(pdata->vcc)) { - pt_debug(dev, DL_ERROR, "vddi or vcc is not valid\n"); - return -EINVAL; - } - - if (on == PT_MT_POWER_ON) { - rc = regulator_enable(pdata->vddi); - if (rc) { - pt_debug(dev, DL_ERROR, - "enable vddi regulator failed,rc=%d", rc); - } - /* Ensure the power goes stable */ - usleep_range(3000, 4000); - - rc = regulator_enable(pdata->vcc); - if (rc) { - pt_debug(dev, DL_ERROR, - "enable vcc regulator failed,rc=%d", rc); - } - /* Ensure the power goes stable */ - usleep_range(3000, 4000); - } else { - rc = regulator_disable(pdata->vcc); - if (rc) { - pt_debug(dev, DL_ERROR, - "disable vcc regulator failed,rc=%d", rc); - } - rc = regulator_disable(pdata->vddi); - if (rc) { - pt_debug(dev, DL_ERROR, - "disable vddi regulator failed,rc=%d", rc); - } - /* Ensure the power ramp down completely */ - usleep_range(10000, 12000); - } - - return rc; -} - -/******************************************************************************* - * FUNCTION: pt_regulator_release - * - * SUMMARY: With regulator framework to release regulator resource - * - * NOTE: The regulator MUST be disabled before this call. - * NOTE: The function only contains setup for VCC and VDDI since AVDD AVEE is - * usually used by TDDI products while the power is setup on other driver. - * - * RETURN: - * 0 = success - * !0 = fail - * - * PARAMETERS: - * *pdata - pointer to the platform data structure - * *dev - pointer to Device structure - ******************************************************************************/ -static int pt_regulator_release(struct pt_core_platform_data *pdata, - struct device *dev) -{ - if (IS_ERR(pdata->vddi) || IS_ERR(pdata->vcc)) - return -EINVAL; - - /* - * regulator_set_voltage always select minimum legal voltage between - * min_uV and max_uV. To set the minuV to 0 means to restore the default - * value of regulator. Since regulator_set_voltage is the part to - * release regulator source, it's not necessary to check the returned - * value of it. - */ - if (regulator_count_voltages(pdata->vddi) > 0) - regulator_set_voltage(pdata->vddi, 0, PT_VDDI_MAX_UV); - devm_regulator_put(pdata->vddi); - - if (regulator_count_voltages(pdata->vcc) > 0) - regulator_set_voltage(pdata->vcc, 0, PT_VCC_MAX_UV); - devm_regulator_put(pdata->vcc); - - return 0; -} -#endif /* PT_REGULATOR_EN */ /******************************************************************************* * FUNCTION: pt_init * @@ -577,8 +386,8 @@ int pt_init(struct pt_core_platform_data *pdata, int ddi_rst_gpio = pdata->ddi_rst_gpio; int rc = 0; - if (on) { #ifdef PT_PINCTRL_EN + if (on) { rc = pt_pinctrl_init(pdata, dev); if (!rc) { pt_pinctrl_select_normal(pdata, dev); @@ -586,17 +395,8 @@ int pt_init(struct pt_core_platform_data *pdata, pt_debug(dev, DL_ERROR, "%s: Failed to request pinctrl\n", __func__); } -#endif -#ifdef PT_REGULATOR_EN - rc = pt_regulator_init(pdata, dev); - if (rc) { - pt_debug(dev, DL_ERROR, - "%s: Failed requesting regulator rc=%d", - __func__, rc); - } -#endif } - +#endif if (on && rst_gpio) { /* Configure RST GPIO */ pt_debug(dev, DL_WARN, "%s: Request RST GPIO %d", @@ -687,9 +487,6 @@ int pt_init(struct pt_core_platform_data *pdata, gpio_free(irq_gpio); if (rst_gpio) gpio_free(rst_gpio); -#ifdef PT_REGULATOR_EN - pt_regulator_release(pdata, dev); -#endif #ifdef PT_PINCTRL_EN pt_pinctrl_select_release(pdata, dev); #endif @@ -721,6 +518,193 @@ success: return rc; } +/******************************************************************************* + * FUNCTION: pt_wakeup + * + * SUMMARY: Resume power for "power on/off" sleep strategy which against to + * "deepsleep" strategy. + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * *dev - pointer to Device structure + * *ignore_irq - pointer to atomic structure to allow the host ignoring false + * IRQ during power up + ******************************************************************************/ +static int pt_wakeup(struct pt_core_platform_data *pdata, + struct device *dev, atomic_t *ignore_irq) +{ + /* Example for TT7XXX */ + int rc = 0; + +#ifdef PT_PINCTRL_EN + pt_pinctrl_select_normal(pdata, dev); +#endif + +#ifdef TT7XXX_EXAMPLE + pt_debug(dev, DL_INFO, + "%s: Enable defined pwr: VDDI, VCC\n", __func__); + /* + * Force part into RESET by holding XRES#(TP_XRES) + * while powering it up + */ + if (pdata->rst_gpio) + gpio_set_value(pdata->rst_gpio, 0); + + /* Turn on VDDI [Digital Interface] (+1.8v) */ + if (pdata->vddi_gpio) { + rc = gpio_request(pdata->vddi_gpio, NULL); + if (rc < 0) { + gpio_free(pdata->vddi_gpio); + rc = gpio_request(pdata->vddi_gpio, NULL); + } + if (rc < 0) { + pr_err("%s: Failed requesting VDDI GPIO %d\n", + __func__, pdata->vddi_gpio); + } + rc = gpio_direction_output(pdata->vddi_gpio, 1); + if (rc) + pr_err("%s: setcfg for VDDI GPIO %d failed\n", + __func__, pdata->vddi_gpio); + gpio_free(pdata->vddi_gpio); + usleep_range(3000, 4000); + } + + /* Turn on VCC */ + if (pdata->vcc_gpio) { + rc = gpio_request(pdata->vcc_gpio, NULL); + if (rc < 0) { + gpio_free(pdata->vcc_gpio); + rc = gpio_request(pdata->vcc_gpio, NULL); + } + if (rc < 0) { + pr_err("%s: Failed requesting VCC GPIO %d\n", + __func__, pdata->vcc_gpio); + } + rc = gpio_direction_output(pdata->vcc_gpio, 1); + if (rc) + pr_err("%s: setcfg for VCC GPIO %d failed\n", + __func__, pdata->vcc_gpio); + gpio_free(pdata->vcc_gpio); + usleep_range(3000, 4000); + } + + usleep_range(12000, 15000); + /* Force part out of RESET by releasing XRES#(TP_XRES) */ + if (pdata->rst_gpio) + gpio_set_value(pdata->rst_gpio, 1); +#else + pt_debug(dev, DL_INFO, "%s: Enable defined pwr\n", __func__); +#endif + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_sleep + * + * SUMMARY: Suspend power for "power on/off" sleep strategy which against to + * "deepsleep" strategy. + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * *dev - pointer to Device structure + * *ignore_irq - pointer to atomic structure to allow the host ignoring false + * IRQ during power down + ******************************************************************************/ +static int pt_sleep(struct pt_core_platform_data *pdata, + struct device *dev, atomic_t *ignore_irq) +{ + /* Example for TT7XXX */ + int rc = 0; + +#ifdef TT7XXX_EXAMPLE + pt_debug(dev, DL_INFO, + "%s: Turn off defined pwr: VCC, VDDI\n", __func__); + /* + * Force part into RESET by holding XRES#(TP_XRES) + * while powering it up + */ + if (pdata->rst_gpio) + gpio_set_value(pdata->rst_gpio, 0); + + /* Turn off VCC */ + if (pdata->vcc_gpio) { + rc = gpio_request(pdata->vcc_gpio, NULL); + if (rc < 0) { + gpio_free(pdata->vcc_gpio); + rc = gpio_request(pdata->vcc_gpio, NULL); + } + if (rc < 0) { + pr_err("%s: Failed requesting VCC GPIO %d\n", + __func__, pdata->vcc_gpio); + } + rc = gpio_direction_output(pdata->vcc_gpio, 0); + if (rc) + pr_err("%s: setcfg for VCC GPIO %d failed\n", + __func__, pdata->vcc_gpio); + gpio_free(pdata->vcc_gpio); + } + + /* Turn off VDDI [Digital Interface] (+1.8v) */ + if (pdata->vddi_gpio) { + rc = gpio_request(pdata->vddi_gpio, NULL); + if (rc < 0) { + gpio_free(pdata->vddi_gpio); + rc = gpio_request(pdata->vddi_gpio, NULL); + } + if (rc < 0) { + pr_err("%s: Failed requesting VDDI GPIO %d\n", + __func__, pdata->vddi_gpio); + } + rc = gpio_direction_output(pdata->vddi_gpio, 0); + if (rc) + pr_err("%s: setcfg for VDDI GPIO %d failed\n", + __func__, pdata->vddi_gpio); + gpio_free(pdata->vddi_gpio); + usleep_range(10000, 12000); + } +#else + pt_debug(dev, DL_INFO, "%s: Turn off defined pwr\n", __func__); +#endif +#ifdef PT_PINCTRL_EN + pt_pinctrl_select_suspend(pdata, dev); +#endif + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_power + * + * SUMMARY: Wrapper function to resume/suspend power with function + * pt_wakeup()/pt_sleep(). + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * *pdata - pointer to the platform data structure + * on - flag to remsume/suspend power(0:resume; 1:suspend) + * *dev - pointer to Device structure + * *ignore_irq - pointer to atomic structure to allow the host ignoring false + * IRQ during power up/down + ******************************************************************************/ +int pt_power(struct pt_core_platform_data *pdata, + int on, struct device *dev, atomic_t *ignore_irq) +{ + if (on) + return pt_wakeup(pdata, dev, ignore_irq); + + return pt_sleep(pdata, dev, ignore_irq); +} + /******************************************************************************* * FUNCTION: pt_irq_stat * @@ -780,29 +764,42 @@ int pt_detect(struct pt_core_platform_data *pdata, } #endif -#ifndef PT_REGULATOR_EN /******************************************************************************* - * FUNCTION: pt_setup_power_by_gpio + * FUNCTION: pt_setup_power * - * SUMMARY: With GPIOs to control LDO directly to set power on/off. + * SUMMARY: Turn on/turn off voltage regulator * * RETURN: * 0 = success - * !0 = fail + * !0 = failure * * PARAMETERS: - * *pdata - pointer to the platform data structure - * on - flag to decide power state,PT_MT_POWER_ON/PT_MT_POWER_OFF - * *dev - pointer to Device structure + * *pdata - pointer to core platform data + * on - flag to decide power state,PT_MT_POWER_ON/PT_MT_POWER_OFF + * *dev - pointer to device ******************************************************************************/ -static int pt_setup_power_by_gpio(struct pt_core_platform_data *pdata, - int on, struct device *dev) +int pt_setup_power(struct pt_core_platform_data *pdata, int on, + struct device *dev) { - int rc = 0; int en_vcc = pdata->vcc_gpio; int en_vddi = pdata->vddi_gpio; int en_avdd = pdata->avdd_gpio; int en_avee = pdata->avee_gpio; + int rc = 0; + + /* + * For TDDI parts, force part into RESET by holding DDI XRES + * while powering it up + */ + if (pdata->ddi_rst_gpio) + gpio_set_value(pdata->ddi_rst_gpio, 0); + + /* + * Force part into RESET by holding XRES#(TP_XRES) + * while powering it up + */ + if (pdata->rst_gpio) + gpio_set_value(pdata->rst_gpio, 0); if (on == PT_MT_POWER_ON) { /* @@ -975,58 +972,6 @@ static int pt_setup_power_by_gpio(struct pt_core_platform_data *pdata, } } - return rc; -} -#endif /* PT_REGULATOR_EN */ -/******************************************************************************* - * FUNCTION: pt_setup_power - * - * SUMMARY: Turn on/turn off voltage regulator - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *pdata - pointer to core platform data - * on - flag to decide power state,PT_MT_POWER_ON/PT_MT_POWER_OFF - * *dev - pointer to device - ******************************************************************************/ -int pt_setup_power(struct pt_core_platform_data *pdata, int on, - struct device *dev) -{ - int rc = 0; - - /* - * For TDDI parts, force part into RESET by holding DDI XRES - * while powering it up - */ - if (pdata->ddi_rst_gpio) - gpio_set_value(pdata->ddi_rst_gpio, 0); - - /* - * Force part into RESET by holding XRES#(TP_XRES) - * while powering it up - */ - if (pdata->rst_gpio) - gpio_set_value(pdata->rst_gpio, 0); - -#ifdef PT_REGULATOR_EN - rc = pt_setup_power_by_regulator(pdata, on, dev); - if (rc) { - pt_debug(dev, DL_ERROR, - "%s: Failed setup power by regulator rc=%d", - __func__, rc); - } -#else /* PT_REGULATOR_EN */ - rc = pt_setup_power_by_gpio(pdata, on, dev); - if (rc) { - pt_debug(dev, DL_ERROR, - "%s: Failed setup power by gpio rc=%d", - __func__, rc); - } -#endif /* PT_REGULATOR_EN */ - /* Force part out of RESET by releasing XRES#(TP_XRES) */ if (pdata->rst_gpio) gpio_set_value(pdata->rst_gpio, 1); @@ -1038,137 +983,6 @@ int pt_setup_power(struct pt_core_platform_data *pdata, int on, return rc; } -/******************************************************************************* - * FUNCTION: pt_wakeup - * - * SUMMARY: Resume power for "power on/off" sleep strategy which against to - * "deepsleep" strategy. - * - * RETURN: - * 0 = success - * !0 = fail - * - * PARAMETERS: - * *pdata - pointer to the platform data structure - * *dev - pointer to Device structure - * *ignore_irq - pointer to atomic structure to allow the host ignoring false - * IRQ during power up - ******************************************************************************/ -static int pt_wakeup(struct pt_core_platform_data *pdata, - struct device *dev, atomic_t *ignore_irq) -{ - int rc = 0; - -#ifdef PT_PINCTRL_EN - pt_pinctrl_select_normal(pdata, dev); -#endif - rc = pt_setup_power(pdata, PT_MT_POWER_ON, dev); - if (rc) - pt_debug(dev, DL_ERROR, "%s: Failed setup power\n", __func__); - - return rc; -} - -/******************************************************************************* - * FUNCTION: pt_sleep - * - * SUMMARY: Suspend power for "power on/off" sleep strategy which against to - * "deepsleep" strategy. - * - * RETURN: - * 0 = success - * !0 = fail - * - * PARAMETERS: - * *pdata - pointer to the platform data structure - * *dev - pointer to Device structure - * *ignore_irq - pointer to atomic structure to allow the host ignoring false - * IRQ during power down - ******************************************************************************/ -static int pt_sleep(struct pt_core_platform_data *pdata, - struct device *dev, atomic_t *ignore_irq) -{ - int rc = 0; - - rc = pt_setup_power(pdata, PT_MT_POWER_OFF, dev); - if (rc) - pt_debug(dev, DL_ERROR, "%s: Failed setup power\n", __func__); - -#ifdef PT_PINCTRL_EN - pt_pinctrl_select_suspend(pdata, dev); -#endif - return rc; -} - -/******************************************************************************* - * FUNCTION: pt_power - * - * SUMMARY: Wrapper function to resume/suspend power with function - * pt_wakeup()/pt_sleep(). - * - * RETURN: - * 0 = success - * !0 = fail - * - * PARAMETERS: - * *pdata - pointer to the platform data structure - * on - flag to remsume/suspend power(0:resume; 1:suspend) - * *dev - pointer to Device structure - * *ignore_irq - pointer to atomic structure to allow the host ignoring false - * IRQ during power up/down - ******************************************************************************/ -int pt_power(struct pt_core_platform_data *pdata, - int on, struct device *dev, atomic_t *ignore_irq) -{ - if (on) - return pt_wakeup(pdata, dev, ignore_irq); - - return pt_sleep(pdata, dev, ignore_irq); -} - -#ifdef PT_PTSBC_SUPPORT - -static struct workqueue_struct *parade_wq; -static u32 int_handle; - -/******************************************************************************* - * FUNCTION: pt_irq_work_function - * - * SUMMARY: Work function for queued IRQ activity - * - * RETURN: Void - * - * PARAMETERS: - * *work - pointer to work structure - ******************************************************************************/ -static void pt_irq_work_function(struct work_struct *work) -{ - struct pt_core_data *cd = container_of(work, - struct pt_core_data, irq_work); - - pt_irq(cd->irq, (void *)cd); -} - -/******************************************************************************* - * FUNCTION: pt_irq_wrapper - * - * SUMMARY: Wrapper function for IRQ to queue the irq_work function - * - * RETURN: - * 0 = success - * !0 = failure - * - * PARAMETERS: - * *handle - void pointer to contain the core_data pointer - ******************************************************************************/ -peint_handle *pt_irq_wrapper(void *handle) -{ - struct pt_core_data *cd = (struct pt_core_data *)handle; - - queue_work(parade_wq, &cd->irq_work); - return 0; -} -#endif /* PT_PTSBC_SUPPORT */ /******************************************************************************* * FUNCTION: pt_setup_irq @@ -1209,73 +1023,23 @@ int pt_setup_irq(struct pt_core_platform_data *pdata, int on, } if (cd->irq < 0) return -EINVAL; - cd->irq_enabled = true; - pt_debug(dev, DL_INFO, "%s: initialize threaded irq=%d\n", __func__, cd->irq); - if (pdata->level_irq_udelay > 0) -#ifdef PT_PTSBC_SUPPORT /* use level triggered interrupts */ - irq_flags = TRIG_LEVL_LOW; + irq_flags = IRQF_TRIGGER_LOW; else /* use edge triggered interrupts */ - irq_flags = TRIG_EDGE_NEGATIVE; -#else - /* use level triggered interrupts */ - irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; - else - /* use edge triggered interrupts */ - irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; -#endif /* PT_PTSBC_SUPPORT */ - -#ifdef PT_PTSBC_SUPPORT - /* Adding new work queue to cd struct */ - INIT_WORK(&cd->irq_work, pt_irq_work_function); - - parade_wq = create_singlethread_workqueue("parade_wq"); - if (!parade_wq) - pt_debug(dev, DL_ERROR, "%s Create workqueue failed.\n", - __func__); - - int_handle = sw_gpio_irq_request(pdata->irq_gpio, irq_flags, - (peint_handle)pt_irq_wrapper, cd); - if (!int_handle) { - pt_debug(dev, DL_ERROR, - "%s: PARADE could not request irq\n", __func__); - rc = -1; - } else { - rc = 0; - /* clk=0: 32Khz; clk=1: 24Mhz*/ - ctp_set_int_port_rate(pdata->irq_gpio, 1); - /* - * Debounce INT Line by clock divider: 2^n. E.g. The - * para:0x03 means the period of interrupt controller is - * 0.33 us = (2^3)/24. It has ability to measure the - * high/low width of the pulse bigger than 1 us. - */ - ctp_set_int_port_deb(pdata->irq_gpio, 0x03); - pt_debug(cd->dev, DL_INFO, - "%s: Parade sw_gpio_irq_request SUCCESS\n", - __func__); - } -#else + irq_flags = IRQF_TRIGGER_FALLING; rc = request_threaded_irq(cd->irq, NULL, pt_irq, - irq_flags, dev_name(dev), cd); + irq_flags | IRQF_ONESHOT, dev_name(dev), cd); if (rc < 0) pt_debug(dev, DL_ERROR, "%s: Error, could not request irq\n", __func__); -#endif /* PT_PTSBC_SUPPORT */ } else { disable_irq_nosync(cd->irq); -#ifndef PT_PTSBC_SUPPORT free_irq(cd->irq, cd); -#else - sw_gpio_irq_free(int_handle); - cancel_work_sync(&cd->irq_work); - destroy_workqueue(parade_wq); -#endif /* PT_PTSBC_SUPPORT */ } return rc; } diff --git a/pt/pt_proximity.c b/pt/pt_proximity.c index ae2998b580..1ad0006c08 100644 --- a/pt/pt_proximity.c +++ b/pt/pt_proximity.c @@ -1,4 +1,3 @@ -#ifndef TTDL_KERNEL_SUBMISSION /* * pt_proximity.c * Parade TrueTouch(TM) Standard Product Proximity Module. @@ -75,6 +74,42 @@ static void pt_report_proximity(struct pt_proximity_data *pd, input_sync(pd->input); } +/******************************************************************************* + * FUNCTION: pt_get_touch_axis + * + * SUMMARY: Calculates touch axis + * + * PARAMETERS: + * *pd - pointer to proximity data structure + * *axis - pointer to axis calculation result + * size - size in bytes + * max - max value of result + * *xy_data - pointer to input data to be parsed + * bofs - bit offset + ******************************************************************************/ +static void pt_get_touch_axis(struct pt_proximity_data *pd, + int *axis, int size, int max, u8 *xy_data, int bofs) +{ + int nbyte; + int next; + + for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) { + pt_debug(pd->dev, DL_INFO, + "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d) bofs=%d\n", + __func__, *axis, *axis, size, max, xy_data, next, + xy_data[next], xy_data[next], bofs); + *axis = *axis + ((xy_data[next] >> bofs) << (nbyte * 8)); + next++; + } + + *axis &= max - 1; + + pt_debug(pd->dev, DL_INFO, + "%s: *axis=%02X(%d) size=%d max=%08X xy_data=%p xy_data[%d]=%02X(%d)\n", + __func__, *axis, *axis, size, max, xy_data, next, + xy_data[next], xy_data[next]); +} + /******************************************************************************* * FUNCTION: pt_get_touch_hdr * @@ -95,7 +130,7 @@ static void pt_get_touch_hdr(struct pt_proximity_data *pd, for (hdr = PT_TCH_TIME; hdr < PT_TCH_NUM_HDR; hdr++) { if (!si->tch_hdr[hdr].report) continue; - pt_get_touch_field(dev, &touch->hdr[hdr], + pt_get_touch_axis(pd, &touch->hdr[hdr], si->tch_hdr[hdr].size, si->tch_hdr[hdr].max, xy_mode + si->tch_hdr[hdr].ofs, @@ -126,7 +161,7 @@ static void pt_get_touch(struct pt_proximity_data *pd, for (abs = PT_TCH_X; abs < PT_TCH_NUM_ABS; abs++) { if (!si->tch_abs[abs].report) continue; - pt_get_touch_field(dev, &touch->abs[abs], + pt_get_touch_axis(pd, &touch->abs[abs], si->tch_abs[abs].size, si->tch_abs[abs].max, xy_data + si->tch_abs[abs].ofs, @@ -778,4 +813,3 @@ int pt_proximity_release(struct device *dev) return 0; } -#endif /* !TTDL_KERNEL_SUBMISSION */ diff --git a/pt/pt_regs.h b/pt/pt_regs.h index 2062a1bbcf..78e78798bf 100644 --- a/pt/pt_regs.h +++ b/pt/pt_regs.h @@ -74,9 +74,12 @@ #include #include +#include +#include #include #include #include +#include #define STATUS_SUCCESS 0 #define STATUS_FAIL -1 @@ -85,7 +88,6 @@ #define PT_FW_FILE_SUFFIX ".bin" #define PT_FW_FILE_NAME "tt_fw.bin" #define PT_FW_RAM_FILE_NAME "tt_fw_ram.bin" -#ifndef TTDL_KERNEL_SUBMISSION /* Enable special TTDL features */ #ifndef TTHE_TUNER_SUPPORT #define TTHE_TUNER_SUPPORT @@ -98,7 +100,6 @@ #ifndef EASYWAKE_TSG6 #define EASYWAKE_TSG6 #endif -#endif /* !TTDL_KERNEL_SUBMISSION */ #ifdef TTHE_TUNER_SUPPORT #define PT_TTHE_TUNER_FILE_NAME "tthe_tuner" @@ -118,13 +119,11 @@ * The largest PIP message is the PIP2 FILE_WRITE which has: * 2 byte register * 4 byte header - * 1 byte file handle * 256 byte payload * 2 byte CRC */ -#define PT_MAX_PIP2_MSG_SIZE 265 +#define PT_MAX_PIP2_MSG_SIZE 264 #define PT_MAX_PIP1_MSG_SIZE 255 -#define PT_HID_DESC_SIZE 30 /* * The minimun size of PIP2 packet includes: @@ -165,26 +164,6 @@ enum PT_STARTUP_STATUS { STARTUP_STATUS_FULL = 0x1FF }; -#define SLAVE_DETECT_MASK 0x01 -/* TTDL Built In Self Test selection bit masks */ -enum PT_TTDL_BIST_TESTS { - PT_BIST_BUS_TEST = 0x01, - PT_BIST_IRQ_TEST = 0x02, - PT_BIST_TP_XRES_TEST = 0x04, - PT_BIST_SLAVE_BUS_TEST = 0x08, - PT_BIST_SLAVE_IRQ_TEST = 0x10, - PT_BIST_SLAVE_XRES_TEST = 0x20 -}; - -/* tthe_tuner node format options */ -enum PT_TTHE_TUNER_FORMAT { - PT_TTHE_TUNER_FORMAT_HID_USB = 0x01, - PT_TTHE_TUNER_FORMAT_HID_I2C = 0x02, - PT_TTHE_TUNER_FORMAT_HID_FINGER_TO_PIP = 0x03, - PT_TTHE_TUNER_FORMAT_HID_FINGER_AND_PEN_TO_PIP = 0x04, - PT_TTHE_TUNER_FORMAT_RESERVED = 0xFE, -}; - #define PT_INITIAL_SHOW_TIME_STAMP 0 /* @@ -220,9 +199,7 @@ enum PT_PIP_REPORT_ID { enum PT_HID_REPORT_ID { PT_HID_FINGER_REPORT_ID = 0x01, - PT_HID_PEN_REPORT_ID = 0x02, - PT_HID_VS_FINGER_REPORT_ID = 0x41, /* Vendor Specific ID */ - PT_HID_VS_PEN_REPORT_ID = 0x42 /* Vendor Specific ID */ + PT_HID_PEN_REPORT_ID = 0x02 }; @@ -230,7 +207,6 @@ enum PT_HID_REPORT_ID { #define HID_VENDOR_ID 0x04B4 #define HID_APP_PRODUCT_ID 0xC101 #define HID_VERSION 0x0100 -#define HID_REPORT_DESC_ID 0xF6 #define HID_APP_REPORT_ID 0xF7 #define HID_BL_REPORT_ID 0xFF #define HID_RESPONSE_REPORT_ID 0xF0 @@ -259,7 +235,6 @@ enum PT_HID_REPORT_ID { #define PT_BL_WAIT_FOR_SENTINEL 500 #define PT_REQUEST_ENUM_TIMEOUT 4000 #define PT_GET_HID_DESCRIPTOR_TIMEOUT 500 -#define PT_HID_GET_REPORT_DESCRIPTOR_TIMEOUT 500 #define PT_HID_CMD_DEFAULT_TIMEOUT 500 #define PT_PIP_CMD_DEFAULT_TIMEOUT 2000 #define PT_PIP1_CMD_DEFAULT_TIMEOUT 1000 @@ -272,11 +247,6 @@ enum PT_HID_REPORT_ID { #define PT_PIP1_CMD_INITIATE_BL_TIMEOUT 20000 #define PT_PIP1_CMD_PROGRAM_AND_VERIFY_TIMEOUT 400 #define PT_PIP2_CMD_FILE_ERASE_TIMEOUT 3000 -/* - * BL internal timeout is 500 ms and here it is slightly larger to consider - * the BUS communication cost. (Bugz#92376) - */ -#define PT_PIP2_CMD_FILE_SECTOR_ERASE_TIMEOUT 600 /* Max counts */ #define PT_WATCHDOG_RETRY_COUNT 30 @@ -293,7 +263,6 @@ enum PT_HID_REPORT_ID { #define BTN_INPUT_HEADER_SIZE 5 #define SENSOR_REPORT_SIZE 150 #define SENSOR_HEADER_SIZE 4 -#define MAX_TOUCH_NUM 6 /* helpers */ #define GET_NUM_TOUCHES(x) ((x) & 0x1F) @@ -318,22 +287,6 @@ enum PT_HID_REPORT_ID { '\255') #define HEXOF(x) (x - _base(x)) -#define HID_ITEM_SIZE_MASK 0x03 -#define HID_ITEM_TYPE_MASK 0x0C -#define HID_ITEM_TAG_MASK 0xF0 - -#define HID_ITEM_SIZE_SHIFT 0 -#define HID_ITEM_TYPE_SHIFT 2 -#define HID_ITEM_TAG_SHIFT 4 - -#define HID_GET_ITEM_SIZE(x) \ - ((x & HID_ITEM_SIZE_MASK) >> HID_ITEM_SIZE_SHIFT) -#define HID_GET_ITEM_TYPE(x) \ - ((x & HID_ITEM_TYPE_MASK) >> HID_ITEM_TYPE_SHIFT) -#define HID_GET_ITEM_TAG(x) \ - ((x & HID_ITEM_TAG_MASK) >> HID_ITEM_TAG_SHIFT) - - #define IS_EASY_WAKE_CONFIGURED(x) \ ((x) != 0 && (x) != 0xFF) @@ -381,7 +334,6 @@ enum PT_HID_REPORT_ID { #define PT_DRV_DBG_CLEAR_PARM_LIST 110 #define PT_DRV_DBG_FORCE_BUS_READ 111 #define PT_DRV_DBG_CLEAR_CAL_DATA 112 -#define PT_DUT_DBG_REPORT_DESC 113 /* * Commands that require additional parameters @@ -406,15 +358,25 @@ enum PT_HID_REPORT_ID { #define PT_DRV_DBG_SET_FORCE_SEQ 214 #define PT_DRV_DBG_BL_WITH_NO_INT 215 #define PT_DRV_DBG_CAL_CACHE_IN_HOST 216 -#define PT_DRV_DBG_NUM_DEVICES 217 +#define PT_DRV_DBG_MULTI_CHIP 217 #define PT_DRV_DBG_SET_PANEL_ID_TYPE 218 #define PT_DRV_DBG_PIP_TIMEOUT 219 -#define PT_DRV_DBG_CORE_PLATFORM_FLAG 220 +#define PT_DRV_DBG_TTHE_HID_USB_FORMAT 220 #ifdef TTDL_PTVIRTDUT_SUPPORT #define PT_DRV_DBG_SET_HW_DETECT 298 #define PT_DRV_DBG_VIRTUAL_I2C_DUT 299 #endif /* TTDL_PTVIRTDUT_SUPPORT */ +/* TTDL Built In Self Test selection bit masks */ +#define PT_TTDL_BIST_BUS_TEST 0x01 +#define PT_TTDL_BIST_IRQ_TEST 0x02 +#define PT_TTDL_BIST_TP_XRES_TEST 0x04 +#define PT_TTDL_BIST_SLAVE_BUS_TEST 0x08 +#define PT_TTDL_BIST_SLAVE_IRQ_TEST 0x10 +#define PT_TTDL_BIST_SLAVE_XRES_TEST 0x20 + +#define SLAVE_DETECT_MASK 0x01 + #define VIRT_MAX_IRQ_RELEASE_TIME_US 500000 #endif /* TTDL DIAGNOSTICS */ @@ -448,9 +410,8 @@ enum PT_HID_REPORT_ID { #define HID_PT_BUTTONSIGNAL 0xff010065 #define HID_PT_MAJOR_CONTACT_AXIS_LENGTH 0xff010066 #define HID_PT_MINOR_CONTACT_AXIS_LENGTH 0xff010067 -#define HID_PT_TCH_COL_USAGE_PG 0x000D0004 +#define HID_PT_TCH_COL_USAGE_PG 0x000D0022 #define HID_PT_BTN_COL_USAGE_PG 0xFF010020 -#define HID_PT_PEN_COL_USAGE_PG 0x000D0020 #define PANEL_ID_NOT_ENABLED 0xFF @@ -621,30 +582,26 @@ enum PIP2_FW_SYSTEM_MODE { /* PIP2 Command/Response data and structures */ enum PIP2_FILE_ID { - PIP2_RAM_FILE = 0x00, - PIP2_FW_FILE = 0x01, - PIP2_CONFIG_FILE = 0x02, - PIP2_FILE_3 = 0x03, - PIP2_FILE_4 = 0x04, - PIP2_FILE_5 = 0x05, - PIP2_FILE_6 = 0x06, - PIP2_FILE_7 = 0x07, - PIP2_FILE_8 = 0x08, - PIP2_FILE_RESERVED = 0x0F, - PIP2_FILE_MAX = PIP2_FILE_RESERVED, + PIP2_RAM_FILE = 0x00, + PIP2_FW_FILE = 0x01, + PIP2_CONFIG_FILE = 0x02, + PIP2_FILE_3 = 0x03, + PIP2_FILE_4 = 0x04, + PIP2_FILE_5 = 0x05, + PIP2_FILE_6 = 0x06, + PIP2_FILE_7 = 0x07, + PIP2_FILE_MAX = PIP2_FILE_7, }; /* Optimize packet sizes per Allwinner H3 bus drivers */ -#define PIP2_FILE_WRITE_LEN_PER_PACKET (245) -#define PIP2_BL_I2C_FILE_WRITE_LEN_PER_PACKET (245) -#define PIP2_BL_SPI_FILE_WRITE_LEN_PER_PACKET (256) -#define PIP2_FILE_WRITE_MAX_LEN_PER_PACKET \ - PIP2_BL_SPI_FILE_WRITE_LEN_PER_PACKET +#define PIP2_FILE_WRITE_LEN_PER_PACKET 245 +#define PIP2_BL_I2C_FILE_WRITE_LEN_PER_PACKET 245 +#define PIP2_BL_SPI_FILE_WRITE_LEN_PER_PACKET 256 enum DUT_GENERATION { DUT_UNKNOWN = 0x00, - DUT_PIP1_ONLY = 0x01, - DUT_PIP2_CAPABLE = 0x02, + DUT_PIP1_ONLY = 0x01, + DUT_PIP2_CAPABLE = 0x02, }; enum PIP2_RSP_ERR { @@ -673,10 +630,6 @@ enum PIP2_RSP_ERR { PIP2_RSP_ERR_UNKNOWN_REGISTER = 0x16, PIP2_RSP_ERR_BAD_LENGTH = 0x17, PIP2_RSP_ERR_TRIM_FAILURE = 0x18, - PIP2_RSP_ERR_BAD_SEQ = 0x19, - PIP2_RSP_ERR_BUF_TOO_SMALL = 0x1A, - PIP2_RSP_ERR_ASYNC_SEQ = 0x1B, - PIP2_RSP_ERR_EXEC_IMAGE = 0x1C, }; /* @@ -731,7 +684,7 @@ enum pip1_bl_status { ERROR_FLASH_ARRAY, ERROR_FLASH_ROW, ERROR_FLASH_PROTECTION, - ERROR_UKNOWN = 15, + ERROR_UNKNOWN = 15, ERROR_INVALID, }; @@ -742,18 +695,6 @@ enum pt_mode { PT_MODE_IGNORE = 255, }; -enum pt_protocol_mode { - PT_PROTOCOL_MODE_PIP = 0, - PT_PROTOCOL_MODE_HID = 1, - PT_PROTOCOL_MODE_IGNORE = 255, -}; - -struct pt_dut_status { - enum PIP2_FW_SYSTEM_MODE fw_system_mode; - enum pt_mode mode; - enum pt_protocol_mode protocol_mode; -} __packed; - enum PT_ENTER_BL_RESULT { PT_ENTER_BL_PASS = 0, PT_ENTER_BL_ERROR = 1, @@ -823,12 +764,8 @@ enum pt_self_test_result { }; #define PT_ST_PRINT_RESULTS true #define PT_ST_NOPRINT false - -enum pt_st_get_result { - PT_ST_DONT_GET_RESULTS = 0, - PT_ST_GET_RESULTS = 1, - PT_ST_GET_RESULTS_BASED_ON_DATA = 2, -}; +#define PT_ST_GET_RESULTS true +#define PT_ST_DONT_GET_RESULTS false /* * Maximum number of parameters for the fw_self_test sysfs (255 - 12 + 2) @@ -1058,8 +995,7 @@ struct pt_tch_abs_params { size_t min; /* min value */ size_t max; /* max value */ size_t bofs; /* bit offset */ - u8 report; /* non-zero: valid; 0: invalid */ - size_t logical_max; /* logical max value */ + u8 report; }; struct pt_touch { @@ -1067,55 +1003,6 @@ struct pt_touch { int abs[PT_TCH_NUM_ABS]; }; -enum pt_pen_abs { /* for ordering within the extracted pen data array */ - PT_PEN_X, /* X */ - PT_PEN_Y, /* Y */ - PT_PEN_P, /* P (Z) */ - PT_PEN_X_TILT, /* X TILT */ - PT_PEN_Y_TILT, /* Y TILT */ - PT_PEN_TS, /* Tip Switch */ - PT_PEN_BS, /* Barrel Switch */ - PT_PEN_IV, /* Invert */ - PT_PEN_ER, /* Eraser */ - PT_PEN_2ND_BS, /* 2nd Barrel Switch */ - PT_PEN_IR, /* In Range */ - PT_PEN_NUM_ABS, -}; - -static const int pt_pen_abs_field_map[] = { - [PT_PEN_X] = 0x00010030 /* HID_GD_X */, - [PT_PEN_Y] = 0x00010031 /* HID_GD_Y */, - [PT_PEN_P] = 0x000D0030, - [PT_PEN_X_TILT] = 0x000D003D, - [PT_PEN_Y_TILT] = 0x000D003E, - [PT_PEN_TS] = 0x000D0042, - [PT_PEN_BS] = 0x000D0044, - [PT_PEN_IV] = 0x000D003C, - [PT_PEN_ER] = 0x000D0045, - [PT_PEN_2ND_BS] = 0x000D005A, - [PT_PEN_IR] = 0x000D0032, - [PT_PEN_NUM_ABS] = 0, -}; - -static const char * const pt_pen_abs_string[] = { - [PT_PEN_X] = "X", - [PT_PEN_Y] = "Y", - [PT_PEN_P] = "P", - [PT_PEN_X_TILT] = "X_TILT", - [PT_PEN_Y_TILT] = "Y_TILT", - [PT_PEN_TS] = "Tip_Switch", - [PT_PEN_BS] = "Barrel_Switch", - [PT_PEN_IV] = "Invert", - [PT_PEN_ER] = "Eraser", - [PT_PEN_2ND_BS] = "2nd Barrel_Switch", - [PT_PEN_IR] = "In_Range", - [PT_PEN_NUM_ABS] = "INVALID", -}; - -struct pt_pen { - int abs[PT_PEN_NUM_ABS]; -}; - /* button to keycode support */ #define PT_BITS_PER_BTN 1 #define PT_NUM_BTN_EVENT_ID ((1 << PT_BITS_PER_BTN) - 1) @@ -1154,9 +1041,6 @@ struct pt_report_desc_data { u16 tch_record_size; u16 tch_header_size; u16 btn_report_id; - u16 pen_report_id; - u8 max_touch_num; - u8 max_tch_per_packet; }; struct pt_sysinfo { @@ -1169,7 +1053,6 @@ struct pt_sysinfo { struct pt_ttconfig ttconfig; struct pt_tch_abs_params tch_hdr[PT_TCH_NUM_HDR]; struct pt_tch_abs_params tch_abs[PT_TCH_NUM_ABS]; - struct pt_tch_abs_params pen_abs[PT_PEN_NUM_ABS]; u8 *xy_mode; u8 *xy_data; }; @@ -1238,14 +1121,13 @@ struct pt_hid_core { u16 hid_max_output_len; }; -#define PT_HID_MAX_REPORTS 20 -#define PT_HID_MAX_FIELDS 128 -#define PT_HID_MAX_COLLECTIONS 3 -#define PT_HID_MAX_NESTED_COLLECTIONS PT_HID_MAX_COLLECTIONS -#define PT_HID_MAX_CONTINUOUS_USAGES 8 +#define PT_HID_MAX_REPORTS 8 +#define PT_HID_MAX_FIELDS 128 +#define PT_HID_MAX_COLLECTIONS 3 +#define PT_HID_MAX_NESTED_COLLECTIONS PT_HID_MAX_COLLECTIONS /* Max input is for ASCII representation of hex characters */ -#define PT_MAX_INPUT 2048 +#define PT_MAX_INPUT (PT_MAX_PIP2_MSG_SIZE * 2) #define PT_PIP_1P7_EMPTY_BUF 0xFF00 enum pt_module_id { @@ -1285,18 +1167,6 @@ struct pt_mt_data { int t_max; }; -struct pt_pen_data { - struct device *dev; - struct pt_pen_platform_data *pdata; - struct pt_sysinfo *si; - struct input_dev *input; - struct mutex pen_lock; - bool is_suspended; - bool input_device_registered; - bool input_device_allocated; - char phys[NAME_MAX]; -}; - struct pt_btn_data { struct device *dev; struct pt_btn_platform_data *pdata; @@ -1423,8 +1293,7 @@ struct pt_core_nonhid_cmd { int (*get_bl_pip2_version)(struct device *dev); int (*pip2_file_open)(struct device *dev, u8 file_no); int (*pip2_file_close)(struct device *dev, u8 file_no); - int (*pip2_file_erase)(struct device *dev, u8 file_no, u16 file_sector, - int *status); + int (*pip2_file_erase)(struct device *dev, u8 file_no, int *status); int (*read_us_file)(struct device *dev, u8 *file_path, u8 *buf, int *size); int (*pip2_file_read)(struct device *dev, u8 file_no, @@ -1472,11 +1341,9 @@ struct pt_core_commands { struct pt_bin_file_hdr *hdr); int (*request_dut_generation)(struct device *dev); int (*request_hw_version)(struct device *dev, char *hw_version); -#ifndef TTDL_KERNEL_SUBMISSION int (*parse_sysfs_input)(struct device *dev, const char *buf, size_t buf_size, u32 *out_buf, size_t out_buf_size); -#endif #ifdef TTHE_TUNER_SUPPORT int (*request_tthe_print)(struct device *dev, u8 *buf, int buf_len, const u8 *data_name); @@ -1529,26 +1396,18 @@ struct pt_bus_ops { int (*read_default)(struct device *dev, void *buf, int size); int (*read_default_nosize)(struct device *dev, u8 *buf, u32 max); int (*write_read_specific)(struct device *dev, u16 write_len, - u8 *write_buf, u8 *read_buf, u16 read_len); -}; - -#define PT_MAX_DEVICES 3 -struct pt_bist_data { - u8 detected; - u8 mask; - u8 boot_err; - u8 bus_toggled; - u8 irq_toggled; - u8 xres_toggled; - char *bus_err_str; - char *irq_err_str; - char *xres_err_str; - char *print_buf; - u16 pr_index; - int status; + u8 *write_buf, u8 *read_buf); }; struct pt_core_data { + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; + + struct regulator *vdd; + struct regulator *vcc_i2c; + struct list_head node; struct list_head module_list; /* List of probed modules */ char core_id[20]; @@ -1560,11 +1419,9 @@ struct pt_core_data { struct mutex sysfs_lock; struct mutex ttdl_restart_lock; struct mutex firmware_class_lock; - struct mutex hid_report_lock; enum pt_mode mode; spinlock_t spinlock; struct pt_mt_data md; - struct pt_pen_data pend; struct pt_btn_data bd; struct pt_proximity_data pd; int phys_num; @@ -1602,32 +1459,23 @@ struct pt_core_data { #endif struct pt_sysinfo sysinfo; struct pt_bl_info bl_info; - struct pt_dut_status dut_status; void *exclusive_dev; int exclusive_waits; struct timer_list watchdog_timer; struct work_struct watchdog_work; struct work_struct enum_work; struct work_struct ttdl_restart_work; -#ifdef PT_PTSBC_SUPPORT - struct work_struct irq_work; - struct work_struct probe_work; - struct timer_list probe_timer; -#endif u16 startup_retry_count; struct pt_hid_core hid_core; int hid_cmd_state; int hid_reset_cmd_state; /* reset can happen any time */ struct pt_hid_desc hid_desc; - struct pt_hid_report *hid_reports[PT_HID_MAX_REPORTS]; - int num_hid_reports; struct pt_features features; #define PT_PREALLOCATED_CMD_BUFFER 32 u8 cmd_buf[PT_PREALLOCATED_CMD_BUFFER]; u8 input_buf[PT_MAX_INPUT]; u8 response_buf[PT_MAX_INPUT]; u8 cmd_rsp_buf[PT_MAX_INPUT]; - u8 touch_buf[PT_MAX_INPUT]; u16 cmd_rsp_buf_len; int raw_cmd_status; #ifdef CONFIG_HAS_EARLYSUSPEND @@ -1663,7 +1511,7 @@ struct pt_core_data { u8 flashless_dut; u8 bl_with_no_int; u8 cal_cache_in_host; - u8 num_devices; + u8 multi_chip; u8 tthe_hid_usb_format; u8 flashless_auto_bl; u8 pip2_us_file_path[PT_MAX_PATH_SIZE]; @@ -1741,10 +1589,10 @@ static inline int pt_adap_read_default_nosize(struct pt_core_data *cd, } static inline int pt_adap_write_read_specific(struct pt_core_data *cd, - u16 write_len, u8 *write_buf, u8 *read_buf, u16 read_len) + u16 write_len, u8 *write_buf, u8 *read_buf) { return cd->bus_ops->write_read_specific(cd->dev, write_len, write_buf, - read_buf, read_len); + read_buf); } static inline void *pt_get_dynamic_data(struct device *dev, int id) @@ -1815,9 +1663,6 @@ int pt_release(struct pt_core_data *cd); struct pt_core_commands *pt_get_commands(void); struct pt_core_data *pt_get_core_data(char *id); -#ifdef PT_AUX_BRIDGE_ENABLED -int pt_trigger_ttdl_irq(void); -#endif int pt_mt_release(struct device *dev); int pt_mt_probe(struct device *dev); @@ -1841,9 +1686,9 @@ static inline int pt_proximity_release(struct device *dev) { return 0; } static inline unsigned int pt_get_time_stamp(void) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)) - struct timespec64 ts; + struct timespec ts; - ktime_get_real_ts64(&ts); + getnstimeofday(&ts); return (ts.tv_sec*1000 + ts.tv_nsec/1000000); #else struct timeval tv; @@ -1854,8 +1699,6 @@ static inline unsigned int pt_get_time_stamp(void) } void pt_init_function_ptrs(struct pt_mt_data *md); -void pt_get_touch_field(struct device *dev, - int *field, int size, int max, u8 *data, int bofs); int _pt_subscribe_attention(struct device *dev, enum pt_atten_type type, char *id, int (*func)(struct device *), int mode); diff --git a/pt/pt_spi.c b/pt/pt_spi.c index c921ee8c86..d8db405282 100644 --- a/pt/pt_spi.c +++ b/pt/pt_spi.c @@ -51,179 +51,6 @@ static u8 *tmp_rbuf; static u8 *tmp_wbuf; DEFINE_MUTEX(pt_spi_bus_lock); -#ifdef TTDL_PTVIRTDUT_SUPPORT -#define VIRT_DUT_BUF_SIZE 300 -static unsigned char pt_dut_cmd_buf[VIRT_DUT_BUF_SIZE]; -static unsigned char pt_dut_out_buf[VIRT_DUT_BUF_SIZE]; -static int pt_dut_cmd_len; -static int pt_dut_out_len; -DEFINE_MUTEX(virt_spi_lock); - -/******************************************************************************* - * FUNCTION: virt_spi_transfer - * - * SUMMARY: Copies the current spi output message to the temporary buffer - * used by the dut_cmd sysfs node - * - * RETURN VALUE: - * Number of messages transferred which in this function will be 1 - * - * PARAMETERS: - * *buf - pointer to spi command - * len - length of command in the buffer - ******************************************************************************/ -static int virt_spi_transfer(u8 *buf, int len) -{ - int rc = 0; - - mutex_lock(&virt_spi_lock); - if (len <= sizeof(pt_dut_cmd_buf)) { - memcpy(pt_dut_cmd_buf, buf, len); - pt_dut_cmd_len = len; - rc = 1; - } else - rc = 0; - mutex_unlock(&virt_spi_lock); - - return rc; -} - -/******************************************************************************* - * FUNCTION: virt_spi_master_recv - * - * SUMMARY: Copies the spi input message from the dut_out sysfs node into a - * temporary buffer. - * - * RETURN VALUE: - * Length of data transferred - * - * PARAMETERS: - * *dev - pointer to device struct - * *buf - pointer to spi incoming report - * size - size to be read - ******************************************************************************/ -static int virt_spi_master_recv(struct device *dev, u8 *buf, int size) -{ -#ifndef PT_POLL_RESP_BY_BUS - struct pt_core_data *cd = dev_get_drvdata(dev); - int i = 0; -#endif - - mutex_lock(&virt_spi_lock); - memcpy(buf, pt_dut_out_buf, size); - - /* Set "empty buffer" */ - pt_dut_out_buf[1] = 0xFF; - pt_dut_out_len = 0; - mutex_unlock(&virt_spi_lock); - -#ifndef PT_POLL_RESP_BY_BUS - if (cd->bl_with_no_int) { - /* - * Wait for IRQ gpio to be released, make read operation - * synchronize with PtVirtDut tool. - * Safety net: Exit after 500ms (50us * 10000 loops = 500ms) - */ - while (i < VIRT_MAX_IRQ_RELEASE_TIME_US && - !gpio_get_value(cd->cpdata->irq_gpio)) { - pt_debug(dev, DL_INFO, "%s: %d IRQ still Enabled\n", - __func__, i); - usleep_range(50, 60); - i += 50; - } - } -#endif - - pt_debug(dev, DL_INFO, - "%s: Copy msg from dut_out to spi buffer, size=%d\n", - __func__, size); - - return size; -} - -/******************************************************************************* - * FUNCTION: pt_dut_cmd_show - * - * SUMMARY: The show function for the dut_cmd sysfs node. Provides read access - * to the pt_dut_cmd_buf and clears it after it has been read. - * - * RETURN VALUE: - * Number of bytes transferred - * - * PARAMETERS: - * *dev - pointer to device structure - * *attr - pointer to device attributes - * *buf - pointer to output buffer - ******************************************************************************/ -static ssize_t pt_dut_cmd_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int i; - int index = 0; - - /* Only print to sysfs if the buffer has data */ - mutex_lock(&virt_spi_lock); - if (pt_dut_cmd_len > 0) { - for (i = 0; i < pt_dut_cmd_len; i++) - index += scnprintf(buf + index, strlen(buf), "%02X", - pt_dut_cmd_buf[i]); - index += scnprintf(buf + index, strlen(buf), "\n"); - } - pt_dut_cmd_len = 0; - mutex_unlock(&virt_spi_lock); - return index; -} -static DEVICE_ATTR(dut_cmd, 0444, pt_dut_cmd_show, NULL); - -/******************************************************************************* - * FUNCTION: pt_dut_out_store - * - * SUMMARY: The store function for the dut_out sysfs node. Provides write - * access to the pt_dut_out_buf. The smallest valid PIP response is 2 - * bytes so don't update buffer if only 1 byte passed in. - * - * RETURN VALUE: - * Number of bytes read from virtual DUT - * - * PARAMETERS: - * *dev - pointer to device structure - * *attr - pointer to device attributes - * *buf - pointer to buffer that hold the command parameters - * size - size of buf - ******************************************************************************/ -static ssize_t pt_dut_out_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) -{ - int loop_max = ARRAY_SIZE(pt_dut_out_buf); - int hex_str_len = strlen(buf)/2; - int i; - const char *pos = buf; - - /* Clear out the last message */ - mutex_lock(&virt_spi_lock); - memset(pt_dut_out_buf, 0, VIRT_DUT_BUF_SIZE); - pt_dut_out_len = 0; - - /* Only update the dut_out buffer if at least 2 byte payload */ - if (size >= 2 && hex_str_len <= loop_max) { - /* Convert string of hex values to byte array */ - for (i = 0; i < hex_str_len; i++) { - pt_dut_out_buf[i] = ((HEXOF(*pos)) << 4) + - HEXOF(*(pos + 1)); - pos += 2; - } - pt_dut_out_len = get_unaligned_le16(&pt_dut_out_buf[0]); - } else if (size >= PT_PIP_1P7_EMPTY_BUF) { - /* Message too large, set to 'empty buffer' message */ - pt_dut_out_buf[1] = 0xFF; - pt_dut_out_len = 0; - } - mutex_unlock(&virt_spi_lock); - - return size; -} -static DEVICE_ATTR(dut_out, 0200, NULL, pt_dut_out_store); -#endif /* TTDL_PTVIRTDUT_SUPPORT */ /******************************************************************************* * FUNCTION: pt_spi_xfer @@ -312,23 +139,13 @@ exit: ******************************************************************************/ static int pt_spi_read_default(struct device *dev, void *buf, int size) { -#ifdef TTDL_PTVIRTDUT_SUPPORT - struct pt_core_data *cd = dev_get_drvdata(dev); -#endif /* TTDL_PTVIRTDUT_SUPPORT */ int rc = 0; if (!buf || !size || size > PT_MAX_PIP2_MSG_SIZE) return -EINVAL; mutex_lock(&pt_spi_bus_lock); -#ifdef TTDL_PTVIRTDUT_SUPPORT - if (cd->route_bus_virt_dut) - virt_spi_master_recv(dev, buf, size); - else - rc = pt_spi_xfer(dev, PT_SPI_RD_OP, buf, size); -#else rc = pt_spi_xfer(dev, PT_SPI_RD_OP, buf, size); -#endif /* TTDL_PTVIRTDUT_SUPPORT */ mutex_unlock(&pt_spi_bus_lock); return rc; @@ -357,21 +174,6 @@ static int pt_spi_read_default_nosize(struct device *dev, u8 *buf, u32 max) { u32 size; int rc = 0; -#ifdef TTDL_PTVIRTDUT_SUPPORT - struct pt_core_data *cd = dev_get_drvdata(dev); - - if (cd->route_bus_virt_dut) { - mutex_lock(&virt_spi_lock); - size = pt_dut_out_len; - mutex_unlock(&virt_spi_lock); - /* Only copy 2 bytes for "empty buffer" or "FW sentinel" */ - if (!size || size == 2 || size >= PT_PIP_1P7_EMPTY_BUF) - size = 2; - virt_spi_master_recv(dev, buf, size); - return 0; - } - -#endif /* TTDL_PTVIRTDUT_SUPPORT */ if (!buf) return 0; @@ -418,18 +220,14 @@ exit: * !0 = failure * * PARAMETERS: - * *dev - pointer to Device structure - * write_len - length of data buffer write_buf - * *write_buf - pointer to buffer to write - * *read_buf - pointer to buffer to read response into - * read_len - length to read, 0 to use pt_spi_read_default_nosize + * *dev - pointer to Device structure + * write_len - length of data buffer write_buf + * *write_buf - pointer to buffer to write + * *read_buf - pointer to buffer to read response into ******************************************************************************/ static int pt_spi_write_read_specific(struct device *dev, u16 write_len, - u8 *write_buf, u8 *read_buf, u16 read_len) + u8 *write_buf, u8 *read_buf) { -#ifdef TTDL_PTVIRTDUT_SUPPORT - struct pt_core_data *cd = dev_get_drvdata(dev); -#endif /* TTDL_PTVIRTDUT_SUPPORT */ int rc = 0; /* Ensure no packet larger than what the PIP spec allows */ @@ -447,21 +245,9 @@ static int pt_spi_write_read_specific(struct device *dev, u16 write_len, } mutex_lock(&pt_spi_bus_lock); -#ifdef TTDL_PTVIRTDUT_SUPPORT - if (cd->route_bus_virt_dut) { - virt_spi_transfer(write_buf, write_len); - pt_debug(dev, DL_DEBUG, "%s: Virt transfer size = %d", - __func__, write_len); - } else { - rc = pt_spi_xfer(dev, PT_SPI_WR_OP, write_buf, write_len); - if (rc < 0) - goto error; - } -#else rc = pt_spi_xfer(dev, PT_SPI_WR_OP, write_buf, write_len); if (rc < 0) goto error; -#endif /* TTDL_PTVIRTDUT_SUPPORT */ mutex_unlock(&pt_spi_bus_lock); if (read_buf) @@ -534,10 +320,6 @@ static int pt_spi_probe(struct spi_device *spi) if (!tmp_wbuf || !tmp_rbuf) return -ENOMEM; -#ifdef TTDL_PTVIRTDUT_SUPPORT - device_create_file(dev, &dev_attr_dut_cmd); - device_create_file(dev, &dev_attr_dut_out); -#endif /* TTDL_PTVIRTDUT_SUPPORT */ rc = pt_probe(&pt_spi_bus_ops, &spi->dev, spi->irq, PT_SPI_DATA_BUF_SIZE); @@ -547,12 +329,6 @@ static int pt_spi_probe(struct spi_device *spi) pt_devtree_clean_pdata(dev); #endif -#ifdef TTDL_PTVIRTDUT_SUPPORT - if (rc) { - device_remove_file(dev, &dev_attr_dut_cmd); - device_remove_file(dev, &dev_attr_dut_out); - } -#endif /* TTDL_PTVIRTDUT_SUPPORT */ return rc; } @@ -580,10 +356,6 @@ static int pt_spi_remove(struct spi_device *spi) kfree(tmp_rbuf); kfree(tmp_wbuf); -#ifdef TTDL_PTVIRTDUT_SUPPORT - device_remove_file(dev, &dev_attr_dut_cmd); - device_remove_file(dev, &dev_attr_dut_out); -#endif /* TTDL_PTVIRTDUT_SUPPORT */ pt_release(cd); From 857b6fc9944d9ea3dbaef8e1e325389141abc117 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Mon, 19 Sep 2022 15:00:44 +0530 Subject: [PATCH 037/170] touch: Adding of voltage regulators APIs Inclusion of API's for enablement and disablement of voltages for touch and enabled defconfig for Power APIs. Change-Id: I677b05d472bb9ce4c1ffa9443dc8256721d1a502 Signed-off-by: Surya Teja Kudiri --- pt/pt_core.c | 211 +++++++++++++++++++++++++++++++-------------------- pt/pt_regs.h | 9 +++ 2 files changed, 138 insertions(+), 82 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index 426b9eeaa2..cf19cca83b 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -10290,6 +10290,117 @@ static void pt_enum_work_function(struct work_struct *work) __func__, rc); } +static int pt_get_regulator(struct pt_core_data *cd, bool get) +{ + int rc; + + if (!get) { + rc = 0; + goto regulator_put; + } + + cd->vdd = regulator_get(cd->dev, "vdd"); + if (IS_ERR(cd->vdd)) { + rc = PTR_ERR(cd->vdd); + dev_err(cd->dev, + "Regulator get failed vdd rc=%d\n", rc); + goto regulator_put; + } + + cd->vcc_i2c = regulator_get(cd->dev, "vcc_i2c"); + if (IS_ERR(cd->vcc_i2c)) { + rc = PTR_ERR(cd->vcc_i2c); + dev_err(cd->dev, + "Regulator get failed vcc_i2c rc=%d\n", rc); + goto regulator_put; + } + + return 0; + +regulator_put: + if (cd->vdd) { + regulator_put(cd->vdd); + cd->vdd = NULL; + } + + if (cd->vcc_i2c) { + regulator_put(cd->vcc_i2c); + cd->vcc_i2c = NULL; + } + + return rc; +} + +static int pt_enable_regulator(struct pt_core_data *cd, bool en) +{ + int rc; + + if (!en) { + rc = 0; + goto disable_vcc_i2c_reg; + } + + if (cd->vdd) { + if (regulator_count_voltages(cd->vdd) > 0) { + rc = regulator_set_voltage(cd->vdd, FT_VTG_MIN_UV, + FT_VTG_MAX_UV); + if (rc) { + dev_err(cd->dev, + "Regulator set_vtg failed vdd rc=%d\n", rc); + goto exit; + } + } + + rc = regulator_enable(cd->vdd); + if (rc) { + dev_err(cd->dev, + "Regulator vdd enable failed rc=%d\n", rc); + goto exit; + } + } + + if (cd->vcc_i2c) { + if (regulator_count_voltages(cd->vcc_i2c) > 0) { + rc = regulator_set_voltage(cd->vcc_i2c, FT_I2C_VTG_MIN_UV, + FT_I2C_VTG_MAX_UV); + if (rc) { + dev_err(cd->dev, + "Regulator set_vtg failed vcc_i2c rc=%d\n", rc); + goto disable_vdd_reg; + } + } + + rc = regulator_enable(cd->vcc_i2c); + if (rc) { + dev_err(cd->dev, + "Regulator vcc_i2c enable failed rc=%d\n", rc); + goto disable_vdd_reg; + } + } + + return 0; + +disable_vcc_i2c_reg: + if (cd->vcc_i2c) { + if (regulator_count_voltages(cd->vcc_i2c) > 0) + regulator_set_voltage(cd->vcc_i2c, 0, FT_I2C_VTG_MAX_UV); + + regulator_disable(cd->vcc_i2c); + } + +disable_vdd_reg: + if (cd->vdd) { + if (regulator_count_voltages(cd->vdd) > 0) + regulator_set_voltage(cd->vdd, 0, FT_VTG_MAX_UV); + + regulator_disable(cd->vdd); + } + +exit: + return rc; +} + + #if (KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE) #define KERNEL_VER_GT_3_19 #endif @@ -16664,71 +16775,6 @@ static void remove_sysfs_and_modules(struct device *dev) remove_sysfs_interfaces(dev); } -static int pt_power_init(struct pt_core_data *cd, bool on) -{ - int rc; - - if (!on) - goto pwr_deinit; - - cd->vdd = regulator_get(cd->dev, "vdd"); - if (IS_ERR(cd->vdd)) { - rc = PTR_ERR(cd->vdd); - dev_err(cd->dev, - "Regulator get failed vdd rc=%d\n", rc); - return rc; - } - if (regulator_count_voltages(cd->vdd) > 0) { - rc = regulator_set_voltage(cd->vdd, FT_VTG_MIN_UV, - FT_VTG_MAX_UV); - if (rc) { - dev_err(cd->dev, - "Regulator set_vtg failed vdd rc=%d\n", rc); - goto reg_vdd_put; - } - } - - cd->vcc_i2c = regulator_get(cd->dev, "vcc_i2c"); - if (IS_ERR(cd->vcc_i2c)) { - rc = PTR_ERR(cd->vcc_i2c); - dev_err(cd->dev, - "Regulator get failed vcc_i2c rc=%d\n", rc); - goto reg_vdd_set_vtg; - } - if (regulator_count_voltages(cd->vcc_i2c) > 0) { - rc = regulator_set_voltage(cd->vcc_i2c, FT_I2C_VTG_MIN_UV, - FT_I2C_VTG_MAX_UV); - if (rc) { - dev_err(cd->dev, - "Regulator set_vtg failed vcc_i2c rc=%d\n", rc); - goto reg_vcc_i2c_put; - } - } - - return 0; - -reg_vcc_i2c_put: - regulator_put(cd->vcc_i2c); -reg_vdd_set_vtg: - if (regulator_count_voltages(cd->vdd) > 0) - regulator_set_voltage(cd->vdd, 0, FT_VTG_MAX_UV); -reg_vdd_put: - regulator_put(cd->vdd); - return rc; - -pwr_deinit: - if (regulator_count_voltages(cd->vdd) > 0) - regulator_set_voltage(cd->vdd, 0, FT_VTG_MAX_UV); - - regulator_put(cd->vdd); - - if (regulator_count_voltages(cd->vcc_i2c) > 0) - regulator_set_voltage(cd->vcc_i2c, 0, FT_I2C_VTG_MAX_UV); - - regulator_put(cd->vcc_i2c); - return 0; -} - static int pt_ts_pinctrl_init(struct pt_core_data *cd) { int retval; @@ -16837,7 +16883,7 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, cd = kzalloc(sizeof(*cd), GFP_KERNEL); if (!cd) { rc = -ENOMEM; - goto error_alloc_data; + goto error_no_pdata; } /* Initialize device info */ cd->dev = dev; @@ -16942,20 +16988,16 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, if (rc < 0) dev_err(&client->dev, "failed to select pin to active state\n"); } - rc = pt_power_init(cd, true); - if (rc < 0) - dev_err(&client->dev, "failed to cyttsp5_power_init\n"); - - rc = regulator_enable(cd->vdd); + rc = pt_get_regulator(cd, true); if (rc) { - dev_err(dev, "Regulator vdd enable failed rc=%d\n", rc); - goto error_power; + dev_err(&client->dev, "Failed to get voltage regulators\n"); + goto error_alloc_data; } - rc = regulator_enable(cd->vcc_i2c); + + rc = pt_enable_regulator(cd, true); if (rc) { - dev_err(dev, "Regulator vcc_i2c enable failed rc=%d\n", rc); - regulator_disable(cd->vdd); - goto error_power; + dev_err(dev, "Failed to enable regulators: rc=%d\n", rc); + goto error_get_regulator; } /* Initialize works */ @@ -16991,10 +17033,11 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, pt_add_core(dev); rc = sysfs_create_group(&dev->kobj, &early_attr_group); - if (rc) + if (rc) { pt_debug(cd->dev, DL_ERROR, "%s:create early attrs failed\n", __func__); - + goto error_enable_regulator; + } /* * Save the pointer to a global value, which will be used * in ttdl_restart function @@ -17255,11 +17298,13 @@ error_detect: if (cd->cpdata->setup_power) cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev); sysfs_remove_group(&dev->kobj, &early_attr_group); + kfree(cd); +error_enable_regulator: pt_del_core(dev); dev_set_drvdata(dev, NULL); -error_power: - pt_power_init(cd, false); - kfree(cd); + pt_enable_regulator(cd, false); +error_get_regulator: + pt_get_regulator(cd, false); error_alloc_data: error_no_pdata: pr_err("%s failed.\n", __func__); @@ -17351,6 +17396,8 @@ int pt_release(struct pt_core_data *cd) cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev); dev_set_drvdata(dev, NULL); pt_del_core(dev); + pt_enable_regulator(cd, false); + pt_get_regulator(cd, false); pt_free_si_ptrs(cd); kfree(cd); return 0; diff --git a/pt/pt_regs.h b/pt/pt_regs.h index 78e78798bf..0ef17d1ab9 100644 --- a/pt/pt_regs.h +++ b/pt/pt_regs.h @@ -115,6 +115,15 @@ #define PT_MAX_ELEN 100 #endif +/* Power Management Macros Enablement */ +#ifndef CONFIG_PM_SLEEP +#define CONFIG_PM_SLEEP +#endif + +#ifndef CONFIG_PM_RUNTIME +#define CONFIG_PM_RUNTIME +#endif + /* * The largest PIP message is the PIP2 FILE_WRITE which has: * 2 byte register From 24ad9327a20a613c4353e994a6565eabad9b1ce6 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Mon, 19 Sep 2022 15:06:40 +0530 Subject: [PATCH 038/170] touch: Enable and Disable voltages Added regulator API's for enablement and disablement of voltages for touch during SUSPEND and RESUME state. Change-Id: Idcf4148d59c6fb23cc1942818585cac19834e4f9 Signed-off-by: Surya Teja Kudiri --- pt/pt_core.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index cf19cca83b..b91f94454e 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -10425,6 +10425,8 @@ static int pt_core_rt_suspend(struct device *dev) struct pt_core_data *cd = dev_get_drvdata(dev); int rc = 0; + dev_info(dev, "%s: Entering into runtime suspend mode:\n", + __func__); if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) return 0; @@ -10433,6 +10435,12 @@ static int pt_core_rt_suspend(struct device *dev) pt_debug(dev, DL_ERROR, "%s: Error on sleep\n", __func__); return -EAGAIN; } + + rc = pt_enable_regulator(cd, false); + if (rc < 0) { + dev_err(dev, "%s: Failed to disable regulators: rc=%d\n", + __func__, rc); + } return 0; } @@ -10453,9 +10461,20 @@ static int pt_core_rt_resume(struct device *dev) struct pt_core_data *cd = dev_get_drvdata(dev); int rc = 0; + dev_info(dev, "%s: Entering into runtime resume mode:\n", + __func__); if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) return 0; + rc = pt_enable_regulator(cd, true); + if (rc < 0) { + dev_err(dev, "%s: Failed to enable regulators: rc=%d\n", + __func__, rc); + } + + dev_info(dev, "%s: Runtime voltage regulator enabled: rc=%d\n", + __func__, rc); + rc = pt_core_wake(cd); if (rc < 0) { pt_debug(dev, DL_ERROR, "%s: Error on wake\n", __func__); @@ -10483,9 +10502,22 @@ static int pt_core_rt_resume(struct device *dev) ******************************************************************************/ static int pt_core_suspend_(struct device *dev) { + int rc; struct pt_core_data *cd = dev_get_drvdata(dev); - pt_core_sleep(cd); + rc = pt_core_sleep(cd); + if (rc < 0) { + pt_debug(dev, DL_ERROR, "%s: Error on sleep\n", __func__); + return -EAGAIN; + } + + rc = pt_enable_regulator(cd, false); + if (rc) { + dev_err(dev, "%s: Failed to disable regulators: rc=%d\n", + __func__, rc); + } + dev_info(dev, "%s: Sayantan1: Voltage regulators disabled: rc=%d\n", + __func__, rc); if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) return 0; @@ -10504,7 +10536,7 @@ static int pt_core_suspend_(struct device *dev) __func__); } - return 0; + return rc; } /******************************************************************************* @@ -10545,7 +10577,19 @@ static int pt_core_suspend(struct device *dev) ******************************************************************************/ static int pt_core_resume_(struct device *dev) { + int rc; struct pt_core_data *cd = dev_get_drvdata(dev); + + dev_info(dev, "%s: Entering into resume mode:\n", + __func__); + rc = pt_enable_regulator(cd, true); + if (rc < 0) { + dev_err(dev, "%s: Failed to enable regulators: rc=%d\n", + __func__, rc); + } + dev_info(dev, "%s: Voltage regulator enabled: rc=%d\n", + __func__, rc); + if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) goto exit; From 9f8b0948ed1fd6ad6e4a5fb064c3b9ee76893a63 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Mon, 19 Sep 2022 15:12:05 +0530 Subject: [PATCH 039/170] touch: fix regulator min voltage during deinit voltage range should fall in between regulator supported range. updated min voltage from zero to regulator min voltage. Change-Id: I3df1e2638d69348b0550d175063df2e3ac65cf0d Signed-off-by: Surya Teja Kudiri --- pt/pt_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index b91f94454e..9a46b1747c 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -10383,7 +10383,8 @@ static int pt_enable_regulator(struct pt_core_data *cd, bool en) disable_vcc_i2c_reg: if (cd->vcc_i2c) { if (regulator_count_voltages(cd->vcc_i2c) > 0) - regulator_set_voltage(cd->vcc_i2c, 0, FT_I2C_VTG_MAX_UV); + regulator_set_voltage(cd->vcc_i2c, FT_I2C_VTG_MIN_UV, + FT_I2C_VTG_MAX_UV); regulator_disable(cd->vcc_i2c); } @@ -10391,7 +10392,8 @@ disable_vcc_i2c_reg: disable_vdd_reg: if (cd->vdd) { if (regulator_count_voltages(cd->vdd) > 0) - regulator_set_voltage(cd->vdd, 0, FT_VTG_MAX_UV); + regulator_set_voltage(cd->vdd, FT_VTG_MIN_UV, + FT_VTG_MAX_UV); regulator_disable(cd->vdd); } From 0fb4da9875d0420619bbf58c95fd41db6fd75e86 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Mon, 19 Sep 2022 15:17:27 +0530 Subject: [PATCH 040/170] touch: fix parade driver deinit sequence driver context is freed before calling power off sequence, which is resulting invalid memory access causing system crash. complete power off sequence before free up driver context. Change-Id: Ib6665d904d2747ec87a62b47cfadcb91f713ffe6 Signed-off-by: Surya Teja Kudiri --- pt/pt_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index 9a46b1747c..46856bed98 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -17344,7 +17344,6 @@ error_detect: if (cd->cpdata->setup_power) cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev); sysfs_remove_group(&dev->kobj, &early_attr_group); - kfree(cd); error_enable_regulator: pt_del_core(dev); dev_set_drvdata(dev, NULL); @@ -17352,6 +17351,7 @@ error_enable_regulator: error_get_regulator: pt_get_regulator(cd, false); error_alloc_data: + kfree(cd); error_no_pdata: pr_err("%s failed.\n", __func__); return rc; From f8c8176c7a29c4f66cea1782d68083ec4d5dda6d Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Mon, 19 Sep 2022 15:22:20 +0530 Subject: [PATCH 041/170] touch: Pull down GPIOs during SUSPEND Enablement and disablement of GPIO pins along with LDOs during RESUME and SUSPEND state respectively. Change-Id: If24272ca1410ac583ba9ea6792c44459a8429334 Signed-off-by: Surya Teja Kudiri --- pt/pt_core.c | 34 +++++++++++++++++----------------- pt/pt_platform.c | 10 ++++++++-- pt/pt_regs.h | 9 +++++++-- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index 46856bed98..24093e35a0 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -7664,8 +7664,14 @@ static int pt_core_sleep_(struct pt_core_data *cd) if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) rc = pt_put_device_into_easy_wakeup_(cd); - else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) + else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) { + pt_debug(cd->dev, DL_INFO, + "%s: Entering into poweroff mode:\n", __func__); rc = pt_core_poweroff_device_(cd); + if (rc < 0) + pr_err("%s: Poweroff error detected :rc=%d\n", + __func__, rc); + } else if (cd->cpdata->flags & PT_CORE_FLAG_DEEP_STANDBY) rc = pt_put_device_into_deep_standby_(cd); else @@ -9490,8 +9496,14 @@ static int pt_core_wake_(struct pt_core_data *cd) if (!(cd->cpdata->flags & PT_CORE_FLAG_SKIP_RESUME)) { if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) rc = pt_core_wake_device_from_easy_wake_(cd); - else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) + else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) { + pt_debug(cd->dev, DL_INFO, + "%s: Entering into poweron mode:\n", __func__); rc = pt_core_poweron_device_(cd); + if (rc < 0) + pr_err("%s: Poweron error detected: rc=%d\n", + __func__, rc); + } else if (cd->cpdata->flags & PT_CORE_FLAG_DEEP_STANDBY) rc = pt_core_wake_device_from_deep_standby_(cd); else /* Default action to exit DeepSleep */ @@ -10438,11 +10450,6 @@ static int pt_core_rt_suspend(struct device *dev) return -EAGAIN; } - rc = pt_enable_regulator(cd, false); - if (rc < 0) { - dev_err(dev, "%s: Failed to disable regulators: rc=%d\n", - __func__, rc); - } return 0; } @@ -10468,15 +10475,6 @@ static int pt_core_rt_resume(struct device *dev) if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) return 0; - rc = pt_enable_regulator(cd, true); - if (rc < 0) { - dev_err(dev, "%s: Failed to enable regulators: rc=%d\n", - __func__, rc); - } - - dev_info(dev, "%s: Runtime voltage regulator enabled: rc=%d\n", - __func__, rc); - rc = pt_core_wake(cd); if (rc < 0) { pt_debug(dev, DL_ERROR, "%s: Error on wake\n", __func__); @@ -10507,6 +10505,8 @@ static int pt_core_suspend_(struct device *dev) int rc; struct pt_core_data *cd = dev_get_drvdata(dev); + pt_debug(dev, DL_INFO, "%s: Entering into suspend mode:\n", + __func__); rc = pt_core_sleep(cd); if (rc < 0) { pt_debug(dev, DL_ERROR, "%s: Error on sleep\n", __func__); @@ -10579,7 +10579,7 @@ static int pt_core_suspend(struct device *dev) ******************************************************************************/ static int pt_core_resume_(struct device *dev) { - int rc; + int rc = 0; struct pt_core_data *cd = dev_get_drvdata(dev); dev_info(dev, "%s: Entering into resume mode:\n", diff --git a/pt/pt_platform.c b/pt/pt_platform.c index c4bd2b039a..3b2c8e88fe 100644 --- a/pt/pt_platform.c +++ b/pt/pt_platform.c @@ -541,7 +541,10 @@ static int pt_wakeup(struct pt_core_platform_data *pdata, int rc = 0; #ifdef PT_PINCTRL_EN - pt_pinctrl_select_normal(pdata, dev); + rc = pt_pinctrl_select_normal(pdata, dev); + if (rc) + pr_err("%s: GPIO pins activation error: rc=%d\n", + __func__, rc); #endif #ifdef TT7XXX_EXAMPLE @@ -674,7 +677,10 @@ static int pt_sleep(struct pt_core_platform_data *pdata, pt_debug(dev, DL_INFO, "%s: Turn off defined pwr\n", __func__); #endif #ifdef PT_PINCTRL_EN - pt_pinctrl_select_suspend(pdata, dev); + rc = pt_pinctrl_select_suspend(pdata, dev); + if (rc) + pr_err("%s: GPIO pins suspend error: rc=%d\n", + __func__, rc); #endif return rc; } diff --git a/pt/pt_regs.h b/pt/pt_regs.h index 0ef17d1ab9..7e5f9a657a 100644 --- a/pt/pt_regs.h +++ b/pt/pt_regs.h @@ -120,8 +120,13 @@ #define CONFIG_PM_SLEEP #endif -#ifndef CONFIG_PM_RUNTIME -#define CONFIG_PM_RUNTIME +/* Pin Control Macro Enablement */ +#ifndef PT_PINCTRL_EN +#define PT_PINCTRL_EN +#endif + +#ifndef TT7XXX_EXAMPLE +#define TT7XXX_EXAMPLE #endif /* From 1a7ece05e4d20884456b9b7a370089637f97f3ab Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Mon, 19 Sep 2022 15:29:25 +0530 Subject: [PATCH 042/170] touch: Fetch DRM panel from device tree Fetching of the active DRM panel from the DSI panels in device tree and storing in core platform data structure. Change-Id: I2a10380abb568f483e756bb9d60d5cdae952febe Signed-off-by: Surya Teja Kudiri --- pt/pt_devtree.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/pt/pt_devtree.c b/pt/pt_devtree.c index 3fb3d2f561..9b6261a0cf 100644 --- a/pt/pt_devtree.c +++ b/pt/pt_devtree.c @@ -746,6 +746,49 @@ static char *touch_setting_names[PT_IC_GRPNUM_NUM] = { NULL, /* PT_IC_GRPNUM_TTHE_REGS */ }; +/******************************************************************************* + * FUNCTION: pt_check_dsi_panel_dt + * + * SUMMARY: Get the DSI active panel information from dtsi + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * np - pointer to device_node structure + * active_panel - name of active DSI panel + ******************************************************************************/ + + +static int pt_check_dsi_panel_dt(struct device_node *np, struct drm_panel **active_panel) +{ + int i; + int count; + struct device_node *node; + struct drm_panel *panel; + + count = of_count_phandle_with_args(np, "panel", NULL); + pr_info("%s: Active panel count: %d\n", __func__, count); + if (count <= 0) + return 0; + + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + if (node != NULL) + pr_info("%s: Node handle successfully parsed !\n", __func__); + panel = of_drm_find_panel(node); + of_node_put(node); + if (!IS_ERR(panel)) { + pr_info("%s: Active panel selected !\n", __func__); + *active_panel = panel; + return 0; + } + } + pr_err("%s: Active panel NOT selected !\n", __func__); + return PTR_ERR(panel); +} + /******************************************************************************* * FUNCTION: create_and_get_core_pdata * @@ -944,10 +987,13 @@ int pt_devtree_create_and_get_pdata(struct device *adap_dev) { struct pt_platform_data *pdata; struct device_node *core_node, *dev_node, *dev_node_fail; + struct drm_panel *active_panel = NULL; enum pt_device_type type; int count = 0; int rc = 0; + pr_info("%s: Start of fetch dtsi..\n", __func__); + if (is_create_and_get_pdata == true) return 0; @@ -969,12 +1015,28 @@ int pt_devtree_create_and_get_pdata(struct device *adap_dev) if (!rc) pr_debug("%s: name:%s\n", __func__, name); + rc = pt_check_dsi_panel_dt(core_node, &active_panel); + if (rc) { + pr_err("%s: Panel not selected, rc=%d\n", __func__, rc); + if (rc == -EPROBE_DEFER) { + pr_err("%s: Probe defer selected, rc=%d\n", __func__, rc); + kfree(pdata); + return rc; + } + } + pdata->core_pdata = create_and_get_core_pdata(core_node); if (IS_ERR(pdata->core_pdata)) { + pr_err("%s: Error in fetch dtsi..\n", __func__); rc = PTR_ERR(pdata->core_pdata); break; } + pr_info("%s: End of fetch dtsi..\n", __func__); + pdata->core_pdata->active_panel = active_panel; + pr_info("%s: Successful insert of active panel in core data\n", + __func__); + /* Increment reference count */ of_node_get(core_node); From ef5fab87656bca74a4b05e9e047bf930b38ec478 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Mon, 19 Sep 2022 15:35:39 +0530 Subject: [PATCH 043/170] touch: Insertion of DRM and RUNTIME macros Inclusion of the DRM notifier callback macros and RUNTIME macros in header file. Change-Id: Ie23e7e5f94028cc9a80de47906f13b0eb5e2f8cf Signed-off-by: Surya Teja Kudiri --- pt/pt_regs.h | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/pt/pt_regs.h b/pt/pt_regs.h index 7e5f9a657a..acfca02126 100644 --- a/pt/pt_regs.h +++ b/pt/pt_regs.h @@ -41,12 +41,17 @@ #define PT_PIP2_MAX_FILE_SIZE 0x18000 #define PT_PIP2_FILE_SECTOR_SIZE 0x1000 +#ifndef CONFIG_DRM +#define CONFIG_DRM +#endif + #include +#include +#include #ifdef CONFIG_HAS_EARLYSUSPEND #include -#elif defined(CONFIG_FB) -#include -#include +#elif defined(CONFIG_DRM) +#include #endif #include @@ -116,6 +121,15 @@ #endif /* Power Management Macros Enablement */ + +#ifndef CONFIG_PM +#define CONFIG_PM +#endif + +#ifndef CONFIG_PM_RUNTIME +#define CONFIG_PM_RUNTIME +#endif + #ifndef CONFIG_PM_SLEEP #define CONFIG_PM_SLEEP #endif @@ -1453,6 +1467,7 @@ struct pt_core_data { bool irq_wake; bool irq_disabled; bool hw_detected; + bool runtime; u8 easy_wakeup_gesture; #ifdef EASYWAKE_TSG6 u8 gesture_id; @@ -1494,7 +1509,7 @@ struct pt_core_data { int raw_cmd_status; #ifdef CONFIG_HAS_EARLYSUSPEND struct early_suspend es; -#elif defined(CONFIG_FB) +#elif defined(CONFIG_FB) || defined(CONFIG_DRM) struct notifier_block fb_notifier; enum pt_fb_state fb_state; #endif From 03a5e19b615819816296154428662a854606082d Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Mon, 19 Sep 2022 15:40:00 +0530 Subject: [PATCH 044/170] touch: Setting up of DRM notifier Implementation of DRM notifier callbacks and registration of callback during touch probing. Change-Id: I56e6d19708e207cee4d63089a73725be30f9cd84 Signed-off-by: Surya Teja Kudiri --- pt/pt_core.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 134 insertions(+), 12 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index 24093e35a0..44a43453fb 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -45,6 +45,10 @@ #define PT_STATUS_STR_LEN (50) +#if defined(CONFIG_DRM) +static struct drm_panel *active_panel; +#endif + MODULE_FIRMWARE(PT_FW_FILE_NAME); static const char *pt_driver_core_name = PT_CORE_NAME; @@ -7662,16 +7666,10 @@ static int pt_core_sleep_(struct pt_core_data *cd) cancel_work_sync(&cd->enum_work); pt_stop_wd_timer(cd); - if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) + if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture) && cd->runtime) rc = pt_put_device_into_easy_wakeup_(cd); - else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) { - pt_debug(cd->dev, DL_INFO, - "%s: Entering into poweroff mode:\n", __func__); + else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) rc = pt_core_poweroff_device_(cd); - if (rc < 0) - pr_err("%s: Poweroff error detected :rc=%d\n", - __func__, rc); - } else if (cd->cpdata->flags & PT_CORE_FLAG_DEEP_STANDBY) rc = pt_put_device_into_deep_standby_(cd); else @@ -8407,7 +8405,7 @@ static int pt_read_input(struct pt_core_data *cd) */ mutex_lock(&cd->system_lock); - if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) { + if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture) && cd->runtime) { if (cd->sleep_state == SS_SLEEP_ON) { mutex_unlock(&cd->system_lock); if (!dev->power.is_suspended) @@ -9494,7 +9492,7 @@ static int pt_core_wake_(struct pt_core_data *cd) mutex_unlock(&cd->system_lock); if (!(cd->cpdata->flags & PT_CORE_FLAG_SKIP_RESUME)) { - if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) + if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture) && cd->runtime) rc = pt_core_wake_device_from_easy_wake_(cd); else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) { pt_debug(cd->dev, DL_INFO, @@ -10444,6 +10442,9 @@ static int pt_core_rt_suspend(struct device *dev) if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) return 0; + if (cd->sleep_state == SS_SLEEP_OFF) + cd->runtime = 1; + rc = pt_core_sleep(cd); if (rc < 0) { pt_debug(dev, DL_ERROR, "%s: Error on sleep\n", __func__); @@ -10481,6 +10482,9 @@ static int pt_core_rt_resume(struct device *dev) return -EAGAIN; } + if (cd->sleep_state == SS_SLEEP_OFF) + cd->runtime = 0; + return 0; } #endif /* CONFIG_PM_SLEEP */ @@ -10521,7 +10525,7 @@ static int pt_core_suspend_(struct device *dev) dev_info(dev, "%s: Sayantan1: Voltage regulators disabled: rc=%d\n", __func__, rc); - if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) + if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture) && !cd->runtime) return 0; /* Required to prevent interrupts before bus awake */ @@ -10592,7 +10596,7 @@ static int pt_core_resume_(struct device *dev) dev_info(dev, "%s: Voltage regulator enabled: rc=%d\n", __func__, rc); - if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) + if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture) && !cd->runtime) goto exit; /* @@ -12392,6 +12396,112 @@ static void pt_setup_early_suspend(struct pt_core_data *cd) register_early_suspend(&cd->es); } +#elif defined(CONFIG_DRM) + +/******************************************************************************* + * FUNCTION: drm_notifier_callback + * + * SUMMARY: Call back function for DRM notifier to allow to call + * resume/suspend attention list. + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * *self - pointer to notifier_block structure + * event - event type of fb notifier + * *data - pointer to fb_event structure + ******************************************************************************/ +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct pt_core_data *cd = + container_of(self, struct pt_core_data, fb_notifier); + struct drm_panel_notifier *evdata = data; + int *blank; + + pt_debug(cd->dev, DL_INFO, "%s: DRM notifier called!\n", __func__); + + if (!evdata) + goto exit; + + if (!(event == DRM_PANEL_EARLY_EVENT_BLANK || + event == DRM_PANEL_EVENT_BLANK)) { + pt_debug(cd->dev, DL_INFO, "%s: Event(%lu) do not need process\n", + __func__, event); + goto exit; + } + + blank = evdata->data; + pt_debug(cd->dev, DL_INFO, "%s: DRM event:%lu,blank:%d", __func__, event, *blank); + if (*blank == DRM_PANEL_BLANK_UNBLANK) { + pt_debug(cd->dev, DL_INFO, "%s: UNBLANK!\n", __func__); + if (event == DRM_PANEL_EARLY_EVENT_BLANK) { + pt_debug(cd->dev, DL_INFO, "%s: resume: event = %lu, not care\n", + __func__, event); + } else if (event == DRM_PANEL_EVENT_BLANK) { + if (cd->fb_state != FB_ON) { + call_atten_cb(cd, PT_ATTEN_RESUME, 0); +#if defined(CONFIG_PM_SLEEP) + pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n", + __func__); + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) + pt_core_resume_(cd->dev); +#endif + cd->fb_state = FB_ON; + pt_debug(cd->dev, DL_INFO, "%s: Resume notified!\n", __func__); + } + } + } else if (*blank == DRM_PANEL_BLANK_LP) { + pt_debug(cd->dev, DL_INFO, "%s: LOWPOWER!\n", __func__); + if (event == DRM_PANEL_EARLY_EVENT_BLANK) { + if (cd->fb_state != FB_OFF) { +#if defined(CONFIG_PM_SLEEP) + pt_debug(cd->dev, DL_INFO, "%s: Suspend notifier called!\n", + __func__); + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) + pt_core_suspend_(cd->dev); +#endif + call_atten_cb(cd, PT_ATTEN_SUSPEND, 0); + cd->fb_state = FB_OFF; + pt_debug(cd->dev, DL_INFO, "%s: Suspend notified!\n", __func__); + } + } else if (event == DRM_PANEL_EVENT_BLANK) { + pt_debug(cd->dev, DL_INFO, "%s: suspend: event = %lu, not care\n", + __func__, event); + } + } else { + pt_debug(cd->dev, DL_INFO, "%s: DRM BLANK(%d) do not need process\n", + __func__, *blank); + } +exit: + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_setup_drm_notifier + * + * SUMMARY: Set up call back function into drm notifier. + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static void pt_setup_drm_notifier(struct pt_core_data *cd) +{ + cd->fb_state = FB_ON; + cd->fb_notifier.notifier_call = drm_notifier_callback; + pt_debug(cd->dev, DL_INFO, "%s: Setting up drm notifier\n", __func__); + + if (!active_panel) + pt_debug(cd->dev, DL_ERROR, + "%s: Active panel not registered!\n", __func__); + + if (active_panel && + drm_panel_notifier_register(active_panel, + &cd->fb_notifier) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: Register notifier failed!\n", __func__); +} #elif defined(CONFIG_FB) /******************************************************************************* * FUNCTION: fb_notifier_callback @@ -16949,6 +17059,7 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, cd->watchdog_enabled = 0; cd->startup_retry_count = 0; cd->core_probe_complete = 0; + cd->runtime = 0; cd->fw_system_mode = FW_SYS_MODE_BOOT; cd->pip_cmd_timeout = PT_PIP_CMD_DEFAULT_TIMEOUT; cd->pip_cmd_timeout_default = PT_PIP_CMD_DEFAULT_TIMEOUT; @@ -17062,6 +17173,11 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, /* Set platform easywake value */ cd->easy_wakeup_gesture = cd->cpdata->easy_wakeup_gesture; +#ifdef CONFIG_DRM + /* Setup active dsi panel */ + active_panel = cd->cpdata->active_panel; +#endif + /* Set platform panel_id value */ cd->panel_id_support = cd->cpdata->panel_id_support; @@ -17298,6 +17414,9 @@ skip_enum: #ifdef CONFIG_HAS_EARLYSUSPEND pt_setup_early_suspend(cd); +#elif defined(CONFIG_DRM) + pt_debug(dev, DL_ERROR, "%s: Probe: Setup drm notifier\n", __func__); + pt_setup_drm_notifier(cd); #elif defined(CONFIG_FB) pt_setup_fb_notifier(cd); #endif @@ -17408,6 +17527,9 @@ int pt_release(struct pt_core_data *cd) #ifdef CONFIG_HAS_EARLYSUSPEND unregister_early_suspend(&cd->es); +#elif defined(CONFIG_DRM) + if (active_panel) + drm_panel_notifier_unregister(active_panel, &cd->fb_notifier); #elif defined(CONFIG_FB) fb_unregister_client(&cd->fb_notifier); #endif From d66a17ccb35743908fb9f36e051bb337f9b95d1d Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Fri, 16 Sep 2022 18:24:12 +0530 Subject: [PATCH 045/170] touch: Add regulator support Added regulator support to RM6D030 touch sensor. Change-Id: I74b2560ae5e02ceea272c15609e7dd624560a5bd Signed-off-by: Srikanth Katteboina --- raydium/raydium_driver.c | 140 ++++++++++++++++++++++++++++++++++++++- raydium/raydium_driver.h | 7 ++ 2 files changed, 145 insertions(+), 2 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index f0ec4c31a9..5fe81c421c 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -1820,6 +1820,126 @@ exit_error: return i32_ret; } +static int raydium_get_regulator(struct raydium_ts_data *cd, bool get) +{ + int rc; + + if (!get) { + rc = 0; + goto regulator_put; + } +#ifdef VDD_ANALOG_ENABLE + cd->vdd = regulator_get(&cd->client->dev, "vdd"); + if (IS_ERR(cd->vdd)) { + rc = PTR_ERR(cd->vdd); + dev_err(&cd->client->dev, + "Regulator get failed vdd rc=%d\n", rc); + goto regulator_put; + } +#endif + + cd->vcc_i2c = regulator_get(&cd->client->dev, "vcc_i2c"); + if (IS_ERR(cd->vcc_i2c)) { + rc = PTR_ERR(cd->vcc_i2c); + dev_err(&cd->client->dev, + "Regulator get failed vcc_i2c rc=%d\n", rc); + goto regulator_put; + } + + return 0; + +regulator_put: +#ifdef VDD_ANALOG_ENABLE + if (cd->vdd) { + regulator_put(cd->vdd); + cd->vdd = NULL; + } +#endif + + if (cd->vcc_i2c) { + regulator_put(cd->vcc_i2c); + cd->vcc_i2c = NULL; + } + + return rc; +} + +static int raydium_enable_regulator(struct raydium_ts_data *cd, bool en) +{ + int rc; + + if (!en) { + rc = 0; + goto disable_vcc_i2c_reg; + } +#ifdef VDD_ANALOG_ENABLE + if (cd->vdd) { + if (regulator_count_voltages(cd->vdd) > 0) { + rc = regulator_set_voltage(cd->vdd, VTG_MIN_UV, + VTG_MAX_UV); + if (rc) { + dev_err(&cd->client->dev, + "Regulator set_vtg failed vdd rc=%d\n", rc); + goto exit; + } + } + + rc = regulator_enable(cd->vdd); + if (rc) { + dev_err(&cd->client->dev, + "Regulator vdd enable failed rc=%d\n", rc); + goto exit; + } + } +#endif + + if (cd->vcc_i2c) { + if (regulator_count_voltages(cd->vcc_i2c) > 0) { + rc = regulator_set_voltage(cd->vcc_i2c, I2C_VTG_MIN_UV, + I2C_VTG_MAX_UV); + if (rc) { + dev_err(&cd->client->dev, + "Regulator set_vtg failed vcc_i2c rc=%d\n", rc); + goto disable_vdd_reg; + } + } + + rc = regulator_enable(cd->vcc_i2c); + if (rc) { + dev_err(&cd->client->dev, + "Regulator vcc_i2c enable failed rc=%d\n", rc); + goto disable_vdd_reg; + } + } + + return 0; + +disable_vcc_i2c_reg: + if (cd->vcc_i2c) { + if (regulator_count_voltages(cd->vcc_i2c) > 0) + regulator_set_voltage(cd->vcc_i2c, I2C_VTG_MIN_UV, + I2C_VTG_MAX_UV); + + regulator_disable(cd->vcc_i2c); + } + +disable_vdd_reg: +#ifdef VDD_ANALOG_ENABLE + if (cd->vdd) { + if (regulator_count_voltages(cd->vdd) > 0) + regulator_set_voltage(cd->vdd, VTG_MIN_UV, + VTG_MAX_UV); + + regulator_disable(cd->vdd); + } +#endif + +#ifdef VDD_ANALOG_ENABLE +exit: +#endif + return rc; +} + static int raydium_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1900,6 +2020,17 @@ static int raydium_ts_probe(struct i2c_client *client, } #endif /*end of MSM_NEW_VER*/ + ret = raydium_get_regulator(g_raydium_ts, true); + if (ret) { + dev_err(&client->dev, "Failed to get voltage regulators\n"); + goto error_alloc_data; + } + + ret = raydium_enable_regulator(g_raydium_ts, true); + if (ret) { + dev_err(&client->dev, "Failed to enable regulators: rc=%d\n", ret); + goto error_get_regulator; + } ret = raydium_gpio_configure(true); if (ret < 0) { LOGD(LOG_ERR, "[touch]failed to configure the gpios\n"); @@ -2031,6 +2162,10 @@ err_gpio_req: } #endif/*end of MSM_NEW_VER*/ +error_get_regulator: + raydium_get_regulator(g_raydium_ts, false); +error_alloc_data: + kfree(g_raydium_ts); parse_dt_failed: exit_check_functionality_failed: return ret; @@ -2064,8 +2199,9 @@ static int raydium_ts_remove(struct i2c_client *client) cancel_work_sync(&g_raydium_ts->work); destroy_workqueue(g_raydium_ts->workqueue); - - //kfree(g_raydium_ts); + raydium_enable_regulator(g_raydium_ts, false); + raydium_get_regulator(g_raydium_ts, false); + kfree(g_raydium_ts); i2c_set_clientdata(client, NULL); return 0; diff --git a/raydium/raydium_driver.h b/raydium/raydium_driver.h index 60ed6f4d37..65248c96f8 100644 --- a/raydium/raydium_driver.h +++ b/raydium/raydium_driver.h @@ -23,6 +23,10 @@ #define COORDS_ARR_SIZE 4 #define I2C_VTG_MIN_UV 1800000 #define I2C_VTG_MAX_UV 1800000 +#ifdef VDD_ANALOG_ENABLE +#define VTG_MIN_UV 2800000 +#define VTG_MAX_UV 2800000 +#endif #define RAD_MAIN_VERSION 0x01 #define RAD_MINOR_VERSION 0x01 #define RAD_CUSTOMER_VERSION 0x0100 @@ -275,6 +279,9 @@ struct raydium_ts_data { #endif /*end of CONFIG_FB*/ /*struct regulator *vdd;*/ +#ifdef VDD_ANALOG_ENABLE + struct regulator *vdd; +#endif struct regulator *vcc_i2c; unsigned int fw_version; unsigned short id; From 4f002c78b1a490c143bae82671584ea60d4d85e9 Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Fri, 16 Sep 2022 18:43:55 +0530 Subject: [PATCH 046/170] touch: Enable and Disable voltages Added regulator API's for enablement and disablement of voltages for touch during SUSPEND and RESUME states. Change-Id: Idfb0f0890700576acb15dfc11fa0118ed8fa94ee Signed-off-by: Srikanth Katteboina --- raydium/raydium_driver.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 5fe81c421c..979723aa05 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -1332,6 +1332,7 @@ exit_error: static void raydium_ts_do_suspend(void) { unsigned char u8_i = 0; + int rc; if (g_u8_raw_data_type == 0) g_u8_resetflag = false; @@ -1340,6 +1341,14 @@ static void raydium_ts_do_suspend(void) return; } + rc = raydium_enable_regulator(g_raydium_ts, false); + if (rc < 0) { + LOGD(LOG_ERR, "[touch]%s:Failed to disable regulators:rc=%d\n", + __func__, rc); + } + LOGD(LOG_INFO, "[touch]%s:voltage regulators disabled:rc=%d\n", + __func__, rc); + /*#ifndef GESTURE_EN*/ raydium_irq_control(DISABLE); /*#endif*/ @@ -1420,6 +1429,13 @@ static void raydium_ts_do_resume(void) g_u8_checkflag = false; } #endif + rc = raydium_enable_regulator(g_raydium_ts, true); + if (rc < 0) { + LOGD(LOG_ERR, "[touch]%s:Failed to disable regulators:rc=%d\n", + __func__, rc); + } + LOGD(LOG_INFO, "[touch]%s:voltage regulators disabled:rc=%d\n", + __func__, rc); raydium_irq_control(ENABLE); #ifdef GESTURE_EN if (device_may_wakeup(&g_raydium_ts->client->dev)) { From 03bb25d1f14f31ae060d24273cdacadf4464168e Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Fri, 16 Sep 2022 18:59:11 +0530 Subject: [PATCH 047/170] touch: fixing compilation errors fixing compilation errors in raydium driver code. Change-Id: Ia2b5d1b12c8fe7f5c5832ac5ee656555822ec7db Signed-off-by: Srikanth Katteboina --- raydium/chip_raydium/f303_ic_test.c | 44 +---------------------------- raydium/raydium_driver.c | 1 + raydium/raydium_sysfs.c | 16 +++++------ 3 files changed, 10 insertions(+), 51 deletions(-) diff --git a/raydium/chip_raydium/f303_ic_test.c b/raydium/chip_raydium/f303_ic_test.c index ad471bb49f..0694356601 100644 --- a/raydium/chip_raydium/f303_ic_test.c +++ b/raydium/chip_raydium/f303_ic_test.c @@ -501,7 +501,7 @@ STATUS burn_header_log_to_flash_3x(bool is_test_finish, bool is_in_header_data_p DEBUGOUT("End burn header\r\n"); return SUCCESS; } - +#endif STATUS turn_on_flash_3x(void) { unsigned int u32_read = 0; @@ -1921,49 +1921,7 @@ STATUS load_test_fw_3x(void) return u8_ret; } -/* - * STATUS check_ext_flash_fw_version(void) - * { - * unsigned int u32_addr, u32_normal_fw_version, u32_test_fw_version; - * unsigned short u16_data_length = 0; - * u16_data_length = 4; - * - * - * //get normal FW version from ext flash// - * u32_addr = FLASH_NORMAL_FW_FW_VERSION_ADDR; - * if (read_flash_data(u32_addr, u16_data_length) == ERROR) { - * DEBUGOUT("Read Ext Flash NG [0x%08X] \r\n", u32_addr); - * return ERROR; - * } - * u32_normal_fw_version = 0; - * u32_normal_fw_version |= g_u8_data_buf[0]; - * u32_normal_fw_version |= g_u8_data_buf[1] << 8; - * u32_normal_fw_version |= g_u8_data_buf[2] << 16; - * u32_normal_fw_version |= g_u8_data_buf[3] << 24; - * //DEBUGOUT("SPI Read_addr = 0x%08X, u16DataLength = %d \r\n", u32_normal_fw_version, u16_data_length); - */ - /* Test FW Version*/ - u32_addr = FLASH_TEST_FW_FW_VERSION_ADDR; - if (read_flash_data(u32_addr, u16_data_length) == ERROR) { - DEBUGOUT("Read Ext Flash NG [0x%08X] \r\n", u32_addr); - return ERROR; - } - u32_test_fw_version = 0; - u32_test_fw_version |= g_u8_data_buf[0]; - u32_test_fw_version |= g_u8_data_buf[1] << 8; - u32_test_fw_version |= g_u8_data_buf[2] << 16; - u32_test_fw_version |= g_u8_data_buf[3] << 24; - /*DEBUGOUT("SPI Read_addr = 0x%08X, u16DataLength = %d \r\n", u32_test_fw_version, u16_data_length);*/ - if (g_st_test_para_resv.u32_normal_fw_version != u32_normal_fw_version - || g_st_test_para_resv.u32_test_fw_version != u32_test_fw_version) { - DEBUGOUT("INI FW Version NG ,0x%08X VS 0x%08X \r\n", g_st_test_para_resv.u32_normal_fw_version, u32_normal_fw_version); - DEBUGOUT("INI Test FW Version NG ,0x%08X VS 0x%08X \r\n", g_st_test_para_resv.u32_test_fw_version, u32_test_fw_version); - return ERROR; - } - return SUCCESS; -} -#endif STATUS system_test_3x(void) { STATUS u8_test_result = SUCCESS; diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 979723aa05..816d63bea0 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -55,6 +55,7 @@ struct raydium_slot_status { struct raydium_slot_status gst_slot[MAX_TOUCH_NUM * 2]; struct raydium_slot_status gst_slot_init = {0xFF, 0, 0}; +static int raydium_enable_regulator(struct raydium_ts_data *cd, bool en); #if (defined(CONFIG_RM_SYSFS_DEBUG)) const struct attribute_group raydium_attr_group; #endif /*end of CONFIG_RM_SYSFS_DEBUG*/ diff --git a/raydium/raydium_sysfs.c b/raydium/raydium_sysfs.c index 34eadf9885..f04c89cdda 100644 --- a/raydium/raydium_sysfs.c +++ b/raydium/raydium_sysfs.c @@ -567,18 +567,18 @@ static ssize_t raydium_mem_store(struct device *dev, kfree(g_rad_fw_image); g_rad_fw_image = NULL; } - if (g_rad_para_image) { - ree(g_rad_para_image); - g_rad_para_image = NULL; - } + + kfree(g_rad_para_image); + g_rad_para_image = NULL; + if (g_rad_testfw_image) { kfree(g_rad_testfw_image); g_rad_testfw_image = NULL; } - if (g_rad_testpara_image) { - free(g_rad_testpara_image); - g_rad_testpara_image = NULL; - } + + kfree(g_rad_testpara_image); + g_rad_testpara_image = NULL; + if (!raydium_id_init(u8_type)) { LOGD(LOG_ERR, "[touch]Set Raydium id failed!\n"); return count; From dedef8c90dfa4e8e5e005cd581d41160bcd36434 Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Fri, 16 Sep 2022 19:36:22 +0530 Subject: [PATCH 048/170] touch: exit in probe Adjust exit labels to avoid the use-after-free issue. Change-Id: I46368c80014aa3cbb680cf6e9967e20e188c1340 Signed-off-by: Srikanth Katteboina --- raydium/raydium_driver.c | 43 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 816d63bea0..acdcadae18 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -1867,13 +1867,15 @@ static int raydium_get_regulator(struct raydium_ts_data *cd, bool get) regulator_put: #ifdef VDD_ANALOG_ENABLE - if (cd->vdd) { + if (!IS_ERR(cd->vdd)) { + dev_err(&cd->client->dev, "Regulator put vdd\n"); regulator_put(cd->vdd); cd->vdd = NULL; } #endif - if (cd->vcc_i2c) { + if (!IS_ERR(cd->vcc_i2c)) { + dev_err(&cd->client->dev, "Regulator put vcc_i2c\n"); regulator_put(cd->vcc_i2c); cd->vcc_i2c = NULL; } @@ -2063,9 +2065,9 @@ static int raydium_ts_probe(struct i2c_client *client, /*print touch i2c ready*/ ret = raydium_check_i2c_ready(&u16_i2c_data); - if (ret < 0) { + if (ret < 0 || (u16_i2c_data == 0)) { LOGD(LOG_ERR, "[touch]Check I2C failed\n"); - ret = -ENODEV; + ret = -EPROBE_DEFER; goto exit_check_i2c; } @@ -2143,6 +2145,8 @@ static int raydium_ts_probe(struct i2c_client *client, ret = -ENODEV; goto exit_irq_request_failed; } + + LOGD(LOG_INFO, "[touch] probe: done\n"); return 0; exit_irq_request_failed: @@ -2158,6 +2162,20 @@ exit_input_register_device_failed: exit_input_dev_alloc_failed: exit_check_i2c: +#ifdef MSM_NEW_VER + if (g_raydium_ts->ts_pinctrl) { + if (IS_ERR_OR_NULL(g_raydium_ts->pinctrl_state_release)) { + devm_pinctrl_put(g_raydium_ts->ts_pinctrl); + g_raydium_ts->ts_pinctrl = NULL; + } else { + if (pinctrl_select_state(g_raydium_ts->ts_pinctrl, + g_raydium_ts->pinctrl_state_release)) + LOGD(LOG_ERR, + "[touch]pinctrl_select_state failed\n"); + } + } +#endif/*end of MSM_NEW_VER*/ + if (gpio_is_valid(pdata->reset_gpio)) gpio_free(pdata->reset_gpio); @@ -2165,24 +2183,11 @@ exit_check_i2c: gpio_free(pdata->irq_gpio); err_gpio_req: -#ifdef MSM_NEW_VER - if (g_raydium_ts->ts_pinctrl) { - if (IS_ERR_OR_NULL(g_raydium_ts->pinctrl_state_release)) { - devm_pinctrl_put(g_raydium_ts->ts_pinctrl); - g_raydium_ts->ts_pinctrl = NULL; - } else { - ret = pinctrl_select_state(g_raydium_ts->ts_pinctrl, - g_raydium_ts->pinctrl_state_release); - if (ret) - LOGD(LOG_ERR, "[touch]pinctrl_select_state failed\n"); - } - } -#endif/*end of MSM_NEW_VER*/ - + raydium_get_regulator(g_raydium_ts, false); error_get_regulator: raydium_get_regulator(g_raydium_ts, false); error_alloc_data: - kfree(g_raydium_ts); + parse_dt_failed: exit_check_functionality_failed: return ret; From 748d0d396a6571f7a5b6fdb469dcbc398e106874 Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Fri, 16 Sep 2022 21:02:24 +0530 Subject: [PATCH 049/170] touch: Correct long press and swiping Correct long press and swiping on the edge of screen. Change-Id: Id8758fbb1092719f0e0e6db2aa4ac4e9a75cc447 Signed-off-by: Srikanth Katteboina --- raydium/rad_fw_image_30.h | 9130 ++++++++++++++++++------------------- raydium/raydium_driver.h | 2 +- 2 files changed, 4566 insertions(+), 4566 deletions(-) diff --git a/raydium/rad_fw_image_30.h b/raydium/rad_fw_image_30.h index 1f33fe207a..60f9ff3197 100644 --- a/raydium/rad_fw_image_30.h +++ b/raydium/rad_fw_image_30.h @@ -294,7 +294,7 @@ const unsigned char u8_rad_init_30[] = { 0x3C, 0x07, 0x00, 0x20, 0x17, 0x26, 0x28, 0x2A, }; const unsigned char u8_rad_fw_30[] = { -0xD0, 0x0C, 0x00, 0x20, 0xB1, 0x08, 0x00, 0x00, +0xD0, 0x0D, 0x00, 0x20, 0xB1, 0x08, 0x00, 0x00, 0xB9, 0x08, 0x00, 0x00, 0x95, 0x08, 0x00, 0x00, 0xBD, 0x08, 0x00, 0x00, 0xBF, 0x08, 0x00, 0x00, 0xC1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -305,17 +305,17 @@ const unsigned char u8_rad_fw_30[] = { 0xE1, 0x0C, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x00, 0xA1, 0x0D, 0x00, 0x00, 0x41, 0x0A, 0x00, 0x00, -0xCB, 0x08, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00, +0xCB, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, 0x41, 0x0B, 0x00, 0x00, 0xE1, 0x0A, 0x00, 0x00, 0xA1, 0x0A, 0x00, 0x00, 0xC1, 0x0A, 0x00, 0x00, 0x81, 0x0A, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, 0x03, 0x48, 0x85, 0x46, 0x00, 0xF0, 0x80, 0xF8, -0x00, 0x48, 0x00, 0x47, 0x19, 0x44, 0x00, 0x00, -0xD0, 0x0C, 0x00, 0x20, 0x04, 0x20, 0x71, 0x46, +0x00, 0x48, 0x00, 0x47, 0x71, 0x4C, 0x00, 0x00, +0xD0, 0x0D, 0x00, 0x20, 0x04, 0x20, 0x71, 0x46, 0x08, 0x42, 0x02, 0xD0, 0xEF, 0xF3, 0x09, 0x80, 0x01, 0xE0, 0xEF, 0xF3, 0x08, 0x80, 0x71, 0x46, -0x00, 0x4A, 0x10, 0x47, 0xC1, 0x42, 0x00, 0x00, +0x00, 0x4A, 0x10, 0x47, 0x19, 0x4B, 0x00, 0x00, 0x06, 0x48, 0x80, 0x47, 0x06, 0x48, 0x00, 0x47, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, @@ -346,8 +346,8 @@ const unsigned char u8_rad_fw_30[] = { 0x06, 0x4C, 0x01, 0x25, 0x06, 0x4E, 0x05, 0xE0, 0x20, 0x46, 0xE3, 0x68, 0x07, 0xC8, 0x2B, 0x43, 0x98, 0x47, 0x10, 0x34, 0xB4, 0x42, 0xF7, 0xD3, -0xFF, 0xF7, 0x72, 0xFF, 0x58, 0x5C, 0x00, 0x00, -0x78, 0x5C, 0x00, 0x00, 0xC1, 0x06, 0xC9, 0x0E, +0xFF, 0xF7, 0x72, 0xFF, 0xA0, 0x64, 0x00, 0x00, +0xC0, 0x64, 0x00, 0x00, 0xC1, 0x06, 0xC9, 0x0E, 0x01, 0x20, 0x88, 0x40, 0x01, 0x49, 0x08, 0x60, 0x70, 0x47, 0x00, 0x00, 0x00, 0xE1, 0x00, 0xE0, 0x0B, 0x49, 0x10, 0xB5, 0x88, 0x42, 0x01, 0xD9, @@ -369,7 +369,7 @@ const unsigned char u8_rad_fw_30[] = { 0x05, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, 0x01, 0x20, 0x80, 0x07, 0x01, 0x6A, 0x03, 0x22, 0xD2, 0x03, 0x11, 0x43, 0x01, 0x62, 0x70, 0x47, -0xD0, 0x00, 0x00, 0x20, 0x02, 0xE0, 0x08, 0xC8, +0x68, 0x02, 0x00, 0x20, 0x02, 0xE0, 0x08, 0xC8, 0x12, 0x1F, 0x08, 0xC1, 0x00, 0x2A, 0xFA, 0xD1, 0x70, 0x47, 0x00, 0x20, 0x01, 0xE0, 0x01, 0xC1, 0x12, 0x1F, 0x00, 0x2A, 0xFB, 0xD1, 0x70, 0x47, @@ -380,30 +380,30 @@ const unsigned char u8_rad_fw_30[] = { 0xFF, 0xF7, 0xEE, 0xFF, 0x10, 0xBD, 0x00, 0x00, 0x03, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, 0x02, 0x49, 0x01, 0x20, 0xC8, 0x60, 0x70, 0x47, -0x08, 0x03, 0x00, 0x20, 0x00, 0x03, 0x00, 0x50, -0x10, 0xB5, 0x02, 0xF0, 0x8F, 0xF8, 0x10, 0xBD, +0x18, 0x03, 0x00, 0x20, 0x00, 0x03, 0x00, 0x50, +0x10, 0xB5, 0x02, 0xF0, 0xDF, 0xFC, 0x10, 0xBD, 0x70, 0x47, 0x00, 0x00, 0x04, 0x49, 0x06, 0x22, 0x00, 0x28, 0x08, 0x68, 0x01, 0xD0, 0x10, 0x43, 0x00, 0xE0, 0x90, 0x43, 0x08, 0x60, 0x70, 0x47, 0x00, 0x09, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, -0x70, 0x47, 0x00, 0x00, 0x64, 0x20, 0x05, 0x49, -0x02, 0xE0, 0x00, 0xBF, 0x40, 0x1E, 0xC0, 0xB2, -0x4A, 0x68, 0x12, 0x06, 0x01, 0xD5, 0x00, 0x28, -0xF7, 0xD1, 0x70, 0x47, 0x00, 0x06, 0x00, 0x50, -0x00, 0xB5, 0x07, 0x48, 0x01, 0x69, 0x02, 0x29, -0x06, 0xD1, 0x01, 0x61, 0x05, 0x49, 0x8A, 0x78, -0x00, 0x2A, 0x02, 0xD0, 0x02, 0xF0, 0x28, 0xFB, -0x00, 0xBD, 0x01, 0x20, 0xC8, 0x70, 0x00, 0xBD, -0x00, 0x02, 0x00, 0x50, 0x5C, 0x04, 0x00, 0x20, -0x04, 0x49, 0x00, 0x20, 0x08, 0x60, 0x01, 0x20, -0x80, 0x07, 0x41, 0x68, 0x42, 0x14, 0x11, 0x43, -0x41, 0x60, 0x70, 0x47, 0xD0, 0x00, 0x00, 0x20, +0x70, 0x47, 0x00, 0x00, 0x30, 0xB5, 0x07, 0x49, +0x07, 0x4B, 0x0A, 0x68, 0x00, 0x20, 0x41, 0x00, +0xCD, 0x18, 0x54, 0x5A, 0x2D, 0x88, 0x40, 0x1C, +0x64, 0x1B, 0xC0, 0xB2, 0x54, 0x52, 0x30, 0x28, +0xF5, 0xD3, 0x30, 0xBD, 0x00, 0x03, 0x00, 0x20, +0x00, 0x00, 0x01, 0x20, 0x10, 0xB5, 0x08, 0x4A, +0x00, 0x21, 0x12, 0x68, 0x60, 0x32, 0x12, 0x78, +0x52, 0x1C, 0xD4, 0xB2, 0x4A, 0x00, 0x83, 0x5E, +0x49, 0x1C, 0x63, 0x43, 0x1B, 0x11, 0xC9, 0xB2, +0x83, 0x52, 0x30, 0x29, 0xF6, 0xD3, 0x10, 0xBD, +0xAC, 0x04, 0x00, 0x20, 0x10, 0xB5, 0x02, 0xF0, +0x91, 0xFD, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x30, 0xB5, 0x1E, 0x4B, 0x58, 0x68, 0x99, 0x68, 0x00, 0x28, 0x01, 0xDA, 0xDA, 0x04, 0x5A, 0x60, 0x1B, 0x4A, 0x15, 0x68, 0x01, 0x24, 0x05, 0x40, 0x00, 0x2D, 0x02, 0xD1, 0x50, 0x68, 0x08, 0x42, -0x0F, 0xD0, 0x18, 0x48, 0x04, 0x70, 0x00, 0xF0, -0x6F, 0xF9, 0x14, 0x72, 0x16, 0x48, 0x00, 0x78, +0x0F, 0xD0, 0x18, 0x48, 0x04, 0x70, 0x02, 0xF0, +0x03, 0xFA, 0x14, 0x72, 0x16, 0x48, 0x00, 0x78, 0x00, 0x28, 0x06, 0xD0, 0x18, 0x68, 0x01, 0x21, 0x00, 0x09, 0x00, 0x01, 0x89, 0x02, 0x08, 0x43, 0x18, 0x60, 0x58, 0x68, 0x80, 0x02, 0x02, 0xD5, @@ -414,14 +414,14 @@ const unsigned char u8_rad_fw_30[] = { 0xD4, 0x72, 0x58, 0x68, 0x40, 0x00, 0x03, 0xD5, 0x01, 0x20, 0x80, 0x07, 0x58, 0x60, 0x94, 0x72, 0x30, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, -0x50, 0x02, 0x00, 0x20, 0x99, 0x01, 0x00, 0x20, -0xCF, 0x00, 0x00, 0x20, 0x30, 0xB5, 0x07, 0x49, -0x07, 0x4B, 0x0A, 0x68, 0x00, 0x20, 0x41, 0x00, -0xCD, 0x18, 0x54, 0x5A, 0x2D, 0x88, 0x40, 0x1C, -0x64, 0x1B, 0xC0, 0xB2, 0x54, 0x52, 0x30, 0x28, -0xF5, 0xD3, 0x30, 0xBD, 0xF8, 0x02, 0x00, 0x20, -0x00, 0x00, 0x01, 0x20, 0x10, 0xB5, 0x02, 0xF0, -0x09, 0xF9, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00, +0x50, 0x02, 0x00, 0x20, 0x9B, 0x01, 0x00, 0x20, +0x99, 0x01, 0x00, 0x20, 0x00, 0xB5, 0x03, 0x46, +0x00, 0x20, 0xFF, 0xF7, 0x77, 0xFF, 0x04, 0x49, +0x01, 0x20, 0x09, 0x68, 0x0A, 0x88, 0x9A, 0x43, +0x0A, 0x80, 0xFF, 0xF7, 0x6F, 0xFF, 0x00, 0xBD, +0x08, 0x00, 0x00, 0x20, 0x03, 0x49, 0x0A, 0x68, +0x10, 0x18, 0x0A, 0x68, 0x90, 0x42, 0xFC, 0xD1, +0x70, 0x47, 0x00, 0x00, 0x18, 0x03, 0x00, 0x20, 0x70, 0xB5, 0x2B, 0x4B, 0x05, 0x20, 0x19, 0x7A, 0x40, 0x04, 0x02, 0x29, 0x4C, 0xD1, 0x29, 0x4C, 0x21, 0x78, 0x00, 0x29, 0x45, 0xD1, 0x28, 0x49, @@ -433,7 +433,7 @@ const unsigned char u8_rad_fw_30[] = { 0x02, 0xD0, 0x1E, 0x7A, 0x02, 0x2E, 0xF1, 0xD0, 0x18, 0x7A, 0x02, 0x28, 0x26, 0xD1, 0x10, 0x78, 0x81, 0x28, 0x23, 0xD0, 0x00, 0x29, 0x21, 0xD1, -0xFF, 0xF7, 0x44, 0xFF, 0x1A, 0x48, 0x40, 0x68, +0x00, 0xF0, 0x76, 0xF9, 0x1A, 0x48, 0x40, 0x68, 0x40, 0x01, 0x40, 0x0D, 0xBC, 0x28, 0x01, 0xD9, 0xBC, 0x38, 0x80, 0xB2, 0x17, 0x49, 0x49, 0x68, 0x09, 0x06, 0x0D, 0xD4, 0x16, 0x49, 0x09, 0x88, @@ -443,101 +443,101 @@ const unsigned char u8_rad_fw_30[] = { 0x00, 0x20, 0x00, 0xBF, 0x40, 0x1C, 0xC0, 0xB2, 0x14, 0x28, 0xFA, 0xD3, 0x20, 0x78, 0x00, 0x28, 0x02, 0xD0, 0x20, 0x78, 0x40, 0x1E, 0x20, 0x70, -0xFF, 0xF7, 0x3E, 0xFF, 0x70, 0xBD, 0x00, 0x00, +0x01, 0xF0, 0x88, 0xFD, 0x70, 0xBD, 0x00, 0x00, 0x50, 0x02, 0x00, 0x20, 0xEC, 0x02, 0x00, 0x20, -0x99, 0x01, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, -0x72, 0x04, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, +0x9B, 0x01, 0x00, 0x20, 0x60, 0x07, 0x00, 0x20, +0x4E, 0x07, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, 0x00, 0x11, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50, -0x60, 0x02, 0x00, 0x20, 0x62, 0x02, 0x00, 0x20, -0x10, 0xB5, 0x02, 0xF0, 0xF1, 0xF9, 0x10, 0xBD, +0x62, 0x02, 0x00, 0x20, 0x64, 0x02, 0x00, 0x20, +0x10, 0xB5, 0xFF, 0xF7, 0x87, 0xFF, 0x10, 0xBD, 0x70, 0xB5, 0x01, 0x25, 0xAD, 0x07, 0xE8, 0x68, -0x40, 0x08, 0x40, 0x00, 0xE8, 0x60, 0x1B, 0x48, +0x40, 0x08, 0x40, 0x00, 0xE8, 0x60, 0x20, 0x48, 0x41, 0x68, 0x01, 0x26, 0x49, 0x07, 0x00, 0x29, -0x2A, 0xDA, 0x42, 0x68, 0x04, 0x21, 0x0A, 0x43, -0x42, 0x60, 0x17, 0x48, 0x00, 0x68, 0x17, 0x4C, -0x40, 0x05, 0x40, 0x0F, 0x20, 0x73, 0x20, 0x7B, -0x06, 0x28, 0x00, 0xD1, 0x21, 0x73, 0x20, 0x7B, -0x02, 0x28, 0x1D, 0xD0, 0x20, 0x7B, 0x06, 0x28, -0x05, 0xD8, 0x20, 0x7B, 0x04, 0x28, 0x02, 0xD3, -0x01, 0x20, 0x01, 0xF0, 0xA1, 0xF9, 0x20, 0x7B, -0x0D, 0x49, 0x09, 0x78, 0x88, 0x42, 0x0B, 0xD0, -0x0C, 0x4C, 0xA0, 0x79, 0x00, 0x28, 0x01, 0xD0, -0x01, 0xF0, 0xF8, 0xF9, 0x0A, 0x48, 0x01, 0x78, -0x31, 0x43, 0x01, 0x70, 0x81, 0x20, 0x20, 0x70, -0xE8, 0x68, 0x30, 0x43, 0xE8, 0x60, 0x70, 0xBD, -0x00, 0x20, 0xE6, 0xE7, 0x40, 0x00, 0x00, 0x50, -0x00, 0x11, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, -0xE5, 0x02, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, -0xCE, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x08, 0x4A, -0x00, 0x21, 0x12, 0x68, 0x60, 0x32, 0x12, 0x78, -0x52, 0x1C, 0xD4, 0xB2, 0x4A, 0x00, 0x83, 0x5E, -0x49, 0x1C, 0x63, 0x43, 0x1B, 0x11, 0xC9, 0xB2, -0x83, 0x52, 0x30, 0x29, 0xF6, 0xD3, 0x10, 0xBD, -0xB8, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, +0x33, 0xDA, 0x42, 0x68, 0x04, 0x21, 0x0A, 0x43, +0x42, 0x60, 0x1C, 0x48, 0x00, 0x68, 0x1C, 0x4A, +0x40, 0x05, 0x40, 0x0F, 0x01, 0x28, 0x07, 0xD0, +0x00, 0x23, 0x1A, 0x4C, 0x13, 0x70, 0x20, 0x73, +0x20, 0x7B, 0x06, 0x28, 0x03, 0xD0, 0x03, 0xE0, +0x03, 0x20, 0x10, 0x70, 0x1D, 0xE0, 0x21, 0x73, +0x20, 0x7B, 0x02, 0x28, 0x1D, 0xD0, 0x20, 0x7B, +0x06, 0x28, 0x05, 0xD8, 0x20, 0x7B, 0x04, 0x28, +0x02, 0xD3, 0x01, 0x20, 0x01, 0xF0, 0x4A, 0xFD, +0x20, 0x7B, 0x0F, 0x49, 0x09, 0x78, 0x88, 0x42, +0x0B, 0xD0, 0x0E, 0x4C, 0xA0, 0x79, 0x00, 0x28, +0x01, 0xD0, 0x01, 0xF0, 0x9D, 0xFD, 0x0C, 0x48, +0x01, 0x78, 0x31, 0x43, 0x01, 0x70, 0x81, 0x20, +0x20, 0x70, 0xE8, 0x68, 0x30, 0x43, 0xE8, 0x60, +0x70, 0xBD, 0x00, 0x20, 0xE6, 0xE7, 0x00, 0x00, +0x40, 0x00, 0x00, 0x50, 0x00, 0x11, 0x00, 0x50, +0x61, 0x02, 0x00, 0x20, 0x50, 0x02, 0x00, 0x20, +0xE5, 0x02, 0x00, 0x20, 0x60, 0x07, 0x00, 0x20, +0x98, 0x01, 0x00, 0x20, 0x01, 0x20, 0x80, 0x07, +0x41, 0x68, 0x82, 0x14, 0x11, 0x43, 0x41, 0x60, +0x70, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xB5, 0x14, 0x48, 0x80, 0x69, 0x40, 0x04, 0x23, 0xD5, 0x13, 0x4C, 0xE0, 0x68, 0xA2, 0x68, 0x02, 0x40, 0xE0, 0x68, 0xA1, 0x68, 0x88, 0x43, 0xE0, 0x60, 0x10, 0x07, 0x03, 0xD5, 0x08, 0x20, -0xE0, 0x60, 0x04, 0xF0, 0x3F, 0xFB, 0x50, 0x07, +0xE0, 0x60, 0x04, 0xF0, 0xFF, 0xFE, 0x50, 0x07, 0x0E, 0xD5, 0x04, 0x20, 0xE0, 0x60, 0x04, 0xF0, -0x39, 0xFB, 0x02, 0xF0, 0xFD, 0xFC, 0x00, 0x28, +0xF9, 0xFE, 0x03, 0xF0, 0x53, 0xF9, 0x00, 0x28, 0x06, 0xD1, 0x08, 0x48, 0x00, 0x78, 0x00, 0x28, 0x02, 0xD0, 0x01, 0x20, 0xFF, 0xF7, 0x68, 0xFE, 0xFF, 0x20, 0xF3, 0x30, 0xE0, 0x60, 0x01, 0x20, 0xE0, 0x60, 0x10, 0xBD, 0x00, 0x09, 0x00, 0x50, 0x00, 0x05, 0x00, 0x50, 0x90, 0x02, 0x00, 0x20, -0x00, 0xB5, 0x03, 0x46, 0x00, 0x20, 0xFF, 0xF7, -0x5D, 0xFE, 0x04, 0x49, 0x01, 0x20, 0x09, 0x68, -0x0A, 0x88, 0x9A, 0x43, 0x0A, 0x80, 0xFF, 0xF7, -0x55, 0xFE, 0x00, 0xBD, 0x08, 0x00, 0x00, 0x20, 0x01, 0x28, 0x05, 0xD0, 0x02, 0x28, 0x05, 0xD0, 0x04, 0x28, 0x06, 0xD0, 0x00, 0x20, 0x70, 0x47, 0x03, 0x48, 0x70, 0x47, 0x02, 0x48, 0xC0, 0x30, 0x70, 0x47, 0x02, 0x48, 0x70, 0x47, 0x00, 0x00, 0x78, 0x7C, 0x00, 0x00, 0xF8, 0x7D, 0x00, 0x00, -0x03, 0x48, 0x02, 0x49, 0x41, 0x60, 0x03, 0x49, -0x81, 0x60, 0x70, 0x47, 0x1F, 0x1F, 0x5F, 0x1F, -0x00, 0x10, 0x00, 0x50, 0x1F, 0x1F, 0x1F, 0x1F, -0xF8, 0xB5, 0x2B, 0x48, 0x80, 0x69, 0x40, 0x04, -0x51, 0xD5, 0x2A, 0x4D, 0xA8, 0x6A, 0xE9, 0x68, +0x00, 0xB5, 0x08, 0x49, 0x0A, 0x28, 0x05, 0xD0, +0x07, 0x48, 0x00, 0x0C, 0x48, 0x63, 0x07, 0x48, +0x08, 0x63, 0x00, 0xBD, 0x06, 0x48, 0x00, 0x68, +0x08, 0x62, 0x0D, 0x20, 0x01, 0xF0, 0xB4, 0xF8, +0x00, 0xBD, 0x00, 0x00, 0x00, 0x06, 0x00, 0x50, +0xBC, 0x02, 0x00, 0x20, 0xCC, 0x02, 0x00, 0x20, +0xC0, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, +0xF8, 0xB5, 0x34, 0x48, 0x80, 0x69, 0x40, 0x04, +0x62, 0xD5, 0x33, 0x4D, 0xA8, 0x6A, 0xE9, 0x68, 0xAC, 0x68, 0x0C, 0x40, 0xE9, 0x68, 0xAA, 0x68, -0x91, 0x43, 0xE9, 0x60, 0x26, 0x4E, 0xC0, 0x07, +0x91, 0x43, 0xE9, 0x60, 0x2F, 0x4E, 0xC0, 0x07, 0x15, 0xD0, 0x68, 0x69, 0x89, 0x27, 0xC0, 0xB2, 0xEF, 0x60, 0xAA, 0x6A, 0x01, 0x21, 0x0A, 0x43, 0xAA, 0x62, 0x0B, 0x28, 0x03, 0xD2, 0x31, 0x70, -0x01, 0xF0, 0x04, 0xF8, 0x03, 0xE0, 0x00, 0x21, -0x31, 0x70, 0x00, 0xF0, 0xDB, 0xFF, 0xBC, 0x43, -0x01, 0x20, 0xFF, 0xF7, 0x05, 0xFE, 0x1B, 0x48, -0xA1, 0x04, 0x04, 0xD5, 0x29, 0x6A, 0x81, 0x43, -0x29, 0x62, 0x41, 0x14, 0xE9, 0x60, 0xE1, 0x04, -0x05, 0xD5, 0x29, 0x6A, 0x81, 0x43, 0x29, 0x62, -0x01, 0x20, 0x00, 0x03, 0xE8, 0x60, 0xA0, 0x05, -0x02, 0xD5, 0x01, 0x20, 0x40, 0x02, 0xE8, 0x60, -0x20, 0x07, 0x09, 0xD5, 0x30, 0x78, 0x01, 0x28, -0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, 0xBA, 0xFF, -0x64, 0x08, 0x64, 0x00, 0x09, 0x20, 0xE8, 0x60, -0x60, 0x07, 0x07, 0xD5, 0x30, 0x78, 0x01, 0x28, -0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, 0xAE, 0xFF, -0x05, 0x20, 0xE8, 0x60, 0xFF, 0x20, 0xF3, 0x30, -0xE8, 0x60, 0x01, 0x20, 0xE8, 0x60, 0xF8, 0xBD, -0x00, 0x09, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50, -0x9B, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, -0x70, 0xB5, 0x41, 0x18, 0x49, 0x1E, 0x64, 0x24, -0x09, 0x04, 0x0B, 0x4D, 0x01, 0x43, 0x69, 0x63, -0xE8, 0x68, 0x81, 0x21, 0x09, 0x06, 0x08, 0x43, -0xE8, 0x60, 0x02, 0xE0, 0x01, 0x20, 0xFF, 0xF7, -0xCB, 0xFE, 0xE8, 0x68, 0xC0, 0x01, 0x04, 0xD5, -0x20, 0x46, 0x64, 0x1E, 0xA4, 0xB2, 0x00, 0x28, -0xF4, 0xD1, 0xA8, 0x6B, 0x70, 0xBD, 0x00, 0x00, -0x40, 0x09, 0x00, 0x50, 0x04, 0x49, 0x29, 0x20, -0xC8, 0x60, 0x04, 0x49, 0x35, 0x20, 0x08, 0x63, -0x04, 0x20, 0x01, 0x07, 0x88, 0x60, 0x70, 0x47, -0x40, 0x00, 0x00, 0x50, 0x80, 0x10, 0x00, 0x50, -0x10, 0xB5, 0x00, 0xF0, 0x09, 0xF8, 0x10, 0xBD, +0xFF, 0xF7, 0xC6, 0xFF, 0x03, 0xE0, 0x00, 0x21, +0x31, 0x70, 0x01, 0xF0, 0x85, 0xF8, 0xBC, 0x43, +0x01, 0x20, 0xFF, 0xF7, 0x05, 0xFE, 0x24, 0x49, +0xA0, 0x04, 0x04, 0xD5, 0x28, 0x6A, 0x88, 0x43, +0x28, 0x62, 0x48, 0x14, 0xE8, 0x60, 0xE0, 0x04, +0x16, 0xD5, 0x28, 0x6A, 0x88, 0x43, 0x28, 0x62, +0x01, 0x20, 0x00, 0x03, 0xE8, 0x60, 0xE8, 0x6B, +0x1C, 0x4F, 0x00, 0x04, 0x79, 0x78, 0x00, 0x0E, +0x02, 0x29, 0x02, 0xD0, 0x00, 0x28, 0x02, 0xD0, +0x06, 0xE0, 0x03, 0x28, 0x04, 0xD1, 0x01, 0x20, +0xFF, 0xF7, 0xE6, 0xFD, 0x00, 0x20, 0x38, 0x70, +0xA0, 0x05, 0x02, 0xD5, 0x01, 0x20, 0x40, 0x02, +0xE8, 0x60, 0x20, 0x07, 0x09, 0xD5, 0x30, 0x78, +0x01, 0x28, 0x02, 0xD1, 0x0A, 0x20, 0x01, 0xF0, +0x53, 0xF8, 0x64, 0x08, 0x64, 0x00, 0x09, 0x20, +0xE8, 0x60, 0x60, 0x07, 0x07, 0xD5, 0x30, 0x78, +0x01, 0x28, 0x02, 0xD1, 0x0A, 0x20, 0x01, 0xF0, +0x47, 0xF8, 0x05, 0x20, 0xE8, 0x60, 0xFF, 0x20, +0xF3, 0x30, 0xE8, 0x60, 0x01, 0x20, 0xE8, 0x60, +0xF8, 0xBD, 0x00, 0x00, 0x00, 0x09, 0x00, 0x50, +0x00, 0x06, 0x00, 0x50, 0x80, 0x02, 0x00, 0x20, +0x00, 0x00, 0x00, 0x40, 0x90, 0x02, 0x00, 0x20, +0x64, 0x20, 0x05, 0x49, 0x02, 0xE0, 0x00, 0xBF, +0x40, 0x1E, 0xC0, 0xB2, 0x4A, 0x68, 0x12, 0x06, +0x01, 0xD5, 0x00, 0x28, 0xF7, 0xD1, 0x70, 0x47, +0x00, 0x06, 0x00, 0x50, 0x10, 0xB5, 0x01, 0x22, +0x92, 0x07, 0x13, 0x68, 0x03, 0x4C, 0x23, 0x43, +0x13, 0x60, 0x89, 0x04, 0x09, 0x0C, 0x01, 0xF0, +0xF9, 0xFC, 0x10, 0xBD, 0x10, 0x01, 0x42, 0x88, 0x02, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, -0x70, 0x47, 0x00, 0x00, 0x80, 0x04, 0x00, 0x20, -0x01, 0x20, 0x80, 0x07, 0x41, 0x68, 0x82, 0x14, -0x11, 0x43, 0x41, 0x60, 0x70, 0x47, 0x00, 0x00, +0x70, 0x47, 0x00, 0x00, 0x5C, 0x07, 0x00, 0x20, +0x10, 0xB5, 0xFF, 0xF7, 0xFB, 0xFE, 0x10, 0xBD, +0x10, 0xB5, 0x00, 0xF0, 0x17, 0xFB, 0x10, 0xBD, 0x05, 0x20, 0x00, 0x07, 0x82, 0x69, 0x0C, 0x49, 0x00, 0x2A, 0x07, 0xDA, 0x82, 0x69, 0xC3, 0x00, 0x92, 0x00, 0x92, 0x08, 0xD2, 0x18, 0x82, 0x61, @@ -545,902 +545,1178 @@ const unsigned char u8_rad_fw_30[] = { 0x08, 0xD5, 0x82, 0x69, 0x01, 0x23, 0x92, 0x00, 0x92, 0x08, 0x9B, 0x07, 0xD2, 0x18, 0x82, 0x61, 0x00, 0x20, 0x48, 0x72, 0x70, 0x47, 0x00, 0x00, -0x84, 0x04, 0x00, 0x20, 0xF0, 0xB5, 0x00, 0x20, -0x84, 0x46, 0x91, 0x48, 0x87, 0xB0, 0x00, 0x68, -0x90, 0x49, 0xB0, 0x30, 0x00, 0x90, 0x00, 0x20, -0x08, 0x5E, 0x8F, 0x49, 0x01, 0x90, 0x00, 0x20, -0x08, 0x5E, 0x02, 0x90, 0x0B, 0xE1, 0x34, 0x21, -0x48, 0x43, 0x81, 0x19, 0x0C, 0x46, 0x20, 0x34, -0x60, 0x7D, 0xFF, 0x28, 0x31, 0xD1, 0x00, 0x22, -0x00, 0x20, 0x08, 0xE0, 0x34, 0x25, 0x45, 0x43, -0xAD, 0x19, 0x20, 0x35, 0x6D, 0x7D, 0x95, 0x42, -0x03, 0xD0, 0x40, 0x1C, 0xC0, 0xB2, 0x83, 0x42, -0xF4, 0xD8, 0x83, 0x42, 0x01, 0xD1, 0x62, 0x75, -0x03, 0xE0, 0x52, 0x1C, 0xD2, 0xB2, 0x02, 0x2A, -0xEA, 0xD3, 0x0A, 0x25, 0x4D, 0x5F, 0x30, 0x20, -0x7C, 0x4E, 0x42, 0x43, 0xAB, 0x01, 0x90, 0x19, -0x83, 0x60, 0xC3, 0x60, 0xB5, 0x50, 0x0C, 0x22, -0x45, 0x60, 0x8A, 0x5E, 0x93, 0x01, 0x83, 0x61, -0x02, 0x61, 0xC3, 0x61, 0x42, 0x61, 0x0E, 0x22, -0x8A, 0x5E, 0x93, 0x01, 0x83, 0x62, 0x02, 0x62, -0xC3, 0x62, 0x42, 0x62, 0x60, 0x7D, 0xFF, 0x28, -0x7E, 0xD0, 0x71, 0x4A, 0xC7, 0xB2, 0x12, 0x78, -0xC8, 0x69, 0x00, 0x2A, 0x11, 0xD1, 0x00, 0x9A, -0xD3, 0x79, 0x94, 0x79, 0x1A, 0x02, 0x22, 0x43, -0x82, 0x42, 0x01, 0xD2, 0x02, 0x22, 0x09, 0xE0, -0x00, 0x9A, 0x53, 0x7A, 0x14, 0x7A, 0x1A, 0x02, -0x22, 0x43, 0x82, 0x42, 0x01, 0xD2, 0x03, 0x22, -0x00, 0xE0, 0x04, 0x22, 0x65, 0x4D, 0x28, 0x78, -0x82, 0x42, 0x28, 0xD0, 0x82, 0x42, 0x02, 0xD3, -0x13, 0x1A, 0x02, 0x2B, 0x23, 0xDA, 0x62, 0x4C, -0x23, 0x78, 0x00, 0x2B, 0x14, 0xD0, 0x61, 0x4E, -0x33, 0x78, 0x9A, 0x42, 0x02, 0xD0, 0x00, 0x23, -0x23, 0x70, 0x32, 0x70, 0x22, 0x78, 0x5D, 0x4B, -0x52, 0x1C, 0xD2, 0xB2, 0x22, 0x70, 0x1B, 0x78, -0x98, 0x42, 0x08, 0xD9, 0x01, 0x2A, 0x06, 0xD9, -0x00, 0x20, 0x20, 0x70, 0x2B, 0x70, 0x0A, 0xE0, -0x56, 0x4B, 0x1A, 0x70, 0xEE, 0xE7, 0x98, 0x42, -0x05, 0xD2, 0x02, 0x2A, 0x03, 0xD9, 0x00, 0x22, -0x22, 0x70, 0x40, 0x1C, 0x28, 0x70, 0x28, 0x78, -0x02, 0x28, 0x6B, 0xD0, 0x03, 0x28, 0x6D, 0xD0, -0x29, 0x22, 0x64, 0x23, 0x05, 0x24, 0x3E, 0x46, +0x60, 0x07, 0x00, 0x20, 0xF0, 0xB5, 0x00, 0x20, +0x84, 0x46, 0x8F, 0x48, 0x87, 0xB0, 0x00, 0x68, +0x8E, 0x49, 0xB0, 0x30, 0x00, 0x90, 0x00, 0x20, +0x08, 0x5E, 0x8D, 0x49, 0x01, 0x90, 0x00, 0x20, +0x08, 0x5E, 0x02, 0x90, 0x07, 0xE1, 0x34, 0x21, +0x48, 0x43, 0x41, 0x19, 0xC8, 0x7A, 0xFF, 0x28, +0x30, 0xD1, 0x00, 0x22, 0x00, 0x20, 0x07, 0xE0, +0x34, 0x24, 0x44, 0x43, 0x64, 0x19, 0xE4, 0x7A, +0x94, 0x42, 0x03, 0xD0, 0x40, 0x1C, 0xC0, 0xB2, +0x83, 0x42, 0xF5, 0xD8, 0x83, 0x42, 0x01, 0xD1, +0xCA, 0x72, 0x03, 0xE0, 0x52, 0x1C, 0xD2, 0xB2, +0x02, 0x2A, 0xEB, 0xD3, 0x0C, 0x24, 0x0C, 0x5F, +0x30, 0x20, 0x7C, 0x4D, 0x42, 0x43, 0xA3, 0x01, +0x50, 0x19, 0x83, 0x60, 0xC3, 0x60, 0xAC, 0x50, +0x0E, 0x22, 0x44, 0x60, 0x8A, 0x5E, 0x93, 0x01, +0x83, 0x61, 0x02, 0x61, 0xC3, 0x61, 0x42, 0x61, +0x10, 0x22, 0x8A, 0x5E, 0x93, 0x01, 0x83, 0x62, +0x02, 0x62, 0xC3, 0x62, 0x42, 0x62, 0xC8, 0x7A, +0xFF, 0x28, 0x7D, 0xD0, 0x70, 0x4A, 0xC6, 0xB2, +0x12, 0x78, 0x08, 0x6A, 0x00, 0x2A, 0x11, 0xD1, +0x00, 0x9A, 0xD3, 0x79, 0x94, 0x79, 0x1A, 0x02, +0x22, 0x43, 0x82, 0x42, 0x01, 0xD2, 0x02, 0x22, +0x09, 0xE0, 0x00, 0x9A, 0x53, 0x7A, 0x14, 0x7A, +0x1A, 0x02, 0x22, 0x43, 0x82, 0x42, 0x01, 0xD2, +0x03, 0x22, 0x00, 0xE0, 0x04, 0x22, 0x65, 0x4D, +0x28, 0x78, 0x82, 0x42, 0x28, 0xD0, 0x82, 0x42, +0x02, 0xD3, 0x13, 0x1A, 0x02, 0x2B, 0x23, 0xDA, +0x61, 0x4C, 0x23, 0x78, 0x00, 0x2B, 0x14, 0xD0, +0x60, 0x4F, 0x3B, 0x78, 0x9A, 0x42, 0x02, 0xD0, +0x00, 0x23, 0x23, 0x70, 0x3A, 0x70, 0x22, 0x78, +0x5C, 0x4B, 0x52, 0x1C, 0xD2, 0xB2, 0x22, 0x70, +0x1B, 0x78, 0x98, 0x42, 0x08, 0xD9, 0x01, 0x2A, +0x06, 0xD9, 0x00, 0x20, 0x20, 0x70, 0x2B, 0x70, +0x0A, 0xE0, 0x56, 0x4B, 0x1A, 0x70, 0xEE, 0xE7, +0x98, 0x42, 0x05, 0xD2, 0x02, 0x2A, 0x03, 0xD9, +0x00, 0x22, 0x22, 0x70, 0x40, 0x1C, 0x28, 0x70, +0x28, 0x78, 0x02, 0x28, 0x6A, 0xD0, 0x03, 0x28, +0x6C, 0xD0, 0x29, 0x22, 0x64, 0x23, 0x05, 0x24, 0x30, 0x20, 0x46, 0x43, 0x47, 0x48, 0x30, 0x18, 0xC5, 0x68, 0x03, 0x95, 0x87, 0x68, 0x5D, 0x43, 0x57, 0x43, 0xED, 0x1B, 0x20, 0x35, 0xAD, 0x11, -0xAE, 0x46, 0x0A, 0x25, 0x4D, 0x5F, 0x41, 0x4F, +0xAE, 0x46, 0x0C, 0x25, 0x4D, 0x5F, 0x41, 0x4F, 0x04, 0x95, 0xBF, 0x59, 0xEF, 0x19, 0x45, 0x68, 0x6D, 0x00, 0x7F, 0x19, 0x67, 0x43, 0xBF, 0x1C, 0xBF, 0x10, 0x75, 0x46, 0x7D, 0x19, 0xAE, 0x46, 0xC5, 0x69, 0x87, 0x69, 0x5D, 0x43, 0x57, 0x43, 0xED, 0x1B, 0x20, 0x35, 0xAD, 0x11, 0x06, 0x95, -0x0C, 0x25, 0x4D, 0x5F, 0x05, 0x95, 0x07, 0x69, +0x0E, 0x25, 0x4D, 0x5F, 0x05, 0x95, 0x07, 0x69, 0xEF, 0x19, 0x45, 0x69, 0x6D, 0x00, 0x7D, 0x19, 0x65, 0x43, 0xAD, 0x1C, 0xAF, 0x10, 0x00, 0xE0, 0x4D, 0xE0, 0x06, 0x9D, 0x7F, 0x19, 0xC5, 0x6A, 0x5D, 0x43, 0x83, 0x6A, 0x53, 0x43, 0xEA, 0x1A, -0x0E, 0x23, 0x20, 0x32, 0xCB, 0x5E, 0x95, 0x11, -0x06, 0x93, 0x02, 0x6A, 0x9A, 0x18, 0x43, 0x6A, +0x20, 0x32, 0x95, 0x11, 0x10, 0x22, 0x8A, 0x5E, +0x06, 0x92, 0x03, 0x6A, 0xD2, 0x18, 0x43, 0x6A, 0x5B, 0x00, 0xD2, 0x18, 0x62, 0x43, 0x92, 0x1C, -0x92, 0x10, 0x52, 0x19, 0x03, 0x9D, 0x85, 0x60, -0x75, 0x46, 0xC5, 0x60, 0xC3, 0x69, 0xC7, 0x61, -0x83, 0x61, 0xC3, 0x6A, 0x83, 0x62, 0xC2, 0x62, -0x20, 0x4C, 0x43, 0x68, 0xA3, 0x51, 0x04, 0x9D, -0x45, 0x60, 0x43, 0x69, 0x03, 0x61, 0x05, 0x9D, -0x45, 0x61, 0x43, 0x6A, 0x03, 0x62, 0x06, 0x9B, -0x43, 0x62, 0x75, 0x46, 0x01, 0x9C, 0xA8, 0x11, -0xBB, 0x11, 0x92, 0x11, 0xA0, 0x42, 0x09, 0xDB, +0x92, 0x10, 0x53, 0x19, 0x03, 0x9D, 0x85, 0x60, +0x75, 0x46, 0xC5, 0x60, 0xC2, 0x69, 0xC7, 0x61, +0x82, 0x61, 0xC2, 0x6A, 0xC3, 0x62, 0x82, 0x62, +0x20, 0x4C, 0x42, 0x68, 0xA2, 0x51, 0x04, 0x9D, +0x45, 0x60, 0x42, 0x69, 0x02, 0x61, 0x05, 0x9D, +0x45, 0x61, 0x42, 0x6A, 0x02, 0x62, 0x06, 0x9A, +0x42, 0x62, 0x75, 0x46, 0x01, 0x9C, 0xA8, 0x11, +0xBA, 0x11, 0x9B, 0x11, 0xA0, 0x42, 0x09, 0xDB, 0x60, 0x1E, 0x0A, 0xE0, 0x1C, 0x22, 0x4D, 0x23, -0x0F, 0x24, 0x94, 0xE7, 0x22, 0x22, 0x58, 0x23, -0x0A, 0x24, 0x90, 0xE7, 0x00, 0x28, 0x00, 0xDA, -0x00, 0x20, 0x02, 0x9C, 0xA3, 0x42, 0x01, 0xDB, -0x63, 0x1E, 0x02, 0xE0, 0x00, 0x2B, 0x00, 0xDA, -0x00, 0x23, 0x00, 0x2A, 0x00, 0xDA, 0x00, 0x22, -0x48, 0x81, 0x8B, 0x81, 0xCA, 0x81, 0x60, 0x46, -0x40, 0x1C, 0xC0, 0xB2, 0x84, 0x46, 0x0C, 0x4E, -0x60, 0x46, 0x33, 0x78, 0x63, 0x45, 0x00, 0xD9, -0xED, 0xE6, 0x07, 0xB0, 0xF0, 0xBD, 0x00, 0x00, -0xB8, 0x02, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20, -0x44, 0x00, 0x00, 0x20, 0x38, 0x01, 0x00, 0x20, -0x2A, 0x00, 0x00, 0x20, 0x4A, 0x00, 0x00, 0x20, -0x49, 0x00, 0x00, 0x20, 0x4B, 0x00, 0x00, 0x20, -0xA0, 0x04, 0x00, 0x20, 0xF0, 0xB5, 0x93, 0xB0, -0x02, 0xF0, 0x62, 0xFD, 0x0F, 0x90, 0x8D, 0x48, -0x00, 0x25, 0x41, 0x5F, 0x0F, 0x98, 0x81, 0x42, -0x7D, 0xDD, 0x8B, 0x48, 0x30, 0x21, 0x00, 0x68, -0x06, 0x7E, 0x40, 0x7E, 0x11, 0x90, 0x02, 0xA8, -0xFF, 0xF7, 0x73, 0xFB, 0x87, 0x48, 0x00, 0x78, -0x06, 0x28, 0x71, 0xD2, 0x01, 0x20, 0x10, 0x90, +0x0F, 0x24, 0x95, 0xE7, 0x22, 0x22, 0x58, 0x23, +0x0A, 0x24, 0x91, 0xE7, 0x00, 0x28, 0x00, 0xDA, +0x00, 0x20, 0x02, 0x9C, 0xA2, 0x42, 0x01, 0xDB, +0x62, 0x1E, 0x02, 0xE0, 0x00, 0x2A, 0x00, 0xDA, +0x00, 0x22, 0x00, 0x2B, 0x00, 0xDA, 0x00, 0x23, +0x88, 0x81, 0xCA, 0x81, 0x0B, 0x82, 0x60, 0x46, +0x40, 0x1C, 0xC0, 0xB2, 0x84, 0x46, 0x0C, 0x4D, +0x60, 0x46, 0x2B, 0x78, 0x63, 0x45, 0x00, 0xD9, +0xF1, 0xE6, 0x07, 0xB0, 0xF0, 0xBD, 0x00, 0x00, +0xAC, 0x04, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, +0x46, 0x00, 0x00, 0x20, 0x38, 0x01, 0x00, 0x20, +0x34, 0x00, 0x00, 0x20, 0xCF, 0x00, 0x00, 0x20, +0xCE, 0x00, 0x00, 0x20, 0xD0, 0x00, 0x00, 0x20, +0x7C, 0x07, 0x00, 0x20, 0xF0, 0xB5, 0x93, 0xB0, +0x00, 0x20, 0x11, 0x90, 0x03, 0xF0, 0x94, 0xF9, +0x0F, 0x90, 0xA5, 0x48, 0x00, 0x25, 0x05, 0x70, +0xA4, 0x48, 0x41, 0x5F, 0x0F, 0x98, 0x81, 0x42, +0x7E, 0xDD, 0xA3, 0x48, 0x30, 0x21, 0x00, 0x68, +0x06, 0x7E, 0x40, 0x7E, 0x12, 0x90, 0x02, 0xA8, +0xFF, 0xF7, 0x73, 0xFB, 0x9F, 0x48, 0x00, 0x78, +0x06, 0x28, 0x72, 0xD2, 0x01, 0x20, 0x10, 0x90, 0x08, 0xA8, 0x0E, 0x90, 0x34, 0x20, 0x29, 0x46, -0x41, 0x43, 0x83, 0x48, 0x00, 0x27, 0x0C, 0x18, -0x27, 0x76, 0x10, 0x98, 0x00, 0x28, 0x04, 0xD0, -0x7C, 0x48, 0x01, 0x88, 0x7F, 0x48, 0x01, 0x80, +0x41, 0x43, 0x9B, 0x48, 0x00, 0x27, 0x0C, 0x18, +0x27, 0x77, 0x10, 0x98, 0x00, 0x28, 0x04, 0xD0, +0x94, 0x48, 0x01, 0x88, 0x97, 0x48, 0x01, 0x80, 0x09, 0xE0, 0x01, 0xA9, 0x68, 0x46, 0x0F, 0x9A, -0x04, 0xF0, 0x58, 0xF8, 0x00, 0x28, 0x57, 0xD0, -0x10, 0x98, 0x00, 0x28, 0x0E, 0xD0, 0x7A, 0x48, +0x04, 0xF0, 0x18, 0xFC, 0x00, 0x28, 0x58, 0xD0, +0x10, 0x98, 0x00, 0x28, 0x0E, 0xD0, 0x92, 0x48, 0x31, 0x46, 0x00, 0x88, 0x10, 0x90, 0xFF, 0xF7, 0x09, 0xFB, 0x69, 0x46, 0x08, 0x70, 0x10, 0x99, 0x70, 0x43, 0x08, 0x1A, 0x69, 0x46, 0x08, 0x71, 0x00, 0x20, 0x10, 0x90, 0x0F, 0x98, 0x02, 0xAA, -0x83, 0xB2, 0x01, 0xA9, 0x68, 0x46, 0x02, 0xF0, -0x65, 0xFD, 0x70, 0x49, 0x88, 0x42, 0x7D, 0xD0, -0x6C, 0x4A, 0x00, 0x21, 0x51, 0x5E, 0x61, 0x86, -0xE0, 0x81, 0x0A, 0x22, 0x50, 0x43, 0xFF, 0xF7, -0x03, 0xFB, 0x34, 0x21, 0x08, 0x55, 0xFF, 0x20, -0x20, 0x71, 0x00, 0x20, 0xE0, 0x61, 0x68, 0x46, -0x01, 0x79, 0x02, 0xAA, 0x01, 0x20, 0x02, 0xF0, -0xC7, 0xFC, 0x65, 0x49, 0x60, 0x81, 0x0A, 0x78, -0x00, 0x2A, 0x0B, 0xD0, 0x63, 0x4A, 0x00, 0x23, -0xD3, 0x5E, 0x63, 0x4A, 0x12, 0x78, 0x9B, 0x1A, -0x98, 0x42, 0x03, 0xDA, 0x90, 0x42, 0x01, 0xDD, -0x00, 0x22, 0x0A, 0x70, 0x6A, 0x46, 0x12, 0x79, -0x00, 0x2A, 0x12, 0xD0, 0x73, 0x1E, 0x9A, 0x42, -0x28, 0xD1, 0x5B, 0x4A, 0x00, 0x27, 0x12, 0x78, -0x53, 0x00, 0xD2, 0x18, 0x93, 0x08, 0x57, 0x4A, -0xD7, 0x5F, 0xFA, 0x1A, 0x90, 0x42, 0x14, 0xDD, -0x01, 0x20, 0x08, 0x70, 0x14, 0xE0, 0x7E, 0xE0, -0x87, 0xE0, 0x53, 0x4A, 0x12, 0x78, 0x53, 0x00, -0xD2, 0x18, 0x92, 0x08, 0x90, 0x42, 0x02, 0xDA, -0x01, 0x20, 0x08, 0x70, 0x02, 0xE0, 0x08, 0x78, -0x00, 0x28, 0x0B, 0xD0, 0x02, 0xAA, 0x01, 0x21, -0x04, 0xE0, 0x08, 0x78, 0x00, 0x28, 0x05, 0xD0, -0x02, 0xAA, 0x00, 0x21, 0x01, 0x20, 0x02, 0xF0, -0xEF, 0xFB, 0x60, 0x81, 0x68, 0x46, 0x01, 0x78, -0x00, 0x20, 0x0E, 0x9A, 0x02, 0xF0, 0x80, 0xFC, -0x44, 0x49, 0xA0, 0x81, 0x0A, 0x78, 0x00, 0x2A, -0x0B, 0xD0, 0x43, 0x4A, 0x00, 0x23, 0xD3, 0x5E, -0x42, 0x4A, 0x12, 0x78, 0x9B, 0x1A, 0x98, 0x42, -0x03, 0xDA, 0x90, 0x42, 0x01, 0xDD, 0x00, 0x22, -0x0A, 0x70, 0x6A, 0x46, 0x12, 0x78, 0x00, 0x2A, -0x13, 0xD0, 0x11, 0x9B, 0x5B, 0x1E, 0x9A, 0x42, -0x28, 0xD1, 0x3A, 0x4A, 0x12, 0x78, 0x53, 0x00, -0xD2, 0x18, 0x93, 0x08, 0x36, 0x4A, 0x00, 0x27, -0xD7, 0x5F, 0x00, 0xE0, 0x37, 0xE0, 0xFA, 0x1A, -0x90, 0x42, 0x12, 0xDD, 0x01, 0x20, 0x08, 0x70, -0x12, 0xE0, 0x32, 0x4A, 0x12, 0x78, 0x53, 0x00, -0xD2, 0x18, 0x92, 0x08, 0x90, 0x42, 0x02, 0xDA, -0x01, 0x20, 0x08, 0x70, 0x02, 0xE0, 0x08, 0x78, -0x00, 0x28, 0x0B, 0xD0, 0x01, 0x21, 0x0E, 0x9A, -0x04, 0xE0, 0x08, 0x78, 0x00, 0x28, 0x05, 0xD0, -0x0E, 0x9A, 0x00, 0x21, 0x00, 0x20, 0x02, 0xF0, -0xA7, 0xFB, 0xA0, 0x81, 0x07, 0x98, 0xA0, 0x82, -0x0D, 0x98, 0xE0, 0x82, 0x68, 0x46, 0x00, 0x79, -0x00, 0x28, 0x0A, 0xD0, 0x71, 0x1E, 0x88, 0x42, -0x07, 0xD0, 0x68, 0x46, 0x00, 0x78, 0x00, 0x28, -0x03, 0xD0, 0x11, 0x99, 0x49, 0x1E, 0x88, 0x42, -0x01, 0xD1, 0x01, 0x20, 0x00, 0xE0, 0x00, 0x20, -0x6D, 0x1C, 0x20, 0x76, 0xED, 0xB2, 0x03, 0x2D, -0x00, 0xD2, 0x13, 0xE7, 0x09, 0xE0, 0x0E, 0x49, -0x00, 0x20, 0x08, 0x70, 0x16, 0x48, 0x40, 0x88, -0x0A, 0x28, 0x02, 0xD9, 0x00, 0x20, 0x13, 0xB0, -0xF0, 0xBD, 0x08, 0x48, 0x00, 0x78, 0x03, 0x28, -0x03, 0xD3, 0x07, 0x49, 0x00, 0x20, 0x08, 0x70, -0x01, 0xE0, 0x05, 0x48, 0x05, 0x70, 0x01, 0x20, -0xF1, 0xE7, 0x00, 0x00, 0x24, 0x00, 0x00, 0x20, -0xB8, 0x02, 0x00, 0x20, 0x73, 0x04, 0x00, 0x20, -0xA0, 0x04, 0x00, 0x20, 0x28, 0x00, 0x00, 0x20, -0x32, 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0xFF, -0xCC, 0x00, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20, -0x2E, 0x00, 0x00, 0x20, 0xCD, 0x00, 0x00, 0x20, -0x44, 0x00, 0x00, 0x20, 0x2F, 0x00, 0x00, 0x20, -0x84, 0x04, 0x00, 0x20, 0xF0, 0xB5, 0x8E, 0x46, -0x02, 0x21, 0x4B, 0x00, 0xC3, 0x5E, 0x00, 0x2B, -0x03, 0xDD, 0x49, 0x1E, 0x09, 0xB2, 0x00, 0x29, -0xF7, 0xDA, 0x49, 0x1C, 0x00, 0x27, 0x0B, 0xB2, -0x9C, 0x46, 0x3C, 0x46, 0x01, 0x25, 0x0E, 0xE0, -0x5E, 0x00, 0x86, 0x5F, 0x00, 0x2E, 0x02, 0xDC, -0x5B, 0x1E, 0x1B, 0xB2, 0x09, 0xE0, 0x31, 0x46, -0x69, 0x43, 0x6D, 0x1C, 0xCF, 0x19, 0x34, 0x19, -0x5B, 0x1C, 0x2D, 0xB2, 0x1B, 0xB2, 0x05, 0x2B, -0xEE, 0xDB, 0x61, 0x46, 0x59, 0x1A, 0x49, 0x1C, -0x66, 0x08, 0x63, 0x46, 0x00, 0x25, 0x51, 0x61, -0x06, 0xE0, 0x59, 0x00, 0x41, 0x5E, 0x4D, 0x19, -0xB5, 0x42, 0x03, 0xD8, 0x5B, 0x1C, 0x1B, 0xB2, -0x05, 0x2B, 0xF6, 0xDB, 0x5E, 0x00, 0x81, 0x5F, -0x69, 0x1A, 0x11, 0x60, 0x80, 0x5F, 0x50, 0x60, -0x60, 0x1B, 0xD4, 0x60, 0x90, 0x60, 0x60, 0x46, -0x18, 0x1A, 0x60, 0x43, 0x38, 0x1A, 0x10, 0x61, -0x70, 0x46, 0x00, 0x78, 0x9B, 0x1E, 0xC0, 0x18, -0x71, 0x46, 0x08, 0x70, 0x20, 0xB2, 0xF0, 0xBD, -0xF8, 0xB5, 0x8D, 0x4E, 0x00, 0x27, 0x30, 0x78, -0x3C, 0x46, 0x00, 0x28, 0x01, 0xD0, 0x03, 0xF0, -0xE7, 0xF9, 0x00, 0x20, 0x05, 0x46, 0x89, 0x4A, -0x0D, 0xE0, 0x34, 0x21, 0x41, 0x43, 0x89, 0x18, -0x0B, 0x79, 0x1B, 0x06, 0x01, 0xD5, 0xCD, 0x71, -0x03, 0xE0, 0xCB, 0x79, 0x5B, 0x1C, 0xCB, 0x71, -0x01, 0x27, 0x40, 0x1C, 0xC0, 0xB2, 0x11, 0x78, -0x81, 0x42, 0xEE, 0xD8, 0x7F, 0x49, 0x30, 0x78, -0x09, 0x78, 0x88, 0x42, 0x01, 0xD1, 0x00, 0x2F, -0x02, 0xD0, 0x03, 0xF0, 0x63, 0xFD, 0x0E, 0xE0, -0xB0, 0x70, 0x00, 0x20, 0x34, 0x21, 0x41, 0x43, -0x89, 0x19, 0x8D, 0x71, 0x40, 0x1C, 0xCD, 0x71, -0xC0, 0xB2, 0x0D, 0x72, 0x03, 0x28, 0xF5, 0xD3, -0x74, 0x48, 0x75, 0x70, 0x45, 0x70, 0x30, 0x78, -0xB1, 0x78, 0x88, 0x42, 0x01, 0xD0, 0x03, 0xF0, -0x61, 0xFB, 0x02, 0xF0, 0x89, 0xFE, 0x70, 0x49, -0x70, 0x4A, 0x08, 0x88, 0x10, 0x80, 0x84, 0x46, -0x00, 0x20, 0x08, 0x80, 0x6A, 0x49, 0x6E, 0x4D, -0x0A, 0x78, 0x2B, 0xE0, 0x34, 0x23, 0x43, 0x43, -0x5E, 0x18, 0x69, 0x4B, 0xF1, 0x69, 0x1F, 0x88, -0xB9, 0x42, 0x00, 0xD9, 0x19, 0x80, 0x61, 0x46, -0x00, 0x29, 0x0B, 0xD1, 0x29, 0x68, 0xB0, 0x31, -0x4F, 0x7A, 0x09, 0x7A, 0x3F, 0x02, 0x0F, 0x43, -0x19, 0x88, 0x8F, 0x42, 0x02, 0xD2, 0x63, 0x4B, -0x01, 0x21, 0x19, 0x70, 0x29, 0x68, 0x20, 0x36, -0xA0, 0x31, 0x33, 0x7D, 0x49, 0x78, 0x8B, 0x42, -0x17, 0xD9, 0x5F, 0x4B, 0x01, 0x24, 0x19, 0x78, -0x49, 0x1C, 0xC9, 0xB2, 0x19, 0x70, 0x02, 0x29, -0x02, 0xD9, 0x5C, 0x4B, 0x01, 0x21, 0x19, 0x70, -0x40, 0x1C, 0xC0, 0xB2, 0x52, 0x49, 0x82, 0x42, -0xD0, 0xD8, 0x59, 0x49, 0x00, 0x2C, 0x08, 0x78, -0x06, 0xD0, 0x64, 0x28, 0x08, 0xD2, 0x40, 0x1C, -0x05, 0xE0, 0x00, 0x21, 0x52, 0x4B, 0xEE, 0xE7, -0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, 0x08, 0x70, -0x03, 0x20, 0x81, 0x1A, 0x34, 0x20, 0x48, 0x4E, -0x41, 0x43, 0x42, 0x43, 0x90, 0x19, 0x00, 0x1D, -0xFF, 0xF7, 0x6F, 0xF9, 0x00, 0x24, 0x45, 0x4F, -0x0F, 0xE0, 0x38, 0x78, 0x34, 0x21, 0x00, 0x19, -0x48, 0x43, 0xC1, 0x19, 0x30, 0x78, 0x34, 0x22, -0x00, 0x19, 0x50, 0x43, 0x80, 0x19, 0x09, 0x1D, -0x00, 0x1D, 0xFF, 0xF7, 0x45, 0xF9, 0x64, 0x1C, -0xE4, 0xB2, 0x71, 0x78, 0xA1, 0x42, 0xEC, 0xD8, -0x32, 0x78, 0x00, 0x20, 0x53, 0x18, 0x0E, 0xE0, -0x34, 0x22, 0x42, 0x43, 0x91, 0x19, 0x06, 0x22, -0x42, 0x43, 0xD2, 0x19, 0xA0, 0x32, 0x4C, 0x89, -0x14, 0x80, 0x8C, 0x89, 0x54, 0x80, 0xC9, 0x89, -0x40, 0x1C, 0x91, 0x80, 0xC0, 0xB2, 0x31, 0x46, -0x83, 0x42, 0xED, 0xD8, 0x00, 0x20, 0x37, 0x4C, -0x01, 0x22, 0x20, 0x80, 0x0C, 0xE0, 0x34, 0x26, -0x46, 0x43, 0x76, 0x18, 0x36, 0x79, 0x77, 0x06, -0x7F, 0x0E, 0x16, 0x46, 0xBE, 0x40, 0x27, 0x88, -0x3E, 0x43, 0x40, 0x1C, 0x26, 0x80, 0xC0, 0xB2, -0x83, 0x42, 0xF0, 0xD8, 0x23, 0x88, 0x00, 0x20, -0x16, 0x46, 0x32, 0x46, 0x82, 0x40, 0x1A, 0x42, +0x83, 0xB2, 0x01, 0xA9, 0x68, 0x46, 0x03, 0xF0, +0x91, 0xF9, 0x88, 0x49, 0x88, 0x42, 0x7D, 0xD0, +0x84, 0x4A, 0x00, 0x21, 0x51, 0x5E, 0xE1, 0x82, +0x20, 0x82, 0x0A, 0x22, 0x50, 0x43, 0xFF, 0xF7, +0x03, 0xFB, 0xC0, 0xB2, 0x7C, 0x49, 0xA0, 0x72, +0x09, 0x68, 0xA0, 0x31, 0x49, 0x78, 0x88, 0x42, +0x01, 0xD9, 0x01, 0x21, 0x11, 0x91, 0x0A, 0x28, +0x68, 0xD3, 0xFF, 0x20, 0x20, 0x71, 0x00, 0x20, +0x20, 0x62, 0x68, 0x46, 0x01, 0x79, 0x02, 0xAA, +0x01, 0x20, 0x03, 0xF0, 0xED, 0xF8, 0x78, 0x49, +0xA0, 0x81, 0x0A, 0x78, 0x00, 0x2A, 0x0B, 0xD0, +0x76, 0x4A, 0x00, 0x23, 0xD3, 0x5E, 0x76, 0x4A, +0x12, 0x78, 0x9B, 0x1A, 0x98, 0x42, 0x03, 0xDA, +0x90, 0x42, 0x01, 0xDD, 0x00, 0x22, 0x0A, 0x70, +0x6A, 0x46, 0x12, 0x79, 0x00, 0x2A, 0x13, 0xD0, +0x73, 0x1E, 0x9A, 0x42, 0x29, 0xD1, 0x6E, 0x4A, +0x12, 0x78, 0x53, 0x00, 0xD2, 0x18, 0x01, 0xE0, +0x93, 0xE0, 0x9C, 0xE0, 0x93, 0x08, 0x69, 0x4A, +0x00, 0x27, 0xD7, 0x5F, 0xFA, 0x1A, 0x90, 0x42, +0x12, 0xDD, 0x01, 0x20, 0x08, 0x70, 0x12, 0xE0, +0x65, 0x4A, 0x12, 0x78, 0x53, 0x00, 0xD2, 0x18, +0x92, 0x08, 0x90, 0x42, 0x02, 0xDA, 0x01, 0x20, +0x08, 0x70, 0x02, 0xE0, 0x08, 0x78, 0x00, 0x28, +0x0B, 0xD0, 0x02, 0xAA, 0x01, 0x21, 0x04, 0xE0, +0x08, 0x78, 0x00, 0x28, 0x05, 0xD0, 0x02, 0xAA, +0x00, 0x21, 0x01, 0x20, 0x03, 0xF0, 0x14, 0xF8, +0xA0, 0x81, 0x68, 0x46, 0x01, 0x78, 0x00, 0x20, +0x0E, 0x9A, 0x03, 0xF0, 0xA5, 0xF8, 0x57, 0x49, +0xE0, 0x81, 0x0A, 0x78, 0x00, 0x2A, 0x0B, 0xD0, +0x55, 0x4A, 0x00, 0x23, 0xD3, 0x5E, 0x55, 0x4A, +0x12, 0x78, 0x9B, 0x1A, 0x98, 0x42, 0x03, 0xDA, +0x90, 0x42, 0x01, 0xDD, 0x00, 0x22, 0x0A, 0x70, +0x6A, 0x46, 0x12, 0x78, 0x00, 0x2A, 0x13, 0xD0, +0x12, 0x9B, 0x00, 0xE0, 0x4D, 0xE0, 0x5B, 0x1E, +0x9A, 0x42, 0x31, 0xD1, 0x4B, 0x4A, 0x00, 0x27, +0x12, 0x78, 0x53, 0x00, 0xD2, 0x18, 0x93, 0x08, +0x47, 0x4A, 0xD7, 0x5F, 0xFA, 0x1A, 0x90, 0x42, +0x16, 0xDD, 0x01, 0x20, 0x08, 0x70, 0x16, 0xE0, +0x44, 0x4A, 0x12, 0x78, 0x53, 0x00, 0xD2, 0x18, +0x92, 0x08, 0x90, 0x42, 0x02, 0xDA, 0x01, 0x20, +0x08, 0x70, 0x02, 0xE0, 0x08, 0x78, 0x00, 0x28, +0x16, 0xD0, 0x01, 0x21, 0x00, 0x20, 0x0E, 0x9A, +0x02, 0xF0, 0xD2, 0xFF, 0xE0, 0x81, 0x0F, 0xE0, +0x08, 0x78, 0x00, 0x28, 0x05, 0xD0, 0x00, 0x21, +0x08, 0x46, 0x0E, 0x9A, 0x02, 0xF0, 0xC8, 0xFF, +0xE0, 0x81, 0x37, 0x48, 0x00, 0x78, 0x00, 0x28, +0x02, 0xD1, 0x36, 0x49, 0x01, 0x20, 0x08, 0x70, +0x07, 0x98, 0x20, 0x83, 0x0D, 0x98, 0x60, 0x83, +0x68, 0x46, 0x00, 0x79, 0x00, 0x28, 0x0A, 0xD0, +0x71, 0x1E, 0x88, 0x42, 0x07, 0xD0, 0x68, 0x46, +0x00, 0x78, 0x00, 0x28, 0x03, 0xD0, 0x12, 0x99, +0x49, 0x1E, 0x88, 0x42, 0x01, 0xD1, 0x01, 0x20, +0x00, 0xE0, 0x00, 0x20, 0x6D, 0x1C, 0x20, 0x77, +0xED, 0xB2, 0x03, 0x2D, 0x00, 0xD2, 0xFD, 0xE6, +0x09, 0xE0, 0x1B, 0x49, 0x00, 0x20, 0x08, 0x70, +0x25, 0x48, 0x40, 0x88, 0x0A, 0x28, 0x02, 0xD9, +0x00, 0x20, 0x13, 0xB0, 0xF0, 0xBD, 0x15, 0x48, +0x01, 0x78, 0x01, 0x29, 0x03, 0xD1, 0x11, 0x99, +0x00, 0x29, 0x00, 0xD1, 0x01, 0x70, 0x1C, 0x49, +0x09, 0x78, 0x00, 0x29, 0x03, 0xD1, 0x00, 0x78, +0x03, 0x28, 0x00, 0xD3, 0x00, 0x25, 0x0E, 0x48, +0x00, 0x2D, 0x05, 0x70, 0x0B, 0xD0, 0x19, 0x48, +0x00, 0x78, 0xC0, 0x07, 0x07, 0xD0, 0x06, 0x48, +0x01, 0x78, 0x17, 0x48, 0x00, 0x29, 0x01, 0xD0, +0x01, 0x78, 0x49, 0x1C, 0x01, 0x70, 0x15, 0x48, +0x05, 0x70, 0x01, 0x20, 0xD9, 0xE7, 0x00, 0x00, +0xD1, 0x00, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20, +0xAC, 0x04, 0x00, 0x20, 0x4F, 0x07, 0x00, 0x20, +0x7C, 0x07, 0x00, 0x20, 0x32, 0x00, 0x00, 0x20, +0x3C, 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0xFF, +0xD2, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, +0x38, 0x00, 0x00, 0x20, 0xD3, 0x00, 0x00, 0x20, +0x46, 0x00, 0x00, 0x20, 0x39, 0x00, 0x00, 0x20, +0x1C, 0x08, 0x00, 0x20, 0xA8, 0x04, 0x00, 0x20, +0x60, 0x07, 0x00, 0x20, 0xBD, 0x02, 0x00, 0x20, +0xBE, 0x02, 0x00, 0x20, 0xCB, 0x02, 0x00, 0x20, +0xF0, 0xB5, 0x8E, 0x46, 0x02, 0x21, 0x4B, 0x00, +0xC3, 0x5E, 0x00, 0x2B, 0x03, 0xDD, 0x49, 0x1E, +0x09, 0xB2, 0x00, 0x29, 0xF7, 0xDA, 0x49, 0x1C, +0x00, 0x27, 0x0B, 0xB2, 0x9C, 0x46, 0x3C, 0x46, +0x01, 0x25, 0x0E, 0xE0, 0x5E, 0x00, 0x86, 0x5F, +0x00, 0x2E, 0x02, 0xDC, 0x5B, 0x1E, 0x1B, 0xB2, +0x09, 0xE0, 0x31, 0x46, 0x69, 0x43, 0x6D, 0x1C, +0xCF, 0x19, 0x34, 0x19, 0x5B, 0x1C, 0x2D, 0xB2, +0x1B, 0xB2, 0x05, 0x2B, 0xEE, 0xDB, 0x61, 0x46, +0x59, 0x1A, 0x49, 0x1C, 0x66, 0x08, 0x63, 0x46, +0x00, 0x25, 0x51, 0x61, 0x06, 0xE0, 0x59, 0x00, +0x41, 0x5E, 0x4D, 0x19, 0xB5, 0x42, 0x03, 0xD8, +0x5B, 0x1C, 0x1B, 0xB2, 0x05, 0x2B, 0xF6, 0xDB, +0x5E, 0x00, 0x81, 0x5F, 0x69, 0x1A, 0x11, 0x60, +0x80, 0x5F, 0x50, 0x60, 0x60, 0x1B, 0xD4, 0x60, +0x90, 0x60, 0x60, 0x46, 0x18, 0x1A, 0x60, 0x43, +0x38, 0x1A, 0x10, 0x61, 0x70, 0x46, 0x00, 0x78, +0x9B, 0x1E, 0xC0, 0x18, 0x71, 0x46, 0x08, 0x70, +0x20, 0xB2, 0xF0, 0xBD, 0xFE, 0xB5, 0x00, 0x26, +0xA8, 0x4C, 0x00, 0x96, 0x20, 0x78, 0x00, 0x28, +0x01, 0xD0, 0x03, 0xF0, 0xED, 0xFD, 0x00, 0x20, +0x05, 0x46, 0xA5, 0x4A, 0x0D, 0xE0, 0x34, 0x21, +0x41, 0x43, 0x89, 0x18, 0x0B, 0x79, 0x1B, 0x06, +0x01, 0xD5, 0xCD, 0x71, 0x03, 0xE0, 0xCB, 0x79, +0x5B, 0x1C, 0xCB, 0x71, 0x01, 0x26, 0x40, 0x1C, +0xC0, 0xB2, 0x11, 0x78, 0x81, 0x42, 0xEE, 0xD8, +0x9B, 0x49, 0x20, 0x78, 0x09, 0x78, 0x88, 0x42, +0x01, 0xD1, 0x00, 0x2E, 0x02, 0xD0, 0x04, 0xF0, +0x97, 0xF8, 0x0E, 0xE0, 0xA0, 0x70, 0x00, 0x20, +0x34, 0x21, 0x41, 0x43, 0x09, 0x19, 0x8D, 0x71, +0x40, 0x1C, 0xCD, 0x71, 0xC0, 0xB2, 0x0D, 0x72, +0x03, 0x28, 0xF5, 0xD3, 0x90, 0x48, 0x65, 0x70, +0x45, 0x70, 0x20, 0x78, 0xA1, 0x78, 0x88, 0x42, +0x01, 0xD0, 0x03, 0xF0, 0x7D, 0xFF, 0x20, 0x78, +0x00, 0x28, 0x08, 0xD1, 0x60, 0x78, 0x00, 0x28, +0x05, 0xD1, 0x8A, 0x48, 0x01, 0x78, 0xC9, 0x07, +0x01, 0xD0, 0x80, 0x21, 0x01, 0x70, 0x03, 0xF0, +0x6F, 0xFA, 0x87, 0x48, 0x87, 0x4A, 0x01, 0x88, +0x11, 0x80, 0x00, 0x21, 0x01, 0x80, 0x0B, 0x46, +0x41, 0xE0, 0x34, 0x21, 0x59, 0x43, 0x0A, 0x18, +0x81, 0x49, 0x10, 0x6A, 0x0C, 0x88, 0xA0, 0x42, +0x00, 0xD9, 0x08, 0x80, 0x80, 0x48, 0x96, 0x7A, +0x00, 0x68, 0xA0, 0x30, 0x01, 0x90, 0x40, 0x78, +0x86, 0x42, 0x01, 0xD9, 0x01, 0x20, 0x00, 0x90, +0x10, 0x46, 0x20, 0x30, 0x01, 0x7A, 0xC9, 0x07, +0x27, 0xD0, 0x75, 0x4C, 0x00, 0x21, 0x24, 0x78, +0xA4, 0x46, 0x20, 0xE0, 0x15, 0x79, 0x80, 0x24, +0x25, 0x43, 0x34, 0x27, 0x0C, 0x46, 0x7C, 0x43, +0x6F, 0x4F, 0xE4, 0x19, 0x27, 0x79, 0xBD, 0x42, +0x13, 0xD1, 0x20, 0x34, 0x65, 0x79, 0x45, 0x71, +0xA7, 0x79, 0x87, 0x71, 0xE4, 0x79, 0xC4, 0x71, +0x04, 0x79, 0x01, 0x2C, 0x1C, 0xD0, 0x01, 0x9C, +0x64, 0x78, 0xA6, 0x42, 0x05, 0xD9, 0x05, 0x2D, +0x03, 0xD2, 0x01, 0x24, 0x04, 0x71, 0x6D, 0x1C, +0x45, 0x71, 0x49, 0x1C, 0xC9, 0xB2, 0x8C, 0x45, +0xDC, 0xD8, 0x5B, 0x1C, 0xDB, 0xB2, 0x5F, 0x48, +0x01, 0x78, 0x99, 0x42, 0xB9, 0xD8, 0x00, 0x98, +0x62, 0x49, 0x00, 0x28, 0x08, 0x78, 0x1C, 0xD0, +0x64, 0x28, 0x1E, 0xD2, 0x40, 0x1C, 0x1B, 0xE0, +0x8C, 0x45, 0xEE, 0xD9, 0x51, 0x7A, 0x5E, 0x4A, +0x12, 0x78, 0xD2, 0x08, 0x91, 0x42, 0xE8, 0xD9, +0xC1, 0x79, 0xB1, 0x42, 0x09, 0xD9, 0x81, 0x79, +0x49, 0x1C, 0xC9, 0xB2, 0x81, 0x71, 0x05, 0x29, +0xDF, 0xD9, 0x00, 0x21, 0x81, 0x71, 0x01, 0x71, +0xDB, 0xE7, 0x00, 0x21, 0x81, 0x71, 0xC6, 0x71, +0xD7, 0xE7, 0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, +0x08, 0x70, 0x4A, 0x4D, 0x03, 0x21, 0x28, 0x78, +0x34, 0x22, 0x09, 0x1A, 0x50, 0x43, 0x40, 0x19, +0x51, 0x43, 0x00, 0x1D, 0xFF, 0xF7, 0xFD, 0xF8, +0x00, 0x24, 0x45, 0x4E, 0x0F, 0xE0, 0x30, 0x78, +0x34, 0x21, 0x00, 0x19, 0x48, 0x43, 0x81, 0x19, +0x28, 0x78, 0x34, 0x22, 0x00, 0x19, 0x50, 0x43, +0x40, 0x19, 0x09, 0x1D, 0x00, 0x1D, 0xFF, 0xF7, +0xD3, 0xF8, 0x64, 0x1C, 0xE4, 0xB2, 0x69, 0x78, +0xA1, 0x42, 0xEC, 0xD8, 0x2A, 0x78, 0x00, 0x20, +0x53, 0x18, 0x0E, 0xE0, 0x34, 0x21, 0x41, 0x43, +0x4A, 0x19, 0x06, 0x21, 0x41, 0x43, 0x89, 0x19, +0xA0, 0x31, 0x94, 0x89, 0x0C, 0x80, 0xD4, 0x89, +0x4C, 0x80, 0x12, 0x8A, 0x40, 0x1C, 0x8A, 0x80, +0xC0, 0xB2, 0x83, 0x42, 0xEE, 0xD8, 0x00, 0x20, +0x36, 0x4A, 0x01, 0x21, 0x10, 0x80, 0x0C, 0xE0, +0x34, 0x24, 0x44, 0x43, 0x64, 0x19, 0x24, 0x79, +0x66, 0x06, 0x76, 0x0E, 0x0C, 0x46, 0xB4, 0x40, +0x16, 0x88, 0x34, 0x43, 0x40, 0x1C, 0x14, 0x80, +0xC0, 0xB2, 0x83, 0x42, 0xF0, 0xD8, 0x12, 0x88, +0x00, 0x20, 0x0B, 0x46, 0x83, 0x40, 0x13, 0x42, 0x05, 0xD0, 0x40, 0x1C, 0xC0, 0xB2, 0x02, 0x28, -0xF7, 0xD3, 0x00, 0x26, 0x37, 0xE0, 0x28, 0x4A, -0x10, 0x70, 0xFA, 0xE7, 0x31, 0x46, 0x34, 0x22, -0x51, 0x43, 0x0C, 0x18, 0x27, 0x46, 0x20, 0x37, -0x38, 0x7C, 0x02, 0x28, 0x29, 0xD2, 0x0C, 0x23, -0x0A, 0x22, 0x2E, 0x21, 0x2C, 0x20, 0xE3, 0x5E, -0xA2, 0x5E, 0x61, 0x5E, 0x20, 0x5E, 0x04, 0xF0, -0x95, 0xF9, 0x29, 0x68, 0xB0, 0x31, 0xCA, 0x7B, +0xF7, 0xD3, 0x00, 0x26, 0x3A, 0xE0, 0x28, 0x49, +0x08, 0x70, 0xFA, 0xE7, 0x34, 0x20, 0x70, 0x43, +0x44, 0x19, 0x27, 0x46, 0x20, 0x37, 0xB8, 0x7D, +0x02, 0x28, 0x2D, 0xD2, 0x0E, 0x23, 0x0C, 0x22, +0x34, 0x21, 0x32, 0x20, 0xE3, 0x5E, 0xA2, 0x5E, +0x61, 0x5E, 0x20, 0x5E, 0x04, 0xF0, 0x4A, 0xFD, +0x19, 0x49, 0x09, 0x68, 0xB0, 0x31, 0xCA, 0x7B, 0x8B, 0x7B, 0x11, 0x02, 0x19, 0x43, 0x81, 0x42, -0x14, 0xD2, 0x38, 0x7C, 0x2C, 0x22, 0x0A, 0x23, -0xA2, 0x5E, 0x02, 0x21, 0xE3, 0x5E, 0x09, 0x1A, -0x4A, 0x43, 0x43, 0x43, 0xD2, 0x18, 0x52, 0x10, -0x62, 0x81, 0x2E, 0x22, 0xA2, 0x5E, 0x4A, 0x43, -0x0C, 0x21, 0x61, 0x5E, 0x41, 0x43, 0x50, 0x18, -0x40, 0x10, 0xA0, 0x81, 0x38, 0x7C, 0x40, 0x1C, -0x38, 0x74, 0x76, 0x1C, 0xF6, 0xB2, 0x02, 0x48, -0x01, 0x78, 0xB1, 0x42, 0xC6, 0xD8, 0xF8, 0xBD, -0xA0, 0x04, 0x00, 0x20, 0x40, 0x05, 0x00, 0x20, -0x34, 0x00, 0x00, 0x20, 0x36, 0x00, 0x00, 0x20, -0xB8, 0x02, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, -0x46, 0x00, 0x00, 0x20, 0x47, 0x00, 0x00, 0x20, -0x2A, 0x00, 0x00, 0x20, 0x3E, 0x00, 0x00, 0x20, -0x31, 0x00, 0x00, 0x20, 0xF1, 0xB5, 0x59, 0x48, -0x8A, 0xB0, 0x00, 0x68, 0x08, 0x90, 0x1B, 0x30, -0x57, 0x4B, 0x09, 0x90, 0x00, 0x20, 0x18, 0x5E, -0xC1, 0x0F, 0x08, 0x18, 0xC0, 0x03, 0x01, 0x0C, -0x08, 0x98, 0x00, 0x91, 0x90, 0x30, 0x41, 0x7B, -0x02, 0x7B, 0x08, 0x02, 0x10, 0x43, 0x00, 0x99, -0x07, 0x90, 0x88, 0x42, 0x00, 0xD9, 0x00, 0x90, -0x4F, 0x48, 0x4E, 0x4A, 0x02, 0x80, 0x4F, 0x49, -0xD0, 0x43, 0x08, 0x80, 0x4E, 0x49, 0x00, 0x23, -0x0A, 0x80, 0x49, 0x49, 0x1E, 0x46, 0x08, 0x80, -0x4C, 0x49, 0x0B, 0x70, 0x01, 0x21, 0xC9, 0x03, -0x74, 0x00, 0x01, 0xA8, 0x01, 0x53, 0x48, 0x1E, -0x03, 0xA9, 0x08, 0x53, 0x00, 0x20, 0x01, 0x46, -0x35, 0x46, 0x1C, 0xE0, 0x09, 0x9A, 0x52, 0x5D, -0x41, 0x2A, 0x16, 0xD0, 0x0A, 0x9A, 0x6B, 0x00, -0xD2, 0x5E, 0x01, 0xAB, 0x49, 0x1C, 0x1F, 0x5F, -0x80, 0x18, 0xC9, 0xB2, 0x97, 0x42, 0x00, 0xDA, -0x1A, 0x53, 0x03, 0xAB, 0x1F, 0x5F, 0x97, 0x42, -0x00, 0xDD, 0x1A, 0x53, 0x00, 0x9B, 0x9A, 0x42, -0x03, 0xDD, 0x3A, 0x4B, 0x1A, 0x78, 0x52, 0x1C, -0x1A, 0x70, 0x2D, 0x1D, 0xED, 0xB2, 0x30, 0x2D, -0xE0, 0xD3, 0x00, 0x29, 0x02, 0xD0, 0xFF, 0xF7, -0x4B, 0xF8, 0x00, 0xE0, 0x00, 0x20, 0x05, 0xA9, -0x08, 0x53, 0x2D, 0x49, 0x01, 0xAD, 0x00, 0x22, -0x28, 0x5F, 0x8A, 0x5E, 0x90, 0x42, 0x00, 0xDD, -0x08, 0x80, 0x2D, 0x49, 0x03, 0xAF, 0x00, 0x22, -0x38, 0x5F, 0x8A, 0x5E, 0x90, 0x42, 0x00, 0xDA, -0x08, 0x80, 0x76, 0x1C, 0xF6, 0xB2, 0x04, 0x2E, -0xB8, 0xD3, 0x07, 0x98, 0x26, 0x49, 0x43, 0x08, -0x00, 0x20, 0x08, 0x5E, 0x26, 0x4A, 0x83, 0x42, -0x06, 0xDA, 0x1F, 0x49, 0x00, 0x23, 0xCB, 0x5E, -0x19, 0x1A, 0x07, 0x98, 0x81, 0x42, 0x06, 0xDB, -0x08, 0x98, 0x80, 0x30, 0x81, 0x7B, 0x1F, 0x48, -0x00, 0x78, 0x81, 0x42, 0x01, 0xD9, 0x01, 0x20, -0x00, 0xE0, 0x00, 0x20, 0x10, 0x70, 0x00, 0x24, -0x05, 0xAE, 0x60, 0x00, 0x31, 0x5E, 0x00, 0x91, -0x3B, 0x5E, 0x2A, 0x5E, 0x21, 0x46, 0x0A, 0x98, -0x00, 0xF0, 0x5C, 0xF9, 0x64, 0x1C, 0xE4, 0xB2, -0x04, 0x2C, 0xF2, 0xD3, 0x0D, 0x48, 0x00, 0x68, -0x90, 0x30, 0x41, 0x7B, 0x02, 0x7B, 0x08, 0x02, -0x10, 0x43, 0x0E, 0x49, 0x00, 0x22, 0x8A, 0x5E, -0x82, 0x42, 0x0D, 0xDA, 0x0F, 0x48, 0x41, 0x88, -0x0A, 0x29, 0x09, 0xD9, 0x40, 0x88, 0x0B, 0x28, -0x03, 0xD1, 0x80, 0x21, 0x0C, 0x48, 0xFF, 0xF7, -0x20, 0xF8, 0x00, 0x20, 0x0B, 0xB0, 0xF0, 0xBD, -0x01, 0x20, 0xFB, 0xE7, 0xB8, 0x02, 0x00, 0x20, -0x7C, 0x04, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, -0x76, 0x04, 0x00, 0x20, 0x78, 0x04, 0x00, 0x20, -0x7A, 0x04, 0x00, 0x20, 0x8F, 0x02, 0x00, 0x20, -0x9A, 0x01, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, -0xF4, 0x05, 0x00, 0x20, 0xF0, 0xB5, 0x00, 0x20, -0x41, 0x1F, 0x8C, 0x46, 0x1E, 0x49, 0x05, 0x24, -0x09, 0x78, 0xC9, 0x07, 0x36, 0xD1, 0x1D, 0x49, +0x17, 0xD2, 0x1A, 0x49, 0x03, 0x20, 0x08, 0x70, +0xB8, 0x7D, 0x32, 0x22, 0x0C, 0x23, 0xA2, 0x5E, +0x02, 0x21, 0xE3, 0x5E, 0x09, 0x1A, 0x4A, 0x43, +0x43, 0x43, 0xD2, 0x18, 0x52, 0x10, 0xA2, 0x81, +0x34, 0x22, 0xA2, 0x5E, 0x4A, 0x43, 0x0E, 0x21, +0x61, 0x5E, 0x41, 0x43, 0x50, 0x18, 0x40, 0x10, +0xE0, 0x81, 0xB8, 0x7D, 0x40, 0x1C, 0xB8, 0x75, +0x76, 0x1C, 0xF6, 0xB2, 0x28, 0x78, 0xB0, 0x42, +0xC4, 0xD8, 0xFE, 0xBD, 0x7C, 0x07, 0x00, 0x20, +0x1C, 0x08, 0x00, 0x20, 0xBD, 0x02, 0x00, 0x20, +0x14, 0x00, 0x00, 0x20, 0x16, 0x00, 0x00, 0x20, +0xAC, 0x04, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20, +0xB7, 0x02, 0x00, 0x20, 0x40, 0x00, 0x00, 0x20, +0x3B, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x20, +0xF1, 0xB5, 0x66, 0x48, 0x8A, 0xB0, 0x00, 0x68, +0x08, 0x90, 0x1B, 0x30, 0x64, 0x4A, 0x09, 0x90, +0x00, 0x20, 0x10, 0x5E, 0xC1, 0x0F, 0x08, 0x18, +0xC0, 0x03, 0x01, 0x0C, 0x08, 0x98, 0x00, 0x91, +0x90, 0x30, 0x41, 0x7B, 0x03, 0x7B, 0x08, 0x02, +0x18, 0x43, 0x00, 0x99, 0x07, 0x90, 0x88, 0x42, +0x00, 0xD9, 0x00, 0x90, 0x5C, 0x49, 0x5B, 0x48, +0x08, 0x80, 0x5C, 0x4B, 0xC1, 0x43, 0x19, 0x80, +0x5B, 0x4B, 0x57, 0x4A, 0x18, 0x80, 0x11, 0x80, +0x00, 0x23, 0x5A, 0x4A, 0x1E, 0x46, 0x13, 0x70, +0x01, 0x20, 0xC0, 0x03, 0x74, 0x00, 0x01, 0xA9, +0x08, 0x53, 0x40, 0x1E, 0x03, 0xA9, 0x08, 0x53, +0x00, 0x20, 0x01, 0x46, 0x35, 0x46, 0x1C, 0xE0, +0x09, 0x9A, 0x52, 0x5D, 0x41, 0x2A, 0x16, 0xD0, +0x0A, 0x9A, 0x6B, 0x00, 0xD2, 0x5E, 0x01, 0xAB, +0x49, 0x1C, 0x1F, 0x5F, 0x80, 0x18, 0xC9, 0xB2, +0x97, 0x42, 0x00, 0xDA, 0x1A, 0x53, 0x03, 0xAB, +0x1F, 0x5F, 0x97, 0x42, 0x00, 0xDD, 0x1A, 0x53, +0x00, 0x9B, 0x9A, 0x42, 0x03, 0xDD, 0x47, 0x4B, +0x1A, 0x78, 0x52, 0x1C, 0x1A, 0x70, 0x2D, 0x1D, +0xED, 0xB2, 0x30, 0x2D, 0xE0, 0xD3, 0x00, 0x29, +0x02, 0xD0, 0xFE, 0xF7, 0xD9, 0xFF, 0x00, 0xE0, +0x00, 0x20, 0x05, 0xA9, 0x08, 0x53, 0x3A, 0x49, +0x01, 0xAD, 0x00, 0x22, 0x28, 0x5F, 0x8A, 0x5E, +0x90, 0x42, 0x00, 0xDD, 0x08, 0x80, 0x3A, 0x49, +0x03, 0xAF, 0x00, 0x22, 0x38, 0x5F, 0x8A, 0x5E, +0x90, 0x42, 0x00, 0xDA, 0x08, 0x80, 0x76, 0x1C, +0xF6, 0xB2, 0x04, 0x2E, 0xB8, 0xD3, 0x35, 0x48, +0x01, 0x26, 0x01, 0x78, 0x34, 0x48, 0x19, 0x29, +0x02, 0xD0, 0x00, 0x29, 0x02, 0xD0, 0x03, 0xE0, +0x06, 0x70, 0x01, 0xE0, 0x00, 0x22, 0x02, 0x70, +0x07, 0x98, 0x2D, 0x4A, 0x44, 0x08, 0x00, 0x20, +0x10, 0x5E, 0x2E, 0x4B, 0x84, 0x42, 0x06, 0xDA, +0x25, 0x4A, 0x00, 0x24, 0x14, 0x5F, 0x07, 0x9A, +0x24, 0x1A, 0x94, 0x42, 0x04, 0xDB, 0x08, 0x9A, +0x80, 0x32, 0x92, 0x7B, 0x8A, 0x42, 0x01, 0xD9, +0x1E, 0x70, 0x01, 0xE0, 0x00, 0x21, 0x19, 0x70, +0x25, 0x49, 0x26, 0x4A, 0x09, 0x78, 0x12, 0x78, +0x91, 0x42, 0x05, 0xD2, 0x24, 0x49, 0x00, 0x22, +0x8A, 0x5E, 0x90, 0x42, 0x00, 0xDA, 0x08, 0x80, +0x00, 0x24, 0x05, 0xAE, 0x60, 0x00, 0x31, 0x5E, +0x00, 0x91, 0x3B, 0x5E, 0x2A, 0x5E, 0x21, 0x46, +0x0A, 0x98, 0x00, 0xF0, 0xEF, 0xF8, 0x64, 0x1C, +0xE4, 0xB2, 0x04, 0x2C, 0xF2, 0xD3, 0x00, 0xF0, +0xA1, 0xFB, 0x0E, 0x48, 0x00, 0x68, 0x90, 0x30, +0x41, 0x7B, 0x02, 0x7B, 0x08, 0x02, 0x10, 0x43, +0x0E, 0x49, 0x00, 0x22, 0x8A, 0x5E, 0x82, 0x42, +0x0D, 0xDA, 0x14, 0x48, 0x41, 0x88, 0x0A, 0x29, +0x09, 0xD9, 0x40, 0x88, 0x0B, 0x28, 0x03, 0xD1, +0x80, 0x21, 0x11, 0x48, 0xFE, 0xF7, 0x95, 0xFF, +0x00, 0x20, 0x0B, 0xB0, 0xF0, 0xBD, 0x01, 0x20, +0xFB, 0xE7, 0x00, 0x00, 0xAC, 0x04, 0x00, 0x20, +0x58, 0x07, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, +0x52, 0x07, 0x00, 0x20, 0x54, 0x07, 0x00, 0x20, +0x56, 0x07, 0x00, 0x20, 0xB6, 0x02, 0x00, 0x20, +0x85, 0x02, 0x00, 0x20, 0x60, 0x02, 0x00, 0x20, +0xEE, 0x02, 0x00, 0x20, 0xB7, 0x02, 0x00, 0x20, +0xFC, 0x02, 0x00, 0x20, 0x60, 0x07, 0x00, 0x20, +0x28, 0x04, 0x00, 0x20, 0xF0, 0xB5, 0x00, 0x20, +0x41, 0x1F, 0x8C, 0x46, 0x1F, 0x49, 0x05, 0x24, +0x09, 0x78, 0xC9, 0x07, 0x39, 0xD1, 0x1E, 0x49, 0x09, 0x68, 0x90, 0x31, 0x4A, 0x7B, 0x0B, 0x7B, -0x11, 0x02, 0x1B, 0x4A, 0x19, 0x43, 0x13, 0x5E, -0x99, 0x42, 0x06, 0xDA, 0x19, 0x49, 0x09, 0x88, +0x11, 0x02, 0x1C, 0x4A, 0x19, 0x43, 0x13, 0x5E, +0x99, 0x42, 0x06, 0xDA, 0x1A, 0x49, 0x09, 0x88, 0x00, 0x29, 0x01, 0xD0, 0x0A, 0x24, 0x00, 0xE0, -0x5A, 0x24, 0x17, 0x4F, 0x17, 0x4D, 0x00, 0x26, -0x17, 0x4A, 0x41, 0x00, 0x52, 0x5E, 0xC9, 0x19, -0x00, 0x23, 0xCB, 0x5E, 0x9A, 0x42, 0x02, 0xDD, -0x2A, 0x5C, 0x52, 0x1C, 0x03, 0xE0, 0x9A, 0x42, -0x03, 0xDA, 0x2A, 0x5C, 0x52, 0x1E, 0x2A, 0x54, -0x00, 0xE0, 0x2E, 0x54, 0x2A, 0x56, 0xA2, 0x42, -0x02, 0xDB, 0x0A, 0x88, 0x52, 0x1C, 0x03, 0xE0, -0x62, 0x45, 0x03, 0xDC, 0x0A, 0x88, 0x52, 0x1E, -0x0A, 0x80, 0x2E, 0x54, 0x40, 0x1C, 0xC0, 0xB2, -0x30, 0x28, 0xDD, 0xD3, 0xF0, 0xBD, 0x00, 0x00, -0xEB, 0x02, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, -0x78, 0x04, 0x00, 0x20, 0xF0, 0x02, 0x00, 0x20, -0x00, 0x00, 0x01, 0x20, 0x0C, 0x03, 0x00, 0x20, -0xD4, 0x00, 0x00, 0x20, 0xFE, 0xB5, 0x04, 0x46, -0x37, 0x48, 0x00, 0x78, 0x00, 0x28, 0x46, 0xD0, -0x1E, 0x20, 0x00, 0x90, 0x40, 0x42, 0x01, 0x90, -0x00, 0x29, 0x12, 0xD0, 0x33, 0x48, 0x00, 0x78, -0x15, 0x28, 0x40, 0xD0, 0x32, 0x4B, 0x00, 0x20, -0x41, 0x00, 0x5A, 0x5A, 0x40, 0x1C, 0xD5, 0x00, -0xAA, 0x1A, 0x65, 0x5A, 0xC0, 0xB2, 0x52, 0x19, -0x12, 0xB2, 0xD2, 0x10, 0x5A, 0x52, 0x30, 0x28, -0xF2, 0xD3, 0x00, 0x25, 0x00, 0x21, 0x0A, 0x46, -0x08, 0x46, 0x2A, 0x4E, 0x2B, 0x18, 0x36, 0x68, -0x9E, 0x19, 0xF6, 0x7E, 0x41, 0x2E, 0x19, 0xD0, -0x5F, 0x00, 0x25, 0x4B, 0xE6, 0x5F, 0xDB, 0x5F, -0xB4, 0x46, 0xF3, 0x1A, 0xDE, 0x0F, 0xF3, 0x18, -0x5B, 0x10, 0x00, 0x9E, 0x1B, 0xB2, 0xB3, 0x42, -0x02, 0xDA, 0x66, 0x46, 0xF6, 0x1A, 0xE6, 0x53, -0x00, 0x9E, 0xB3, 0x42, 0x04, 0xDA, 0x01, 0x9E, -0xB3, 0x42, 0x01, 0xDD, 0xD2, 0x18, 0x12, 0xB2, -0x49, 0x1C, 0xC9, 0xB2, 0x40, 0x1C, 0xC0, 0xB2, -0x03, 0x28, 0xDA, 0xD3, 0x00, 0x29, 0x21, 0xD0, -0x01, 0x29, 0x0A, 0xD0, 0x0A, 0xE0, 0x0F, 0x20, -0x00, 0x90, 0x40, 0x42, 0xB7, 0xE7, 0x60, 0x22, -0x21, 0x46, 0x11, 0x48, 0xFE, 0xF7, 0x48, 0xFF, -0xC7, 0xE7, 0x02, 0x21, 0x10, 0x46, 0xFE, 0xF7, -0x2F, 0xFF, 0x02, 0xB2, 0x0D, 0x4B, 0x00, 0x20, -0x1E, 0x68, 0x29, 0x18, 0x8E, 0x19, 0xF6, 0x7E, -0x41, 0x2E, 0x03, 0xD0, 0x49, 0x00, 0x66, 0x5A, -0xB6, 0x1A, 0x66, 0x52, 0x40, 0x1C, 0xC0, 0xB2, -0x03, 0x28, 0xF1, 0xD3, 0x2D, 0x1D, 0xED, 0xB2, -0x30, 0x2D, 0xAF, 0xD3, 0xFE, 0xBD, 0x00, 0x00, -0x85, 0x02, 0x00, 0x20, 0x70, 0x04, 0x00, 0x20, -0x3C, 0x03, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, -0x10, 0xB5, 0x21, 0x49, 0x1F, 0x48, 0xC8, 0x61, -0x20, 0x48, 0x81, 0x68, 0x01, 0x22, 0x52, 0x02, -0x11, 0x43, 0x81, 0x60, 0x1E, 0x4C, 0x20, 0x68, -0x30, 0x21, 0x88, 0x43, 0x20, 0x60, 0x1D, 0x48, -0x00, 0x78, 0x00, 0x28, 0x11, 0xD0, 0x1C, 0xA0, -0x03, 0xF0, 0xA4, 0xFA, 0x1C, 0x48, 0x01, 0x68, +0x3C, 0x24, 0x18, 0x4F, 0x18, 0x4D, 0x00, 0x26, +0x18, 0x4A, 0x41, 0x00, 0x53, 0x5E, 0xC9, 0x19, +0x00, 0x22, 0x8A, 0x5E, 0x93, 0x42, 0x05, 0xDD, +0x9A, 0x1A, 0x0A, 0x2A, 0x09, 0xDA, 0x2A, 0x5C, +0x52, 0x1C, 0x03, 0xE0, 0x93, 0x42, 0x03, 0xDA, +0x2A, 0x5C, 0x52, 0x1E, 0x2A, 0x54, 0x00, 0xE0, +0x2E, 0x54, 0x2A, 0x56, 0xA2, 0x42, 0x02, 0xDB, +0x0A, 0x88, 0x52, 0x1C, 0x03, 0xE0, 0x62, 0x45, +0x03, 0xDC, 0x0A, 0x88, 0x52, 0x1E, 0x0A, 0x80, +0x2E, 0x54, 0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, +0xDA, 0xD3, 0xF0, 0xBD, 0xEB, 0x02, 0x00, 0x20, +0xAC, 0x04, 0x00, 0x20, 0x54, 0x07, 0x00, 0x20, +0xF4, 0x02, 0x00, 0x20, 0x00, 0x00, 0x01, 0x20, +0x1C, 0x03, 0x00, 0x20, 0xD4, 0x00, 0x00, 0x20, +0x10, 0xB5, 0x21, 0x48, 0x21, 0x4A, 0x01, 0x68, +0x21, 0x48, 0x91, 0x42, 0x01, 0xD1, 0x21, 0x49, +0x00, 0xE0, 0x21, 0x49, 0xC1, 0x61, 0x21, 0x48, +0x81, 0x68, 0x01, 0x22, 0x52, 0x02, 0x11, 0x43, +0x81, 0x60, 0x1F, 0x4A, 0x10, 0x68, 0x30, 0x21, +0x88, 0x43, 0x10, 0x60, 0x1D, 0x48, 0x00, 0x78, +0x00, 0x28, 0x0E, 0xD0, 0x1C, 0x48, 0x01, 0x68, 0x4A, 0x06, 0x90, 0x21, 0x00, 0x2A, 0x02, 0x68, 0x02, 0xDA, 0x0A, 0x43, 0x02, 0x60, 0x10, 0xBD, 0x8A, 0x43, 0x02, 0x60, 0x17, 0x49, 0x41, 0x61, -0x10, 0xBD, 0x17, 0xA0, 0x03, 0xF0, 0x92, 0xFA, -0x05, 0x21, 0x17, 0x48, 0x09, 0x07, 0x48, 0x62, -0x80, 0x07, 0x41, 0x6A, 0x82, 0x13, 0x11, 0x43, -0x41, 0x62, 0x14, 0x49, 0x0A, 0x68, 0x03, 0x12, -0x1A, 0x43, 0x0A, 0x60, 0xE2, 0x68, 0x09, 0x68, -0x0A, 0x43, 0xE2, 0x60, 0x01, 0x68, 0x02, 0x14, -0x11, 0x43, 0x01, 0x60, 0x0E, 0x49, 0x40, 0x14, -0x08, 0x60, 0x10, 0xBD, 0x0B, 0x0B, 0x0B, 0x0B, -0x40, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50, -0x00, 0x10, 0x00, 0x50, 0xCF, 0x00, 0x00, 0x20, -0x41, 0x53, 0x49, 0x0D, 0x0A, 0x00, 0x00, 0x00, -0x00, 0x11, 0x00, 0x50, 0x16, 0x00, 0x03, 0x00, -0x41, 0x53, 0x54, 0x0D, 0x0A, 0x00, 0x00, 0x00, +0x10, 0xBD, 0x05, 0x21, 0x16, 0x48, 0x09, 0x07, +0x48, 0x62, 0x80, 0x07, 0x41, 0x6A, 0x83, 0x13, +0x19, 0x43, 0x41, 0x62, 0x13, 0x49, 0x0B, 0x68, +0x04, 0x12, 0x23, 0x43, 0x0B, 0x60, 0xD3, 0x68, +0x09, 0x68, 0x0B, 0x43, 0xD3, 0x60, 0x01, 0x68, +0x02, 0x14, 0x11, 0x43, 0x01, 0x60, 0x0E, 0x49, +0x40, 0x14, 0x08, 0x60, 0x10, 0xBD, 0x00, 0x00, +0xE4, 0x06, 0x00, 0x20, 0xA2, 0x00, 0x03, 0xF3, +0x40, 0x00, 0x00, 0x50, 0x0B, 0x0B, 0x00, 0x0B, +0x0B, 0x0B, 0x0B, 0x0B, 0x00, 0x06, 0x00, 0x50, +0x00, 0x10, 0x00, 0x50, 0x99, 0x01, 0x00, 0x20, +0x00, 0x11, 0x00, 0x50, 0x13, 0x00, 0x03, 0x00, 0xF1, 0x2A, 0x02, 0x00, 0x50, 0x02, 0x00, 0x20, 0x00, 0xE1, 0x00, 0xE0, 0xFF, 0xB5, 0x04, 0x46, -0x55, 0x48, 0x1D, 0x46, 0x02, 0x68, 0x00, 0x21, +0x58, 0x48, 0x1D, 0x46, 0x02, 0x68, 0x83, 0xB0, 0x17, 0x46, 0x1B, 0x32, 0x94, 0x46, 0x3A, 0x46, 0x80, 0x32, 0x53, 0x7A, 0x16, 0x7A, 0x1A, 0x06, 0x12, 0x14, 0x03, 0x23, 0x32, 0x43, 0xDB, 0x03, -0x5A, 0x43, 0x12, 0x14, 0x96, 0x46, 0x4D, 0x4A, -0x83, 0xB0, 0x16, 0x78, 0x3A, 0x46, 0x90, 0x32, +0x5A, 0x43, 0x12, 0x14, 0x96, 0x46, 0x50, 0x4A, +0x00, 0x21, 0x16, 0x78, 0x4F, 0x4A, 0x08, 0x46, +0x12, 0x78, 0x02, 0x92, 0x3A, 0x46, 0x90, 0x32, 0x57, 0x7B, 0x12, 0x7B, 0x3F, 0x02, 0x3A, 0x43, -0x08, 0x46, 0x00, 0x2E, 0x03, 0xD1, 0x48, 0x4B, -0x1B, 0x78, 0x14, 0x2B, 0x01, 0xD2, 0x53, 0x08, -0x00, 0xE0, 0x93, 0x08, 0x00, 0x93, 0x45, 0x4B, -0x76, 0x46, 0x1E, 0x80, 0x0C, 0x9B, 0x00, 0x2B, -0x07, 0xDA, 0x43, 0x4E, 0x00, 0x27, 0xF7, 0x5F, -0x00, 0x9E, 0xB7, 0x42, 0x01, 0xDA, 0x53, 0x42, -0x01, 0xE0, 0x53, 0x08, 0x5B, 0x42, 0x0C, 0x9F, -0x1B, 0xB2, 0x05, 0x9E, 0x01, 0x93, 0xF6, 0x1B, -0x3B, 0x46, 0xEF, 0x1B, 0x00, 0x2E, 0x00, 0xDA, -0x76, 0x42, 0x00, 0x2F, 0x00, 0xDA, 0x7F, 0x42, -0xBE, 0x42, 0x01, 0xDA, 0x00, 0x2B, 0x15, 0xDD, -0x00, 0x9B, 0x0C, 0x9E, 0xDF, 0x00, 0xF3, 0x1B, -0xAB, 0x42, 0x06, 0xDD, 0x00, 0x2D, 0x04, 0xDA, -0x05, 0x9B, 0x5B, 0x19, 0xD2, 0x18, 0x52, 0x10, -0x01, 0xE0, 0x00, 0x9A, 0xAA, 0x18, 0x13, 0xB2, -0x72, 0x46, 0x73, 0x45, 0x00, 0xDD, 0x13, 0x46, -0x04, 0x9A, 0x19, 0xE0, 0x05, 0x9B, 0x9A, 0x1A, -0x13, 0xB2, 0x72, 0x46, 0x52, 0x42, 0x93, 0x42, -0x00, 0xDA, 0x13, 0xB2, 0x04, 0x9A, 0x1F, 0xE0, -0x66, 0x46, 0xB6, 0x5C, 0x41, 0x2E, 0x09, 0xD0, -0x56, 0x00, 0xA6, 0x5F, 0x9E, 0x42, 0x05, 0xDA, -0x01, 0x9F, 0xBE, 0x42, 0x02, 0xDD, 0x30, 0x18, -0x49, 0x1C, 0xC9, 0xB2, 0x12, 0x1D, 0xD2, 0xB2, -0x30, 0x2A, 0xED, 0xD3, 0x0E, 0xE0, 0x66, 0x46, -0xB6, 0x5C, 0x41, 0x2E, 0x06, 0xD0, 0x56, 0x00, -0xA6, 0x5F, 0x9E, 0x42, 0x02, 0xDD, 0x30, 0x18, -0x49, 0x1C, 0xC9, 0xB2, 0x12, 0x1D, 0xD2, 0xB2, -0x30, 0x2A, 0xF0, 0xD3, 0x00, 0x29, 0x02, 0xD0, -0xFE, 0xF7, 0x22, 0xFE, 0x01, 0x46, 0x04, 0x98, -0x05, 0xE0, 0x42, 0x00, 0xA3, 0x5A, 0x5B, 0x1A, -0x00, 0x1D, 0xA3, 0x52, 0xC0, 0xB2, 0x30, 0x28, -0xF7, 0xD3, 0x0E, 0x4A, 0x00, 0x23, 0x68, 0x1A, -0xD3, 0x5E, 0x00, 0xB2, 0x98, 0x42, 0x00, 0xDA, -0x10, 0x80, 0x05, 0x98, 0x00, 0x22, 0x40, 0x1A, -0x09, 0x49, 0x00, 0xB2, 0x8A, 0x5E, 0x90, 0x42, -0x00, 0xDD, 0x08, 0x80, 0x07, 0xB0, 0xF0, 0xBD, -0xB8, 0x02, 0x00, 0x20, 0x9A, 0x01, 0x00, 0x20, -0xEA, 0x02, 0x00, 0x20, 0xEE, 0x02, 0x00, 0x20, -0x7C, 0x04, 0x00, 0x20, 0x76, 0x04, 0x00, 0x20, -0x78, 0x04, 0x00, 0x20, 0xF8, 0xB5, 0x06, 0x46, -0x0D, 0x46, 0x30, 0x23, 0xFF, 0xF7, 0x80, 0xF8, +0x00, 0x2E, 0x02, 0xD1, 0x02, 0x9B, 0x04, 0x2B, +0x01, 0xD0, 0x53, 0x08, 0x00, 0xE0, 0x93, 0x08, +0x00, 0x93, 0x47, 0x4B, 0x76, 0x46, 0x1E, 0x80, +0x0C, 0x9B, 0x00, 0x2B, 0x07, 0xDA, 0x45, 0x4E, +0x00, 0x27, 0xF7, 0x5F, 0x00, 0x9E, 0xB7, 0x42, +0x01, 0xDA, 0x53, 0x42, 0x01, 0xE0, 0x53, 0x08, +0x5B, 0x42, 0x0C, 0x9F, 0x1B, 0xB2, 0x05, 0x9E, +0x01, 0x93, 0xF6, 0x1B, 0x3B, 0x46, 0xEF, 0x1B, +0x00, 0x2E, 0x00, 0xDA, 0x76, 0x42, 0x00, 0x2F, +0x00, 0xDA, 0x7F, 0x42, 0xBE, 0x42, 0x01, 0xDA, +0x00, 0x2B, 0x18, 0xDD, 0x00, 0x9B, 0x0C, 0x9E, +0xDF, 0x00, 0xF3, 0x1B, 0xAB, 0x42, 0x01, 0xDD, +0x00, 0x2D, 0x02, 0xDB, 0x02, 0x9B, 0x04, 0x2B, +0x04, 0xD0, 0x05, 0x9B, 0x5B, 0x19, 0xD2, 0x18, +0x52, 0x10, 0x01, 0xE0, 0x00, 0x9A, 0xAA, 0x18, +0x13, 0xB2, 0x72, 0x46, 0x73, 0x45, 0x00, 0xDD, +0x13, 0x46, 0x04, 0x9A, 0x19, 0xE0, 0x05, 0x9B, +0x9A, 0x1A, 0x13, 0xB2, 0x72, 0x46, 0x52, 0x42, +0x93, 0x42, 0x00, 0xDA, 0x13, 0xB2, 0x04, 0x9A, +0x1F, 0xE0, 0x66, 0x46, 0xB6, 0x5C, 0x41, 0x2E, +0x09, 0xD0, 0x56, 0x00, 0xA6, 0x5F, 0x9E, 0x42, +0x05, 0xDA, 0x01, 0x9F, 0xBE, 0x42, 0x02, 0xDD, +0x30, 0x18, 0x49, 0x1C, 0xC9, 0xB2, 0x12, 0x1D, +0xD2, 0xB2, 0x30, 0x2A, 0xED, 0xD3, 0x0E, 0xE0, +0x66, 0x46, 0xB6, 0x5C, 0x41, 0x2E, 0x06, 0xD0, +0x56, 0x00, 0xA6, 0x5F, 0x9E, 0x42, 0x02, 0xDD, +0x30, 0x18, 0x49, 0x1C, 0xC9, 0xB2, 0x12, 0x1D, +0xD2, 0xB2, 0x30, 0x2A, 0xF0, 0xD3, 0x00, 0x29, +0x02, 0xD0, 0xFE, 0xF7, 0x01, 0xFE, 0x01, 0x46, +0x04, 0x98, 0x05, 0xE0, 0x42, 0x00, 0xA3, 0x5A, +0x5B, 0x1A, 0x00, 0x1D, 0xA3, 0x52, 0xC0, 0xB2, +0x30, 0x28, 0xF7, 0xD3, 0x0E, 0x4A, 0x00, 0x23, +0x68, 0x1A, 0xD3, 0x5E, 0x00, 0xB2, 0x98, 0x42, +0x00, 0xDA, 0x10, 0x80, 0x05, 0x98, 0x00, 0x22, +0x40, 0x1A, 0x0A, 0x49, 0x00, 0xB2, 0x8A, 0x5E, +0x90, 0x42, 0x00, 0xDD, 0x08, 0x80, 0x07, 0xB0, +0xF0, 0xBD, 0x00, 0x00, 0xAC, 0x04, 0x00, 0x20, +0x60, 0x02, 0x00, 0x20, 0x8F, 0x02, 0x00, 0x20, +0xF2, 0x02, 0x00, 0x20, 0x58, 0x07, 0x00, 0x20, +0x52, 0x07, 0x00, 0x20, 0x54, 0x07, 0x00, 0x20, +0xF8, 0xB5, 0x20, 0x4A, 0x20, 0x4B, 0x12, 0x68, +0x9A, 0x42, 0x16, 0xD0, 0x1F, 0x4F, 0x00, 0x29, +0x1A, 0xD0, 0x1F, 0x49, 0x3A, 0x46, 0x09, 0x78, +0x13, 0x68, 0x1D, 0x29, 0x0E, 0xD0, 0x00, 0x21, +0x4A, 0x00, 0x9C, 0x5A, 0x49, 0x1C, 0x65, 0x00, +0x64, 0x19, 0x85, 0x5A, 0xC9, 0xB2, 0x64, 0x19, +0x24, 0xB2, 0xA4, 0x10, 0x9C, 0x52, 0x30, 0x29, +0xF2, 0xD3, 0xF8, 0xBD, 0x01, 0x46, 0x60, 0x22, +0x18, 0x46, 0xFE, 0xF7, 0xC5, 0xFD, 0xF8, 0xBD, +0x00, 0x23, 0x59, 0x00, 0x3A, 0x68, 0x44, 0x5E, +0x55, 0x5E, 0x62, 0x1B, 0xD6, 0x17, 0x76, 0x0F, +0xB2, 0x18, 0xD2, 0x10, 0x00, 0xD5, 0x52, 0x42, +0x52, 0x1C, 0x12, 0xB2, 0x07, 0x2A, 0x00, 0xDD, +0x07, 0x22, 0x08, 0x26, 0xB6, 0x1A, 0x36, 0xB2, +0x54, 0x43, 0x75, 0x43, 0x62, 0x19, 0xD2, 0x10, +0x5B, 0x1C, 0xDB, 0xB2, 0x42, 0x52, 0x30, 0x2B, +0xE3, 0xD3, 0xF8, 0xBD, 0xE4, 0x06, 0x00, 0x20, +0xA1, 0x00, 0x03, 0xF3, 0x14, 0x03, 0x00, 0x20, +0x4C, 0x07, 0x00, 0x20, 0xF8, 0xB5, 0x06, 0x46, +0x0D, 0x46, 0x30, 0x23, 0xFF, 0xF7, 0x04, 0xF8, 0x04, 0x00, 0x28, 0xD0, 0x19, 0x4F, 0x00, 0x2A, -0x21, 0xD0, 0x00, 0x20, 0xFE, 0xF7, 0xCA, 0xFE, +0x21, 0xD0, 0x00, 0x20, 0xFE, 0xF7, 0x5E, 0xFE, 0x02, 0xE0, 0x98, 0x00, 0x29, 0x58, 0x21, 0x50, 0x18, 0x46, 0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, 0xF7, 0xD1, 0x38, 0x68, 0x01, 0x88, 0x31, 0x43, 0x01, 0x80, 0x11, 0x49, 0x09, 0x68, 0xCA, 0x79, 0x8B, 0x79, 0x11, 0x02, 0x19, 0x43, 0x41, 0x80, -0xC1, 0x21, 0x89, 0x00, 0x0D, 0x48, 0xFF, 0xF7, -0xDF, 0xF8, 0x0D, 0x49, 0xC8, 0x63, 0x01, 0x20, -0xFE, 0xF7, 0xAC, 0xFE, 0x0C, 0xE0, 0x38, 0x68, +0xC1, 0x21, 0x89, 0x00, 0x0D, 0x48, 0x00, 0xF0, +0x1D, 0xF8, 0x0D, 0x49, 0xC8, 0x63, 0x01, 0x20, +0xFE, 0xF7, 0x40, 0xFE, 0x0C, 0xE0, 0x38, 0x68, 0x00, 0x88, 0x30, 0x40, 0x03, 0xD1, 0xF8, 0xBD, 0x98, 0x00, 0x21, 0x58, 0x29, 0x50, 0x18, 0x46, 0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, 0xF7, 0xD1, 0x01, 0x20, 0xF8, 0xBD, 0x08, 0x00, 0x00, 0x20, -0xB8, 0x02, 0x00, 0x20, 0x78, 0x7C, 0x00, 0x00, -0x40, 0x7F, 0x00, 0x00, 0x70, 0xB5, 0x17, 0x48, -0x5A, 0x25, 0x01, 0x78, 0x16, 0x48, 0x00, 0x29, -0x25, 0xD0, 0x09, 0x21, 0x01, 0x70, 0x15, 0x4C, -0x01, 0x21, 0xE0, 0x89, 0x89, 0x02, 0x88, 0x42, -0x01, 0xD0, 0x03, 0x20, 0x60, 0x71, 0x11, 0x48, -0x30, 0x21, 0x28, 0x30, 0xFE, 0xF7, 0xC9, 0xFD, -0x00, 0x20, 0x60, 0x72, 0xA0, 0x71, 0x60, 0x62, -0x16, 0x21, 0x21, 0x72, 0x0C, 0x49, 0x20, 0x71, -0x08, 0x70, 0xA5, 0x81, 0x60, 0x81, 0x60, 0x8A, -0x09, 0x21, 0x09, 0x03, 0x08, 0x43, 0x60, 0x82, -0x03, 0x20, 0x40, 0x02, 0xE0, 0x61, 0x0D, 0x20, -0xC0, 0x01, 0x20, 0x62, 0x70, 0xBD, 0x05, 0x70, -0xD9, 0xE7, 0x00, 0x00, 0x85, 0x02, 0x00, 0x20, -0xB5, 0x02, 0x00, 0x20, 0xE4, 0x02, 0x00, 0x20, -0x87, 0x02, 0x00, 0x20, 0x0E, 0x48, 0x03, 0x21, -0x41, 0x71, 0x0E, 0x49, 0x41, 0x61, 0x0D, 0x49, -0x60, 0x31, 0x81, 0x61, 0x01, 0x21, 0x01, 0x70, -0x07, 0x22, 0x42, 0x70, 0x0A, 0x4B, 0x05, 0x22, -0x1A, 0x70, 0x0A, 0x4B, 0x1A, 0x70, 0x0A, 0x4B, -0x55, 0x22, 0xDA, 0x70, 0x04, 0x22, 0x02, 0x82, -0x00, 0x22, 0xC2, 0x70, 0x09, 0x22, 0x12, 0x03, -0x42, 0x82, 0x81, 0x70, 0x70, 0x47, 0x00, 0x00, +0xAC, 0x04, 0x00, 0x20, 0x78, 0x7C, 0x00, 0x00, +0x40, 0x7F, 0x00, 0x00, 0x70, 0xB5, 0x41, 0x18, +0x49, 0x1E, 0x64, 0x24, 0x09, 0x04, 0x0B, 0x4D, +0x01, 0x43, 0x69, 0x63, 0xE8, 0x68, 0x81, 0x21, +0x09, 0x06, 0x08, 0x43, 0xE8, 0x60, 0x02, 0xE0, +0x01, 0x20, 0xFE, 0xF7, 0x21, 0xFF, 0xE8, 0x68, +0xC0, 0x01, 0x04, 0xD5, 0x20, 0x46, 0x64, 0x1E, +0xA4, 0xB2, 0x00, 0x28, 0xF4, 0xD1, 0xA8, 0x6B, +0x70, 0xBD, 0x00, 0x00, 0x40, 0x09, 0x00, 0x50, +0x10, 0xB5, 0x1D, 0x4C, 0x01, 0x21, 0x60, 0x8A, +0x89, 0x02, 0x88, 0x42, 0x01, 0xD0, 0x03, 0x20, +0x60, 0x71, 0x19, 0x48, 0x30, 0x21, 0x38, 0x30, +0xFE, 0xF7, 0x47, 0xFD, 0x00, 0x20, 0x60, 0x72, +0xA0, 0x71, 0x60, 0x63, 0x1E, 0x21, 0x21, 0x72, +0x14, 0x49, 0x20, 0x71, 0x08, 0x70, 0x14, 0x49, +0x09, 0x22, 0x09, 0x78, 0x21, 0x82, 0xE0, 0x81, +0x61, 0x8B, 0x12, 0x03, 0x11, 0x43, 0x61, 0x83, +0xE0, 0x72, 0x10, 0x49, 0x20, 0x73, 0x08, 0x70, +0x0F, 0x49, 0x08, 0x70, 0x0F, 0x49, 0x08, 0x70, +0x0F, 0x49, 0x08, 0x70, 0x0F, 0x49, 0x08, 0x70, +0xA0, 0x72, 0xE0, 0x82, 0x20, 0x83, 0x03, 0x20, +0x40, 0x02, 0x60, 0x62, 0x0D, 0x20, 0xC0, 0x01, +0xA0, 0x62, 0x07, 0x20, 0x00, 0x02, 0xE0, 0x62, +0x0F, 0x20, 0xC0, 0x01, 0x20, 0x63, 0x10, 0xBD, +0xE4, 0x02, 0x00, 0x20, 0x8F, 0x02, 0x00, 0x20, +0xB7, 0x02, 0x00, 0x20, 0xBA, 0x02, 0x00, 0x20, +0xA8, 0x04, 0x00, 0x20, 0xBF, 0x02, 0x00, 0x20, +0xCA, 0x02, 0x00, 0x20, 0xCB, 0x02, 0x00, 0x20, +0x11, 0x48, 0xC0, 0x6B, 0x11, 0x49, 0x08, 0x60, +0x11, 0x48, 0x03, 0x21, 0x41, 0x71, 0x11, 0x49, +0xC1, 0x61, 0x10, 0x49, 0x60, 0x31, 0x01, 0x62, +0x01, 0x21, 0x01, 0x70, 0x07, 0x22, 0x42, 0x70, +0x0D, 0x4B, 0x05, 0x22, 0x1A, 0x70, 0x0D, 0x4B, +0x1A, 0x70, 0x0D, 0x4B, 0x55, 0x22, 0xDA, 0x70, +0x04, 0x22, 0x82, 0x82, 0x00, 0x22, 0x09, 0x23, +0xC2, 0x70, 0x1B, 0x03, 0x43, 0x83, 0x81, 0x70, +0x08, 0x48, 0x02, 0x70, 0x70, 0x47, 0x00, 0x00, +0x80, 0x09, 0x00, 0x50, 0xE4, 0x06, 0x00, 0x20, 0xE4, 0x02, 0x00, 0x20, 0x00, 0x00, 0x04, 0x20, -0x85, 0x02, 0x00, 0x20, 0x71, 0x04, 0x00, 0x20, -0x90, 0x02, 0x00, 0x20, 0x04, 0x22, 0x0F, 0x49, -0x0C, 0x28, 0x10, 0xD0, 0x8B, 0x05, 0x0D, 0x28, -0x08, 0x6A, 0x10, 0xD0, 0x18, 0x43, 0x08, 0x62, -0x88, 0x6A, 0x10, 0x43, 0x88, 0x62, 0x0A, 0x4A, -0x01, 0x20, 0x10, 0x70, 0xC8, 0x68, 0xC8, 0x60, -0x88, 0x6A, 0x88, 0x62, 0x70, 0x47, 0x08, 0x6A, -0x40, 0x00, 0x40, 0x08, 0x00, 0xE0, 0x18, 0x43, -0x08, 0x62, 0x88, 0x6A, 0x90, 0x43, 0x88, 0x62, -0xF0, 0xE7, 0x00, 0x00, 0x00, 0x06, 0x00, 0x50, -0x9B, 0x01, 0x00, 0x20, 0x00, 0xB5, 0x08, 0x49, -0x0A, 0x28, 0x05, 0xD0, 0x07, 0x48, 0x00, 0x0C, -0x48, 0x63, 0x07, 0x48, 0x08, 0x63, 0x00, 0xBD, -0x06, 0x48, 0x00, 0x68, 0x08, 0x62, 0x0D, 0x20, -0xFF, 0xF7, 0xCC, 0xFF, 0x00, 0xBD, 0x00, 0x00, -0x00, 0x06, 0x00, 0x50, 0xBC, 0x02, 0x00, 0x20, -0xCC, 0x02, 0x00, 0x20, 0xC0, 0x02, 0x00, 0x20, -0x10, 0xB5, 0x1B, 0x49, 0x00, 0x20, 0x03, 0x00, -0xFE, 0xF7, 0xA0, 0xFD, 0x0C, 0x07, 0x0A, 0x0E, -0x26, 0x26, 0x11, 0x14, 0x17, 0x1A, 0x1D, 0x20, -0x23, 0x26, 0x16, 0x4A, 0x0A, 0x80, 0x1E, 0xE0, -0x14, 0x4A, 0x12, 0x1D, 0x4A, 0x80, 0x1A, 0xE0, -0x13, 0x4A, 0x8A, 0x80, 0x17, 0xE0, 0x13, 0x4A, -0x4A, 0x81, 0x14, 0xE0, 0x12, 0x4A, 0x8A, 0x81, -0x11, 0xE0, 0x12, 0x4A, 0xCA, 0x81, 0x0E, 0xE0, -0x11, 0x4A, 0x0A, 0x82, 0x0B, 0xE0, 0x11, 0x4A, -0x4A, 0x82, 0x08, 0xE0, 0x10, 0x4A, 0x8A, 0x82, -0x05, 0xE0, 0x10, 0x4A, 0xCA, 0x82, 0x02, 0xE0, -0x0F, 0x4A, 0x43, 0x00, 0xCA, 0x52, 0x40, 0x1C, -0x80, 0xB2, 0x0C, 0x28, 0xCF, 0xD3, 0x0D, 0xA0, -0x03, 0xF0, 0x88, 0xF8, 0x10, 0xBD, 0x00, 0x00, +0x87, 0x02, 0x00, 0x20, 0x4D, 0x07, 0x00, 0x20, +0x90, 0x02, 0x00, 0x20, 0xBD, 0x02, 0x00, 0x20, +0x04, 0x22, 0x0F, 0x49, 0x0C, 0x28, 0x10, 0xD0, +0x8B, 0x05, 0x0D, 0x28, 0x08, 0x6A, 0x10, 0xD0, +0x18, 0x43, 0x08, 0x62, 0x88, 0x6A, 0x10, 0x43, +0x88, 0x62, 0x0A, 0x4A, 0x01, 0x20, 0x10, 0x70, +0xC8, 0x68, 0xC8, 0x60, 0x88, 0x6A, 0x88, 0x62, +0x70, 0x47, 0x08, 0x6A, 0x40, 0x00, 0x40, 0x08, +0x00, 0xE0, 0x18, 0x43, 0x08, 0x62, 0x88, 0x6A, +0x90, 0x43, 0x88, 0x62, 0xF0, 0xE7, 0x00, 0x00, +0x00, 0x06, 0x00, 0x50, 0x80, 0x02, 0x00, 0x20, +0x00, 0xB5, 0x1B, 0x48, 0x00, 0x21, 0x0B, 0x00, +0xFE, 0xF7, 0x10, 0xFD, 0x0C, 0x07, 0x0A, 0x0E, +0x11, 0x2A, 0x15, 0x18, 0x1B, 0x1E, 0x21, 0x24, +0x27, 0x2A, 0x16, 0x4A, 0x02, 0x80, 0x22, 0xE0, +0x14, 0x4A, 0x12, 0x1D, 0x42, 0x80, 0x1E, 0xE0, +0x13, 0x4A, 0x82, 0x80, 0x1B, 0xE0, 0x11, 0x4A, +0x10, 0x32, 0xC2, 0x80, 0x17, 0xE0, 0x11, 0x4A, +0x42, 0x81, 0x14, 0xE0, 0x10, 0x4A, 0x82, 0x81, +0x11, 0xE0, 0x10, 0x4A, 0xC2, 0x81, 0x0E, 0xE0, +0x0F, 0x4A, 0x02, 0x82, 0x0B, 0xE0, 0x0F, 0x4A, +0x42, 0x82, 0x08, 0xE0, 0x0E, 0x4A, 0x82, 0x82, +0x05, 0xE0, 0x0E, 0x4A, 0xC2, 0x82, 0x02, 0xE0, +0x0D, 0x4A, 0x4B, 0x00, 0xC2, 0x52, 0x49, 0x1C, +0x89, 0xB2, 0x0C, 0x29, 0xCB, 0xD3, 0x00, 0xBD, 0xCC, 0x02, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20, 0x34, 0x01, 0x00, 0x20, -0xE0, 0x06, 0x00, 0x20, 0xE4, 0x06, 0x00, 0x20, +0xE0, 0x06, 0x00, 0x20, 0x10, 0x05, 0x00, 0x20, 0xD8, 0x06, 0x00, 0x20, 0xC0, 0x02, 0x00, 0x20, 0xBC, 0x02, 0x00, 0x20, 0x9C, 0x01, 0x00, 0x20, -0xC4, 0x02, 0x00, 0x20, 0x49, 0x32, 0x43, 0x20, -0x4F, 0x4B, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x70, 0xB5, 0x01, 0x25, 0xCA, 0x07, 0x0A, 0xD0, -0x00, 0x20, 0x70, 0xBD, 0x93, 0x00, 0xC4, 0x58, -0x66, 0x1C, 0x02, 0xD0, 0x1B, 0x18, 0x5B, 0x68, -0x23, 0x60, 0x92, 0x1C, 0x92, 0xB2, 0x8A, 0x42, -0xF4, 0xD3, 0x28, 0x46, 0x70, 0xBD, 0x00, 0x00, -0x10, 0xB5, 0x1D, 0x49, 0x0A, 0x68, 0x1C, 0x48, -0x40, 0x30, 0x02, 0x61, 0x4A, 0x68, 0x42, 0x61, -0x8A, 0x68, 0x82, 0x61, 0xC9, 0x68, 0xC1, 0x61, -0x82, 0x69, 0xF0, 0x21, 0x8A, 0x43, 0x82, 0x61, -0x82, 0x69, 0x0A, 0x43, 0x82, 0x61, 0x41, 0x69, -0x49, 0x04, 0x04, 0xD5, 0x41, 0x69, 0x01, 0x22, -0x52, 0x03, 0x89, 0x1A, 0x41, 0x61, 0x10, 0x48, -0x40, 0x38, 0x01, 0x68, 0x01, 0x22, 0x12, 0x06, -0x11, 0x43, 0x01, 0x60, 0x30, 0x21, 0x0D, 0x48, -0xFF, 0xF7, 0xC6, 0xFF, 0x0B, 0x48, 0x40, 0x21, -0xC0, 0x30, 0xFF, 0xF7, 0xC1, 0xFF, 0x0B, 0x20, -0xFE, 0xF7, 0xD4, 0xFC, 0x03, 0x20, 0xFE, 0xF7, -0xD1, 0xFC, 0x00, 0x20, 0xFE, 0xF7, 0xCE, 0xFC, -0x05, 0x20, 0xFE, 0xF7, 0xCB, 0xFC, 0x09, 0x20, -0xFE, 0xF7, 0xC8, 0xFC, 0x01, 0x20, 0x10, 0xBD, -0x40, 0x14, 0x00, 0x50, 0x98, 0x5A, 0x00, 0x00, -0x0E, 0x4A, 0x00, 0x21, 0x11, 0x60, 0x0E, 0x4A, -0x20, 0x21, 0x11, 0x60, 0x5A, 0x28, 0x13, 0xD0, -0x0C, 0x48, 0x01, 0x23, 0x00, 0x05, 0x00, 0x0D, -0x5B, 0x03, 0xC0, 0x18, 0x05, 0x22, 0x12, 0x07, -0x10, 0x62, 0x90, 0x00, 0xC2, 0x68, 0x0A, 0x43, -0xC2, 0x60, 0x05, 0x4A, 0x80, 0x3A, 0x11, 0x60, -0x41, 0x68, 0x19, 0x43, 0x41, 0x60, 0x70, 0x47, -0xFF, 0x20, 0xEA, 0xE7, 0xD0, 0x00, 0x00, 0x20, -0x80, 0xE1, 0x00, 0xE0, 0x24, 0x09, 0x00, 0x00, -0x10, 0xB5, 0x17, 0x4A, 0x11, 0x78, 0x81, 0x42, -0x29, 0xD0, 0x16, 0x49, 0x0B, 0x68, 0x01, 0x24, -0xA4, 0x02, 0x23, 0x43, 0x0B, 0x60, 0x10, 0x70, -0x05, 0x22, 0x40, 0x24, 0x12, 0x49, 0x12, 0x07, -0x80, 0x23, 0x00, 0x28, 0x09, 0xD0, 0x08, 0x68, -0x40, 0x06, 0x0C, 0xD4, 0x08, 0x68, 0x20, 0x43, -0x08, 0x60, 0xD0, 0x68, 0x98, 0x43, 0xD0, 0x60, -0x05, 0xE0, 0xD0, 0x68, 0x18, 0x43, 0xD0, 0x60, -0x08, 0x68, 0xA0, 0x43, 0x08, 0x60, 0x08, 0x68, -0x18, 0x43, 0x08, 0x60, 0xD0, 0x68, 0x01, 0x21, -0xC9, 0x03, 0x08, 0x43, 0xD0, 0x60, 0xE1, 0x20, -0x00, 0x02, 0x01, 0xF0, 0x61, 0xF8, 0x10, 0xBD, -0x04, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, -0x00, 0x11, 0x00, 0x50, 0x10, 0xB5, 0x10, 0x4C, -0xA0, 0x79, 0x00, 0x28, 0x18, 0xD1, 0x00, 0xF0, -0xA5, 0xFF, 0x00, 0xF0, 0xA7, 0xFE, 0xFF, 0xF7, -0xF7, 0xFC, 0x0C, 0x48, 0x01, 0x78, 0x0C, 0x48, -0x00, 0x29, 0x02, 0xD0, 0x09, 0x21, 0x09, 0x02, -0x00, 0xE0, 0x0A, 0x49, 0x01, 0x60, 0x01, 0x20, -0xA0, 0x71, 0xE0, 0x71, 0x08, 0x48, 0x00, 0x78, -0xC0, 0x07, 0x02, 0xD0, 0x00, 0xF0, 0x0E, 0xF8, -0x10, 0xBD, 0x00, 0xF0, 0x77, 0xFE, 0x10, 0xBD, -0x84, 0x04, 0x00, 0x20, 0xCF, 0x00, 0x00, 0x20, -0x00, 0x10, 0x00, 0x50, 0x04, 0x08, 0x00, 0x00, -0xCE, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x1D, 0x4C, -0xA0, 0x79, 0x00, 0x28, 0x35, 0xD0, 0x1C, 0xA0, -0x02, 0xF0, 0x88, 0xFF, 0x00, 0x20, 0xA0, 0x71, -0xE0, 0x71, 0x1E, 0x48, 0x81, 0x68, 0x01, 0x22, -0x52, 0x02, 0x91, 0x43, 0x81, 0x60, 0x1C, 0x48, -0x01, 0x68, 0x92, 0x00, 0x91, 0x43, 0x01, 0x60, -0x1A, 0x48, 0x01, 0x68, 0x10, 0x22, 0x91, 0x43, -0x01, 0x60, 0x01, 0x68, 0x80, 0x22, 0x11, 0x43, -0x01, 0x60, 0x17, 0x49, 0x41, 0x61, 0x17, 0x48, -0x00, 0x78, 0x00, 0x28, 0x15, 0xD1, 0x05, 0x20, -0x00, 0x07, 0x41, 0x6A, 0x92, 0x02, 0x91, 0x43, -0x41, 0x62, 0x41, 0x6A, 0x09, 0x22, 0x12, 0x05, -0x11, 0x43, 0x41, 0x62, 0x80, 0x00, 0xC2, 0x68, -0x41, 0x14, 0x8A, 0x43, 0xC2, 0x60, 0x02, 0x68, -0x03, 0x14, 0x9A, 0x43, 0x02, 0x60, 0x0C, 0x48, -0x01, 0x60, 0x10, 0xBD, 0x84, 0x04, 0x00, 0x20, -0x4C, 0x65, 0x61, 0x76, 0x65, 0x20, 0x41, 0x75, -0x74, 0x6F, 0x53, 0x63, 0x61, 0x6E, 0x0D, 0x0A, -0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x50, +0xC4, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x27, 0x4A, +0x11, 0x68, 0x26, 0x48, 0x40, 0x30, 0x01, 0x61, +0x51, 0x68, 0x41, 0x61, 0x91, 0x68, 0x81, 0x61, +0xD1, 0x68, 0xC1, 0x61, 0x83, 0x69, 0xF0, 0x21, +0x8B, 0x43, 0x83, 0x61, 0x83, 0x69, 0x0B, 0x43, +0x83, 0x61, 0x1F, 0x49, 0x1F, 0x4B, 0x09, 0x68, +0x99, 0x42, 0x07, 0xD1, 0x43, 0x69, 0x5B, 0x04, +0x04, 0xD5, 0x43, 0x69, 0x01, 0x24, 0x64, 0x03, +0x1B, 0x1B, 0x43, 0x61, 0x17, 0x48, 0x40, 0x38, +0x03, 0x68, 0x01, 0x24, 0x24, 0x06, 0x23, 0x43, +0x03, 0x60, 0x16, 0x48, 0x40, 0x1C, 0x81, 0x42, +0x09, 0xD1, 0x05, 0x20, 0x00, 0x07, 0x41, 0x69, +0x52, 0x68, 0xD2, 0x0A, 0x12, 0x1F, 0x12, 0x07, +0x12, 0x0A, 0x11, 0x43, 0x41, 0x61, 0x30, 0x21, +0x0F, 0x48, 0xFE, 0xF7, 0x2F, 0xFF, 0x0E, 0x48, +0x40, 0x21, 0xC0, 0x30, 0xFE, 0xF7, 0x2A, 0xFF, +0x0B, 0x20, 0xFE, 0xF7, 0x4B, 0xFC, 0x03, 0x20, +0xFE, 0xF7, 0x48, 0xFC, 0x00, 0x20, 0xFE, 0xF7, +0x45, 0xFC, 0x05, 0x20, 0xFE, 0xF7, 0x42, 0xFC, +0x09, 0x20, 0xFE, 0xF7, 0x3F, 0xFC, 0x01, 0x20, +0x10, 0xBD, 0x00, 0x00, 0x40, 0x14, 0x00, 0x50, +0xE4, 0x06, 0x00, 0x20, 0xA1, 0x00, 0x03, 0xF3, +0xE0, 0x62, 0x00, 0x00, 0xF0, 0xB5, 0x93, 0xB0, +0x00, 0x21, 0xFD, 0x48, 0x0A, 0x91, 0x02, 0x78, +0x08, 0x92, 0x40, 0x78, 0x0B, 0x90, 0x08, 0x46, +0x00, 0x2A, 0x04, 0xD1, 0x0B, 0x9A, 0x00, 0x2A, +0x01, 0xD1, 0xF8, 0x4A, 0x10, 0x70, 0xF8, 0x4A, +0x12, 0x68, 0x90, 0x32, 0x53, 0x7B, 0x14, 0x7B, +0x1B, 0x06, 0x1B, 0x14, 0x23, 0x43, 0x03, 0x93, +0x01, 0x93, 0xF4, 0x4B, 0xF4, 0x4C, 0x1B, 0x78, +0x0C, 0x93, 0x24, 0x78, 0x11, 0x94, 0x23, 0x43, +0x0D, 0x93, 0x03, 0xD1, 0xF1, 0x4B, 0x1B, 0x78, +0x04, 0x2B, 0x04, 0xD9, 0x03, 0x9B, 0x5B, 0x10, +0x01, 0x93, 0x02, 0x23, 0x0A, 0x93, 0x53, 0x7B, +0x14, 0x7B, 0x1A, 0x02, 0x22, 0x43, 0x52, 0x42, +0x13, 0xB2, 0x9C, 0x46, 0xEA, 0x4B, 0x00, 0x22, +0x9A, 0x5E, 0x62, 0x45, 0x06, 0xDA, 0x53, 0x23, +0xDB, 0x43, 0x9A, 0x42, 0x01, 0xDA, 0x9C, 0x46, +0x00, 0xE0, 0x94, 0x46, 0xE5, 0x4A, 0x00, 0x23, +0xD3, 0x5E, 0xE5, 0x4C, 0x00, 0x22, 0xA2, 0x5E, +0x12, 0x92, 0x9A, 0x1A, 0x12, 0xB2, 0x00, 0x92, +0xE2, 0x4C, 0x00, 0x22, 0xA2, 0x5E, 0x04, 0x92, +0x00, 0x2B, 0x7D, 0xD0, 0xE0, 0x4A, 0x12, 0x78, +0x00, 0x2A, 0xFA, 0xD0, 0xDF, 0x4A, 0x12, 0x78, +0x00, 0x2A, 0xF6, 0xD0, 0x0C, 0x9A, 0x00, 0x2A, +0x03, 0xD1, 0xDD, 0x4A, 0x12, 0x78, 0x18, 0x2A, +0x6E, 0xD1, 0xDC, 0x4A, 0xCF, 0x4F, 0x12, 0x78, +0x00, 0x9D, 0xD4, 0x08, 0x0E, 0x94, 0x04, 0x24, +0x3C, 0x5F, 0x05, 0x94, 0xD2, 0x4C, 0xD1, 0x4F, +0x24, 0x88, 0x3F, 0x88, 0x01, 0x9E, 0x3C, 0x1B, +0x27, 0xB2, 0x05, 0x9C, 0xE4, 0x1B, 0x10, 0x94, +0x64, 0x42, 0x0F, 0x94, 0x14, 0x09, 0x09, 0x94, +0xD1, 0x4F, 0x00, 0x24, 0x3C, 0x5F, 0x02, 0x94, +0xB5, 0x42, 0x7D, 0xDD, 0xCF, 0x4C, 0x00, 0x25, +0x65, 0x5F, 0x05, 0x95, 0x06, 0x95, 0xBF, 0x4D, +0x6E, 0x78, 0x07, 0x96, 0x00, 0x2E, 0x0D, 0xD0, +0x57, 0x09, 0xB7, 0x42, 0x13, 0xD9, 0x05, 0x9F, +0x00, 0x9E, 0xF6, 0x19, 0x77, 0x10, 0x27, 0x80, +0x06, 0x26, 0xAE, 0x5F, 0xBE, 0x42, 0x15, 0xDA, +0xEF, 0x80, 0x13, 0xE0, 0x00, 0x9E, 0x26, 0x80, +0xEE, 0x80, 0xC3, 0x4C, 0xA8, 0x70, 0x20, 0x70, +0x01, 0x24, 0x6C, 0x70, 0x7C, 0xE0, 0x05, 0x9E, +0x37, 0x01, 0xBF, 0x1B, 0x00, 0x9E, 0xBE, 0x19, +0x01, 0x27, 0x36, 0x03, 0xFF, 0x03, 0xF6, 0x19, +0x36, 0x14, 0x26, 0x80, 0x26, 0x88, 0x06, 0x9C, +0x34, 0x1B, 0x24, 0xB2, 0x06, 0x94, 0x06, 0x24, +0x2C, 0x5F, 0x36, 0x1B, 0x36, 0xB2, 0x05, 0x96, +0x07, 0x9E, 0x96, 0x42, 0x64, 0xD2, 0x0E, 0x9E, +0x97, 0x1B, 0x07, 0x9E, 0xB7, 0x42, 0x10, 0xDA, +0xA6, 0x11, 0x77, 0x42, 0x05, 0x9E, 0xB7, 0x42, +0x0B, 0xDA, 0x2E, 0x78, 0x00, 0x2E, 0x08, 0xD0, +0xAE, 0x78, 0xAD, 0x4F, 0x76, 0x1C, 0xAE, 0x70, +0x3E, 0x78, 0x00, 0x2E, 0x01, 0xD0, 0x76, 0x1E, +0x3E, 0x70, 0x2F, 0x46, 0x09, 0x9E, 0x00, 0xE0, +0xF7, 0xE1, 0x07, 0x9D, 0xAE, 0x42, 0x0E, 0xD2, +0x06, 0x9C, 0x02, 0x2C, 0x05, 0xDD, 0x08, 0x9C, +0x00, 0x2C, 0x02, 0xD0, 0xBC, 0x78, 0x64, 0x1C, +0xBC, 0x70, 0xBD, 0x78, 0x03, 0x2D, 0x11, 0xD9, +0x01, 0x25, 0x3D, 0x70, 0x0E, 0xE0, 0x24, 0x11, +0x65, 0x42, 0x05, 0x9C, 0xA5, 0x42, 0x14, 0xDD, +0x02, 0x9C, 0x24, 0x11, 0x65, 0x42, 0x04, 0x9C, +0xA5, 0x42, 0x0E, 0xDD, 0x98, 0x4D, 0x2C, 0x78, +0x64, 0x1C, 0x2C, 0x70, 0x96, 0x4C, 0x25, 0x78, +0x03, 0x2D, 0x01, 0xD9, 0x01, 0x21, 0x20, 0x70, +0x07, 0x9C, 0x64, 0x1C, 0x7C, 0x70, 0x1F, 0xE0, +0x01, 0xE0, 0x91, 0x4C, 0xF7, 0xE7, 0x81, 0x4F, +0x8E, 0x4C, 0x78, 0x70, 0x20, 0x80, 0x03, 0x9C, +0x64, 0x10, 0xAC, 0x42, 0x11, 0xDD, 0x38, 0x70, +0x05, 0x9D, 0xAC, 0x42, 0x0D, 0xDD, 0x10, 0x9C, +0x00, 0x2C, 0x00, 0xDA, 0x0F, 0x9C, 0x08, 0x25, +0x7D, 0x5F, 0x24, 0xB2, 0x00, 0x2D, 0x03, 0xD0, +0x6E, 0x01, 0x75, 0x1B, 0x2C, 0x19, 0x64, 0x11, +0x3C, 0x81, 0x83, 0x4C, 0x20, 0x70, 0xB8, 0x70, +0x72, 0x4F, 0x08, 0x25, 0x7D, 0x5F, 0xDC, 0x10, +0x6D, 0x1D, 0xAC, 0x42, 0x01, 0xDA, 0x2C, 0xB2, +0x02, 0xE0, 0x1E, 0x2C, 0x00, 0xDD, 0x1E, 0x24, +0x78, 0x4E, 0x0D, 0x9D, 0x36, 0x78, 0xB6, 0x08, +0x05, 0x96, 0x00, 0x2D, 0x20, 0xD0, 0x3D, 0x78, +0x00, 0x2D, 0x1D, 0xD1, 0x01, 0x9E, 0x00, 0x9D, +0xB5, 0x42, 0x19, 0xDD, 0x10, 0x9E, 0x00, 0x2E, +0x00, 0xDA, 0x0F, 0x9E, 0x73, 0x4D, 0xA6, 0x42, +0x2D, 0x78, 0x72, 0x4C, 0x04, 0xDA, 0x6D, 0x1C, +0x25, 0x70, 0x71, 0x4C, 0x20, 0x70, 0x03, 0xE0, +0x00, 0x2D, 0x01, 0xD0, 0x6D, 0x1E, 0x25, 0x70, +0x6C, 0x4E, 0x05, 0x9C, 0x35, 0x78, 0xAC, 0x42, +0x04, 0xD8, 0x01, 0x21, 0x30, 0x70, 0x01, 0xE0, +0x68, 0x4C, 0x20, 0x70, 0x57, 0x4D, 0x00, 0x9C, +0xAC, 0x80, 0x01, 0x9C, 0x67, 0x4F, 0xA3, 0x42, +0x30, 0xDD, 0x13, 0x25, 0x12, 0x9C, 0xED, 0x43, +0xAC, 0x42, 0x2B, 0xDA, 0x64, 0x4E, 0xB5, 0x88, +0x05, 0x9C, 0xA5, 0x42, 0x26, 0xD2, 0x4F, 0x4C, +0x0A, 0x25, 0x65, 0x5F, 0xAB, 0x42, 0x04, 0xDD, +0xB6, 0x88, 0x09, 0x9D, 0xAE, 0x42, 0x00, 0xD2, +0x63, 0x81, 0x0A, 0x25, 0x65, 0x5F, 0x0A, 0x24, +0x65, 0x43, 0x5A, 0x4C, 0x2D, 0x11, 0x24, 0x78, +0x9D, 0x42, 0x0E, 0xDD, 0x0A, 0x9E, 0x1D, 0x46, +0x35, 0x41, 0x6E, 0x42, 0x04, 0x9D, 0xAE, 0x42, +0x07, 0xDD, 0x64, 0x1C, 0xE4, 0xB2, 0x3C, 0x70, +0x05, 0x2C, 0x0A, 0xD9, 0x38, 0x70, 0x01, 0x21, +0x07, 0xE0, 0x00, 0x2C, 0x05, 0xD0, 0x64, 0x1E, +0x3C, 0x70, 0x02, 0xE0, 0x3B, 0x4C, 0x60, 0x81, +0x38, 0x70, 0x0D, 0x9C, 0x00, 0x2C, 0x15, 0xD0, +0x4B, 0x4C, 0xA4, 0x88, 0x94, 0x42, 0x11, 0xD9, +0x47, 0x4C, 0x4A, 0x4D, 0x26, 0x78, 0x2C, 0x78, +0x00, 0x2E, 0x07, 0xD0, 0x64, 0x1C, 0xE4, 0xB2, +0x2C, 0x70, 0x94, 0x42, 0x06, 0xD9, 0x28, 0x70, +0x01, 0x21, 0x03, 0xE0, 0x00, 0x2C, 0x01, 0xD0, +0x64, 0x1E, 0x2C, 0x70, 0x42, 0x4C, 0x24, 0x78, +0x00, 0x94, 0xE4, 0x07, 0x07, 0xD0, 0x41, 0x4C, +0x55, 0x00, 0x26, 0x78, 0xB5, 0x42, 0x02, 0xD2, +0x20, 0x70, 0x01, 0x21, 0x01, 0xE0, 0x00, 0x29, +0x05, 0xD0, 0x0C, 0x9D, 0x27, 0x4C, 0x00, 0x2D, +0x01, 0xD1, 0x01, 0x25, 0x25, 0x70, 0x36, 0x4C, +0xA6, 0x88, 0x39, 0x4C, 0x05, 0x9D, 0x24, 0x78, +0xAE, 0x42, 0x06, 0xD3, 0x21, 0x4D, 0x2D, 0x78, +0x00, 0x2D, 0x02, 0xD1, 0x11, 0x9D, 0x00, 0x2D, +0x79, 0xD0, 0x65, 0x46, 0x6E, 0x00, 0x04, 0x9D, +0xAE, 0x42, 0x0A, 0xDC, 0xAF, 0x25, 0xAD, 0x00, +0xAB, 0x42, 0x06, 0xDC, 0x03, 0x9D, 0x02, 0x9B, +0xAB, 0x42, 0x0D, 0xDD, 0x04, 0x9B, 0x63, 0x45, +0x0A, 0xDA, 0x2C, 0x4D, 0x2B, 0x78, 0x5B, 0x1C, +0x1B, 0x06, 0x1B, 0x0E, 0x2B, 0x70, 0x05, 0xD0, +0x13, 0x4D, 0x01, 0x21, 0x29, 0x70, 0x01, 0xE0, +0x26, 0x4B, 0x18, 0x70, 0x0F, 0x4B, 0x10, 0x4D, +0x1B, 0x78, 0x2D, 0x78, 0x2B, 0x43, 0x5B, 0xD0, +0x03, 0x9D, 0x02, 0x9B, 0xAB, 0x42, 0x01, 0xDC, +0x00, 0x2C, 0x55, 0xD0, 0x04, 0x9B, 0x5D, 0x42, +0x02, 0x9B, 0x9D, 0x42, 0x02, 0xDC, 0x04, 0x9B, +0x63, 0x45, 0x3F, 0xDA, 0x16, 0x4B, 0x1B, 0x7A, +0x01, 0x2B, 0x36, 0xD9, 0x09, 0x9B, 0x35, 0xE0, +0x7C, 0x07, 0x00, 0x20, 0x1C, 0x04, 0x00, 0x20, +0xAC, 0x04, 0x00, 0x20, 0xB9, 0x02, 0x00, 0x20, +0xBB, 0x02, 0x00, 0x20, 0xE5, 0x02, 0x00, 0x20, +0xFA, 0x02, 0x00, 0x20, 0x54, 0x07, 0x00, 0x20, +0x52, 0x07, 0x00, 0x20, 0x56, 0x07, 0x00, 0x20, +0xE6, 0x02, 0x00, 0x20, 0x84, 0x02, 0x00, 0x20, +0xB5, 0x02, 0x00, 0x20, 0xB7, 0x02, 0x00, 0x20, +0x58, 0x07, 0x00, 0x20, 0x6C, 0x02, 0x00, 0x20, +0x70, 0x02, 0x00, 0x20, 0x71, 0x02, 0x00, 0x20, +0x4F, 0x07, 0x00, 0x20, 0x72, 0x02, 0x00, 0x20, +0x60, 0x07, 0x00, 0x20, 0xBA, 0x02, 0x00, 0x20, +0xBD, 0x02, 0x00, 0x20, 0xBE, 0x02, 0x00, 0x20, +0xF0, 0x02, 0x00, 0x20, 0xEF, 0x02, 0x00, 0x20, +0x0D, 0xE0, 0x0E, 0x9B, 0x5D, 0x4D, 0x1B, 0x19, +0x2B, 0x70, 0x01, 0xE0, 0x5B, 0x4B, 0x18, 0x70, +0x5A, 0x4C, 0x05, 0x9B, 0x24, 0x78, 0xA3, 0x42, +0x06, 0xD2, 0x01, 0x21, 0x04, 0xE0, 0x05, 0x9B, +0x56, 0x4D, 0xA3, 0x42, 0x00, 0xD3, 0x28, 0x70, +0x55, 0x4D, 0x56, 0x4F, 0x2E, 0x78, 0x00, 0x2E, +0x08, 0xD0, 0x55, 0x4B, 0x3C, 0x46, 0x24, 0x78, +0x1B, 0x78, 0x01, 0x2E, 0x0D, 0xD0, 0x02, 0x2E, +0x3A, 0xD0, 0x3E, 0xE0, 0x08, 0x9B, 0x00, 0x2B, +0x3B, 0xD0, 0x0B, 0x9B, 0x00, 0x2B, 0x38, 0xD1, +0x01, 0x23, 0x2B, 0x70, 0x08, 0x9B, 0x3B, 0x70, +0x33, 0xE0, 0xA3, 0x42, 0x26, 0xD9, 0x4B, 0x4C, +0x4B, 0x4E, 0x24, 0x78, 0x77, 0x7A, 0x00, 0x2C, +0x10, 0xD0, 0x1E, 0x2F, 0x0C, 0xD9, 0x47, 0x49, +0x08, 0x70, 0x44, 0x49, 0x28, 0x70, 0x08, 0x70, +0x46, 0x49, 0x09, 0x78, 0x00, 0x29, 0x22, 0xD1, +0x45, 0x4B, 0x01, 0x21, 0x19, 0x70, 0x1E, 0xE0, +0x28, 0x70, 0x1A, 0xE0, 0x41, 0x4C, 0x42, 0x4E, +0x24, 0x78, 0x36, 0x78, 0x34, 0x43, 0x14, 0xD0, +0xB4, 0x2F, 0x04, 0xD9, 0x02, 0x24, 0x2C, 0x70, +0x38, 0x4C, 0x23, 0x70, 0x0D, 0xE0, 0x3D, 0x4B, +0x98, 0x80, 0x0A, 0xE0, 0x35, 0x4E, 0xA3, 0x42, +0x07, 0xD2, 0x28, 0x70, 0x30, 0x70, 0x04, 0xE0, +0xA3, 0x42, 0x02, 0xD2, 0x28, 0x70, 0x38, 0x70, +0x01, 0xE0, 0x00, 0x29, 0x31, 0xD0, 0x36, 0x49, +0x37, 0x4D, 0x08, 0x70, 0x88, 0x70, 0x35, 0x49, +0x36, 0x4C, 0x08, 0x70, 0x36, 0x49, 0x2B, 0x78, +0x09, 0x78, 0x49, 0x08, 0x00, 0x2B, 0x1C, 0xD0, +0x93, 0x42, 0x02, 0xD3, 0x33, 0x4D, 0x01, 0x23, +0x2B, 0x70, 0x33, 0x4B, 0x80, 0x25, 0x18, 0x70, +0x22, 0x4B, 0x18, 0x70, 0x31, 0x4B, 0x18, 0x80, +0x00, 0x98, 0x31, 0x4B, 0x28, 0x43, 0x18, 0x70, +0x24, 0x4B, 0x18, 0x78, 0x00, 0x28, 0x0C, 0xD0, +0x21, 0x70, 0x2E, 0x49, 0xFE, 0x28, 0x0A, 0x80, +0x01, 0xD2, 0x40, 0x1C, 0x18, 0x70, 0x13, 0xB0, +0xF0, 0xBD, 0x01, 0x23, 0x2B, 0x70, 0x21, 0x70, +0xE3, 0xE7, 0x1E, 0x49, 0x01, 0x20, 0x08, 0x72, +0xF5, 0xE7, 0x04, 0x99, 0x61, 0x45, 0x15, 0xDA, +0x25, 0x49, 0x0A, 0x78, 0x1B, 0x49, 0x01, 0x2A, +0x09, 0x78, 0x0B, 0xD0, 0x04, 0x2A, 0x02, 0xD0, +0x05, 0x2A, 0x0B, 0xD1, 0x03, 0xE0, 0x49, 0x08, +0x16, 0x4A, 0x49, 0x00, 0x05, 0xE0, 0x15, 0x4A, +0xFD, 0x23, 0x01, 0xE0, 0x13, 0x4A, 0xFB, 0x23, +0x19, 0x40, 0x11, 0x70, 0x17, 0x49, 0x4B, 0x23, +0x0A, 0x88, 0x1B, 0x01, 0x9A, 0x42, 0xD6, 0xD3, +0x0A, 0x4A, 0x10, 0x70, 0x08, 0x80, 0x0E, 0x49, +0x08, 0x70, 0x09, 0x49, 0x08, 0x70, 0x12, 0x49, +0x08, 0x70, 0xCC, 0xE7, 0xF0, 0x02, 0x00, 0x20, +0xBF, 0x02, 0x00, 0x20, 0xCA, 0x02, 0x00, 0x20, +0xCB, 0x02, 0x00, 0x20, 0xA8, 0x04, 0x00, 0x20, +0x7C, 0x07, 0x00, 0x20, 0xB9, 0x02, 0x00, 0x20, +0xBB, 0x02, 0x00, 0x20, 0x60, 0x07, 0x00, 0x20, +0x1C, 0x04, 0x00, 0x20, 0x9A, 0x01, 0x00, 0x20, +0x73, 0x02, 0x00, 0x20, 0x51, 0x07, 0x00, 0x20, +0xB7, 0x02, 0x00, 0x20, 0x8F, 0x02, 0x00, 0x20, +0xEF, 0x02, 0x00, 0x20, 0x6E, 0x02, 0x00, 0x20, +0xBD, 0x02, 0x00, 0x20, 0xF4, 0x02, 0x00, 0x20, +0xE5, 0x02, 0x00, 0x20, 0x0E, 0x4A, 0x00, 0x21, +0x11, 0x60, 0x0E, 0x4A, 0x20, 0x21, 0x11, 0x60, +0x78, 0x28, 0x13, 0xD0, 0x0C, 0x48, 0x01, 0x23, +0x00, 0x05, 0x00, 0x0D, 0x5B, 0x03, 0xC0, 0x18, +0x05, 0x22, 0x12, 0x07, 0x10, 0x62, 0x90, 0x00, +0xC2, 0x68, 0x0A, 0x43, 0xC2, 0x60, 0x05, 0x4A, +0x80, 0x3A, 0x11, 0x60, 0x41, 0x68, 0x19, 0x43, +0x41, 0x60, 0x70, 0x47, 0xFF, 0x20, 0xEA, 0xE7, +0x68, 0x02, 0x00, 0x20, 0x80, 0xE1, 0x00, 0xE0, +0x24, 0x09, 0x00, 0x00, 0x04, 0x49, 0x00, 0x20, +0x08, 0x60, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, +0x42, 0x14, 0x11, 0x43, 0x41, 0x60, 0x70, 0x47, +0x68, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x15, 0x4A, +0x11, 0x78, 0x81, 0x42, 0x25, 0xD0, 0x14, 0x49, +0x0B, 0x68, 0x01, 0x24, 0xA4, 0x02, 0x23, 0x43, +0x0B, 0x60, 0x10, 0x70, 0x05, 0x22, 0x40, 0x24, +0x10, 0x49, 0x12, 0x07, 0x80, 0x23, 0x00, 0x28, +0x09, 0xD0, 0x08, 0x68, 0x40, 0x06, 0x0C, 0xD4, +0x08, 0x68, 0x20, 0x43, 0x08, 0x60, 0xD0, 0x68, +0x98, 0x43, 0xD0, 0x60, 0x05, 0xE0, 0xD0, 0x68, +0x18, 0x43, 0xD0, 0x60, 0x08, 0x68, 0xA0, 0x43, +0x08, 0x60, 0x08, 0x68, 0x18, 0x43, 0x08, 0x60, +0xD0, 0x68, 0x01, 0x21, 0xC9, 0x03, 0x08, 0x43, +0xD0, 0x60, 0x10, 0xBD, 0x04, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, 0x00, 0x11, 0x00, 0x50, -0xC8, 0x03, 0x60, 0x00, 0xCF, 0x00, 0x00, 0x20, +0x10, 0xB5, 0x10, 0x4C, 0xA0, 0x79, 0x00, 0x28, +0x18, 0xD1, 0x01, 0xF0, 0xED, 0xF8, 0x00, 0xF0, +0x21, 0xFF, 0xFF, 0xF7, 0x65, 0xF9, 0x0C, 0x48, +0x01, 0x78, 0x0C, 0x48, 0x00, 0x29, 0x02, 0xD0, +0x09, 0x21, 0x09, 0x02, 0x00, 0xE0, 0x0A, 0x49, +0x01, 0x60, 0x01, 0x20, 0xA0, 0x71, 0xE0, 0x71, +0x08, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x02, 0xD0, +0x00, 0xF0, 0x0E, 0xF8, 0x10, 0xBD, 0x00, 0xF0, +0xF1, 0xFE, 0x10, 0xBD, 0x60, 0x07, 0x00, 0x20, +0x99, 0x01, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, +0x04, 0x08, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20, +0x1B, 0x48, 0x81, 0x79, 0x00, 0x29, 0x32, 0xD0, +0x00, 0x21, 0x81, 0x71, 0xC1, 0x71, 0x19, 0x48, +0x81, 0x68, 0x01, 0x22, 0x52, 0x02, 0x91, 0x43, +0x81, 0x60, 0x17, 0x48, 0x01, 0x68, 0x92, 0x00, +0x91, 0x43, 0x01, 0x60, 0x15, 0x48, 0x01, 0x68, +0x10, 0x22, 0x91, 0x43, 0x01, 0x60, 0x01, 0x68, +0x80, 0x22, 0x11, 0x43, 0x01, 0x60, 0x12, 0x49, +0x41, 0x61, 0x12, 0x48, 0x00, 0x78, 0x00, 0x28, +0x15, 0xD1, 0x05, 0x20, 0x00, 0x07, 0x41, 0x6A, +0x92, 0x02, 0x91, 0x43, 0x41, 0x62, 0x41, 0x6A, +0x09, 0x22, 0x12, 0x05, 0x11, 0x43, 0x41, 0x62, +0x80, 0x00, 0xC2, 0x68, 0x41, 0x14, 0x8A, 0x43, +0xC2, 0x60, 0x02, 0x68, 0x03, 0x14, 0x9A, 0x43, +0x02, 0x60, 0x07, 0x48, 0x01, 0x60, 0x70, 0x47, +0x60, 0x07, 0x00, 0x20, 0x00, 0x06, 0x00, 0x50, +0x00, 0x10, 0x00, 0x50, 0x00, 0x11, 0x00, 0x50, +0xC8, 0x03, 0x60, 0x00, 0x99, 0x01, 0x00, 0x20, 0x80, 0xE1, 0x00, 0xE0, 0x00, 0xB5, 0x08, 0x49, 0x83, 0x20, 0x08, 0x70, 0x07, 0x49, 0x00, 0x20, 0x08, 0x70, 0x07, 0x48, 0x00, 0x68, 0x07, 0x49, -0x40, 0x05, 0x40, 0x0F, 0x08, 0x73, 0x5A, 0x20, -0xFF, 0xF7, 0x16, 0xFF, 0x00, 0xBD, 0x00, 0x00, -0x84, 0x04, 0x00, 0x20, 0xCE, 0x00, 0x00, 0x20, +0x40, 0x05, 0x40, 0x0F, 0x08, 0x73, 0x78, 0x20, +0xFF, 0xF7, 0x1C, 0xFF, 0x00, 0xBD, 0x00, 0x00, +0x60, 0x07, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, 0x00, 0x11, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, -0xF0, 0xB5, 0x04, 0x46, 0x8F, 0xB0, 0xA6, 0x48, -0x0A, 0x90, 0xA5, 0x48, 0xA6, 0x4D, 0x60, 0x30, -0x0B, 0x90, 0xA4, 0x48, 0x03, 0x90, 0xA5, 0x48, -0x05, 0x95, 0x01, 0x68, 0x22, 0x46, 0xA4, 0xA0, -0x02, 0xF0, 0x10, 0xFF, 0x03, 0x98, 0x08, 0x90, -0xA5, 0x48, 0xA6, 0x4A, 0x00, 0x21, 0x09, 0x95, -0x8B, 0x00, 0x08, 0x9D, 0x49, 0x1C, 0xE8, 0x50, -0x09, 0x9D, 0x89, 0xB2, 0xEA, 0x50, 0x18, 0x29, -0xF6, 0xD3, 0x01, 0x20, 0x80, 0x02, 0x06, 0x90, -0x20, 0x46, 0x07, 0x94, 0x50, 0x30, 0x00, 0xB2, -0x50, 0x3C, 0x0C, 0x90, 0x20, 0xB2, 0x0D, 0x90, -0x00, 0xF0, 0x6E, 0xFC, 0x00, 0xF0, 0xE2, 0xFE, -0x00, 0xF0, 0x6E, 0xF9, 0x98, 0x48, 0x00, 0x78, -0xC0, 0x07, 0x18, 0xD1, 0x01, 0x90, 0x00, 0x20, -0x08, 0x9A, 0x81, 0x00, 0x53, 0x58, 0x0A, 0x9A, -0x40, 0x1C, 0x53, 0x50, 0x09, 0x9A, 0x80, 0xB2, -0x53, 0x58, 0x0B, 0x9A, 0x18, 0x28, 0x53, 0x50, -0xF2, 0xD3, 0x00, 0xF0, 0xB7, 0xFD, 0x00, 0xF0, -0xC9, 0xFE, 0x00, 0xF0, 0x55, 0xF9, 0x8C, 0x48, -0x00, 0x78, 0xC0, 0x07, 0x02, 0xD0, 0x00, 0x20, -0x0F, 0xB0, 0xF0, 0xBD, 0x3B, 0x46, 0x89, 0xA0, -0x00, 0x9A, 0x01, 0x99, 0x02, 0xF0, 0xCA, 0xFE, -0x00, 0x25, 0x2D, 0xE0, 0x00, 0x2D, 0x2B, 0xD0, -0x01, 0x98, 0x05, 0x28, 0x73, 0xD9, 0x30, 0x2F, -0x71, 0xD2, 0x7A, 0x4C, 0x05, 0x98, 0x40, 0x3C, -0x04, 0x90, 0x60, 0x6B, 0x03, 0x26, 0x36, 0x05, -0x01, 0x21, 0xB0, 0x43, 0x49, 0x05, 0x40, 0x18, -0x60, 0x63, 0x00, 0xF0, 0x8F, 0xFD, 0x00, 0xF0, -0xA1, 0xFE, 0x00, 0xF0, 0x2D, 0xF9, 0x60, 0x6B, -0x01, 0x21, 0xB0, 0x43, 0x09, 0x05, 0x40, 0x18, -0x60, 0x63, 0x00, 0x27, 0x00, 0x26, 0x79, 0x48, -0x00, 0x68, 0x80, 0x19, 0xC0, 0x7E, 0x41, 0x28, -0x0E, 0xD0, 0x70, 0x00, 0x04, 0x99, 0x02, 0x90, -0x0C, 0x5A, 0x16, 0x2E, 0x12, 0xD0, 0x1A, 0xE0, -0x00, 0x98, 0x30, 0x28, 0x77, 0xD0, 0x03, 0x98, -0x04, 0x90, 0x00, 0x20, 0x00, 0x90, 0xE9, 0xE7, -0x00, 0x2D, 0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, -0x68, 0xE0, 0x00, 0x98, 0x40, 0x1C, 0x80, 0xB2, -0x00, 0x90, 0x63, 0xE0, 0x6A, 0x48, 0x2C, 0x23, -0x00, 0x68, 0x22, 0x46, 0xC3, 0x5E, 0x31, 0x46, -0x68, 0xA0, 0x02, 0xF0, 0x7F, 0xFE, 0x01, 0x98, -0x0A, 0x28, 0x0C, 0xD2, 0x64, 0x49, 0x02, 0x98, -0x09, 0x68, 0x09, 0x5E, 0x07, 0x98, 0x81, 0x42, -0x01, 0xDA, 0x06, 0x98, 0x84, 0x43, 0x06, 0x98, -0x40, 0x08, 0x04, 0x43, 0x32, 0xE0, 0x5E, 0x48, -0x02, 0x99, 0x00, 0x68, 0x41, 0x5E, 0x00, 0x29, -0x01, 0xDB, 0x08, 0x46, 0x00, 0xE0, 0x48, 0x42, -0x00, 0xB2, 0x40, 0x30, 0xC2, 0x17, 0x52, 0x0E, -0x10, 0x18, 0x0D, 0x9A, 0xC0, 0x11, 0x91, 0x42, -0x10, 0xDA, 0x21, 0x05, 0x06, 0xD5, 0x20, 0x1A, -0x84, 0xB2, 0x01, 0x20, 0xC0, 0x02, 0x84, 0x42, -0x11, 0xD3, 0x17, 0xE0, 0x84, 0x42, 0x03, 0xDD, -0x20, 0x1A, 0x84, 0xB2, 0x12, 0xE0, 0x2E, 0xE0, -0x00, 0x24, 0x0F, 0xE0, 0x0C, 0x9A, 0x91, 0x42, -0x15, 0xDD, 0x21, 0x05, 0x05, 0xD5, 0x20, 0x18, -0x84, 0xB2, 0x4D, 0x48, 0x04, 0xE0, 0x04, 0x46, -0x04, 0xE0, 0x20, 0x18, 0x84, 0xB2, 0x4B, 0x48, -0x84, 0x42, 0xF8, 0xD8, 0x00, 0x2D, 0x0F, 0xD0, -0x01, 0x20, 0xC0, 0x02, 0x04, 0x43, 0x05, 0x99, -0x02, 0x98, 0x0C, 0x52, 0x0E, 0xE0, 0x00, 0x2D, -0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, 0xF3, 0xE7, -0x00, 0x98, 0x40, 0x1C, 0x80, 0xB2, 0x00, 0x90, -0x03, 0x99, 0x02, 0x98, 0x0C, 0x52, 0x01, 0x98, -0x05, 0x28, 0xE9, 0xD9, 0x76, 0x1C, 0xB6, 0xB2, -0x30, 0x2E, 0x00, 0xD2, 0x77, 0xE7, 0x6D, 0x1C, -0xED, 0xB2, 0x02, 0x2D, 0x00, 0xD2, 0x51, 0xE7, -0x06, 0x98, 0x40, 0x08, 0x06, 0x90, 0x38, 0xA0, -0x02, 0xF0, 0x14, 0xFE, 0x00, 0x98, 0x30, 0x28, -0x03, 0xD1, 0x30, 0x2F, 0x01, 0xD1, 0x40, 0x20, -0x01, 0x90, 0x01, 0x98, 0x40, 0x1C, 0x80, 0xB2, -0x01, 0x90, 0x40, 0x28, 0x00, 0xD8, 0x1A, 0xE7, -0x1C, 0x4C, 0x40, 0x3C, 0x60, 0x6B, 0x03, 0x25, -0x2D, 0x05, 0x01, 0x21, 0xA8, 0x43, 0x09, 0x05, -0x40, 0x18, 0x60, 0x63, 0x00, 0xF0, 0xD6, 0xFC, -0x00, 0xF0, 0xE8, 0xFD, 0x00, 0xF0, 0x74, 0xF8, -0x21, 0x4E, 0x01, 0x21, 0x30, 0x68, 0x00, 0xF0, -0x31, 0xFE, 0x60, 0x6B, 0x01, 0x21, 0xA8, 0x43, -0x49, 0x05, 0x40, 0x18, 0x60, 0x63, 0x00, 0xF0, -0xC5, 0xFC, 0x00, 0xF0, 0xD7, 0xFD, 0x00, 0xF0, -0x63, 0xF8, 0x01, 0x21, 0x30, 0x68, 0x00, 0xF0, -0x21, 0xFE, 0x60, 0x6B, 0x28, 0x43, 0x60, 0x63, -0x00, 0xF0, 0xB8, 0xFC, 0x00, 0xF0, 0xCA, 0xFD, -0x00, 0xF0, 0x56, 0xF8, 0x01, 0x21, 0x30, 0x68, -0x00, 0xF0, 0x14, 0xFE, 0x01, 0x20, 0xFF, 0xE6, -0x00, 0x20, 0x00, 0x50, 0xF4, 0x05, 0x00, 0x20, -0x00, 0x00, 0x01, 0x20, 0xC0, 0x10, 0x00, 0x50, -0x5B, 0x43, 0x46, 0x42, 0x3A, 0x25, 0x78, 0x3A, -0x25, 0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, +0x04, 0x49, 0x29, 0x20, 0xC8, 0x60, 0x04, 0x49, +0x35, 0x20, 0x08, 0x63, 0x04, 0x20, 0x01, 0x07, +0x88, 0x60, 0x70, 0x47, 0x40, 0x00, 0x00, 0x50, +0x80, 0x10, 0x00, 0x50, 0x10, 0xB5, 0x04, 0x46, +0x05, 0x22, 0x01, 0x20, 0x52, 0x04, 0xCB, 0x07, +0x01, 0xD0, 0x00, 0x20, 0x10, 0xBD, 0x07, 0x4B, +0x9C, 0x61, 0x09, 0x02, 0x49, 0x1C, 0x59, 0x61, +0x02, 0xE0, 0x52, 0x1E, 0x00, 0xD1, 0x00, 0x20, +0x59, 0x69, 0xC9, 0x07, 0xF2, 0xD0, 0x00, 0x2A, +0xF7, 0xD1, 0x10, 0xBD, 0x40, 0x14, 0x00, 0x50, +0xF0, 0xB5, 0x91, 0xB0, 0xAD, 0x49, 0x09, 0x91, +0xAC, 0x49, 0xAD, 0x4A, 0x60, 0x31, 0x07, 0x92, +0x0A, 0x91, 0xAC, 0x49, 0xAC, 0x4A, 0x08, 0x91, +0x12, 0x68, 0x0C, 0x92, 0xAB, 0x4A, 0xAC, 0x4E, +0x12, 0x68, 0x0D, 0x92, 0x00, 0x22, 0x17, 0x46, +0x14, 0x46, 0x00, 0x92, 0x07, 0x9A, 0x06, 0x91, +0xA8, 0x4D, 0x05, 0x92, 0x00, 0x21, 0x8A, 0x00, +0x05, 0x9B, 0x49, 0x1C, 0x9E, 0x50, 0x06, 0x9B, +0x89, 0xB2, 0x9D, 0x50, 0x18, 0x29, 0xF6, 0xD3, +0x01, 0x21, 0x89, 0x02, 0x03, 0x91, 0x01, 0x46, +0x04, 0x90, 0x50, 0x31, 0x50, 0x38, 0x09, 0xB2, +0x00, 0xB2, 0x0E, 0x91, 0x0F, 0x90, 0x00, 0xF0, +0xEF, 0xFC, 0x01, 0xF0, 0x0D, 0xF8, 0x00, 0xF0, +0x69, 0xF9, 0x9B, 0x48, 0x00, 0x78, 0xC0, 0x07, +0x20, 0xD1, 0x00, 0x26, 0x00, 0x21, 0x09, 0x9A, +0x88, 0x00, 0x13, 0x58, 0x0C, 0x9A, 0x49, 0x1C, +0x13, 0x50, 0x0A, 0x9A, 0x89, 0xB2, 0x13, 0x58, +0x0D, 0x9A, 0x18, 0x29, 0x13, 0x50, 0x05, 0x9A, +0x13, 0x58, 0x09, 0x9A, 0x13, 0x50, 0x06, 0x9A, +0x13, 0x58, 0x0A, 0x9A, 0x13, 0x50, 0xEA, 0xD3, +0x00, 0xF0, 0x0C, 0xFE, 0x00, 0xF0, 0xEC, 0xFF, +0x00, 0xF0, 0x48, 0xF9, 0x8A, 0x48, 0x00, 0x78, +0xC0, 0x07, 0x02, 0xD0, 0x00, 0x20, 0x11, 0xB0, +0xF0, 0xBD, 0x00, 0x25, 0x3C, 0xE0, 0x00, 0x2D, +0x3A, 0xD0, 0x02, 0x91, 0x01, 0x90, 0x05, 0x2E, +0x7A, 0xD9, 0x30, 0x2F, 0x78, 0xD2, 0x08, 0x98, +0x82, 0x4F, 0x0B, 0x90, 0x78, 0x6B, 0x03, 0x21, +0x09, 0x05, 0x88, 0x43, 0x01, 0x21, 0x49, 0x05, +0x40, 0x18, 0x78, 0x63, 0x00, 0xF0, 0xEA, 0xFD, +0x00, 0xF0, 0xCA, 0xFF, 0x00, 0xF0, 0x26, 0xF9, +0x79, 0x6B, 0x03, 0x20, 0x00, 0x05, 0x81, 0x43, +0x01, 0x20, 0x00, 0x05, 0x08, 0x18, 0x78, 0x63, +0x00, 0x27, 0x00, 0x20, 0x84, 0x46, 0x76, 0x48, +0x01, 0x68, 0x60, 0x46, 0x08, 0x18, 0xC0, 0x7E, +0x41, 0x28, 0x1F, 0xD0, 0x60, 0x46, 0x41, 0x00, +0x0B, 0x98, 0x0A, 0x2E, 0x40, 0x5A, 0x23, 0xD2, +0x70, 0x4A, 0x12, 0x68, 0x53, 0x5E, 0x04, 0x9A, +0x93, 0x42, 0x01, 0xDA, 0x03, 0x9A, 0x90, 0x43, +0x03, 0x9A, 0x52, 0x08, 0x10, 0x43, 0x7D, 0xE0, +0x6B, 0x48, 0x00, 0x68, 0x01, 0x90, 0x62, 0x48, +0x00, 0x68, 0x02, 0x90, 0x00, 0x98, 0x30, 0x28, +0x77, 0xD0, 0x07, 0x98, 0x0B, 0x90, 0x00, 0x20, +0x00, 0x90, 0xD6, 0xE7, 0x00, 0x2D, 0x02, 0xD0, +0x7F, 0x1C, 0xBF, 0xB2, 0x88, 0xE0, 0x00, 0x98, +0x40, 0x1C, 0x80, 0xB2, 0x00, 0x90, 0x83, 0xE0, +0x10, 0x2E, 0x13, 0xD2, 0x5D, 0x4A, 0x01, 0x23, +0x12, 0x68, 0x9B, 0x02, 0x54, 0x5E, 0xE2, 0x1D, +0xFF, 0x32, 0xFA, 0x32, 0x9A, 0x42, 0x09, 0xD9, +0x00, 0x2C, 0x00, 0xDA, 0x64, 0x42, 0x22, 0xB2, +0x40, 0x32, 0xD3, 0x17, 0x5B, 0x0E, 0x9A, 0x18, +0xD4, 0x11, 0x00, 0xE0, 0x01, 0x24, 0x0B, 0x2E, +0x24, 0xD9, 0x01, 0x9A, 0x52, 0x5E, 0x96, 0x46, +0x00, 0x2A, 0x0E, 0xD0, 0x4F, 0x4B, 0x1B, 0x68, +0x5B, 0x5E, 0x5A, 0x43, 0x00, 0x2A, 0x19, 0xDA, +0x02, 0x9A, 0x54, 0x5A, 0x22, 0x1A, 0x01, 0x2A, +0x05, 0xD0, 0x52, 0x1C, 0x03, 0xD0, 0x10, 0xE0, +0x61, 0xE0, 0x00, 0x24, 0x0E, 0xE0, 0x72, 0x46, +0x00, 0x2A, 0x00, 0xDA, 0x52, 0x42, 0x00, 0x2B, +0x00, 0xDA, 0x5B, 0x42, 0x9A, 0x42, 0x04, 0xDA, +0x20, 0x46, 0x01, 0x9A, 0x00, 0x24, 0x54, 0x52, +0x00, 0xE0, 0x01, 0x24, 0x3F, 0x4A, 0x0F, 0x9B, +0x12, 0x68, 0x52, 0x5E, 0x9A, 0x42, 0x0F, 0xDA, +0x02, 0x05, 0x06, 0xD5, 0x00, 0x1B, 0x01, 0x22, +0x80, 0xB2, 0xD2, 0x02, 0x90, 0x42, 0x10, 0xD3, +0x16, 0xE0, 0xA0, 0x42, 0x02, 0xDD, 0x00, 0x1B, +0x80, 0xB2, 0x11, 0xE0, 0x00, 0x20, 0x0F, 0xE0, +0x0E, 0x9B, 0x9A, 0x42, 0x12, 0xDD, 0x02, 0x05, +0x05, 0xD5, 0x00, 0x19, 0x80, 0xB2, 0x33, 0x4A, +0x04, 0xE0, 0x10, 0x46, 0x04, 0xE0, 0x00, 0x19, +0x31, 0x4A, 0x80, 0xB2, 0x90, 0x42, 0xF8, 0xD8, +0x00, 0x2C, 0x04, 0xD0, 0x00, 0x2D, 0x0F, 0xD1, +0x0A, 0xE0, 0x20, 0xE0, 0x00, 0x24, 0x00, 0x2D, +0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, 0x07, 0xE0, +0x00, 0x9A, 0x52, 0x1C, 0x92, 0xB2, 0x00, 0x92, +0x07, 0x9A, 0x05, 0x2E, 0x50, 0x52, 0x04, 0xD8, +0x01, 0x22, 0xD2, 0x02, 0x10, 0x43, 0x08, 0x9A, +0x50, 0x52, 0x00, 0x2C, 0x04, 0xD0, 0x1F, 0x48, +0x01, 0x9A, 0x00, 0x68, 0x40, 0x5A, 0x50, 0x52, +0x60, 0x46, 0x40, 0x1C, 0x80, 0xB2, 0x84, 0x46, +0x30, 0x28, 0x00, 0xD2, 0x43, 0xE7, 0x13, 0x48, +0x1C, 0x4A, 0x01, 0x68, 0x10, 0x68, 0x6D, 0x1C, +0xED, 0xB2, 0x02, 0x2D, 0x00, 0xD2, 0x16, 0xE7, +0x03, 0x98, 0x40, 0x08, 0x03, 0x90, 0x00, 0x98, +0x30, 0x28, 0x02, 0xD1, 0x30, 0x2F, 0x00, 0xD1, +0x40, 0x26, 0x76, 0x1C, 0xB6, 0xB2, 0x40, 0x2E, +0x00, 0xD8, 0xE3, 0xE6, 0x0B, 0x49, 0x48, 0x6B, +0x03, 0x22, 0x12, 0x05, 0x10, 0x43, 0x48, 0x63, +0x01, 0x20, 0xFC, 0xE6, 0x00, 0x20, 0x00, 0x50, +0x28, 0x04, 0x00, 0x20, 0x00, 0x00, 0x01, 0x20, +0x10, 0x03, 0x00, 0x20, 0x14, 0x03, 0x00, 0x20, 0x00, 0x04, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x0C, -0xCE, 0x00, 0x00, 0x20, 0x5B, 0x25, 0x64, 0x3A, -0x25, 0x64, 0x3A, 0x25, 0x64, 0x5D, 0x20, 0x0D, -0x0A, 0x00, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20, -0xF8, 0x02, 0x00, 0x20, 0x25, 0x64, 0x2C, 0x30, -0x78, 0x25, 0x78, 0x2C, 0x25, 0x64, 0x2C, 0x00, -0xFF, 0x0F, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, -0x0D, 0x0A, 0x00, 0x00, 0x10, 0xB5, 0x0B, 0x49, -0x30, 0x24, 0x00, 0x28, 0x0A, 0x4A, 0x0B, 0x4B, -0x08, 0x68, 0x06, 0xD0, 0x20, 0x43, 0x08, 0x60, -0x09, 0x48, 0x10, 0x60, 0x08, 0x48, 0x60, 0x30, -0x05, 0xE0, 0xA0, 0x43, 0x08, 0x60, 0x07, 0x48, -0x10, 0x60, 0x06, 0x48, 0x60, 0x30, 0x18, 0x60, -0x10, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, -0xF8, 0x02, 0x00, 0x20, 0xFC, 0x02, 0x00, 0x20, -0x00, 0x00, 0x04, 0x20, 0x00, 0x10, 0x04, 0x20, -0xF0, 0xB5, 0x11, 0x48, 0x01, 0x68, 0x11, 0x4D, -0x89, 0x06, 0x30, 0x22, 0x60, 0x35, 0x00, 0x29, -0x0E, 0x4B, 0x0F, 0x4C, 0x0F, 0x4E, 0x10, 0x4F, -0x01, 0x68, 0x0A, 0xDA, 0x91, 0x43, 0x01, 0x60, -0x23, 0x60, 0x0E, 0x48, 0x35, 0x60, 0x07, 0x60, -0x0B, 0x48, 0x0D, 0x49, 0x60, 0x30, 0x08, 0x60, -0xF0, 0xBD, 0x11, 0x43, 0x01, 0x60, 0x08, 0x48, -0x27, 0x60, 0x60, 0x30, 0x30, 0x60, 0x07, 0x48, -0x03, 0x60, 0x07, 0x48, 0x05, 0x60, 0xF0, 0xBD, -0x00, 0x10, 0x00, 0x50, 0x00, 0x00, 0x04, 0x20, -0x64, 0x02, 0x00, 0x20, 0x68, 0x02, 0x00, 0x20, -0x00, 0x10, 0x04, 0x20, 0xF8, 0x02, 0x00, 0x20, -0xFC, 0x02, 0x00, 0x20, 0x00, 0xB5, 0x09, 0x48, -0x00, 0x78, 0x00, 0x28, 0x08, 0xD0, 0x01, 0x28, -0x09, 0xD0, 0x02, 0x28, 0x03, 0xD1, 0x06, 0x49, -0x04, 0x20, 0x00, 0xF0, 0x65, 0xF8, 0x00, 0xBD, -0x04, 0x49, 0x01, 0x20, 0xF9, 0xE7, 0x04, 0x49, -0x02, 0x20, 0xF6, 0xE7, 0x71, 0x04, 0x00, 0x20, -0xFC, 0x03, 0x00, 0x20, 0x9C, 0x03, 0x00, 0x20, -0xEC, 0x06, 0x00, 0x20, 0xF8, 0xB5, 0x07, 0x46, -0x00, 0xF0, 0xAA, 0xFA, 0x21, 0x4D, 0x60, 0x21, -0x28, 0x46, 0xFE, 0xF7, 0xAE, 0xF9, 0x00, 0x24, -0x00, 0xF0, 0x18, 0xFD, 0xFF, 0xF7, 0xA4, 0xFF, -0xFE, 0xF7, 0xF4, 0xFA, 0x00, 0x2C, 0x0C, 0xD0, -0x1B, 0x49, 0x00, 0x20, 0x0E, 0x68, 0x42, 0x00, -0x51, 0x19, 0x0B, 0x88, 0xB2, 0x5A, 0x40, 0x1C, -0x9A, 0x18, 0xC0, 0xB2, 0x0A, 0x80, 0x30, 0x28, -0xF5, 0xD3, 0x64, 0x1C, 0xE4, 0xB2, 0x05, 0x2C, -0xE6, 0xD3, 0x00, 0x20, 0x41, 0x00, 0x49, 0x19, -0x00, 0x22, 0x8A, 0x5E, 0x40, 0x1C, 0x92, 0x10, -0xC0, 0xB2, 0x0A, 0x80, 0x30, 0x28, 0xF5, 0xD3, -0x39, 0x46, 0x0E, 0xA0, 0x02, 0xF0, 0x02, 0xFD, -0x01, 0x21, 0x28, 0x46, 0x00, 0xF0, 0x3E, 0xFD, -0x01, 0x20, 0xFE, 0xF7, 0x3D, 0xFB, 0x0C, 0x4C, -0x20, 0x78, 0x38, 0x42, 0x08, 0xD1, 0x0B, 0x48, -0x60, 0x22, 0x29, 0x46, 0x00, 0x68, 0xFE, 0xF7, -0x5B, 0xF9, 0x20, 0x78, 0x38, 0x43, 0x20, 0x70, -0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, 0x01, 0x20, -0xF8, 0x02, 0x00, 0x20, 0x42, 0x43, 0x5F, 0x4F, -0x4B, 0x5F, 0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, -0x98, 0x01, 0x00, 0x20, 0x6C, 0x02, 0x00, 0x20, -0xF0, 0xB5, 0x21, 0x48, 0x00, 0x22, 0x00, 0x68, -0x20, 0x4C, 0xA0, 0x42, 0x28, 0xD8, 0x20, 0x48, -0x00, 0x78, 0x04, 0x28, 0x27, 0xD1, 0x1F, 0x4D, -0x1F, 0x4C, 0x2D, 0x68, 0x00, 0x20, 0x2E, 0x18, -0xF6, 0x7E, 0x41, 0x2E, 0x08, 0xD0, 0x43, 0x00, -0x1F, 0x19, 0x00, 0x26, 0xBE, 0x5F, 0xCB, 0x5E, -0xF3, 0x1A, 0x00, 0xD5, 0x5B, 0x42, 0x1B, 0xB2, -0x9A, 0x42, 0x00, 0xDA, 0x1A, 0x46, 0x40, 0x1C, -0xC0, 0xB2, 0x30, 0x28, 0xEB, 0xD3, 0x60, 0x20, -0x40, 0x5D, 0x90, 0x35, 0x40, 0x1C, 0x50, 0x43, -0x6A, 0x7B, 0x2B, 0x7B, 0x00, 0x11, 0x12, 0x02, -0x00, 0xB2, 0x1A, 0x43, 0x82, 0x42, 0x03, 0xDA, -0x0E, 0x49, 0x00, 0x20, 0x08, 0x70, 0xF0, 0xBD, -0x00, 0x20, 0x42, 0x00, 0x8B, 0x5E, 0x00, 0x26, -0xDD, 0x00, 0xEB, 0x1A, 0x15, 0x19, 0xAE, 0x5F, -0x40, 0x1C, 0x9B, 0x19, 0xDB, 0x10, 0xC0, 0xB2, -0x8B, 0x52, 0x30, 0x28, 0xF1, 0xD3, 0xF0, 0xBD, -0x08, 0x03, 0x00, 0x20, 0x40, 0x77, 0x1B, 0x00, -0x87, 0x02, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, -0x00, 0x00, 0x01, 0x20, 0x98, 0x01, 0x00, 0x20, -0x70, 0xB5, 0x21, 0x48, 0x21, 0x49, 0x00, 0x78, +0x98, 0x01, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, +0xAC, 0x04, 0x00, 0x20, 0x00, 0x03, 0x00, 0x20, +0x08, 0x03, 0x00, 0x20, 0xFF, 0x0F, 0x00, 0x00, +0xFF, 0x07, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x20, +0x10, 0xB5, 0x0B, 0x49, 0x30, 0x24, 0x00, 0x28, +0x0A, 0x4A, 0x0B, 0x4B, 0x08, 0x68, 0x06, 0xD0, +0x20, 0x43, 0x08, 0x60, 0x09, 0x48, 0x10, 0x60, +0x08, 0x48, 0x60, 0x30, 0x05, 0xE0, 0xA0, 0x43, +0x08, 0x60, 0x07, 0x48, 0x10, 0x60, 0x06, 0x48, +0x60, 0x30, 0x18, 0x60, 0x10, 0xBD, 0x00, 0x00, +0x00, 0x10, 0x00, 0x50, 0x00, 0x03, 0x00, 0x20, +0x04, 0x03, 0x00, 0x20, 0x00, 0x00, 0x04, 0x20, +0x00, 0x10, 0x04, 0x20, 0xF0, 0xB5, 0x11, 0x48, +0x01, 0x68, 0x11, 0x4D, 0x89, 0x06, 0x30, 0x22, +0x60, 0x35, 0x00, 0x29, 0x0E, 0x4B, 0x0F, 0x4C, +0x0F, 0x4E, 0x10, 0x4F, 0x01, 0x68, 0x0A, 0xDA, +0x91, 0x43, 0x01, 0x60, 0x23, 0x60, 0x0E, 0x48, +0x35, 0x60, 0x07, 0x60, 0x0B, 0x48, 0x0D, 0x49, +0x60, 0x30, 0x08, 0x60, 0xF0, 0xBD, 0x11, 0x43, +0x01, 0x60, 0x08, 0x48, 0x27, 0x60, 0x60, 0x30, +0x30, 0x60, 0x07, 0x48, 0x03, 0x60, 0x07, 0x48, +0x05, 0x60, 0xF0, 0xBD, 0x00, 0x10, 0x00, 0x50, +0x00, 0x00, 0x04, 0x20, 0x74, 0x02, 0x00, 0x20, +0x78, 0x02, 0x00, 0x20, 0x00, 0x10, 0x04, 0x20, +0x00, 0x03, 0x00, 0x20, 0x04, 0x03, 0x00, 0x20, +0x00, 0xB5, 0x09, 0x48, 0x00, 0x78, 0x00, 0x28, +0x08, 0xD0, 0x01, 0x28, 0x09, 0xD0, 0x02, 0x28, +0x03, 0xD1, 0x06, 0x49, 0x04, 0x20, 0x00, 0xF0, +0x55, 0xF8, 0x00, 0xBD, 0x04, 0x49, 0x01, 0x20, +0xF9, 0xE7, 0x04, 0x49, 0x02, 0x20, 0xF6, 0xE7, +0x4D, 0x07, 0x00, 0x20, 0xAC, 0x03, 0x00, 0x20, +0x4C, 0x03, 0x00, 0x20, 0xEC, 0x06, 0x00, 0x20, +0xF8, 0xB5, 0x07, 0x46, 0x00, 0xF0, 0x30, 0xFB, +0x1C, 0x4D, 0x60, 0x21, 0x28, 0x46, 0xFD, 0xF7, +0xE8, 0xFD, 0x00, 0x24, 0x00, 0xF0, 0x48, 0xFE, +0xFF, 0xF7, 0xA4, 0xFF, 0xFD, 0xF7, 0xCE, 0xFE, +0x00, 0x2C, 0x0C, 0xD0, 0x16, 0x48, 0x00, 0x22, +0x06, 0x68, 0x50, 0x00, 0x43, 0x19, 0x19, 0x88, +0x30, 0x5A, 0x52, 0x1C, 0x08, 0x18, 0xD2, 0xB2, +0x18, 0x80, 0x30, 0x2A, 0xF5, 0xD3, 0x64, 0x1C, +0xE4, 0xB2, 0x05, 0x2C, 0xE6, 0xD3, 0x00, 0x20, +0x29, 0x46, 0x42, 0x00, 0x52, 0x18, 0x00, 0x23, +0xD3, 0x5E, 0x40, 0x1C, 0x9B, 0x10, 0xC0, 0xB2, +0x13, 0x80, 0x30, 0x28, 0xF5, 0xD3, 0x09, 0x4C, +0x20, 0x78, 0x38, 0x42, 0x07, 0xD1, 0x08, 0x48, +0x60, 0x22, 0x00, 0x68, 0xFD, 0xF7, 0xA0, 0xFD, +0x20, 0x78, 0x38, 0x43, 0x20, 0x70, 0x01, 0x20, +0xF8, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, +0x00, 0x03, 0x00, 0x20, 0x9A, 0x01, 0x00, 0x20, +0x7C, 0x02, 0x00, 0x20, 0xF0, 0xB5, 0x22, 0x48, +0x00, 0x22, 0x00, 0x68, 0x14, 0x46, 0x21, 0x4B, +0x98, 0x42, 0x28, 0xD8, 0x20, 0x48, 0x00, 0x78, +0x04, 0x28, 0x27, 0xD1, 0x1F, 0x4B, 0x20, 0x4D, +0x1E, 0x68, 0x00, 0x20, 0x33, 0x18, 0xDB, 0x7E, +0x41, 0x2B, 0x08, 0xD0, 0x42, 0x00, 0x57, 0x19, +0x00, 0x23, 0xFB, 0x5E, 0x8A, 0x5E, 0x9B, 0x1A, +0x00, 0xD5, 0x5B, 0x42, 0x1A, 0xB2, 0x94, 0x42, +0x00, 0xDA, 0x14, 0x46, 0x40, 0x1C, 0xC0, 0xB2, +0x30, 0x28, 0xEB, 0xD3, 0x60, 0x20, 0x80, 0x5D, +0x90, 0x36, 0x40, 0x1C, 0x72, 0x7B, 0x60, 0x43, +0x33, 0x7B, 0x00, 0x11, 0x12, 0x02, 0x00, 0xB2, +0x1A, 0x43, 0x82, 0x42, 0x03, 0xDA, 0x0F, 0x49, +0x00, 0x20, 0x08, 0x70, 0xF0, 0xBD, 0x00, 0x20, +0x42, 0x00, 0x8B, 0x5E, 0x00, 0x26, 0xDC, 0x00, +0xE3, 0x1A, 0x54, 0x19, 0xA6, 0x5F, 0x40, 0x1C, +0x9B, 0x19, 0xDB, 0x10, 0xC0, 0xB2, 0x8B, 0x52, +0x30, 0x28, 0xF1, 0xD3, 0xF0, 0xBD, 0x00, 0x00, +0x18, 0x03, 0x00, 0x20, 0x40, 0x77, 0x1B, 0x00, +0x8F, 0x02, 0x00, 0x20, 0xAC, 0x04, 0x00, 0x20, +0x00, 0x00, 0x01, 0x20, 0x9A, 0x01, 0x00, 0x20, +0xF8, 0xB5, 0x27, 0x48, 0x27, 0x49, 0x00, 0x78, 0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x08, 0xD0, -0x02, 0x28, 0x16, 0xD0, 0x05, 0x28, 0x2F, 0xD1, -0x34, 0xE0, 0x01, 0x25, 0x2C, 0x46, 0x1C, 0x48, -0x02, 0xE0, 0x02, 0x25, 0x1B, 0x48, 0x2C, 0x46, -0x08, 0x60, 0x00, 0x22, 0x1A, 0x49, 0x28, 0x46, -0xFF, 0xF7, 0xD4, 0xFA, 0x00, 0x28, 0x18, 0xD0, -0x18, 0x48, 0x00, 0x78, 0x00, 0x28, 0x04, 0xD0, -0x13, 0xE0, 0x04, 0x25, 0x2C, 0x46, 0x16, 0x48, -0xEE, 0xE7, 0x16, 0x48, 0x41, 0x6B, 0x03, 0x22, -0x12, 0x05, 0x11, 0x43, 0x41, 0x63, 0x14, 0x48, -0x00, 0xF0, 0x30, 0xFA, 0x00, 0x28, 0x0B, 0xD1, -0x20, 0x46, 0xFF, 0xF7, 0x27, 0xFF, 0x00, 0x28, -0x06, 0xD1, 0x01, 0x22, 0x21, 0x46, 0x28, 0x46, -0x00, 0xF0, 0x1E, 0xF8, 0x00, 0x28, 0x04, 0xD0, -0x0C, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, -0x00, 0x20, 0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD, -0x85, 0x02, 0x00, 0x20, 0x6C, 0x02, 0x00, 0x20, -0x9C, 0x03, 0x00, 0x20, 0xEC, 0x06, 0x00, 0x20, -0x00, 0x20, 0x00, 0x50, 0xE7, 0x02, 0x00, 0x20, -0xFC, 0x03, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, -0x00, 0x00, 0x01, 0x20, 0xCE, 0x00, 0x00, 0x20, -0xF7, 0xB5, 0x07, 0x46, 0xFE, 0xF7, 0xD0, 0xFB, +0x02, 0x28, 0x20, 0xD0, 0x05, 0x28, 0x36, 0xD1, +0x3F, 0xE0, 0x01, 0x25, 0x2C, 0x46, 0x22, 0x48, +0x02, 0xE0, 0x02, 0x25, 0x21, 0x48, 0x2C, 0x46, +0x08, 0x60, 0x00, 0x22, 0x20, 0x49, 0x28, 0x46, +0xFE, 0xF7, 0x88, 0xFF, 0x03, 0x27, 0x1F, 0x4E, +0x3F, 0x05, 0x00, 0x28, 0x03, 0xD0, 0x1E, 0x48, +0x00, 0x78, 0x00, 0x28, 0x0B, 0xD0, 0x01, 0x22, +0x21, 0x46, 0x28, 0x46, 0x00, 0xF0, 0x48, 0xF8, +0x00, 0x28, 0x1E, 0xD0, 0x17, 0xE0, 0x04, 0x25, +0x2C, 0x46, 0x18, 0x48, 0xE4, 0xE7, 0x70, 0x6B, +0x38, 0x43, 0x70, 0x63, 0x16, 0x48, 0x00, 0xF0, +0xC1, 0xFA, 0x00, 0x28, 0x0B, 0xD1, 0x20, 0x46, +0xFF, 0xF7, 0x2E, 0xFF, 0x00, 0x28, 0x06, 0xD1, +0x01, 0x22, 0x21, 0x46, 0x28, 0x46, 0x00, 0xF0, +0x2F, 0xF8, 0x00, 0x28, 0x04, 0xD0, 0x0F, 0x48, +0x00, 0x78, 0xC0, 0x07, 0x05, 0xD0, 0x00, 0x20, +0xF8, 0xBD, 0x70, 0x6B, 0x38, 0x43, 0x70, 0x63, +0xF9, 0xE7, 0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, +0x87, 0x02, 0x00, 0x20, 0x7C, 0x02, 0x00, 0x20, +0x4C, 0x03, 0x00, 0x20, 0xEC, 0x06, 0x00, 0x20, +0x00, 0x20, 0x00, 0x50, 0x80, 0x10, 0x00, 0x50, +0xE7, 0x02, 0x00, 0x20, 0xAC, 0x03, 0x00, 0x20, +0x00, 0x00, 0x01, 0x20, 0x98, 0x01, 0x00, 0x20, +0x03, 0x48, 0x02, 0x49, 0x41, 0x60, 0x03, 0x49, +0x81, 0x60, 0x70, 0x47, 0x1F, 0x1F, 0x5F, 0x1F, +0x00, 0x10, 0x00, 0x50, 0x1F, 0x1F, 0x1F, 0x1F, +0xF7, 0xB5, 0x07, 0x46, 0xFD, 0xF7, 0xFE, 0xFE, 0x14, 0x48, 0x00, 0x25, 0x05, 0x70, 0x78, 0x07, 0x1D, 0xD0, 0x01, 0x21, 0x02, 0x20, 0x00, 0xF0, -0x33, 0xFB, 0x11, 0x4C, 0x60, 0x6B, 0x03, 0x26, +0x8B, 0xFB, 0x11, 0x4C, 0x60, 0x6B, 0x03, 0x26, 0x36, 0x05, 0x01, 0x21, 0xB0, 0x43, 0x09, 0x05, 0x40, 0x18, 0x60, 0x63, 0x00, 0x20, 0xFF, 0xF7, -0xFB, 0xFC, 0x00, 0x28, 0x0D, 0xD0, 0x01, 0x98, -0xFF, 0xF7, 0xE4, 0xFE, 0x01, 0x22, 0x09, 0x49, -0x38, 0x46, 0xFF, 0xF7, 0x73, 0xFA, 0x08, 0x48, +0xF3, 0xFC, 0x00, 0x28, 0x0D, 0xD0, 0x01, 0x98, +0xFF, 0xF7, 0xDA, 0xFE, 0x01, 0x22, 0x09, 0x49, +0x38, 0x46, 0xFE, 0xF7, 0x0F, 0xFF, 0x08, 0x48, 0x05, 0x70, 0x08, 0x48, 0x05, 0x70, 0x01, 0x20, 0xFE, 0xBD, 0x60, 0x6B, 0x30, 0x43, 0x60, 0x63, -0x00, 0x20, 0xFE, 0xBD, 0x82, 0x02, 0x00, 0x20, +0x00, 0x20, 0xFE, 0xBD, 0x83, 0x02, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, 0x00, 0x20, 0x00, 0x50, 0xE7, 0x02, 0x00, 0x20, 0xE8, 0x02, 0x00, 0x20, -0xF0, 0xB5, 0xB4, 0x48, 0x01, 0x22, 0x23, 0x23, -0x13, 0x24, 0x92, 0x02, 0x5B, 0x01, 0xA4, 0x01, -0x05, 0x46, 0x1C, 0xC5, 0x29, 0x21, 0x49, 0x01, -0xC1, 0x60, 0xAF, 0x48, 0x07, 0x68, 0x38, 0x46, -0x40, 0x30, 0x45, 0x7C, 0x06, 0x7C, 0x2D, 0x04, -0x36, 0x02, 0x35, 0x43, 0x0C, 0x35, 0x15, 0x60, -0x45, 0x7C, 0x06, 0x7C, 0x2D, 0x04, 0x36, 0x02, -0x35, 0x43, 0x0C, 0x35, 0x55, 0x60, 0x01, 0x25, -0x95, 0x60, 0xD5, 0x60, 0x05, 0x7D, 0xBC, 0x46, -0x6E, 0x1E, 0x6D, 0x08, 0x6D, 0x1E, 0xF6, 0x05, -0xED, 0x05, 0xF6, 0x09, 0xED, 0x0D, 0x2E, 0x43, -0x16, 0x61, 0xA0, 0x4D, 0x55, 0x61, 0x03, 0x25, -0x95, 0x61, 0x03, 0x26, 0x36, 0x02, 0x00, 0x25, -0x16, 0x62, 0xD5, 0x61, 0x06, 0x7E, 0xD6, 0x62, -0x06, 0x7E, 0x87, 0x7E, 0xF6, 0x19, 0x16, 0x63, -0x87, 0x7E, 0x06, 0x7E, 0x7F, 0x00, 0xF6, 0x19, -0x56, 0x63, 0x95, 0x63, 0x06, 0x7E, 0xBC, 0x36, -0xD6, 0x63, 0x06, 0x7E, 0x87, 0x7E, 0xF6, 0x19, -0xBC, 0x36, 0x16, 0x64, 0x07, 0x7E, 0x86, 0x7E, -0x95, 0x64, 0x76, 0x00, 0xBC, 0x36, 0xBE, 0x19, -0x56, 0x64, 0x67, 0x46, 0x55, 0x62, 0x8E, 0x4E, -0x50, 0x37, 0x96, 0x62, 0xBC, 0x46, 0xFE, 0x7B, -0xBF, 0x7B, 0x36, 0x02, 0x3E, 0x43, 0xF6, 0x1C, -0xB7, 0x05, 0x8A, 0x4E, 0xBF, 0x0D, 0xBE, 0x19, -0xD6, 0x64, 0x89, 0x4E, 0x16, 0x65, 0x46, 0x7C, -0x0C, 0x3E, 0xF7, 0xB2, 0x05, 0x26, 0x76, 0x02, -0xBE, 0x19, 0x56, 0x65, 0x06, 0x7D, 0x36, 0x02, -0x21, 0x36, 0x96, 0x65, 0xD5, 0x65, 0x42, 0x7C, -0x06, 0x7C, 0x12, 0x04, 0x36, 0x02, 0x32, 0x43, -0x0C, 0x32, 0x1A, 0x60, 0x42, 0x7C, 0x06, 0x7C, -0x12, 0x04, 0x36, 0x02, 0x32, 0x43, 0x0C, 0x32, -0x5A, 0x60, 0x01, 0x22, 0x9A, 0x60, 0xDA, 0x60, -0x42, 0x7D, 0x56, 0x1E, 0x52, 0x08, 0x52, 0x1E, -0xF6, 0x05, 0xD2, 0x05, 0xF7, 0x09, 0xD2, 0x0D, -0x17, 0x43, 0x72, 0x4A, 0x1F, 0x61, 0x5A, 0x61, -0x03, 0x22, 0xDD, 0x61, 0x9A, 0x61, 0x12, 0x02, -0x1A, 0x62, 0x02, 0x7E, 0xDA, 0x62, 0x02, 0x7E, -0xC6, 0x7E, 0x92, 0x19, 0x1A, 0x63, 0xC6, 0x7E, -0x02, 0x7E, 0x76, 0x00, 0x92, 0x19, 0x9D, 0x63, -0x5A, 0x63, 0x02, 0x7E, 0x67, 0x46, 0xBC, 0x32, -0xDA, 0x63, 0x02, 0x7E, 0xC6, 0x7E, 0x92, 0x19, -0xBC, 0x32, 0x1A, 0x64, 0xC6, 0x7E, 0x02, 0x7E, -0x76, 0x00, 0xBC, 0x36, 0x9D, 0x64, 0x92, 0x19, -0x5D, 0x62, 0x5A, 0x64, 0x60, 0x4A, 0x9A, 0x62, -0xFA, 0x7B, 0xBE, 0x7B, 0x12, 0x02, 0x32, 0x43, -0xD2, 0x1C, 0x96, 0x05, 0x5D, 0x4A, 0xB6, 0x0D, -0xB2, 0x18, 0xDA, 0x64, 0x5C, 0x4A, 0x1A, 0x65, -0x42, 0x7C, 0x0C, 0x3A, 0xD6, 0xB2, 0x05, 0x22, -0x52, 0x02, 0xB2, 0x18, 0x5A, 0x65, 0x42, 0x7D, -0xDD, 0x65, 0x12, 0x02, 0x21, 0x32, 0x9A, 0x65, -0xC2, 0x7C, 0x83, 0x7C, 0x12, 0x04, 0x1B, 0x02, -0x1A, 0x43, 0x0C, 0x32, 0x23, 0x46, 0x1A, 0x60, -0xC2, 0x7C, 0x83, 0x7C, 0x12, 0x04, 0x1B, 0x02, -0x1A, 0x43, 0x0C, 0x32, 0x23, 0x46, 0x5A, 0x60, -0x82, 0x7D, 0x52, 0x1E, 0xD3, 0x05, 0xDB, 0x0D, -0x22, 0x46, 0x93, 0x60, 0x82, 0x7D, 0x52, 0x08, -0x52, 0x1E, 0xD2, 0x05, 0x23, 0x46, 0xD2, 0x0D, -0x1D, 0x61, 0xDA, 0x60, 0x47, 0x4A, 0x5A, 0x61, -0x26, 0x46, 0x01, 0x23, 0xF5, 0x61, 0xB3, 0x61, -0x13, 0x12, 0x33, 0x62, 0x43, 0x7E, 0xF3, 0x62, -0x43, 0x7E, 0x06, 0x7F, 0x9B, 0x19, 0x26, 0x46, -0x33, 0x63, 0x06, 0x7F, 0x43, 0x7E, 0x76, 0x00, -0x9B, 0x19, 0x26, 0x46, 0xB5, 0x63, 0xF5, 0x63, -0x35, 0x64, 0x73, 0x63, 0x65, 0x64, 0x38, 0x4B, -0xA5, 0x64, 0x3F, 0x33, 0x65, 0x62, 0xA3, 0x62, -0x36, 0x4B, 0xDB, 0x1C, 0xE3, 0x64, 0x36, 0x4B, -0x23, 0x65, 0xC6, 0x7C, 0x0C, 0x3E, 0xF7, 0xB2, -0x05, 0x26, 0x76, 0x02, 0xBF, 0x19, 0x67, 0x65, -0x87, 0x7D, 0x7F, 0x08, 0x3F, 0x02, 0x21, 0x37, -0xA7, 0x65, 0xE5, 0x65, 0xC4, 0x7C, 0x80, 0x7C, -0x24, 0x04, 0x00, 0x02, 0x04, 0x43, 0x0C, 0x34, -0x27, 0x48, 0x0C, 0x60, 0x00, 0x68, 0x40, 0x30, -0xC4, 0x7C, 0x87, 0x7C, 0x24, 0x04, 0x3F, 0x02, -0x3C, 0x43, 0x0C, 0x34, 0x4C, 0x60, 0xC4, 0x7D, -0x64, 0x1E, 0xE4, 0x05, 0xE4, 0x0D, 0x8C, 0x60, -0xC4, 0x7D, 0x0D, 0x61, 0x64, 0x08, 0x64, 0x1E, -0xE4, 0x05, 0xE4, 0x0D, 0xCC, 0x60, 0x4A, 0x61, -0x01, 0x22, 0xCD, 0x61, 0x8A, 0x61, 0x12, 0x02, -0x0A, 0x62, 0x42, 0x7E, 0xCA, 0x62, 0x42, 0x7E, -0x44, 0x7F, 0x12, 0x19, 0x0A, 0x63, 0x44, 0x7F, -0x42, 0x7E, 0x64, 0x00, 0x12, 0x19, 0x8D, 0x63, -0x4A, 0x63, 0x42, 0x7E, 0xBC, 0x32, 0xCA, 0x63, -0x42, 0x7E, 0x44, 0x7F, 0x12, 0x19, 0xBC, 0x32, -0x0A, 0x64, 0x42, 0x7F, 0x44, 0x7E, 0x52, 0x00, -0xBC, 0x32, 0xA2, 0x18, 0x8D, 0x64, 0x4A, 0x64, -0x01, 0x22, 0x92, 0x04, 0x4A, 0x62, 0x0C, 0x4A, -0x3F, 0x32, 0x8A, 0x62, 0x0B, 0x4A, 0x0B, 0x65, -0xD2, 0x1C, 0xCA, 0x64, 0xC2, 0x7C, 0x0C, 0x3A, -0xD2, 0xB2, 0x92, 0x19, 0x4A, 0x65, 0xC0, 0x7D, -0xCD, 0x65, 0x40, 0x08, 0x00, 0x02, 0x21, 0x30, -0x88, 0x65, 0xF0, 0xBD, 0x70, 0x02, 0x00, 0x20, -0xB8, 0x02, 0x00, 0x20, 0x03, 0x00, 0x03, 0x00, -0x00, 0x3F, 0x3F, 0x3F, 0x00, 0x30, 0xBC, 0x00, -0x00, 0x26, 0x31, 0x00, 0x01, 0x00, 0x01, 0x00, -0x10, 0xB5, 0x00, 0xF0, 0x73, 0xFA, 0x00, 0xF0, -0x75, 0xF9, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, +0xF0, 0xB5, 0x01, 0x24, 0xEE, 0x4B, 0xA4, 0x02, +0x23, 0x20, 0x13, 0x21, 0x29, 0x22, 0x1C, 0x60, +0x40, 0x01, 0x89, 0x01, 0x52, 0x01, 0x1B, 0x1D, +0x07, 0xC3, 0xEA, 0x4B, 0x1D, 0x68, 0x2B, 0x46, +0x40, 0x33, 0xAC, 0x46, 0x5D, 0x7C, 0x1E, 0x7C, +0x2D, 0x04, 0x36, 0x02, 0x35, 0x43, 0x0C, 0x35, +0x26, 0x46, 0x35, 0x60, 0x5D, 0x7C, 0x1E, 0x7C, +0x2D, 0x04, 0x36, 0x02, 0x35, 0x43, 0x0C, 0x35, +0x26, 0x46, 0x75, 0x60, 0x01, 0x25, 0xB5, 0x60, +0xF5, 0x60, 0x1D, 0x7D, 0x6D, 0x1E, 0xED, 0x05, +0xED, 0x09, 0x6D, 0x1C, 0x35, 0x61, 0xDC, 0x4D, +0x75, 0x61, 0x03, 0x25, 0xB5, 0x61, 0x00, 0x25, +0xF5, 0x61, 0x03, 0x25, 0x2D, 0x02, 0x35, 0x62, +0x9E, 0x7E, 0x1D, 0x7E, 0x77, 0x00, 0xF6, 0x19, +0xAE, 0x19, 0x36, 0x04, 0x2E, 0x43, 0x25, 0x46, +0xEE, 0x62, 0x9D, 0x7E, 0x1E, 0x7E, 0x6F, 0x00, +0xEF, 0x19, 0xBF, 0x1C, 0xF7, 0x19, 0x7F, 0x05, +0x7F, 0x09, 0x75, 0x19, 0x2F, 0x43, 0x25, 0x46, +0x2F, 0x63, 0x1E, 0x7E, 0x9D, 0x7E, 0x6F, 0x00, +0xEF, 0x19, 0x3F, 0x1D, 0xF7, 0x19, 0x6D, 0x00, +0x7F, 0x05, 0x75, 0x19, 0x7F, 0x09, 0x2F, 0x43, +0x25, 0x46, 0x6F, 0x63, 0x00, 0x25, 0x26, 0x46, +0xB5, 0x63, 0x9E, 0x7E, 0x1D, 0x7E, 0x77, 0x00, +0xF6, 0x19, 0xBC, 0x36, 0xAE, 0x19, 0x36, 0x04, +0xBC, 0x35, 0x2E, 0x43, 0x25, 0x46, 0xEE, 0x63, +0x9D, 0x7E, 0x1E, 0x7E, 0x6F, 0x00, 0xEF, 0x19, +0xBE, 0x37, 0xF7, 0x19, 0x7F, 0x05, 0x75, 0x19, +0x7F, 0x09, 0xBC, 0x35, 0x2F, 0x43, 0x25, 0x46, +0x2F, 0x64, 0x9D, 0x7E, 0x1E, 0x7E, 0x6F, 0x00, +0xEF, 0x19, 0xC0, 0x37, 0xF7, 0x19, 0x6D, 0x00, +0xBC, 0x35, 0x7F, 0x05, 0x75, 0x19, 0x7F, 0x09, +0x2F, 0x43, 0x25, 0x46, 0x6F, 0x64, 0x00, 0x25, +0x26, 0x46, 0xB5, 0x64, 0x75, 0x62, 0xB1, 0x4D, +0xB5, 0x62, 0x65, 0x46, 0x50, 0x35, 0xEE, 0x7B, +0xAF, 0x7B, 0x35, 0x02, 0x3D, 0x43, 0xED, 0x1C, +0xAE, 0x05, 0xAD, 0x4D, 0xB6, 0x0D, 0x75, 0x19, +0x26, 0x46, 0xF5, 0x64, 0xAB, 0x4D, 0x2E, 0x68, +0xAB, 0x4D, 0xAE, 0x42, 0x01, 0xD1, 0xAB, 0x4D, +0x00, 0xE0, 0xAB, 0x4D, 0x25, 0x65, 0x5D, 0x7C, +0xA2, 0x4E, 0x0C, 0x3D, 0xEF, 0xB2, 0x05, 0x25, +0x6D, 0x02, 0x7D, 0x19, 0x65, 0x65, 0x1D, 0x7D, +0x00, 0x27, 0x2D, 0x02, 0x21, 0x35, 0xA5, 0x65, +0x02, 0x25, 0xE5, 0x65, 0x5C, 0x7C, 0x1B, 0x7C, +0x24, 0x04, 0x1B, 0x02, 0x1C, 0x43, 0x0C, 0x34, +0x04, 0x60, 0x34, 0x68, 0x23, 0x46, 0x40, 0x33, +0xA4, 0x46, 0x5C, 0x7C, 0x1D, 0x7C, 0x24, 0x04, +0x2D, 0x02, 0x2C, 0x43, 0x0C, 0x34, 0x44, 0x60, +0x01, 0x24, 0x84, 0x60, 0xC4, 0x60, 0x5C, 0x7D, +0x64, 0x1E, 0xE4, 0x05, 0xE4, 0x09, 0x64, 0x1C, +0x04, 0x61, 0x8F, 0x4C, 0x44, 0x61, 0x03, 0x24, +0xC7, 0x61, 0x84, 0x61, 0x24, 0x02, 0x04, 0x62, +0xDD, 0x7E, 0x1C, 0x7E, 0x6E, 0x00, 0xAD, 0x19, +0x65, 0x19, 0x2D, 0x04, 0x25, 0x43, 0xC5, 0x62, +0xDC, 0x7E, 0x1D, 0x7E, 0x66, 0x00, 0xA6, 0x19, +0xB6, 0x1C, 0xAE, 0x19, 0x76, 0x05, 0x76, 0x09, +0x2C, 0x19, 0x26, 0x43, 0x06, 0x63, 0xDC, 0x7E, +0x1D, 0x7E, 0x66, 0x00, 0xA6, 0x19, 0x36, 0x1D, +0xAE, 0x19, 0x76, 0x05, 0x64, 0x00, 0x76, 0x09, +0x2C, 0x19, 0x26, 0x43, 0x87, 0x63, 0x46, 0x63, +0xDD, 0x7E, 0x1C, 0x7E, 0x6E, 0x00, 0xAD, 0x19, +0xBC, 0x35, 0x65, 0x19, 0x2D, 0x04, 0xBC, 0x34, +0x25, 0x43, 0xC5, 0x63, 0xDC, 0x7E, 0x1D, 0x7E, +0x66, 0x00, 0xA6, 0x19, 0xBE, 0x36, 0xAE, 0x19, +0x76, 0x05, 0x2C, 0x19, 0x76, 0x09, 0xBC, 0x34, +0x26, 0x43, 0x06, 0x64, 0xDC, 0x7E, 0x1D, 0x7E, +0x66, 0x00, 0xA6, 0x19, 0xC0, 0x36, 0xAE, 0x19, +0x64, 0x00, 0x76, 0x05, 0xBC, 0x34, 0x76, 0x09, +0x2C, 0x19, 0x26, 0x43, 0x87, 0x64, 0x47, 0x62, +0x6A, 0x4C, 0x46, 0x64, 0x84, 0x62, 0x64, 0x46, +0x50, 0x34, 0xE5, 0x7B, 0xA6, 0x7B, 0x2C, 0x02, +0x34, 0x43, 0xE4, 0x1C, 0xA5, 0x05, 0x66, 0x4C, +0xAD, 0x0D, 0x2C, 0x19, 0xC4, 0x64, 0x65, 0x4C, +0x25, 0x68, 0x65, 0x4C, 0xA5, 0x42, 0x01, 0xD1, +0x64, 0x4C, 0x00, 0xE0, 0x64, 0x4C, 0x04, 0x65, +0x5C, 0x7C, 0x5C, 0x4E, 0x0C, 0x3C, 0xE5, 0xB2, +0x05, 0x24, 0x64, 0x02, 0x2D, 0x19, 0x45, 0x65, +0x5D, 0x7D, 0x2D, 0x02, 0x21, 0x35, 0x85, 0x65, +0x02, 0x25, 0xC5, 0x65, 0xD8, 0x7C, 0x9B, 0x7C, +0x00, 0x04, 0x1B, 0x02, 0x18, 0x43, 0x0C, 0x30, +0x08, 0x60, 0x30, 0x68, 0x40, 0x30, 0xC3, 0x7C, +0x85, 0x7C, 0x1B, 0x04, 0x2D, 0x02, 0x2B, 0x43, +0x0C, 0x33, 0x4B, 0x60, 0x83, 0x7D, 0x55, 0x4D, +0x5B, 0x1E, 0xDB, 0x05, 0xDB, 0x0D, 0x8B, 0x60, +0x83, 0x7D, 0x5B, 0x08, 0x5B, 0x1E, 0xDB, 0x05, +0xDB, 0x0D, 0xCB, 0x60, 0x0F, 0x61, 0x4D, 0x61, +0xCF, 0x61, 0x01, 0x25, 0x8D, 0x61, 0x2D, 0x02, +0x0D, 0x62, 0x45, 0x7E, 0xCD, 0x62, 0x45, 0x7E, +0x06, 0x7F, 0x3B, 0x46, 0xAD, 0x19, 0x0D, 0x63, +0x06, 0x7F, 0x45, 0x7E, 0x8B, 0x63, 0xCB, 0x63, +0x0B, 0x64, 0x40, 0x4F, 0x4B, 0x64, 0x76, 0x00, +0x8B, 0x64, 0x3F, 0x37, 0xAD, 0x19, 0x8F, 0x62, +0x4D, 0x63, 0x3D, 0x4D, 0x4B, 0x62, 0xED, 0x1C, +0xCD, 0x64, 0x3C, 0x4D, 0x3C, 0x4E, 0x2D, 0x68, +0xB5, 0x42, 0x01, 0xD1, 0x3B, 0x4D, 0x00, 0xE0, +0x3B, 0x4D, 0x0D, 0x65, 0xC5, 0x7C, 0x0C, 0x3D, +0xED, 0xB2, 0x2D, 0x19, 0x4D, 0x65, 0x85, 0x7D, +0x6D, 0x08, 0x2D, 0x02, 0x21, 0x35, 0x8D, 0x65, +0xCB, 0x65, 0xC1, 0x7C, 0x80, 0x7C, 0x09, 0x04, +0x00, 0x02, 0x01, 0x43, 0x0C, 0x31, 0x2B, 0x48, +0x11, 0x60, 0x00, 0x68, 0x40, 0x30, 0xC1, 0x7C, +0x85, 0x7C, 0x09, 0x04, 0x2D, 0x02, 0x29, 0x43, +0x0C, 0x31, 0x51, 0x60, 0xC1, 0x7D, 0x49, 0x1E, +0xC9, 0x05, 0xC9, 0x0D, 0x91, 0x60, 0xC1, 0x7D, +0x13, 0x61, 0x49, 0x08, 0x49, 0x1E, 0xC9, 0x05, +0xC9, 0x0D, 0xD1, 0x60, 0x27, 0x49, 0x51, 0x61, +0x01, 0x21, 0xD3, 0x61, 0x91, 0x61, 0x09, 0x02, +0x11, 0x62, 0x41, 0x7E, 0xD1, 0x62, 0x41, 0x7E, +0x45, 0x7F, 0x49, 0x19, 0x11, 0x63, 0x45, 0x7F, +0x41, 0x7E, 0x6D, 0x00, 0x49, 0x19, 0x93, 0x63, +0x51, 0x63, 0x41, 0x7E, 0xBC, 0x31, 0xD1, 0x63, +0x41, 0x7E, 0x45, 0x7F, 0x49, 0x19, 0xBC, 0x31, +0x11, 0x64, 0x45, 0x7F, 0x41, 0x7E, 0x6D, 0x00, +0xBC, 0x35, 0x49, 0x19, 0x93, 0x64, 0x51, 0x64, +0x01, 0x21, 0x89, 0x04, 0x97, 0x62, 0x51, 0x62, +0x0F, 0x49, 0xC9, 0x1C, 0xD1, 0x64, 0x0F, 0x49, +0x09, 0x68, 0xB1, 0x42, 0x01, 0xD1, 0x0F, 0x49, +0x00, 0xE0, 0x0F, 0x49, 0x11, 0x65, 0xC1, 0x7C, +0x0C, 0x39, 0xC9, 0xB2, 0x09, 0x19, 0x51, 0x65, +0xC0, 0x7D, 0xD3, 0x65, 0x40, 0x08, 0x00, 0x02, +0x21, 0x30, 0x90, 0x65, 0xF0, 0xBD, 0x00, 0x00, +0x0C, 0x04, 0x00, 0x20, 0xAC, 0x04, 0x00, 0x20, +0x03, 0x00, 0x03, 0x00, 0x00, 0x3F, 0x3F, 0x3F, +0x00, 0x30, 0xBC, 0x00, 0xE4, 0x06, 0x00, 0x20, +0xA1, 0x00, 0x03, 0xF3, 0x00, 0x26, 0x31, 0x00, +0x00, 0x26, 0x31, 0x08, 0x01, 0x00, 0x01, 0x00, +0x10, 0xB5, 0x00, 0xF0, 0x1D, 0xFB, 0x00, 0xF0, +0x51, 0xF9, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, 0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x00, 0x20, -0xFF, 0xF7, 0xD4, 0xFC, 0x09, 0x48, 0x01, 0x78, +0xFF, 0xF7, 0x4E, 0xFC, 0x09, 0x48, 0x01, 0x78, 0x09, 0x48, 0x00, 0x29, 0x04, 0xD0, 0x01, 0x68, 0xFF, 0x22, 0x01, 0x32, 0x11, 0x43, 0x01, 0x60, 0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x91, 0x43, 0x01, 0x60, 0x04, 0x49, 0x02, 0x20, 0x08, 0x72, -0x10, 0xBD, 0x00, 0x00, 0xCF, 0x00, 0x00, 0x20, +0x10, 0xBD, 0x00, 0x00, 0x99, 0x01, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, -0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x09, 0x48, -0x07, 0x49, 0x41, 0x60, 0x08, 0x49, 0x81, 0x60, -0xFF, 0xF7, 0x52, 0xFE, 0x80, 0x21, 0x07, 0x48, -0xFD, 0xF7, 0xCF, 0xFE, 0x80, 0x21, 0x06, 0x48, -0xFD, 0xF7, 0xCB, 0xFE, 0x10, 0xBD, 0x00, 0x00, +0x04, 0x24, 0x21, 0x43, 0x41, 0x60, 0x0B, 0x48, +0x09, 0x49, 0x41, 0x60, 0x0A, 0x49, 0x81, 0x60, +0xFF, 0xF7, 0xD6, 0xFD, 0x05, 0x20, 0x00, 0x07, +0x81, 0x69, 0x21, 0x43, 0x81, 0x61, 0x80, 0x21, +0x06, 0x48, 0xFD, 0xF7, 0x7E, 0xFA, 0x80, 0x21, +0x05, 0x48, 0xFD, 0xF7, 0x7A, 0xFA, 0x10, 0xBD, 0x1F, 0x1F, 0x5F, 0x1F, 0x00, 0x10, 0x00, 0x50, -0x1F, 0x1F, 0x1F, 0x1F, 0x4C, 0x07, 0x00, 0x20, +0x1F, 0x1F, 0x1F, 0x1F, 0x2C, 0x06, 0x00, 0x20, 0x9C, 0x01, 0x00, 0x20, 0x01, 0x21, 0x89, 0x07, 0x0A, 0x15, 0x00, 0x28, 0x48, 0x69, 0x02, 0xD0, 0x10, 0x43, 0x48, 0x61, 0x70, 0x47, 0x90, 0x43, @@ -1451,10 +1727,10 @@ const unsigned char u8_rad_fw_30[] = { 0x60, 0x22, 0x0A, 0x49, 0x08, 0xE0, 0x89, 0x07, 0x0A, 0xD5, 0x60, 0x22, 0x08, 0x49, 0x03, 0xE0, 0x49, 0x07, 0x05, 0xD5, 0x60, 0x22, 0x07, 0x49, -0xFD, 0xF7, 0x7E, 0xFE, 0x01, 0x20, 0x10, 0xBD, -0x00, 0x20, 0x10, 0xBD, 0x85, 0x02, 0x00, 0x20, -0x98, 0x01, 0x00, 0x20, 0x9C, 0x03, 0x00, 0x20, -0xEC, 0x06, 0x00, 0x20, 0xFC, 0x03, 0x00, 0x20, +0xFD, 0xF7, 0x2E, 0xFA, 0x01, 0x20, 0x10, 0xBD, +0x00, 0x20, 0x10, 0xBD, 0x87, 0x02, 0x00, 0x20, +0x9A, 0x01, 0x00, 0x20, 0x4C, 0x03, 0x00, 0x20, +0xEC, 0x06, 0x00, 0x20, 0xAC, 0x03, 0x00, 0x20, 0x10, 0xB5, 0x13, 0x49, 0x13, 0x4B, 0x09, 0x68, 0x13, 0x4A, 0x40, 0x31, 0x00, 0x28, 0x0F, 0xD0, 0x01, 0x28, 0x12, 0xD0, 0x02, 0x28, 0x15, 0xD0, @@ -1465,371 +1741,95 @@ const unsigned char u8_rad_fw_30[] = { 0xF4, 0xE7, 0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, 0xC9, 0x7E, 0xEF, 0xE7, 0x48, 0x7E, 0x84, 0x1E, 0x1C, 0x80, 0x09, 0x7F, 0xEA, 0xE7, 0x00, 0x00, -0xB8, 0x02, 0x00, 0x20, 0x60, 0x02, 0x00, 0x20, -0x62, 0x02, 0x00, 0x20, 0xF8, 0xB5, 0x05, 0x46, -0x23, 0x48, 0x00, 0x21, 0x01, 0x60, 0x41, 0x60, -0x22, 0x49, 0x01, 0x20, 0x08, 0x70, 0x22, 0x48, -0x22, 0x4C, 0x00, 0x78, 0x00, 0x28, 0x06, 0xD0, -0x2D, 0x26, 0x09, 0x27, 0x01, 0x28, 0x10, 0xD0, -0x02, 0x28, 0x1D, 0xD1, 0x21, 0xE0, 0x1E, 0xA0, -0x02, 0xF0, 0xC0, 0xF9, 0x5A, 0x20, 0x00, 0x2D, -0x20, 0x70, 0x01, 0xD0, 0x00, 0x20, 0x00, 0xE0, -0x01, 0x20, 0x00, 0xF0, 0x23, 0xF9, 0x5A, 0x20, -0x0C, 0xE0, 0x1A, 0xA0, 0x02, 0xF0, 0xB2, 0xF9, -0x00, 0x2D, 0x02, 0xD0, 0x26, 0x70, 0x02, 0x20, -0x01, 0xE0, 0x27, 0x70, 0x03, 0x20, 0x00, 0xF0, -0x15, 0xF9, 0x01, 0x20, 0xFF, 0xF7, 0x90, 0xF9, -0x05, 0x20, 0x14, 0x49, 0x00, 0x02, 0x08, 0x60, -0xF8, 0xBD, 0x13, 0xA0, 0x02, 0xF0, 0x9E, 0xF9, -0x00, 0x2D, 0x02, 0xD0, 0x26, 0x70, 0x02, 0x20, -0x01, 0xE0, 0x27, 0x70, 0x03, 0x20, 0x00, 0xF0, -0x01, 0xF9, 0x10, 0x48, 0x81, 0x6A, 0x10, 0x4A, -0x11, 0x40, 0x81, 0x62, 0xE5, 0xE7, 0x00, 0x00, -0x50, 0x02, 0x00, 0x20, 0xCF, 0x00, 0x00, 0x20, -0x85, 0x02, 0x00, 0x20, 0xB5, 0x02, 0x00, 0x20, -0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x0D, 0x0A, -0x00, 0x00, 0x00, 0x00, 0x49, 0x64, 0x6C, 0x65, -0x0D, 0x0A, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, -0x47, 0x65, 0x73, 0x74, 0x75, 0x72, 0x65, 0x0D, -0x0A, 0x00, 0x00, 0x00, 0xC0, 0x11, 0x00, 0x50, -0xFF, 0xFF, 0x00, 0xF8, 0x70, 0xB5, 0x1C, 0x4D, -0x1C, 0x4C, 0x28, 0x70, 0x02, 0x46, 0x21, 0x78, -0x1B, 0xA0, 0x02, 0xF0, 0x67, 0xF9, 0x1E, 0x49, -0x01, 0x20, 0x08, 0x70, 0x00, 0xF0, 0x7E, 0xF9, -0x00, 0xF0, 0x50, 0xF9, 0x00, 0xF0, 0x52, 0xF8, -0x01, 0x20, 0xFD, 0xF7, 0x8D, 0xFE, 0xFF, 0xF7, -0x09, 0xFC, 0x01, 0x20, 0xFF, 0xF7, 0x7E, 0xFF, -0x00, 0xF0, 0xC6, 0xFF, 0xFE, 0xF7, 0xF6, 0xFF, -0x28, 0x78, 0x20, 0x70, 0x05, 0x28, 0x12, 0xD0, -0xFF, 0xF7, 0xBE, 0xFC, 0x00, 0x28, 0x0F, 0xD0, -0x10, 0x48, 0x00, 0x7A, 0x00, 0x28, 0x03, 0xD1, -0x00, 0x21, 0x02, 0x20, 0x00, 0xF0, 0x4C, 0xF8, -0x00, 0x20, 0x00, 0xF0, 0x5B, 0xF9, 0x0C, 0x48, -0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, 0x00, 0x20, -0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD, 0x00, 0x00, -0x85, 0x02, 0x00, 0x20, 0x71, 0x04, 0x00, 0x20, -0x53, 0x4D, 0x3D, 0x5B, 0x25, 0x64, 0x3A, 0x25, -0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x83, 0x02, 0x00, 0x20, 0x50, 0x02, 0x00, 0x20, -0xCE, 0x00, 0x00, 0x20, 0x08, 0x49, 0x02, 0x20, +0xAC, 0x04, 0x00, 0x20, 0x62, 0x02, 0x00, 0x20, +0x64, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x1F, 0x4A, +0x00, 0x21, 0x11, 0x60, 0x51, 0x60, 0x1E, 0x4A, +0x01, 0x21, 0x11, 0x70, 0x1D, 0x49, 0x1E, 0x4A, +0x09, 0x78, 0x00, 0x29, 0x06, 0xD0, 0x3C, 0x24, +0x0C, 0x23, 0x01, 0x29, 0x0D, 0xD0, 0x02, 0x29, +0x17, 0xD1, 0x1B, 0xE0, 0x78, 0x21, 0x00, 0x28, +0x11, 0x70, 0x01, 0xD0, 0x00, 0x20, 0x00, 0xE0, +0x01, 0x20, 0x00, 0xF0, 0xD7, 0xF9, 0x78, 0x20, +0x09, 0xE0, 0x00, 0x28, 0x02, 0xD0, 0x14, 0x70, +0x02, 0x20, 0x01, 0xE0, 0x13, 0x70, 0x03, 0x20, +0x00, 0xF0, 0xCC, 0xF9, 0x01, 0x20, 0xFF, 0xF7, +0xED, 0xF8, 0x05, 0x20, 0x0D, 0x49, 0x00, 0x02, +0x08, 0x60, 0x10, 0xBD, 0x00, 0x28, 0x02, 0xD0, +0x14, 0x70, 0x02, 0x20, 0x01, 0xE0, 0x13, 0x70, +0x03, 0x20, 0x00, 0xF0, 0xBB, 0xF9, 0x08, 0x48, +0x81, 0x6A, 0x08, 0x4A, 0x11, 0x40, 0x81, 0x62, +0xE8, 0xE7, 0x00, 0x00, 0x50, 0x02, 0x00, 0x20, +0x99, 0x01, 0x00, 0x20, 0x87, 0x02, 0x00, 0x20, +0xB7, 0x02, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, +0xC0, 0x11, 0x00, 0x50, 0xFF, 0xFF, 0x00, 0xF8, +0x10, 0xB5, 0x19, 0x4C, 0x19, 0x49, 0x20, 0x70, +0x01, 0x20, 0x08, 0x70, 0x00, 0xF0, 0x3C, 0xFA, +0x00, 0xF0, 0x16, 0xFA, 0x00, 0xF0, 0x4A, 0xF8, +0x01, 0x20, 0xFD, 0xF7, 0x5D, 0xFA, 0xFF, 0xF7, +0x9F, 0xFB, 0x01, 0x20, 0xFF, 0xF7, 0x9E, 0xFF, +0x00, 0xF0, 0xC2, 0xFF, 0xFE, 0xF7, 0x50, 0xFC, +0x20, 0x78, 0x0F, 0x49, 0x05, 0x28, 0x08, 0x70, +0x12, 0xD0, 0xFF, 0xF7, 0x45, 0xFC, 0x00, 0x28, +0x0F, 0xD0, 0x0C, 0x48, 0x00, 0x7A, 0x00, 0x28, +0x03, 0xD1, 0x00, 0x21, 0x02, 0x20, 0x00, 0xF0, +0x43, 0xF8, 0x00, 0x20, 0x00, 0xF0, 0x18, 0xFA, +0x07, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, +0x00, 0x20, 0x10, 0xBD, 0x01, 0x20, 0x10, 0xBD, +0x87, 0x02, 0x00, 0x20, 0x84, 0x02, 0x00, 0x20, +0x4D, 0x07, 0x00, 0x20, 0x50, 0x02, 0x00, 0x20, +0x98, 0x01, 0x00, 0x20, 0x08, 0x49, 0x02, 0x20, 0x08, 0x72, 0x08, 0x48, 0x01, 0x78, 0x08, 0x48, 0x00, 0x29, 0x01, 0x68, 0x04, 0xD0, 0x01, 0x22, 0x92, 0x02, 0x91, 0x43, 0x01, 0x60, 0x70, 0x47, 0x01, 0x22, 0x11, 0x43, 0xFA, 0xE7, 0x00, 0x00, -0x50, 0x02, 0x00, 0x20, 0xCF, 0x00, 0x00, 0x20, +0x50, 0x02, 0x00, 0x20, 0x99, 0x01, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, 0x10, 0xB5, 0x08, 0x48, 0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x11, 0x43, 0x01, 0x60, 0x00, 0x68, 0x05, 0x4C, 0xC0, 0x07, 0x03, 0xD0, 0x02, 0x20, 0x20, 0x72, 0x00, 0xF0, -0xED, 0xF8, 0x00, 0x20, 0x20, 0x72, 0x10, 0xBD, +0xBB, 0xF9, 0x00, 0x20, 0x20, 0x72, 0x10, 0xBD, 0x00, 0x10, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x00, 0x0E, 0x46, 0x16, 0xD0, -0xFF, 0xF7, 0x6A, 0xFE, 0x00, 0x24, 0x6D, 0x1E, -0x07, 0xE0, 0x00, 0xF0, 0xDB, 0xF8, 0xFF, 0xF7, -0x67, 0xFB, 0xFF, 0xF7, 0xC3, 0xFF, 0x64, 0x1C, +0xFF, 0xF7, 0x8E, 0xFE, 0x00, 0x24, 0x6D, 0x1E, +0x07, 0xE0, 0x00, 0xF0, 0xA9, 0xF9, 0xFF, 0xF7, +0x05, 0xFB, 0xFF, 0xF7, 0xC3, 0xFF, 0x64, 0x1C, 0xE4, 0xB2, 0xAC, 0x42, 0xF5, 0xDB, 0x00, 0xF0, -0xD1, 0xF8, 0xFF, 0xF7, 0x5D, 0xFB, 0x00, 0x2E, +0x9F, 0xF9, 0xFF, 0xF7, 0xFB, 0xFA, 0x00, 0x2E, 0x02, 0xD0, 0xFF, 0xF7, 0xCF, 0xFF, 0x70, 0xBD, 0xFF, 0xF7, 0xB4, 0xFF, 0x70, 0xBD, 0x00, 0x00, -0x30, 0xB5, 0x01, 0x24, 0x1C, 0x4A, 0xA4, 0x07, -0x23, 0x13, 0x1B, 0x49, 0x40, 0x32, 0x00, 0x28, -0x21, 0xD0, 0x01, 0x28, 0x1F, 0xD0, 0x02, 0x28, -0x01, 0xD0, 0x03, 0x28, 0x1A, 0xD1, 0x25, 0x68, -0x1D, 0x43, 0x25, 0x60, 0x62, 0x23, 0x93, 0x61, -0xD3, 0x61, 0x14, 0x4B, 0xCB, 0x63, 0x14, 0x49, -0x09, 0x68, 0x40, 0x31, 0x02, 0x28, 0x48, 0x7E, -0x17, 0xD0, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D, -0x10, 0x60, 0x4B, 0x7E, 0x48, 0x7F, 0x41, 0x00, -0x40, 0x18, 0x80, 0x1C, 0x18, 0x18, 0x40, 0x05, -0x40, 0x0D, 0x90, 0x60, 0x30, 0xBD, 0x20, 0x68, -0x98, 0x43, 0x20, 0x60, 0x01, 0x20, 0x90, 0x61, -0xD0, 0x61, 0x00, 0x20, 0xC8, 0x63, 0x10, 0x60, -0xF3, 0xE7, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D, -0x10, 0x60, 0x4B, 0x7E, 0x08, 0x7F, 0xE6, 0xE7, -0x80, 0x11, 0x00, 0x50, 0x01, 0x00, 0x01, 0x00, -0xB8, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x04, 0x46, -0x81, 0x00, 0x25, 0x48, 0x41, 0x58, 0x25, 0x48, -0x0A, 0x68, 0x42, 0x60, 0x4A, 0x68, 0x82, 0x60, -0x8A, 0x68, 0x02, 0x61, 0xCA, 0x68, 0x42, 0x61, -0x21, 0x4D, 0x08, 0x69, 0x28, 0x62, 0x1F, 0x4A, -0x48, 0x69, 0xC0, 0x32, 0x90, 0x61, 0x88, 0x69, -0xD0, 0x61, 0x48, 0x6A, 0x10, 0x62, 0x88, 0x6A, -0x50, 0x62, 0x1A, 0x48, 0xCB, 0x69, 0x40, 0x38, -0xC3, 0x60, 0x0B, 0x6A, 0x03, 0x61, 0xCB, 0x6A, -0x93, 0x62, 0x0B, 0x6B, 0xD3, 0x63, 0x16, 0x4B, -0x4E, 0x6B, 0x80, 0x3B, 0x1E, 0x61, 0x8E, 0x6B, -0x5E, 0x62, 0xCE, 0x6B, 0xD6, 0x62, 0x0A, 0x6C, -0x1A, 0x60, 0x4A, 0x6C, 0x5A, 0x61, 0x8A, 0x6C, -0x9A, 0x62, 0xCA, 0x6C, 0xAA, 0x62, 0x0D, 0x4B, -0x0A, 0x6D, 0x40, 0x33, 0x5A, 0x63, 0x0B, 0x4B, -0x4A, 0x6D, 0x80, 0x33, 0x9A, 0x60, 0x0B, 0x4A, -0x8B, 0x6D, 0x13, 0x60, 0xC9, 0x6D, 0x51, 0x60, -0xC2, 0x68, 0x09, 0x49, 0x0A, 0x60, 0x00, 0x69, -0x48, 0x60, 0x20, 0x46, 0xFF, 0xF7, 0x44, 0xFE, -0x20, 0x46, 0xFF, 0xF7, 0x71, 0xFF, 0x70, 0xBD, -0x70, 0x02, 0x00, 0x20, 0x40, 0x10, 0x00, 0x50, -0xC0, 0x11, 0x00, 0x50, 0x00, 0x19, 0x00, 0x50, -0x50, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x0E, 0x4C, -0x21, 0x78, 0x81, 0x42, 0x17, 0xD0, 0x20, 0x70, -0x01, 0x46, 0x0C, 0xA0, 0x02, 0xF0, 0x32, 0xF8, -0xFF, 0xF7, 0x24, 0xFF, 0x20, 0x78, 0xFF, 0xF7, -0x55, 0xFE, 0xFF, 0xF7, 0xA9, 0xFA, 0x0B, 0x48, -0x80, 0x79, 0x01, 0x28, 0x05, 0xD1, 0x0A, 0x48, -0x01, 0x68, 0x01, 0x22, 0xD2, 0x02, 0x11, 0x43, -0x01, 0x60, 0xFF, 0xF7, 0xFB, 0xFE, 0x10, 0xBD, -0x74, 0x04, 0x00, 0x20, 0x54, 0x50, 0x20, 0x53, -0x70, 0x65, 0x65, 0x64, 0x20, 0x25, 0x64, 0x0D, -0x0A, 0x00, 0x00, 0x00, 0x84, 0x04, 0x00, 0x20, -0x00, 0x10, 0x00, 0x50, 0x70, 0xB5, 0x05, 0x24, -0x64, 0x04, 0xFD, 0xF7, 0xA9, 0xFF, 0x00, 0x22, -0x0C, 0x49, 0x0D, 0x48, 0x0D, 0x4B, 0x05, 0xE0, -0x05, 0x78, 0xED, 0x07, 0x01, 0xD0, 0x0A, 0x72, -0x09, 0xE0, 0x64, 0x1E, 0x0D, 0x7A, 0x02, 0x2D, -0x02, 0xD0, 0x1D, 0x68, 0xED, 0x07, 0x02, 0xD0, -0x00, 0x2C, 0xF1, 0xD1, 0x01, 0xE0, 0x00, 0x2C, -0x03, 0xD1, 0x0A, 0x72, 0x04, 0xA0, 0x01, 0xF0, -0xF1, 0xFF, 0x70, 0xBD, 0x50, 0x02, 0x00, 0x20, -0xCE, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, -0x57, 0x53, 0x46, 0x20, 0x54, 0x4F, 0x0D, 0x0A, -0x00, 0x00, 0x00, 0x00, 0x00, 0xB5, 0x03, 0x46, -0x00, 0x20, 0x00, 0xF0, 0xF1, 0xF8, 0x05, 0x21, -0x09, 0x07, 0xC8, 0x69, 0x00, 0x2B, 0x03, 0xD0, -0xC0, 0x0B, 0xC0, 0x03, 0x05, 0x4A, 0x03, 0xE0, -0xC0, 0x0B, 0x04, 0x4A, 0xC0, 0x03, 0xFC, 0x3A, -0x80, 0x18, 0xC8, 0x61, 0x01, 0x20, 0x00, 0xF0, -0xDF, 0xF8, 0x00, 0xBD, 0x50, 0x71, 0x00, 0x00, -0x03, 0x49, 0x0A, 0x68, 0x10, 0x18, 0x0A, 0x68, -0x90, 0x42, 0xFC, 0xD1, 0x70, 0x47, 0x00, 0x00, -0x08, 0x03, 0x00, 0x20, 0xF8, 0xB5, 0x1B, 0x4E, -0x05, 0x46, 0x0C, 0x46, 0x80, 0x21, 0x30, 0x46, -0xFD, 0xF7, 0x3B, 0xFC, 0x00, 0x2C, 0x04, 0xD0, -0x31, 0x46, 0x28, 0x46, 0x00, 0xF0, 0x5C, 0xFB, -0x04, 0xE0, 0x80, 0x22, 0x29, 0x46, 0x30, 0x46, -0xFD, 0xF7, 0x16, 0xFC, 0x29, 0x46, 0x12, 0xA0, -0x01, 0xF0, 0xA8, 0xFF, 0x00, 0x25, 0x14, 0x4F, -0x13, 0xE0, 0x00, 0x24, 0x08, 0xE0, 0x68, 0x43, -0x00, 0x19, 0x40, 0x00, 0x31, 0x5E, 0x11, 0xA0, -0x01, 0xF0, 0x9C, 0xFF, 0x64, 0x1C, 0xE4, 0xB2, -0x38, 0x68, 0x00, 0x7E, 0xA0, 0x42, 0xF2, 0xD8, -0x0E, 0xA0, 0x01, 0xF0, 0x93, 0xFF, 0x6D, 0x1C, -0xED, 0xB2, 0x38, 0x68, 0x40, 0x7E, 0xA8, 0x42, -0xE7, 0xD8, 0x0A, 0xA0, 0x01, 0xF0, 0x8A, 0xFF, -0xF8, 0xBD, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x20, -0x49, 0x6D, 0x61, 0x67, 0x65, 0x3A, 0x30, 0x78, -0x25, 0x78, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0xB8, 0x02, 0x00, 0x20, 0x25, 0x36, 0x64, 0x2C, -0x00, 0x00, 0x00, 0x00, 0x0D, 0x0A, 0x00, 0x00, -0x08, 0x49, 0x8A, 0x78, 0x52, 0x1E, 0x8A, 0x70, -0x4B, 0x78, 0x0A, 0x1D, 0xD2, 0x5C, 0x02, 0x70, -0x48, 0x78, 0x40, 0x1C, 0x48, 0x70, 0x48, 0x78, -0x10, 0x28, 0x01, 0xD1, 0x00, 0x20, 0x48, 0x70, -0x70, 0x47, 0x00, 0x00, 0x5C, 0x04, 0x00, 0x20, -0xF8, 0xB5, 0x01, 0x27, 0x05, 0x46, 0xBF, 0x07, -0x38, 0x68, 0x08, 0x21, 0x08, 0x43, 0x38, 0x60, -0x01, 0x23, 0x15, 0x48, 0x80, 0x22, 0x02, 0x60, -0x14, 0x48, 0x00, 0x21, 0x81, 0x70, 0x01, 0x70, -0x41, 0x70, 0xC3, 0x70, 0x12, 0x4B, 0x98, 0x68, -0x02, 0x21, 0x88, 0x43, 0x98, 0x60, 0xF8, 0x68, -0x10, 0x43, 0xF8, 0x60, 0x0F, 0x4C, 0x61, 0x61, -0x0F, 0x48, 0x00, 0x68, 0xE9, 0x00, 0x46, 0x06, -0x0E, 0x48, 0xFD, 0xF7, 0x7F, 0xFB, 0xE0, 0x60, -0x30, 0x20, 0xA0, 0x60, 0x06, 0x49, 0x80, 0x20, -0x80, 0x39, 0x08, 0x60, 0x08, 0x20, 0x78, 0x60, -0xE0, 0x68, 0x68, 0x43, 0xC1, 0x00, 0x08, 0xA0, -0x01, 0xF0, 0x30, 0xFF, 0xF8, 0xBD, 0x00, 0x00, -0x80, 0xE1, 0x00, 0xE0, 0x5C, 0x04, 0x00, 0x20, -0x40, 0x09, 0x00, 0x50, 0x00, 0x02, 0x00, 0x50, -0x00, 0x11, 0x00, 0x50, 0x00, 0x36, 0x6E, 0x01, -0x55, 0x41, 0x52, 0x54, 0x28, 0x25, 0x64, 0x29, -0x21, 0x0D, 0x0A, 0x00, 0x70, 0xB5, 0x14, 0x4A, -0x91, 0x78, 0x14, 0x4C, 0x0E, 0x29, 0x02, 0xD3, -0x61, 0x68, 0x49, 0x07, 0xFC, 0xD5, 0x61, 0x69, -0x02, 0x25, 0xA9, 0x43, 0x61, 0x61, 0x91, 0x78, -0x00, 0x26, 0x10, 0x29, 0x0D, 0xD2, 0x0C, 0x49, -0x13, 0x78, 0x09, 0x1D, 0xC8, 0x54, 0x10, 0x78, -0x40, 0x1C, 0x10, 0x70, 0x10, 0x78, 0x10, 0x28, -0x00, 0xD1, 0x16, 0x70, 0x90, 0x78, 0x40, 0x1C, -0x90, 0x70, 0xD0, 0x78, 0x00, 0x28, 0x03, 0xD0, -0xD6, 0x70, 0x20, 0x46, 0xFF, 0xF7, 0x80, 0xFF, -0x60, 0x69, 0x28, 0x43, 0x60, 0x61, 0x70, 0xBD, -0x5C, 0x04, 0x00, 0x20, 0x00, 0x02, 0x00, 0x50, -0x01, 0x21, 0x89, 0x07, 0x00, 0xB5, 0x8A, 0x14, -0x00, 0x28, 0x08, 0x68, 0x04, 0xD0, 0x10, 0x43, -0x08, 0x60, 0xFD, 0xF7, 0x81, 0xFE, 0x00, 0xBD, -0x90, 0x43, 0x08, 0x60, 0x00, 0xBD, 0x00, 0x00, -0xFE, 0xB5, 0x63, 0x49, 0x61, 0x48, 0x09, 0x68, -0x00, 0x78, 0x90, 0x31, 0x4A, 0x7B, 0x0B, 0x7B, -0x11, 0x02, 0x19, 0x43, 0x5F, 0x4B, 0x00, 0x24, -0x04, 0x25, 0x0F, 0x26, 0x5A, 0x22, 0x1C, 0x5F, -0x00, 0x28, 0x01, 0xD0, 0x01, 0x28, 0x15, 0xD1, -0x58, 0x4B, 0x02, 0x27, 0x38, 0x43, 0x18, 0x70, -0x59, 0x48, 0x00, 0x78, 0x15, 0x28, 0x14, 0xD1, -0x58, 0x48, 0x00, 0x23, 0xC3, 0x5E, 0x99, 0x42, -0x0F, 0xDC, 0x13, 0x20, 0xC0, 0x43, 0x84, 0x42, -0x01, 0xDA, 0x55, 0x48, 0x02, 0x70, 0x17, 0x20, -0x54, 0x4B, 0x18, 0x70, 0x54, 0x48, 0x00, 0x27, -0xC7, 0x5F, 0xB9, 0x42, 0x03, 0xDA, 0x04, 0x20, -0x02, 0xE0, 0x00, 0x20, 0xF4, 0xE7, 0x02, 0x20, -0x00, 0x90, 0x50, 0x48, 0x40, 0x88, 0x00, 0x28, -0x67, 0xD0, 0x46, 0x48, 0x00, 0x78, 0x01, 0x90, -0x83, 0x07, 0x4D, 0x48, 0x01, 0x78, 0x48, 0x1C, -0xC0, 0xB2, 0x00, 0x2B, 0x06, 0xDA, 0x10, 0x2F, -0x04, 0xDA, 0x02, 0x21, 0x00, 0x91, 0x48, 0x49, -0x08, 0x70, 0x19, 0xE0, 0x47, 0x4B, 0x1B, 0x78, -0xDB, 0x07, 0x11, 0xD0, 0x44, 0x49, 0x00, 0x20, -0x08, 0x70, 0x13, 0x20, 0xC0, 0x43, 0x84, 0x42, -0x05, 0xDA, 0x40, 0x48, 0x80, 0x88, 0x05, 0x28, -0x01, 0xD2, 0x41, 0x48, 0x02, 0x80, 0x30, 0x21, -0x40, 0x48, 0xFD, 0xF7, 0x02, 0xFB, 0x03, 0xE0, -0x3B, 0x4A, 0x05, 0x29, 0x00, 0xD2, 0x10, 0x70, -0x39, 0x48, 0x01, 0x78, 0x00, 0x98, 0x81, 0x42, -0x37, 0xD3, 0x3B, 0x49, 0x10, 0x2F, 0x19, 0xDA, -0x1D, 0x20, 0xC0, 0x43, 0x84, 0x42, 0x15, 0xDD, -0x08, 0x78, 0xF0, 0x28, 0x01, 0xD2, 0x40, 0x1C, -0x08, 0x70, 0xC0, 0xB2, 0x02, 0x28, 0x0F, 0xD9, -0x2C, 0x49, 0x08, 0x78, 0x16, 0x28, 0x03, 0xD2, -0x02, 0x28, 0x01, 0xD9, 0x17, 0x20, 0x08, 0x70, -0x27, 0x49, 0x00, 0x20, 0x08, 0x70, 0x2C, 0x49, -0x08, 0x80, 0x01, 0xE0, 0x00, 0x20, 0x08, 0x70, -0x01, 0x98, 0x1E, 0x4A, 0x04, 0x28, 0x12, 0xD0, -0x22, 0x49, 0x08, 0x78, 0x18, 0x28, 0x32, 0xD2, -0x40, 0x1C, 0xC0, 0xB2, 0x08, 0x70, 0x10, 0x2F, -0x02, 0xDB, 0x62, 0x42, 0xBA, 0x42, 0x13, 0xDD, -0x1E, 0x4A, 0x12, 0x7A, 0x01, 0x2A, 0x05, 0xD9, -0x03, 0x26, 0x02, 0x25, 0x04, 0xE0, 0xFE, 0xF7, -0xB9, 0xFA, 0xFE, 0xBD, 0x01, 0x26, 0x35, 0x46, -0x09, 0x22, 0xD2, 0x43, 0x94, 0x42, 0x03, 0xDD, -0x02, 0x28, 0x01, 0xD9, 0x18, 0x20, 0x08, 0x70, -0xC1, 0xB2, 0x2B, 0x46, 0x32, 0x46, 0x19, 0xA0, -0x01, 0xF0, 0x2C, 0xFE, 0x1C, 0x4C, 0x1D, 0x4F, -0x00, 0x20, 0x42, 0x00, 0x11, 0x19, 0x00, 0x23, -0xCB, 0x5E, 0xBA, 0x5E, 0x73, 0x43, 0x9A, 0x18, -0x2A, 0x41, 0x40, 0x1C, 0xC0, 0xB2, 0x0A, 0x80, -0x30, 0x28, 0xF2, 0xD3, 0xFE, 0xBD, 0x04, 0x20, -0x10, 0x70, 0xFE, 0xBD, 0x87, 0x02, 0x00, 0x20, -0xB8, 0x02, 0x00, 0x20, 0x7A, 0x04, 0x00, 0x20, -0x70, 0x04, 0x00, 0x20, 0x78, 0x04, 0x00, 0x20, -0x75, 0x04, 0x00, 0x20, 0x8E, 0x02, 0x00, 0x20, -0x7C, 0x04, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, -0xED, 0x02, 0x00, 0x20, 0xEB, 0x02, 0x00, 0x20, -0xF0, 0x02, 0x00, 0x20, 0x0C, 0x03, 0x00, 0x20, -0xEA, 0x02, 0x00, 0x20, 0x46, 0x61, 0x73, 0x74, -0x4B, 0x25, 0x64, 0x28, 0x25, 0x64, 0x3A, 0x25, -0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x01, 0x20, 0xD4, 0x00, 0x00, 0x20, -0xF8, 0xB5, 0x3F, 0x4F, 0x01, 0x24, 0x38, 0x7B, -0x3E, 0x49, 0x0A, 0x78, 0x3E, 0x4E, 0x3F, 0x4D, -0x90, 0x42, 0x05, 0xD1, 0x28, 0x78, 0x81, 0x28, -0x02, 0xD0, 0x30, 0x78, 0xC0, 0x07, 0x66, 0xD0, -0x00, 0x20, 0x30, 0x70, 0x37, 0x48, 0x00, 0x78, -0x07, 0x28, 0x0C, 0xD3, 0x38, 0x48, 0x00, 0x68, -0x40, 0x05, 0x40, 0x0F, 0x38, 0x73, 0x01, 0x20, -0xFF, 0xF7, 0x06, 0xFE, 0x00, 0x20, 0x30, 0x70, -0x38, 0x7B, 0x02, 0x28, 0x18, 0xD0, 0xA8, 0x79, -0x00, 0x28, 0x01, 0xD0, 0xFE, 0xF7, 0x36, 0xFE, -0x38, 0x7B, 0x2C, 0x4F, 0x38, 0x70, 0x81, 0x20, -0x28, 0x70, 0x2E, 0x4A, 0x01, 0x21, 0x10, 0x88, -0x09, 0x03, 0x08, 0x43, 0x10, 0x80, 0x38, 0x78, -0x03, 0x00, 0xFD, 0xF7, 0x9F, 0xFA, 0x07, 0x25, -0x35, 0x35, 0x3B, 0x0B, 0x18, 0x0B, 0x3B, 0x00, -0x83, 0x20, 0x28, 0x70, 0x05, 0x20, 0xFF, 0xF7, -0x3D, 0xFC, 0xF8, 0xBD, 0x24, 0xA0, 0x01, 0xF0, -0xA9, 0xFD, 0x01, 0x20, 0xFE, 0xF7, 0xB4, 0xFD, -0x00, 0x20, 0xFF, 0xF7, 0x33, 0xFC, 0x00, 0x28, -0x1C, 0xD0, 0x01, 0x20, 0x18, 0xE0, 0x20, 0xA0, -0x01, 0xF0, 0x9C, 0xFD, 0x01, 0x20, 0xFE, 0xF7, -0xA7, 0xFD, 0x01, 0x20, 0xFF, 0xF7, 0x26, 0xFC, -0x00, 0x28, 0x0F, 0xD0, 0x04, 0x20, 0x0B, 0xE0, -0x1B, 0xA0, 0x01, 0xF0, 0x8F, 0xFD, 0x00, 0x20, -0xFE, 0xF7, 0x9A, 0xFD, 0x02, 0x20, 0xFF, 0xF7, -0x19, 0xFC, 0x00, 0x28, 0x02, 0xD0, 0x02, 0x20, -0x28, 0x70, 0x07, 0xE0, 0x00, 0x24, 0x05, 0xE0, -0x83, 0x20, 0x28, 0x70, 0x05, 0x20, 0xFF, 0xF7, -0x0D, 0xFC, 0x04, 0x46, 0x2A, 0x78, 0x39, 0x78, -0x11, 0xA0, 0x01, 0xF0, 0x77, 0xFD, 0x30, 0x78, -0xC0, 0x07, 0x02, 0xD0, 0x81, 0x20, 0x28, 0x70, -0x00, 0x24, 0x20, 0x46, 0xF8, 0xBD, 0x00, 0x00, -0x50, 0x02, 0x00, 0x20, 0xE5, 0x02, 0x00, 0x20, -0xCE, 0x00, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, -0x00, 0x11, 0x00, 0x50, 0xF6, 0x02, 0x00, 0x20, -0x44, 0x41, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x44, 0x49, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x44, 0x53, 0x54, 0x42, 0x0D, 0x0A, 0x00, 0x00, -0x44, 0x53, 0x50, 0x3D, 0x25, 0x64, 0x2C, 0x50, -0x57, 0x52, 0x3D, 0x25, 0x64, 0x20, 0x0D, 0x0A, -0x00, 0x00, 0x00, 0x00, 0x70, 0xB5, 0x0E, 0x4D, -0x28, 0x68, 0xFE, 0xF7, 0xFB, 0xF8, 0x04, 0x00, -0x07, 0xD0, 0x80, 0x21, 0x0B, 0x48, 0xFD, 0xF7, -0xC4, 0xF9, 0x0A, 0x49, 0x28, 0x68, 0x00, 0xF0, -0xE7, 0xF8, 0x09, 0x48, 0x00, 0x88, 0x81, 0x07, -0x06, 0xD0, 0x00, 0x07, 0x04, 0xD5, 0x80, 0x21, -0x04, 0x48, 0xFD, 0xF7, 0xB6, 0xF9, 0x00, 0x24, -0xFF, 0xF7, 0x52, 0xFE, 0x20, 0x46, 0x70, 0xBD, -0xF8, 0x02, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, -0xF6, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x46, -0x01, 0x24, 0xFE, 0xF7, 0xFF, 0xFB, 0x00, 0x2D, -0x09, 0xD0, 0xF0, 0x20, 0xFD, 0xF7, 0xD4, 0xF9, -0xFE, 0xF7, 0xC6, 0xFC, 0xFE, 0xF7, 0x5C, 0xFC, -0x00, 0x20, 0xFE, 0xF7, 0x25, 0xFD, 0xFE, 0xF7, -0xD9, 0xFD, 0xFF, 0xF7, 0xB5, 0xFA, 0x20, 0x46, -0x70, 0xBD, 0x00, 0x00, 0x70, 0xB5, 0x35, 0x49, -0x08, 0x78, 0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, -0x08, 0x70, 0x33, 0x4B, 0x33, 0x49, 0x18, 0x78, -0x09, 0x78, 0x00, 0x25, 0x32, 0x4A, 0x33, 0x4C, -0x00, 0x28, 0x07, 0xD1, 0x5E, 0x78, 0x00, 0x2E, -0x04, 0xD1, 0x9B, 0x78, 0x00, 0x2B, 0x01, 0xD1, -0x00, 0x29, 0x12, 0xD0, 0x65, 0x80, 0xA3, 0x88, -0xE1, 0x26, 0xB6, 0x00, 0xB3, 0x42, 0x08, 0xD2, -0x01, 0x43, 0x06, 0xD0, 0x2A, 0x49, 0x09, 0x78, -0x04, 0x29, 0xA1, 0x88, 0x00, 0xD1, 0x49, 0x1C, -0xA1, 0x80, 0x00, 0x28, 0x19, 0xD0, 0x15, 0x70, -0x1C, 0xE0, 0xA0, 0x88, 0x00, 0x28, 0x05, 0xD0, -0x20, 0x7A, 0xFF, 0x28, 0x02, 0xD2, 0x20, 0x7A, -0x40, 0x1C, 0x20, 0x72, 0xA5, 0x80, 0x60, 0x88, -0x01, 0x21, 0x09, 0x03, 0x88, 0x42, 0x02, 0xD2, -0x60, 0x88, 0x40, 0x1C, 0x60, 0x80, 0x1D, 0x49, -0x08, 0x78, 0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, -0x08, 0x70, 0x10, 0x78, 0x00, 0x28, 0x01, 0xD0, -0x40, 0x1E, 0x10, 0x70, 0x20, 0x78, 0x01, 0x28, -0x16, 0xD0, 0x02, 0x28, 0x14, 0xD0, 0x04, 0x28, -0x12, 0xD0, 0x82, 0x28, 0x12, 0xD1, 0x14, 0x4E, -0x30, 0x78, 0x30, 0x28, 0x0E, 0xD1, 0x13, 0xA0, -0x01, 0xF0, 0xB8, 0xFC, 0x05, 0x20, 0xFF, 0xF7, -0xEB, 0xFC, 0xFD, 0xF7, 0x37, 0xFC, 0x13, 0xA0, -0x01, 0xF0, 0xB0, 0xFC, 0x35, 0x70, 0x01, 0xE0, -0x00, 0xF0, 0xDE, 0xF8, 0x14, 0x48, 0x00, 0x78, -0xC0, 0x07, 0x01, 0xD0, 0x81, 0x20, 0x20, 0x70, -0x70, 0xBD, 0x00, 0x00, 0x70, 0x04, 0x00, 0x20, -0xA0, 0x04, 0x00, 0x20, 0x73, 0x04, 0x00, 0x20, -0x72, 0x04, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, -0xE5, 0x02, 0x00, 0x20, 0x75, 0x04, 0x00, 0x20, -0x88, 0x02, 0x00, 0x20, 0x73, 0x6C, 0x65, 0x65, -0x70, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x0D, 0x0A, -0x00, 0x00, 0x00, 0x00, 0x6C, 0x65, 0x61, 0x76, -0x65, 0x20, 0x73, 0x6C, 0x65, 0x65, 0x70, 0x20, -0x6D, 0x6F, 0x64, 0x65, 0x0D, 0x0A, 0x00, 0x00, -0xCE, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x10, 0x4D, -0x68, 0x78, 0x00, 0x07, 0x01, 0xD5, 0x00, 0xF0, -0x45, 0xF9, 0x0E, 0x4C, 0x60, 0x22, 0x0E, 0x48, -0x21, 0x68, 0xFD, 0xF7, 0xDD, 0xF8, 0xFD, 0xF7, -0x2D, 0xFA, 0x68, 0x78, 0x96, 0x21, 0x08, 0x42, -0x01, 0xD0, 0x00, 0xF0, 0x37, 0xF9, 0x09, 0x48, -0x00, 0x78, 0x00, 0x06, 0x06, 0xD4, 0x01, 0x21, -0x20, 0x68, 0xFE, 0xF7, 0x2F, 0xF9, 0x20, 0x68, -0xFD, 0xF7, 0xF0, 0xFA, 0x70, 0xBD, 0x00, 0x00, -0x88, 0x02, 0x00, 0x20, 0xF8, 0x02, 0x00, 0x20, -0xD4, 0x00, 0x00, 0x20, 0x86, 0x02, 0x00, 0x20, -0x30, 0xB5, 0x08, 0x4A, 0x14, 0x68, 0x1B, 0x34, -0x00, 0x22, 0xA3, 0x5C, 0x41, 0x2B, 0x03, 0xD0, -0x55, 0x00, 0x45, 0x5B, 0x5B, 0x00, 0xCD, 0x52, -0x52, 0x1C, 0xD2, 0xB2, 0x30, 0x2A, 0xF4, 0xD3, -0x30, 0xBD, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20, -0x01, 0x20, 0x80, 0x07, 0x40, 0x69, 0x40, 0x05, -0x01, 0xD5, 0x01, 0x20, 0x70, 0x47, 0x00, 0x20, -0x70, 0x47, 0x00, 0x00, 0x10, 0xB5, 0xFF, 0xF7, -0x25, 0xFC, 0x04, 0x48, 0x00, 0x78, 0x00, 0x28, -0x02, 0xD1, 0x01, 0x20, 0xFF, 0xF7, 0xF2, 0xF9, -0x10, 0xBD, 0x00, 0x00, 0x90, 0x02, 0x00, 0x20, +0xF0, 0xB5, 0x01, 0x21, 0x31, 0x4A, 0x89, 0x07, +0x05, 0x27, 0x0C, 0x13, 0x04, 0x25, 0x3F, 0x07, +0x12, 0x68, 0x2F, 0x4B, 0x00, 0x28, 0x06, 0xD0, +0x01, 0x28, 0x04, 0xD0, 0x02, 0x28, 0x1A, 0xD0, +0x03, 0x28, 0x40, 0xD1, 0x17, 0xE0, 0x08, 0x68, +0xA0, 0x43, 0x08, 0x60, 0x01, 0x20, 0x98, 0x61, +0xD8, 0x61, 0x27, 0x49, 0x00, 0x20, 0x40, 0x39, +0xC8, 0x63, 0x18, 0x60, 0x98, 0x60, 0x25, 0x48, +0x82, 0x42, 0x30, 0xD1, 0x88, 0x6B, 0xA8, 0x43, +0x88, 0x63, 0x78, 0x69, 0x01, 0x21, 0x09, 0x04, +0x88, 0x43, 0x78, 0x61, 0x27, 0xE0, 0x0E, 0x68, +0x26, 0x43, 0x0E, 0x60, 0x62, 0x21, 0x99, 0x61, +0xD9, 0x61, 0x1B, 0x4C, 0x1C, 0x49, 0x40, 0x3C, +0xE1, 0x63, 0x1C, 0x49, 0x09, 0x68, 0x40, 0x31, +0x02, 0x28, 0x48, 0x7E, 0x23, 0xD0, 0x40, 0x1E, +0x40, 0x05, 0x40, 0x0D, 0x18, 0x60, 0x4E, 0x7E, +0x48, 0x7F, 0x41, 0x00, 0x40, 0x18, 0x80, 0x1C, +0x30, 0x18, 0x40, 0x05, 0x40, 0x0D, 0x98, 0x60, +0x10, 0x48, 0x82, 0x42, 0x07, 0xD1, 0xA0, 0x6B, +0x28, 0x43, 0xA0, 0x63, 0x79, 0x69, 0x01, 0x20, +0x00, 0x04, 0x01, 0x43, 0x79, 0x61, 0x19, 0x68, +0x0D, 0x48, 0x41, 0x63, 0x19, 0x68, 0x81, 0x63, +0x99, 0x68, 0x0B, 0x48, 0x40, 0x30, 0x41, 0x60, +0x99, 0x68, 0x81, 0x60, 0xF0, 0xBD, 0x40, 0x1E, +0x40, 0x05, 0x40, 0x0D, 0x18, 0x60, 0x4E, 0x7E, +0x08, 0x7F, 0xDA, 0xE7, 0xE4, 0x06, 0x00, 0x20, +0xC0, 0x11, 0x00, 0x50, 0xA2, 0x00, 0x03, 0xF3, +0x01, 0x00, 0x01, 0x00, 0xAC, 0x04, 0x00, 0x20, +0x80, 0x13, 0x00, 0x50, 0x01, 0x21, 0x89, 0x07, +0x00, 0xB5, 0x8A, 0x14, 0x00, 0x28, 0x08, 0x68, +0x04, 0xD0, 0x10, 0x43, 0x08, 0x60, 0xFD, 0xF7, +0xCD, 0xFA, 0x00, 0xBD, 0x90, 0x43, 0x08, 0x60, +0x00, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x03, 0x35, 0x03, 0x2C, 0x03, 0x9C, 0x03, 0xE9, 0x02, 0x28, 0x03, 0x29, 0x03, 0x1C, 0x03, 0x9D, 0x03, 0x2F, 0x03, 0x4A, 0x03, 0x4D, 0x03, @@ -1842,104 +1842,372 @@ const unsigned char u8_rad_fw_30[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x10, 0xB5, 0x00, 0x20, 0xFD, 0xF7, 0xCC, 0xFA, +0x2F, 0x4A, 0x90, 0x6A, 0x2F, 0x49, 0x08, 0x60, +0xD0, 0x6B, 0x48, 0x60, 0x2C, 0x48, 0x40, 0x30, +0x03, 0x69, 0x8B, 0x60, 0x43, 0x6A, 0xCB, 0x60, +0xD3, 0x6A, 0x0B, 0x61, 0x03, 0x68, 0x4B, 0x61, +0x43, 0x69, 0x8B, 0x61, 0x83, 0x6A, 0xCB, 0x61, +0x93, 0x6A, 0x0B, 0x62, 0xD3, 0x6B, 0x4B, 0x62, +0x03, 0x69, 0x8B, 0x62, 0x43, 0x6A, 0xCB, 0x62, +0xD3, 0x6A, 0x0B, 0x63, 0x03, 0x68, 0x4B, 0x63, +0x43, 0x69, 0x8B, 0x63, 0x83, 0x6A, 0xCB, 0x63, +0x93, 0x6A, 0x1E, 0x49, 0x40, 0x31, 0x0B, 0x60, +0xD3, 0x6B, 0x4B, 0x60, 0x03, 0x69, 0x8B, 0x60, +0x43, 0x6A, 0xCB, 0x60, 0xD3, 0x6A, 0x0B, 0x61, +0x03, 0x68, 0x4B, 0x61, 0x43, 0x69, 0x8B, 0x61, +0x83, 0x6A, 0xCB, 0x61, 0x93, 0x6A, 0x0B, 0x62, +0xD3, 0x6B, 0x4B, 0x62, 0x03, 0x69, 0x8B, 0x62, +0x43, 0x6A, 0xCB, 0x62, 0xD2, 0x6A, 0x0A, 0x63, +0x02, 0x68, 0x4A, 0x63, 0x42, 0x69, 0x8A, 0x63, +0x80, 0x6A, 0xC8, 0x63, 0x0C, 0x49, 0xC0, 0x31, +0x88, 0x6A, 0x80, 0x05, 0x82, 0x09, 0x88, 0x6A, +0x80, 0x05, 0x80, 0x0D, 0x02, 0x43, 0x09, 0x48, +0x80, 0x30, 0x02, 0x60, 0x8A, 0x6A, 0x89, 0x6A, +0x92, 0x05, 0x92, 0x09, 0x89, 0x05, 0x89, 0x0D, +0x0A, 0x43, 0x42, 0x60, 0x04, 0x49, 0xC1, 0x60, +0x01, 0x61, 0xFF, 0x21, 0x81, 0x60, 0x70, 0x47, +0x00, 0x11, 0x00, 0x50, 0x00, 0x13, 0x00, 0x50, +0xBC, 0x00, 0xBC, 0x00, 0x70, 0xB5, 0x04, 0x46, +0x81, 0x00, 0x26, 0x48, 0x41, 0x58, 0x26, 0x48, +0x0A, 0x68, 0x42, 0x60, 0x4A, 0x68, 0x82, 0x60, +0x8A, 0x68, 0x02, 0x61, 0xCA, 0x68, 0x42, 0x61, +0x22, 0x4D, 0x08, 0x69, 0x28, 0x62, 0x20, 0x4A, +0x48, 0x69, 0xC0, 0x32, 0x90, 0x61, 0x88, 0x69, +0xD0, 0x61, 0x48, 0x6A, 0x10, 0x62, 0x88, 0x6A, +0x50, 0x62, 0x1B, 0x48, 0xCB, 0x69, 0x40, 0x38, +0xC3, 0x60, 0x0B, 0x6A, 0x03, 0x61, 0xCB, 0x6A, +0x93, 0x62, 0x0B, 0x6B, 0xD3, 0x63, 0x17, 0x4B, +0x4E, 0x6B, 0x80, 0x3B, 0x1E, 0x61, 0x8E, 0x6B, +0x5E, 0x62, 0xCE, 0x6B, 0xD6, 0x62, 0x0A, 0x6C, +0x1A, 0x60, 0x4A, 0x6C, 0x5A, 0x61, 0x8A, 0x6C, +0x9A, 0x62, 0xCA, 0x6C, 0xAA, 0x62, 0x0E, 0x4B, +0x0A, 0x6D, 0x40, 0x33, 0x5A, 0x63, 0x0C, 0x4B, +0x4A, 0x6D, 0x80, 0x33, 0x9A, 0x60, 0x0C, 0x4A, +0x8B, 0x6D, 0x13, 0x60, 0xC9, 0x6D, 0x51, 0x60, +0xC2, 0x68, 0x0A, 0x49, 0x0A, 0x60, 0x00, 0x69, +0x48, 0x60, 0xFF, 0xF7, 0x51, 0xFF, 0x20, 0x46, +0xFF, 0xF7, 0x92, 0xFD, 0x20, 0x46, 0xFF, 0xF7, +0x97, 0xFE, 0x70, 0xBD, 0x0C, 0x04, 0x00, 0x20, +0x40, 0x10, 0x00, 0x50, 0xC0, 0x11, 0x00, 0x50, +0x00, 0x19, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, +0x10, 0xB5, 0x0C, 0x4C, 0x21, 0x78, 0x81, 0x42, +0x13, 0xD0, 0x20, 0x70, 0xFF, 0xF7, 0x4E, 0xFE, +0x20, 0x78, 0xFF, 0xF7, 0xA7, 0xFD, 0xFF, 0xF7, +0x71, 0xF9, 0x07, 0x48, 0x80, 0x79, 0x01, 0x28, +0x05, 0xD1, 0x06, 0x48, 0x01, 0x68, 0x01, 0x22, +0xD2, 0x02, 0x11, 0x43, 0x01, 0x60, 0xFF, 0xF7, +0x25, 0xFE, 0x10, 0xBD, 0x50, 0x07, 0x00, 0x20, +0x60, 0x07, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, +0x70, 0xB5, 0x05, 0x24, 0x64, 0x04, 0xFD, 0xF7, +0xB1, 0xF9, 0x00, 0x22, 0x0B, 0x49, 0x0C, 0x48, +0x0C, 0x4B, 0x05, 0xE0, 0x05, 0x78, 0xED, 0x07, +0x01, 0xD0, 0x0A, 0x72, 0x09, 0xE0, 0x64, 0x1E, +0x0D, 0x7A, 0x02, 0x2D, 0x02, 0xD0, 0x1D, 0x68, +0xED, 0x07, 0x02, 0xD0, 0x00, 0x2C, 0xF1, 0xD1, +0x01, 0xE0, 0x00, 0x2C, 0x00, 0xD1, 0x0A, 0x72, +0x70, 0xBD, 0x00, 0x00, 0x50, 0x02, 0x00, 0x20, +0x98, 0x01, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, +0x00, 0xB5, 0x03, 0x46, 0x00, 0x20, 0xFF, 0xF7, +0xB5, 0xFE, 0x05, 0x21, 0x09, 0x07, 0xC8, 0x69, +0x00, 0x2B, 0x03, 0xD0, 0xC0, 0x0B, 0xC0, 0x03, +0x05, 0x4A, 0x03, 0xE0, 0xC0, 0x0B, 0x04, 0x4A, +0xC0, 0x03, 0xFC, 0x3A, 0x80, 0x18, 0xC8, 0x61, +0x01, 0x20, 0xFF, 0xF7, 0xA3, 0xFE, 0x00, 0xBD, +0x50, 0x71, 0x00, 0x00, 0xF0, 0xB5, 0x70, 0x48, +0x70, 0x4A, 0x01, 0x78, 0x00, 0x20, 0x85, 0xB0, +0x10, 0x5E, 0x01, 0x90, 0x6E, 0x48, 0x00, 0x24, +0x02, 0x78, 0x6E, 0x48, 0x04, 0x25, 0x04, 0x5F, +0x6D, 0x48, 0x0F, 0x26, 0x00, 0x68, 0x90, 0x30, +0x00, 0x29, 0x01, 0xD0, 0x01, 0x29, 0x17, 0xD1, +0x65, 0x4B, 0x02, 0x27, 0x39, 0x43, 0x19, 0x70, +0x68, 0x49, 0x09, 0x78, 0x1D, 0x29, 0x24, 0xD1, +0x41, 0x7B, 0x03, 0x7B, 0x09, 0x02, 0x19, 0x43, +0x01, 0x9B, 0x99, 0x42, 0x1D, 0xDC, 0x13, 0x21, +0xC9, 0x43, 0x8C, 0x42, 0x01, 0xDA, 0x62, 0x49, +0x0A, 0x70, 0x17, 0x21, 0x61, 0x4B, 0x19, 0x70, +0x61, 0x49, 0x4B, 0x88, 0x58, 0x49, 0x09, 0x78, +0x03, 0x91, 0xC9, 0x07, 0xC9, 0x0F, 0x0B, 0x43, +0x79, 0xD0, 0x41, 0x7B, 0x03, 0x7B, 0x08, 0x02, +0x18, 0x43, 0x5C, 0x49, 0x00, 0x27, 0x00, 0x90, +0xCF, 0x5F, 0xB8, 0x42, 0x03, 0xDA, 0x04, 0x20, +0x02, 0xE0, 0x00, 0x21, 0xE6, 0xE7, 0x02, 0x20, +0x02, 0x90, 0x03, 0x98, 0x83, 0x07, 0x56, 0x48, +0x01, 0x78, 0x48, 0x1C, 0xC0, 0xB2, 0x00, 0x2B, +0x04, 0xDA, 0x10, 0x2F, 0x02, 0xDA, 0x02, 0x22, +0x02, 0x92, 0x15, 0xE0, 0x51, 0x4B, 0x1B, 0x78, +0xDB, 0x07, 0x11, 0xD0, 0x4E, 0x49, 0x00, 0x20, +0x08, 0x70, 0x13, 0x20, 0xC0, 0x43, 0x84, 0x42, +0x05, 0xDA, 0x49, 0x48, 0x80, 0x88, 0x05, 0x28, +0x01, 0xD2, 0x4B, 0x48, 0x02, 0x80, 0x30, 0x21, +0x4A, 0x48, 0xFC, 0xF7, 0xF6, 0xFE, 0x03, 0xE0, +0x45, 0x4A, 0x05, 0x29, 0x00, 0xD2, 0x10, 0x70, +0x43, 0x48, 0x01, 0x78, 0x02, 0x98, 0x81, 0x42, +0x3D, 0xD3, 0x45, 0x49, 0x10, 0x2F, 0x36, 0xDA, +0x1D, 0x20, 0xC0, 0x43, 0x84, 0x42, 0x32, 0xDD, +0x08, 0x78, 0xF0, 0x28, 0x01, 0xD2, 0x40, 0x1C, +0x08, 0x70, 0xC1, 0xB2, 0x02, 0x29, 0x12, 0xD9, +0x36, 0x4A, 0x10, 0x78, 0x16, 0x28, 0x03, 0xD2, +0x02, 0x28, 0x01, 0xD9, 0x17, 0x20, 0x10, 0x70, +0x31, 0x4A, 0x00, 0x20, 0x10, 0x70, 0x36, 0x4A, +0x14, 0x29, 0x10, 0x80, 0x03, 0xD9, 0x37, 0x49, +0x08, 0x70, 0x37, 0x49, 0x08, 0x70, 0x03, 0x98, +0x25, 0x4A, 0x04, 0x28, 0x15, 0xD0, 0x2B, 0x49, +0x08, 0x78, 0x18, 0x28, 0x30, 0xD2, 0x40, 0x1C, +0xC0, 0xB2, 0x08, 0x70, 0x10, 0x2F, 0x02, 0xDB, +0x62, 0x42, 0xBA, 0x42, 0x17, 0xDD, 0x26, 0x4A, +0x12, 0x7A, 0x01, 0x2A, 0x09, 0xD9, 0x03, 0x26, +0x02, 0x25, 0x08, 0xE0, 0x03, 0xE0, 0x00, 0x20, +0xE4, 0xE7, 0xFD, 0xF7, 0x3B, 0xFF, 0x05, 0xB0, +0xF0, 0xBD, 0x01, 0x26, 0x35, 0x46, 0x09, 0x22, +0xD2, 0x43, 0x94, 0x42, 0x03, 0xDD, 0x02, 0x28, +0x01, 0xD9, 0x18, 0x20, 0x08, 0x70, 0x23, 0x4C, +0x23, 0x4F, 0x00, 0x20, 0x42, 0x00, 0x11, 0x19, +0x00, 0x23, 0xCB, 0x5E, 0xBA, 0x5E, 0x73, 0x43, +0x9A, 0x18, 0x2A, 0x41, 0x40, 0x1C, 0xC0, 0xB2, +0x0A, 0x80, 0x30, 0x28, 0xF2, 0xD3, 0xE2, 0xE7, +0x03, 0x98, 0xC0, 0x07, 0x0D, 0xD0, 0x01, 0x9B, +0x00, 0x98, 0x98, 0x42, 0x06, 0xDB, 0x19, 0x4B, +0x00, 0x24, 0x1C, 0x5F, 0x40, 0x08, 0x63, 0x42, +0x98, 0x42, 0x02, 0xDA, 0x0C, 0x20, 0x08, 0x70, +0xD1, 0xE7, 0x04, 0x20, 0x10, 0x70, 0xCE, 0xE7, +0x8F, 0x02, 0x00, 0x20, 0x54, 0x07, 0x00, 0x20, +0xB7, 0x02, 0x00, 0x20, 0x56, 0x07, 0x00, 0x20, +0xAC, 0x04, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, +0x51, 0x07, 0x00, 0x20, 0xB5, 0x02, 0x00, 0x20, +0x60, 0x07, 0x00, 0x20, 0x58, 0x07, 0x00, 0x20, +0xED, 0x02, 0x00, 0x20, 0xEB, 0x02, 0x00, 0x20, +0xF4, 0x02, 0x00, 0x20, 0x1C, 0x03, 0x00, 0x20, +0xEA, 0x02, 0x00, 0x20, 0xEF, 0x02, 0x00, 0x20, +0xF0, 0x02, 0x00, 0x20, 0x00, 0x00, 0x01, 0x20, +0xD4, 0x00, 0x00, 0x20, 0x52, 0x07, 0x00, 0x20, +0xF8, 0xB5, 0x43, 0x4F, 0x01, 0x24, 0x38, 0x7B, +0x42, 0x49, 0x0A, 0x78, 0x42, 0x4B, 0x43, 0x4D, +0x43, 0x4E, 0x90, 0x42, 0x05, 0xD1, 0x28, 0x78, +0x81, 0x28, 0x02, 0xD0, 0x30, 0x78, 0xC0, 0x07, +0x57, 0xD0, 0x00, 0x20, 0x30, 0x70, 0x3B, 0x48, +0x00, 0x78, 0x07, 0x28, 0x0B, 0xD3, 0x18, 0x68, +0x40, 0x05, 0x40, 0x0F, 0x38, 0x73, 0x01, 0x20, +0xFC, 0xF7, 0x80, 0xFF, 0x00, 0x20, 0x30, 0x70, +0x38, 0x7B, 0x02, 0x28, 0x18, 0xD0, 0xA8, 0x79, +0x00, 0x28, 0x01, 0xD0, 0xFE, 0xF7, 0xC8, 0xFD, +0x38, 0x7B, 0x30, 0x49, 0x08, 0x70, 0x81, 0x20, +0x28, 0x70, 0x32, 0x48, 0x01, 0x23, 0x02, 0x88, +0x1B, 0x03, 0x1A, 0x43, 0x02, 0x80, 0x08, 0x78, +0x03, 0x00, 0xFC, 0xF7, 0x83, 0xFE, 0x07, 0x21, +0x2B, 0x2B, 0x49, 0x0B, 0x17, 0x0B, 0x49, 0x00, +0x83, 0x20, 0x28, 0x70, 0x05, 0x20, 0xFF, 0xF7, +0x57, 0xFC, 0xF8, 0xBD, 0x01, 0x20, 0xFE, 0xF7, +0x4D, 0xFD, 0x00, 0x20, 0xFF, 0xF7, 0x50, 0xFC, +0x00, 0x28, 0x01, 0xD0, 0x01, 0x20, 0x32, 0xE0, +0x00, 0x24, 0x31, 0xE0, 0x01, 0x20, 0xFE, 0xF7, +0x41, 0xFD, 0x01, 0x20, 0xFF, 0xF7, 0x44, 0xFC, +0x00, 0x28, 0xF5, 0xD0, 0x04, 0x20, 0x26, 0xE0, +0x00, 0x20, 0xFE, 0xF7, 0x37, 0xFD, 0x02, 0x20, +0xFF, 0xF7, 0x3A, 0xFC, 0x00, 0x28, 0xEB, 0xD0, +0x02, 0x20, 0x1C, 0xE0, 0x83, 0x20, 0x28, 0x70, +0x05, 0x20, 0xFF, 0xF7, 0x31, 0xFC, 0x04, 0x46, +0x16, 0xE0, 0x15, 0x4A, 0x10, 0x78, 0x00, 0x28, +0x12, 0xD0, 0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, +0x10, 0x70, 0x0D, 0xD1, 0x18, 0x68, 0x40, 0x05, +0x40, 0x0F, 0x38, 0x73, 0x38, 0x7B, 0x09, 0x78, +0x88, 0x42, 0x05, 0xD0, 0x30, 0x78, 0x01, 0x21, +0x08, 0x43, 0x30, 0x70, 0x81, 0x20, 0x28, 0x70, +0x30, 0x78, 0xC0, 0x07, 0x02, 0xD0, 0x81, 0x20, +0x28, 0x70, 0x00, 0x24, 0x20, 0x46, 0xF8, 0xBD, +0x50, 0x02, 0x00, 0x20, 0xE5, 0x02, 0x00, 0x20, +0x00, 0x11, 0x00, 0x50, 0x60, 0x07, 0x00, 0x20, +0x98, 0x01, 0x00, 0x20, 0xFE, 0x02, 0x00, 0x20, +0x61, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x0E, 0x4D, +0x28, 0x68, 0xFD, 0xF7, 0x5D, 0xFD, 0x04, 0x00, +0x07, 0xD0, 0x80, 0x21, 0x0B, 0x48, 0xFC, 0xF7, +0xB4, 0xFD, 0x0A, 0x49, 0x28, 0x68, 0x00, 0xF0, +0x2D, 0xF9, 0x09, 0x48, 0x00, 0x88, 0x81, 0x07, +0x06, 0xD0, 0x00, 0x07, 0x04, 0xD5, 0x80, 0x21, +0x04, 0x48, 0xFC, 0xF7, 0xA6, 0xFD, 0x00, 0x24, +0xFF, 0xF7, 0x44, 0xFE, 0x20, 0x46, 0x70, 0xBD, +0x00, 0x03, 0x00, 0x20, 0x2C, 0x06, 0x00, 0x20, +0xFE, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x46, +0x01, 0x24, 0xFE, 0xF7, 0x8D, 0xF8, 0x00, 0x2D, +0x09, 0xD0, 0xF0, 0x20, 0xFC, 0xF7, 0xC4, 0xFD, +0xFE, 0xF7, 0x2C, 0xF9, 0xFE, 0xF7, 0xDC, 0xF8, +0x00, 0x20, 0xFE, 0xF7, 0xC7, 0xFC, 0xFE, 0xF7, +0x69, 0xFD, 0xFF, 0xF7, 0xF1, 0xFA, 0x20, 0x46, +0x70, 0xBD, 0x00, 0x00, 0xF8, 0xB5, 0x54, 0x49, +0x08, 0x78, 0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, +0x08, 0x70, 0x52, 0x4B, 0x53, 0x48, 0x1A, 0x78, +0x51, 0x49, 0x00, 0x78, 0x00, 0x24, 0x04, 0x2A, +0x13, 0xD0, 0x00, 0x28, 0x00, 0xD0, 0x0C, 0x80, +0x50, 0x48, 0x4F, 0x4D, 0x02, 0x78, 0x29, 0x78, +0x50, 0x08, 0x00, 0x29, 0x13, 0xD0, 0x91, 0x42, +0x0F, 0xD2, 0x49, 0x1C, 0xC9, 0xB2, 0x29, 0x70, +0x88, 0x42, 0x0C, 0xD8, 0x01, 0x21, 0x19, 0x70, +0x09, 0xE0, 0x49, 0x4A, 0x12, 0x78, 0x10, 0x43, +0xEA, 0xD0, 0x08, 0x88, 0x40, 0x1C, 0x08, 0x80, +0xE6, 0xE7, 0x78, 0x21, 0x29, 0x70, 0x45, 0x4B, +0x45, 0x4A, 0x19, 0x78, 0x12, 0x78, 0x45, 0x4F, +0x45, 0x4D, 0x00, 0x29, 0x0B, 0xD1, 0x5E, 0x78, +0x00, 0x2E, 0x08, 0xD1, 0x9B, 0x78, 0x00, 0x2B, +0x05, 0xD1, 0x00, 0x2A, 0x03, 0xD1, 0x41, 0x4B, +0x1B, 0x78, 0x00, 0x2B, 0x13, 0xD0, 0x6C, 0x80, +0x3F, 0x48, 0x04, 0x70, 0x3F, 0x48, 0x04, 0x80, +0xA8, 0x88, 0x4B, 0x23, 0x1B, 0x01, 0x98, 0x42, +0x05, 0xD2, 0x08, 0x46, 0x10, 0x43, 0x02, 0xD0, +0xA8, 0x88, 0x40, 0x1C, 0xA8, 0x80, 0x00, 0x29, +0x36, 0xD0, 0x3C, 0x70, 0x39, 0xE0, 0xA9, 0x88, +0x00, 0x29, 0x05, 0xD0, 0x29, 0x7A, 0xFF, 0x29, +0x02, 0xD2, 0x29, 0x7A, 0x49, 0x1C, 0x29, 0x72, +0xAC, 0x80, 0x33, 0x49, 0x0C, 0x70, 0x33, 0x49, +0x0C, 0x70, 0x33, 0x49, 0x0C, 0x70, 0x69, 0x88, +0x01, 0x22, 0x12, 0x03, 0x91, 0x42, 0x02, 0xD2, +0x69, 0x88, 0x49, 0x1C, 0x69, 0x80, 0x2F, 0x4A, +0x11, 0x78, 0x00, 0x29, 0x01, 0xD0, 0x49, 0x1E, +0x11, 0x70, 0x27, 0x4A, 0x11, 0x78, 0x88, 0x42, +0x02, 0xD9, 0x49, 0x1C, 0x11, 0x70, 0x0A, 0xE0, +0x14, 0x70, 0x29, 0x4A, 0x23, 0x49, 0x00, 0x23, +0x00, 0x26, 0xD3, 0x5E, 0x8E, 0x5F, 0x9B, 0x19, +0x5B, 0x10, 0x13, 0x80, 0x0C, 0x80, 0x69, 0x88, +0x81, 0x42, 0x01, 0xD9, 0x23, 0x48, 0x04, 0x70, +0x38, 0x78, 0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, +0x38, 0x70, 0x28, 0x78, 0x01, 0x28, 0x0D, 0xD0, +0x02, 0x28, 0x0B, 0xD0, 0x04, 0x28, 0x09, 0xD0, +0x82, 0x28, 0x09, 0xD1, 0x1C, 0x4E, 0x30, 0x78, +0x30, 0x28, 0x05, 0xD1, 0xFE, 0xF7, 0xE0, 0xFC, +0x34, 0x70, 0x01, 0xE0, 0x00, 0xF0, 0xB6, 0xF8, +0x18, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, +0x81, 0x20, 0x28, 0x70, 0xF8, 0xBD, 0x00, 0x00, +0x4C, 0x07, 0x00, 0x20, 0x8F, 0x02, 0x00, 0x20, +0x6E, 0x02, 0x00, 0x20, 0xB9, 0x02, 0x00, 0x20, +0x73, 0x02, 0x00, 0x20, 0xB7, 0x02, 0x00, 0x20, +0xBB, 0x02, 0x00, 0x20, 0x7C, 0x07, 0x00, 0x20, +0x4F, 0x07, 0x00, 0x20, 0x4E, 0x07, 0x00, 0x20, +0x60, 0x07, 0x00, 0x20, 0x85, 0x02, 0x00, 0x20, +0xEE, 0x02, 0x00, 0x20, 0xFC, 0x02, 0x00, 0x20, +0xBF, 0x02, 0x00, 0x20, 0xCA, 0x02, 0x00, 0x20, +0xCB, 0x02, 0x00, 0x20, 0x51, 0x07, 0x00, 0x20, +0xFA, 0x02, 0x00, 0x20, 0xBE, 0x02, 0x00, 0x20, +0x88, 0x02, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, +0x70, 0xB5, 0x10, 0x4D, 0x68, 0x78, 0x00, 0x07, +0x01, 0xD5, 0x00, 0xF0, 0x11, 0xF9, 0x0E, 0x4C, +0x60, 0x22, 0x0E, 0x48, 0x21, 0x68, 0xFC, 0xF7, +0x87, 0xFC, 0xFC, 0xF7, 0x63, 0xFD, 0x68, 0x78, +0x96, 0x21, 0x08, 0x42, 0x01, 0xD0, 0x00, 0xF0, +0x03, 0xF9, 0x09, 0x48, 0x00, 0x78, 0x00, 0x06, +0x06, 0xD4, 0x01, 0x21, 0x20, 0x68, 0xFD, 0xF7, +0x8F, 0xFE, 0x20, 0x68, 0xFC, 0xF7, 0x66, 0xFD, +0x70, 0xBD, 0x00, 0x00, 0x88, 0x02, 0x00, 0x20, +0x00, 0x03, 0x00, 0x20, 0xD4, 0x00, 0x00, 0x20, +0x8E, 0x02, 0x00, 0x20, 0x30, 0xB5, 0x08, 0x4A, +0x14, 0x68, 0x1B, 0x34, 0x00, 0x22, 0xA3, 0x5C, +0x41, 0x2B, 0x03, 0xD0, 0x55, 0x00, 0x45, 0x5B, +0x5B, 0x00, 0xCD, 0x52, 0x52, 0x1C, 0xD2, 0xB2, +0x30, 0x2A, 0xF4, 0xD3, 0x30, 0xBD, 0x00, 0x00, +0xAC, 0x04, 0x00, 0x20, 0x01, 0x20, 0x80, 0x07, +0x40, 0x69, 0x40, 0x05, 0x01, 0xD5, 0x01, 0x20, +0x70, 0x47, 0x00, 0x20, 0x70, 0x47, 0x00, 0x00, +0x10, 0xB5, 0x00, 0x20, 0xFC, 0xF7, 0x9A, 0xFD, 0x05, 0x49, 0x08, 0x20, 0x08, 0x70, 0x05, 0x49, 0x06, 0x20, 0x08, 0x70, 0x01, 0x20, 0x04, 0x49, 0x80, 0x02, 0x08, 0x80, 0x10, 0xBD, 0x00, 0x00, -0xE5, 0x02, 0x00, 0x20, 0x71, 0x04, 0x00, 0x20, -0xF2, 0x02, 0x00, 0x20, 0x06, 0x49, 0x01, 0x20, +0xE5, 0x02, 0x00, 0x20, 0x4D, 0x07, 0x00, 0x20, +0xF6, 0x02, 0x00, 0x20, 0x06, 0x49, 0x01, 0x20, 0x08, 0x70, 0x06, 0x49, 0x08, 0x20, 0x08, 0x70, 0x05, 0x49, 0x06, 0x20, 0x08, 0x70, 0x01, 0x20, 0x04, 0x49, 0x80, 0x02, 0x08, 0x80, 0x70, 0x47, 0xE7, 0x02, 0x00, 0x20, 0xE5, 0x02, 0x00, 0x20, -0x71, 0x04, 0x00, 0x20, 0xF2, 0x02, 0x00, 0x20, -0xF8, 0xB5, 0x3E, 0x4C, 0x3E, 0x4A, 0x20, 0x88, -0x3E, 0x4D, 0xC1, 0x04, 0x0C, 0xD5, 0x51, 0x10, -0x88, 0x43, 0x20, 0x80, 0x29, 0x78, 0x00, 0x29, -0x03, 0xD0, 0x01, 0x29, 0x01, 0xD0, 0x02, 0x29, -0x65, 0xD1, 0x10, 0x43, 0x20, 0x80, 0x62, 0xE0, -0x81, 0x04, 0x03, 0xD5, 0x90, 0x43, 0x20, 0x80, -0xFE, 0xF7, 0xF8, 0xFB, 0x20, 0x88, 0x81, 0x07, -0x0E, 0xD0, 0x01, 0x04, 0x57, 0xD5, 0x40, 0x04, -0x40, 0x0C, 0x20, 0x80, 0x81, 0x07, 0x01, 0xD5, -0x00, 0x20, 0x02, 0xE0, 0xC0, 0x07, 0x4E, 0xD0, -0x01, 0x20, 0xFF, 0xF7, 0x67, 0xFB, 0x4A, 0xE0, -0x00, 0x04, 0x5A, 0x21, 0x2A, 0x4F, 0x2B, 0x4E, +0x4D, 0x07, 0x00, 0x20, 0xF6, 0x02, 0x00, 0x20, +0x10, 0xB5, 0xFF, 0xF7, 0x99, 0xFC, 0x04, 0x48, +0x00, 0x78, 0x00, 0x28, 0x02, 0xD1, 0x01, 0x20, +0xFF, 0xF7, 0xC0, 0xF9, 0x10, 0xBD, 0x00, 0x00, +0x90, 0x02, 0x00, 0x20, 0xF8, 0xB5, 0x3E, 0x4C, +0x3E, 0x4A, 0x20, 0x88, 0x3E, 0x4D, 0xC1, 0x04, +0x0C, 0xD5, 0x51, 0x10, 0x88, 0x43, 0x20, 0x80, +0x29, 0x78, 0x00, 0x29, 0x03, 0xD0, 0x01, 0x29, +0x01, 0xD0, 0x02, 0x29, 0x67, 0xD1, 0x10, 0x43, +0x20, 0x80, 0x64, 0xE0, 0x81, 0x04, 0x03, 0xD5, +0x90, 0x43, 0x20, 0x80, 0xFE, 0xF7, 0x80, 0xFB, +0x20, 0x88, 0x81, 0x07, 0x0C, 0xD0, 0x01, 0x04, +0x59, 0xD5, 0x40, 0x04, 0x40, 0x0C, 0x20, 0x80, +0x81, 0x07, 0x57, 0xD4, 0xC0, 0x07, 0x52, 0xD0, +0x01, 0x20, 0xFF, 0xF7, 0x45, 0xFC, 0x4E, 0xE0, +0x00, 0x04, 0x78, 0x21, 0x2B, 0x4F, 0x2C, 0x4E, 0x00, 0x28, 0x05, 0xDA, 0x38, 0x78, 0x00, 0x28, 0x00, 0xD0, 0x31, 0x80, 0x00, 0x20, 0x20, 0x80, -0x27, 0x48, 0x40, 0x88, 0x00, 0x28, 0x19, 0xD0, -0x26, 0x48, 0x00, 0x78, 0x04, 0x28, 0x15, 0xD3, -0x25, 0x4A, 0x00, 0x20, 0x10, 0x5E, 0x27, 0x22, -0xD2, 0x43, 0x90, 0x42, 0x0E, 0xDB, 0x23, 0x48, -0x00, 0x68, 0x90, 0x30, 0x42, 0x7B, 0x03, 0x7B, -0x10, 0x02, 0x18, 0x43, 0x42, 0x00, 0x80, 0x18, -0x1F, 0x4A, 0x00, 0x23, 0xD3, 0x5E, 0x80, 0x08, -0x98, 0x42, 0x01, 0xDA, 0x31, 0x80, 0x04, 0xE0, -0x30, 0x88, 0x00, 0x28, 0x1B, 0xD0, 0x5A, 0x28, -0x08, 0xD1, 0x38, 0x78, 0x00, 0x28, 0x05, 0xD1, -0x18, 0xA0, 0x01, 0xF0, 0x6B, 0xFB, 0x01, 0x20, -0xFF, 0xF7, 0x2C, 0xFB, 0x28, 0x78, 0x02, 0x28, -0x04, 0xD1, 0x30, 0x88, 0x5A, 0x28, 0x01, 0xD1, -0x2D, 0x20, 0x30, 0x80, 0x30, 0x88, 0x40, 0x1E, +0x28, 0x48, 0x40, 0x88, 0x00, 0x28, 0x20, 0xD0, +0x27, 0x48, 0x00, 0x78, 0x04, 0x28, 0x1C, 0xD3, +0x26, 0x48, 0x00, 0x78, 0x00, 0x28, 0x18, 0xD1, +0x25, 0x48, 0x00, 0x78, 0x00, 0x28, 0x14, 0xD1, +0x24, 0x4A, 0x10, 0x5E, 0x1D, 0x22, 0xD2, 0x43, +0x90, 0x42, 0x0E, 0xDB, 0x22, 0x48, 0x00, 0x68, +0x90, 0x30, 0x42, 0x7B, 0x03, 0x7B, 0x10, 0x02, +0x18, 0x43, 0x42, 0x00, 0x80, 0x18, 0x1F, 0x4A, +0x00, 0x23, 0xD3, 0x5E, 0x80, 0x08, 0x98, 0x42, +0x01, 0xDA, 0x31, 0x80, 0x04, 0xE0, 0x30, 0x88, +0x00, 0x28, 0x18, 0xD0, 0x78, 0x28, 0x05, 0xD1, +0x38, 0x78, 0x00, 0x28, 0x02, 0xD1, 0x01, 0x20, +0xFF, 0xF7, 0x06, 0xFC, 0x28, 0x78, 0x02, 0x28, +0x04, 0xD1, 0x30, 0x88, 0x78, 0x28, 0x01, 0xD1, +0x3C, 0x20, 0x30, 0x80, 0x30, 0x88, 0x40, 0x1E, 0x80, 0xB2, 0x30, 0x80, 0x39, 0x78, 0x00, 0x29, -0x01, 0xD0, 0x00, 0x28, 0x02, 0xD0, 0xFD, 0xF7, -0x2B, 0xF9, 0xF8, 0xBD, 0x0D, 0xA0, 0x01, 0xF0, -0x51, 0xFB, 0xA5, 0xE7, 0xF6, 0x02, 0x00, 0x20, -0x00, 0x20, 0x00, 0x00, 0x85, 0x02, 0x00, 0x20, -0x74, 0x04, 0x00, 0x20, 0x7E, 0x04, 0x00, 0x20, -0x84, 0x04, 0x00, 0x20, 0x87, 0x02, 0x00, 0x20, -0x76, 0x04, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, -0x78, 0x04, 0x00, 0x20, 0x53, 0x50, 0x55, 0x0D, -0x0A, 0x00, 0x00, 0x00, 0x53, 0x50, 0x44, 0x0D, -0x0A, 0x00, 0x00, 0x00, 0xF8, 0xB5, 0x55, 0x4E, -0x30, 0x78, 0x00, 0x28, 0x7E, 0xD1, 0x70, 0x78, -0x44, 0x08, 0x70, 0x88, 0x64, 0x00, 0x00, 0x0A, -0xC0, 0x07, 0x03, 0xD0, 0x70, 0x88, 0x00, 0x0A, -0x00, 0x02, 0x04, 0x43, 0x30, 0x78, 0x00, 0x28, -0x01, 0xD1, 0x70, 0x88, 0x04, 0x43, 0x70, 0x78, -0xC0, 0x07, 0x6B, 0xD1, 0x4A, 0x48, 0x42, 0x2C, -0x41, 0x69, 0x2F, 0xD0, 0x0F, 0xDC, 0x49, 0x4F, -0x08, 0x2C, 0x2F, 0xD0, 0x05, 0xDC, 0x02, 0x2C, -0x1A, 0xD0, 0x04, 0x2C, 0x7C, 0xD1, 0x46, 0x4F, -0x16, 0xE0, 0x10, 0x2C, 0x26, 0xD0, 0x20, 0x2C, -0x76, 0xD1, 0x80, 0x22, 0x5B, 0xE0, 0x50, 0x2C, -0x04, 0xD0, 0x05, 0xDC, 0x44, 0x2C, 0x1B, 0xD0, -0x48, 0x2C, 0x6D, 0xD1, 0x87, 0x69, 0x05, 0xE0, -0xFF, 0x3C, 0x09, 0x3C, 0x01, 0xD0, 0x08, 0x2C, -0x66, 0xD1, 0x0F, 0x46, 0x00, 0x2F, 0x63, 0xD0, -0x80, 0x21, 0x3A, 0x48, 0xFC, 0xF7, 0x75, 0xFF, -0x39, 0x48, 0x00, 0x78, 0x00, 0x06, 0x5C, 0xD5, -0x60, 0x22, 0x39, 0x46, 0x35, 0x48, 0xFC, 0xF7, -0x53, 0xFF, 0x5A, 0xE0, 0x35, 0x4F, 0xEF, 0xE7, -0x35, 0x4F, 0xED, 0xE7, 0x60, 0x22, 0x35, 0x48, -0xFC, 0xF7, 0x4A, 0xFF, 0x33, 0x4D, 0x20, 0x07, -0x0A, 0xD5, 0x2F, 0x20, 0x41, 0x00, 0xCB, 0x19, -0x6A, 0x5A, 0x1B, 0x88, 0x40, 0x1E, 0xD2, 0x1A, -0x40, 0xB2, 0x6A, 0x52, 0x00, 0x28, 0xF5, 0xDA, -0x00, 0x21, 0x2C, 0x48, 0xFD, 0xF7, 0x9A, 0xFF, -0x2A, 0x48, 0xFD, 0xF7, 0x5B, 0xF9, 0x26, 0x48, -0x00, 0x78, 0x00, 0x06, 0x04, 0xD5, 0x60, 0x22, -0x26, 0x49, 0x27, 0x48, 0xFC, 0xF7, 0x2C, 0xFF, -0x24, 0x48, 0xFD, 0xF7, 0x73, 0xFE, 0x20, 0x07, -0x0A, 0xD5, 0x2F, 0x20, 0x41, 0x00, 0xCB, 0x19, -0x6A, 0x5A, 0x1B, 0x88, 0x40, 0x1E, 0xD2, 0x18, -0x40, 0xB2, 0x6A, 0x52, 0x00, 0x28, 0xF5, 0xDA, -0x19, 0x48, 0x00, 0xE0, 0x1C, 0xE0, 0x00, 0x78, -0x00, 0x06, 0x05, 0xD5, 0x60, 0x22, 0x19, 0x49, -0x14, 0x48, 0xFC, 0xF7, 0x11, 0xFF, 0x0F, 0xE0, -0x18, 0x49, 0x12, 0x4B, 0x0A, 0x68, 0x00, 0x20, -0x11, 0x18, 0xC9, 0x7E, 0x41, 0x29, 0x03, 0xD0, -0x44, 0x00, 0x2C, 0x5B, 0x49, 0x00, 0x5C, 0x52, -0x40, 0x1C, 0x40, 0xB2, 0x30, 0x28, 0xF3, 0xDB, +0x01, 0xD0, 0x00, 0x28, 0x02, 0xD0, 0xFC, 0xF7, +0x03, 0xFD, 0xF8, 0xBD, 0x00, 0x20, 0xA8, 0xE7, +0xFE, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, +0x87, 0x02, 0x00, 0x20, 0x50, 0x07, 0x00, 0x20, +0x5A, 0x07, 0x00, 0x20, 0x60, 0x07, 0x00, 0x20, +0x8F, 0x02, 0x00, 0x20, 0xB9, 0x02, 0x00, 0x20, +0xBB, 0x02, 0x00, 0x20, 0x52, 0x07, 0x00, 0x20, +0xAC, 0x04, 0x00, 0x20, 0x54, 0x07, 0x00, 0x20, +0xF8, 0xB5, 0x5A, 0x4E, 0x30, 0x78, 0x00, 0x28, +0x7E, 0xD1, 0x70, 0x78, 0x44, 0x08, 0x70, 0x88, +0x64, 0x00, 0x00, 0x0A, 0xC0, 0x07, 0x03, 0xD0, +0x70, 0x88, 0x00, 0x0A, 0x00, 0x02, 0x04, 0x43, +0x30, 0x78, 0x00, 0x28, 0x01, 0xD1, 0x70, 0x88, +0x04, 0x43, 0x70, 0x78, 0xC0, 0x07, 0x6B, 0xD1, +0x4F, 0x4A, 0x50, 0x48, 0xD1, 0x69, 0x05, 0x78, +0x42, 0x2C, 0x2B, 0xD0, 0x0E, 0xDC, 0x08, 0x2C, +0x2C, 0xD0, 0x05, 0xDC, 0x02, 0x2C, 0x23, 0xD0, +0x04, 0x2C, 0x5D, 0xD1, 0x4A, 0x4F, 0x16, 0xE0, +0x10, 0x2C, 0x23, 0xD0, 0x20, 0x2C, 0x57, 0xD1, +0x80, 0x22, 0x66, 0xE0, 0x50, 0x2C, 0x04, 0xD0, +0x05, 0xDC, 0x44, 0x2C, 0x18, 0xD0, 0x48, 0x2C, +0x7E, 0xD1, 0x17, 0x6A, 0x05, 0xE0, 0xFF, 0x3C, +0x09, 0x3C, 0x01, 0xD0, 0x08, 0x2C, 0x77, 0xD1, +0x0F, 0x46, 0x00, 0x2F, 0x74, 0xD0, 0x80, 0x21, +0x3E, 0x48, 0xFC, 0xF7, 0x52, 0xFB, 0x28, 0x06, +0x66, 0xD5, 0x60, 0x22, 0x39, 0x46, 0x4D, 0xE0, +0x3B, 0x4F, 0xF4, 0xE7, 0x3B, 0x4F, 0xF2, 0xE7, +0x3B, 0x4F, 0xF0, 0xE7, 0x60, 0x22, 0x3B, 0x48, +0xFC, 0xF7, 0x2A, 0xFB, 0x20, 0x07, 0x0C, 0xD5, +0x38, 0x4F, 0x2F, 0x20, 0x41, 0x00, 0x34, 0x4A, +0x7B, 0x5A, 0x8A, 0x18, 0x12, 0x88, 0x40, 0x1E, +0x9A, 0x1A, 0x40, 0xB2, 0x7A, 0x52, 0x00, 0x28, +0xF4, 0xDA, 0x2C, 0x4F, 0x28, 0x06, 0x08, 0xD4, +0x31, 0x48, 0x32, 0x49, 0x00, 0x68, 0x88, 0x42, +0x03, 0xD0, 0x00, 0x21, 0x2D, 0x48, 0xFD, 0xF7, +0x27, 0xFD, 0x38, 0x78, 0x80, 0x07, 0x02, 0xD5, +0x2A, 0x48, 0xFC, 0xF7, 0xFB, 0xFB, 0x3D, 0x78, +0x28, 0x06, 0x04, 0xD5, 0x60, 0x22, 0x27, 0x49, +0x29, 0x48, 0xFC, 0xF7, 0x01, 0xFB, 0xE8, 0x07, +0x02, 0xD1, 0x24, 0x48, 0xFD, 0xF7, 0xB8, 0xFA, +0x20, 0x07, 0x0E, 0xD5, 0x2F, 0x20, 0x00, 0xE0, +0x2E, 0xE0, 0x1D, 0x4A, 0x1F, 0x4C, 0x41, 0x00, +0x8D, 0x18, 0x63, 0x5A, 0x2D, 0x88, 0x40, 0x1E, +0x5B, 0x19, 0x40, 0xB2, 0x63, 0x52, 0x00, 0x28, +0xF5, 0xDA, 0x38, 0x78, 0x00, 0x06, 0x05, 0xD5, +0x60, 0x22, 0x18, 0x49, 0x13, 0x48, 0xFC, 0xF7, +0xE3, 0xFA, 0x15, 0xE0, 0x19, 0x49, 0x15, 0x4B, +0x10, 0x4C, 0x0A, 0x68, 0x00, 0x20, 0x11, 0x18, +0xC9, 0x7E, 0x41, 0x29, 0x03, 0xD0, 0x45, 0x00, +0x5D, 0x5B, 0x49, 0x00, 0x65, 0x52, 0x40, 0x1C, +0x40, 0xB2, 0x30, 0x28, 0xF3, 0xDB, 0x03, 0xE0, +0x08, 0x49, 0x38, 0x46, 0xFF, 0xF7, 0x62, 0xFE, 0x70, 0x78, 0x01, 0x21, 0x08, 0x43, 0x70, 0x70, -0xF8, 0xBD, 0x08, 0x49, 0x38, 0x46, 0xFF, 0xF7, -0x37, 0xFE, 0x71, 0x78, 0x01, 0x20, 0x01, 0x43, -0x71, 0x70, 0xF8, 0xBD, 0x88, 0x02, 0x00, 0x20, -0xE4, 0x02, 0x00, 0x20, 0x00, 0x00, 0x01, 0x20, +0xF8, 0xBD, 0x00, 0x00, 0x88, 0x02, 0x00, 0x20, +0xE4, 0x02, 0x00, 0x20, 0x8E, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x50, 0x9C, 0x01, 0x00, 0x20, -0x86, 0x02, 0x00, 0x20, 0x00, 0x00, 0x02, 0x20, -0x00, 0x30, 0x00, 0x50, 0x4C, 0x07, 0x00, 0x20, -0x4C, 0x00, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, +0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x02, 0x20, +0x00, 0x30, 0x00, 0x50, 0x2C, 0x06, 0x00, 0x20, +0xE4, 0x06, 0x00, 0x20, 0xA1, 0x00, 0x03, 0xF3, +0x4C, 0x00, 0x00, 0x20, 0xAC, 0x04, 0x00, 0x20, 0xF7, 0xB5, 0x45, 0x49, 0x84, 0xB0, 0x00, 0x28, 0x13, 0xD0, 0x44, 0x48, 0x00, 0x88, 0x84, 0xB2, 0x05, 0x98, 0x00, 0x28, 0x03, 0xD0, 0x90, 0x68, @@ -1956,13 +2224,13 @@ const unsigned char u8_rad_fw_30[] = { 0x01, 0x46, 0xC0, 0x18, 0x00, 0x04, 0x40, 0x0C, 0x83, 0x42, 0x01, 0xDB, 0x18, 0x46, 0x00, 0xE0, 0x50, 0x60, 0x41, 0x18, 0x68, 0x43, 0xFC, 0xF7, -0x87, 0xFE, 0x00, 0xB2, 0x00, 0x90, 0x29, 0x48, +0x57, 0xFA, 0x00, 0xB2, 0x00, 0x90, 0x29, 0x48, 0x00, 0x88, 0xC1, 0x00, 0x40, 0x18, 0xC0, 0x08, 0x28, 0x1A, 0x81, 0xB2, 0x00, 0x98, 0x01, 0x91, 0x88, 0x42, 0x12, 0xDD, 0x09, 0x21, 0xE8, 0x08, -0xFC, 0xF7, 0x76, 0xFE, 0x01, 0x06, 0x09, 0x0E, +0xFC, 0xF7, 0x46, 0xFA, 0x01, 0x06, 0x09, 0x0E, 0x00, 0xD1, 0x01, 0x21, 0x01, 0x9A, 0x00, 0x98, -0x80, 0x1A, 0xFC, 0xF7, 0x6D, 0xFE, 0x20, 0x30, +0x80, 0x1A, 0xFC, 0xF7, 0x3D, 0xFA, 0x20, 0x30, 0xC0, 0xB2, 0x28, 0x28, 0x02, 0xD2, 0x07, 0x46, 0x00, 0xE0, 0x03, 0x9F, 0x00, 0x98, 0x69, 0x00, 0x47, 0x43, 0x08, 0x37, 0x38, 0x11, 0x08, 0x1A, @@ -1971,33 +2239,32 @@ const unsigned char u8_rad_fw_30[] = { 0x05, 0x99, 0x00, 0x29, 0x01, 0xD0, 0x30, 0x18, 0x00, 0xE0, 0x30, 0x1A, 0x02, 0x99, 0x65, 0x08, 0x49, 0x00, 0x40, 0x1B, 0x61, 0x1A, 0x60, 0x43, -0x4A, 0x10, 0x80, 0x18, 0xFC, 0xF7, 0x48, 0xFE, +0x4A, 0x10, 0x80, 0x18, 0xFC, 0xF7, 0x18, 0xFA, 0x28, 0x18, 0x01, 0x28, 0x01, 0xDA, 0x01, 0x20, 0x02, 0xE0, 0xA0, 0x42, 0x00, 0xDD, 0x20, 0x46, 0x40, 0x1E, 0x00, 0xB2, 0x07, 0xB0, 0xF0, 0xBD, -0xB8, 0x02, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20, -0x2E, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, -0x2F, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x20, +0xAC, 0x04, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, +0x38, 0x00, 0x00, 0x20, 0x46, 0x00, 0x00, 0x20, +0x39, 0x00, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20, 0xF7, 0xB5, 0x1A, 0x49, 0x09, 0x68, 0x80, 0x31, 0x00, 0x28, 0x06, 0xD0, 0x18, 0x48, 0x0E, 0x7C, 0x00, 0x88, 0xCD, 0x7C, 0x84, 0xB2, 0x4F, 0x7D, 0x05, 0xE0, 0x16, 0x48, 0x4E, 0x7C, 0x00, 0x88, 0x0D, 0x7D, 0x8F, 0x7D, 0x84, 0xB2, 0x10, 0x69, 0xD1, 0x68, 0x68, 0x43, 0x4A, 0x10, 0x80, 0x18, -0xFC, 0xF7, 0x16, 0xFE, 0xC1, 0x19, 0x01, 0x98, +0xFC, 0xF7, 0xE6, 0xF9, 0xC1, 0x19, 0x01, 0x98, 0x40, 0x1E, 0x68, 0x43, 0x08, 0x18, 0x69, 0x08, 0x40, 0x1A, 0x65, 0x08, 0x71, 0x00, 0x40, 0x1B, 0x61, 0x1A, 0x60, 0x43, 0x4A, 0x10, 0x80, 0x18, -0xFC, 0xF7, 0x06, 0xFE, 0x28, 0x18, 0x01, 0x28, +0xFC, 0xF7, 0xD6, 0xF9, 0x28, 0x18, 0x01, 0x28, 0x01, 0xDA, 0x01, 0x20, 0x02, 0xE0, 0xA0, 0x42, 0x00, 0xDD, 0x20, 0x46, 0x40, 0x1E, 0x00, 0xB2, -0xFE, 0xBD, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20, -0x42, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, -0x05, 0x48, 0x00, 0x68, 0x90, 0x30, 0x41, 0x7B, -0x02, 0x7B, 0x08, 0x02, 0x10, 0x43, 0x03, 0x4A, -0x02, 0x21, 0x11, 0x80, 0x00, 0xB2, 0x70, 0x47, -0xB8, 0x02, 0x00, 0x20, 0x26, 0x00, 0x00, 0x20, -0x70, 0xB5, 0x1A, 0x4D, 0x16, 0x20, 0x28, 0x70, +0xFE, 0xBD, 0x00, 0x00, 0xAC, 0x04, 0x00, 0x20, +0x44, 0x00, 0x00, 0x20, 0x46, 0x00, 0x00, 0x20, +0x04, 0x48, 0x00, 0x68, 0x90, 0x30, 0x41, 0x7B, +0x02, 0x7B, 0x08, 0x02, 0x10, 0x43, 0x00, 0xB2, +0x70, 0x47, 0x00, 0x00, 0xAC, 0x04, 0x00, 0x20, +0x70, 0xB5, 0x1A, 0x4D, 0x1E, 0x20, 0x28, 0x70, 0x68, 0x78, 0x29, 0x46, 0x00, 0x24, 0x14, 0x31, 0x02, 0x28, 0x0C, 0xD8, 0x00, 0x28, 0x05, 0xD1, 0xA8, 0x78, 0x00, 0x28, 0x02, 0xD0, 0x08, 0x46, @@ -2006,11 +2273,11 @@ const unsigned char u8_rad_fw_30[] = { 0x2C, 0x81, 0xEC, 0x80, 0xAC, 0x81, 0x6C, 0x81, 0x4C, 0x80, 0x8C, 0x80, 0x0C, 0x72, 0x0C, 0x48, 0x44, 0x70, 0x84, 0x70, 0x04, 0x70, 0x09, 0x48, -0xB4, 0x21, 0xD0, 0x30, 0xFC, 0xF7, 0xE9, 0xFD, +0xB4, 0x21, 0xD0, 0x30, 0xFC, 0xF7, 0xBD, 0xF9, 0x06, 0x48, 0xA0, 0x21, 0x30, 0x30, 0xFC, 0xF7, -0xE4, 0xFD, 0xEC, 0x70, 0x5A, 0x20, 0xE8, 0x81, -0x2C, 0x71, 0x2C, 0x61, 0x01, 0xF0, 0x66, 0xFC, -0x70, 0xBD, 0x00, 0x00, 0x70, 0x04, 0x00, 0x20, +0xB8, 0xF9, 0xEC, 0x70, 0x78, 0x20, 0xE8, 0x81, +0x01, 0x20, 0x28, 0x71, 0x2C, 0x61, 0x01, 0xF0, +0x49, 0xFC, 0x70, 0xBD, 0x4C, 0x07, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, 0xFF, 0xB5, 0xA5, 0xB0, 0x18, 0xB2, 0x17, 0x90, 0x00, 0x20, 0x0D, 0x90, 0x25, 0x98, 0x01, 0x78, 0x03, 0x91, 0x26, 0x98, @@ -2037,9 +2304,9 @@ const unsigned char u8_rad_fw_30[] = { 0x07, 0x9A, 0x04, 0x98, 0x10, 0x1A, 0x40, 0x1E, 0x23, 0x90, 0x16, 0x98, 0x02, 0x90, 0x00, 0x20, 0x08, 0x90, 0x0C, 0x90, 0x32, 0x21, 0xD6, 0x48, -0xFC, 0xF7, 0x6F, 0xFD, 0x0A, 0x21, 0xD5, 0x48, -0xFC, 0xF7, 0x6B, 0xFD, 0x0A, 0x21, 0xD4, 0x48, -0xFC, 0xF7, 0x67, 0xFD, 0xD2, 0x48, 0x69, 0x46, +0xFC, 0xF7, 0x43, 0xF9, 0x0A, 0x21, 0xD5, 0x48, +0xFC, 0xF7, 0x3F, 0xF9, 0x0A, 0x21, 0xD4, 0x48, +0xFC, 0xF7, 0x3B, 0xF9, 0xD2, 0x48, 0x69, 0x46, 0x00, 0x1D, 0x12, 0x90, 0xCF, 0x48, 0x00, 0x1D, 0x09, 0x90, 0x00, 0x20, 0x48, 0x70, 0xCA, 0x48, 0x00, 0x78, 0x01, 0x28, 0x01, 0xD9, 0x02, 0x20, @@ -2105,7 +2372,7 @@ const unsigned char u8_rad_fw_30[] = { 0x01, 0x99, 0x08, 0x40, 0x01, 0x07, 0x00, 0xE0, 0x39, 0xE0, 0x09, 0x0F, 0x06, 0x98, 0x81, 0x43, 0x1F, 0xD0, 0x1B, 0x98, 0x80, 0xB2, 0x00, 0xF0, -0xFF, 0xFB, 0xC1, 0x07, 0x19, 0xD1, 0x0C, 0xE0, +0x13, 0xFC, 0xC1, 0x07, 0x19, 0xD1, 0x0C, 0xE0, 0x01, 0x46, 0x04, 0x20, 0xC8, 0x40, 0x81, 0x07, 0x89, 0x0F, 0x03, 0xE0, 0x02, 0x21, 0x81, 0x40, 0x0C, 0x20, 0x01, 0x40, 0x01, 0x98, 0x01, 0x43, @@ -2141,11 +2408,11 @@ const unsigned char u8_rad_fw_30[] = { 0x16, 0x98, 0x00, 0x24, 0x69, 0x46, 0x0E, 0x94, 0x02, 0x90, 0x88, 0x78, 0x08, 0x70, 0x68, 0x46, 0x81, 0x78, 0x64, 0x1C, 0xE4, 0xB2, 0x02, 0x2C, -0x10, 0xE0, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20, -0x4C, 0x07, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20, -0x40, 0x05, 0x00, 0x20, 0x2A, 0x00, 0x00, 0x20, -0x1C, 0x02, 0x00, 0x20, 0x1A, 0x00, 0x00, 0x20, -0x10, 0x00, 0x00, 0x20, 0x00, 0xD8, 0x6B, 0xE6, +0x10, 0xE0, 0x00, 0x00, 0xAC, 0x04, 0x00, 0x20, +0x2C, 0x06, 0x00, 0x20, 0x14, 0x00, 0x00, 0x20, +0x1C, 0x08, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20, +0x1C, 0x02, 0x00, 0x20, 0x26, 0x00, 0x00, 0x20, +0x1C, 0x00, 0x00, 0x20, 0x00, 0xD8, 0x6B, 0xE6, 0x08, 0x98, 0x01, 0x28, 0x1B, 0xD0, 0x1F, 0x99, 0x88, 0x42, 0x18, 0xDA, 0x2C, 0x48, 0x00, 0x68, 0xB0, 0x30, 0x41, 0x7A, 0x02, 0x7A, 0x08, 0x02, @@ -2166,14 +2433,14 @@ const unsigned char u8_rad_fw_30[] = { 0x40, 0x1C, 0x54, 0x52, 0x80, 0xB2, 0x08, 0x99, 0x88, 0x42, 0xF3, 0xD3, 0x0D, 0x98, 0x02, 0x28, 0x00, 0xD2, 0xF6, 0xE5, 0x27, 0x9A, 0x0D, 0x48, -0x18, 0x32, 0x25, 0x99, 0xFD, 0xF7, 0x1E, 0xF9, -0x0B, 0x48, 0x27, 0x9A, 0x26, 0x99, 0xFD, 0xF7, -0x19, 0xF9, 0x17, 0x9A, 0x0C, 0x99, 0x91, 0x42, +0x18, 0x32, 0x25, 0x99, 0xFC, 0xF7, 0x2C, 0xFD, +0x0B, 0x48, 0x27, 0x9A, 0x26, 0x99, 0xFC, 0xF7, +0x27, 0xFD, 0x17, 0x9A, 0x0C, 0x99, 0x91, 0x42, 0x00, 0xDC, 0x08, 0x48, 0x29, 0xB0, 0xF0, 0xBD, -0xB8, 0x02, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20, -0x40, 0x05, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, -0x1C, 0x02, 0x00, 0x20, 0x1A, 0x00, 0x00, 0x20, -0x10, 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0xFF, +0xAC, 0x04, 0x00, 0x20, 0x14, 0x00, 0x00, 0x20, +0x1C, 0x08, 0x00, 0x20, 0x2C, 0x06, 0x00, 0x20, +0x1C, 0x02, 0x00, 0x20, 0x26, 0x00, 0x00, 0x20, +0x1C, 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0xFF, 0x06, 0x4A, 0x07, 0x4B, 0x00, 0x21, 0x00, 0x20, 0x40, 0x1C, 0x90, 0x42, 0xFC, 0xDB, 0x49, 0x1C, 0x99, 0x42, 0xF8, 0xDB, 0x03, 0x48, 0x01, 0x21, @@ -2185,14 +2452,14 @@ const unsigned char u8_rad_fw_30[] = { 0x0D, 0x90, 0x1A, 0xE0, 0x34, 0x21, 0x69, 0x43, 0x34, 0x22, 0x0E, 0x18, 0x62, 0x43, 0x17, 0x18, 0x31, 0x79, 0x38, 0x79, 0x81, 0x42, 0x0E, 0xD9, -0x31, 0x1D, 0x34, 0x22, 0x68, 0x46, 0xFC, 0xF7, -0x03, 0xFB, 0x39, 0x1D, 0x30, 0x1D, 0x34, 0x22, -0xFC, 0xF7, 0xFE, 0xFA, 0x38, 0x1D, 0x34, 0x22, -0x69, 0x46, 0xFC, 0xF7, 0xF9, 0xFA, 0x64, 0x1C, +0x31, 0x1D, 0x34, 0x22, 0x68, 0x46, 0xFB, 0xF7, +0xD7, 0xFE, 0x39, 0x1D, 0x30, 0x1D, 0x34, 0x22, +0xFB, 0xF7, 0xD2, 0xFE, 0x38, 0x1D, 0x34, 0x22, +0x69, 0x46, 0xFB, 0xF7, 0xCD, 0xFE, 0x64, 0x1C, 0xE4, 0xB2, 0x06, 0x48, 0x01, 0x78, 0xA1, 0x42, 0xE0, 0xD8, 0x0D, 0x98, 0xC5, 0xB2, 0x03, 0x49, 0x08, 0x78, 0xA8, 0x42, 0xD6, 0xD8, 0x0F, 0xB0, -0xF0, 0xBD, 0x00, 0x00, 0xA0, 0x04, 0x00, 0x20, +0xF0, 0xBD, 0x00, 0x00, 0x7C, 0x07, 0x00, 0x20, 0x70, 0xB5, 0x7B, 0x24, 0x21, 0x48, 0x24, 0x02, 0x04, 0x60, 0xF7, 0x20, 0xC0, 0x01, 0x20, 0x4A, 0xC1, 0x7C, 0x11, 0x70, 0x1F, 0x4A, 0x01, 0x7D, @@ -2203,171 +2470,176 @@ const unsigned char u8_rad_fw_30[] = { 0xA1, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, 0x18, 0x49, 0x08, 0x80, 0x20, 0x7E, 0x61, 0x7E, 0x48, 0x43, 0x17, 0x49, 0x08, 0x80, 0x17, 0x48, -0xFC, 0xF7, 0xE3, 0xFA, 0x10, 0x21, 0xC8, 0x41, +0xFB, 0xF7, 0xB7, 0xFE, 0x10, 0x21, 0xC8, 0x41, 0x15, 0x49, 0x08, 0x60, 0x04, 0x22, 0x21, 0x1D, -0x14, 0x48, 0xFC, 0xF7, 0xB5, 0xFA, 0x11, 0x49, -0x06, 0x22, 0x89, 0x1F, 0x12, 0x48, 0xFC, 0xF7, -0xAF, 0xFA, 0x60, 0x7D, 0x22, 0x7D, 0x01, 0x02, -0x11, 0x43, 0x28, 0x46, 0x0A, 0x22, 0x50, 0x43, -0xFC, 0xF7, 0x92, 0xFA, 0x0D, 0x49, 0x08, 0x80, -0x70, 0xBD, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20, -0x2C, 0x00, 0x00, 0x20, 0x2D, 0x00, 0x00, 0x20, -0x2E, 0x00, 0x00, 0x20, 0x2F, 0x00, 0x00, 0x20, -0x42, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, -0x3C, 0x00, 0x00, 0x20, 0x10, 0x7B, 0x00, 0x00, +0x14, 0x48, 0xFB, 0xF7, 0x89, 0xFE, 0x11, 0x49, +0xFF, 0x22, 0x1D, 0x32, 0x89, 0x1F, 0x12, 0x48, +0xFB, 0xF7, 0x82, 0xFE, 0x60, 0x7D, 0x22, 0x7D, +0x01, 0x02, 0x11, 0x43, 0x28, 0x46, 0x0A, 0x22, +0x50, 0x43, 0xFB, 0xF7, 0x65, 0xFE, 0x0D, 0x49, +0x08, 0x80, 0x70, 0xBD, 0xAC, 0x04, 0x00, 0x20, +0x36, 0x00, 0x00, 0x20, 0x37, 0x00, 0x00, 0x20, +0x38, 0x00, 0x00, 0x20, 0x39, 0x00, 0x00, 0x20, +0x44, 0x00, 0x00, 0x20, 0x46, 0x00, 0x00, 0x20, +0x3E, 0x00, 0x00, 0x20, 0x10, 0x7B, 0x00, 0x00, 0xD8, 0x06, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20, -0xE4, 0x06, 0x00, 0x20, 0x40, 0x00, 0x00, 0x20, -0x64, 0x24, 0x00, 0x20, 0x56, 0x49, 0x08, 0x70, -0xA9, 0xE0, 0x55, 0x48, 0x00, 0x78, 0x03, 0x00, -0xFC, 0xF7, 0xFC, 0xFA, 0x07, 0x05, 0x10, 0x1E, -0x18, 0x37, 0x48, 0x50, 0xA3, 0x00, 0x00, 0xF0, -0xCB, 0xF9, 0x01, 0x28, 0x02, 0xD1, 0x4E, 0x49, -0x08, 0x70, 0x02, 0xE0, 0x03, 0x20, 0x4C, 0x49, -0x08, 0x70, 0x93, 0xE0, 0x00, 0xF0, 0xD6, 0xF8, -0x01, 0x28, 0x02, 0xD1, 0x02, 0x20, 0x48, 0x49, -0x08, 0x70, 0x8B, 0xE0, 0x00, 0xF0, 0xB2, 0xF9, -0x00, 0x20, 0x45, 0x49, 0x08, 0x70, 0x85, 0xE0, -0xFF, 0xF7, 0x06, 0xF8, 0x01, 0x28, 0x10, 0xD1, -0xFF, 0xF7, 0xE0, 0xF8, 0x41, 0x48, 0x00, 0x78, +0x10, 0x05, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20, +0x64, 0x24, 0x00, 0x20, 0x66, 0x49, 0x08, 0x70, +0xC9, 0xE0, 0x65, 0x48, 0x00, 0x78, 0x03, 0x00, +0xFB, 0xF7, 0xD0, 0xFE, 0x07, 0x05, 0x10, 0x1E, +0x18, 0x37, 0x48, 0x50, 0xC3, 0x00, 0x00, 0xF0, +0xE9, 0xF9, 0x01, 0x28, 0x02, 0xD1, 0x5E, 0x49, +0x08, 0x70, 0x02, 0xE0, 0x03, 0x20, 0x5C, 0x49, +0x08, 0x70, 0xB3, 0xE0, 0x00, 0xF0, 0x02, 0xF9, +0x01, 0x28, 0x02, 0xD1, 0x02, 0x20, 0x58, 0x49, +0x08, 0x70, 0xAB, 0xE0, 0x00, 0xF0, 0xD0, 0xF9, +0x00, 0x20, 0x55, 0x49, 0x08, 0x70, 0xA5, 0xE0, +0xFE, 0xF7, 0xF6, 0xFF, 0x01, 0x28, 0x10, 0xD1, +0xFF, 0xF7, 0xC4, 0xF8, 0x51, 0x48, 0x00, 0x78, 0x80, 0x21, 0x08, 0x42, 0x03, 0xD0, 0x01, 0x20, -0x3D, 0x49, 0x08, 0x70, 0x02, 0xE0, 0x04, 0x20, -0x3B, 0x49, 0x08, 0x70, 0xFC, 0xF7, 0x74, 0xFD, -0x02, 0xE0, 0x01, 0x20, 0x38, 0x49, 0x08, 0x70, -0x6C, 0xE0, 0x00, 0xF0, 0xBB, 0xF9, 0x01, 0x28, -0x0B, 0xD1, 0x36, 0x48, 0x00, 0x78, 0x80, 0x21, -0x08, 0x42, 0x03, 0xD0, 0x01, 0x20, 0x32, 0x49, -0x08, 0x70, 0x02, 0xE0, 0x05, 0x20, 0x30, 0x49, -0x08, 0x70, 0x5B, 0xE0, 0x00, 0xF0, 0x6A, 0xF8, -0x01, 0x28, 0x02, 0xD1, 0x06, 0x20, 0x2C, 0x49, -0x08, 0x70, 0x53, 0xE0, 0x2C, 0x48, 0x00, 0x78, -0x00, 0x28, 0x09, 0xD1, 0x01, 0x20, 0xFC, 0xF7, -0xEF, 0xFA, 0x00, 0x20, 0x29, 0x49, 0x08, 0x70, -0x01, 0x20, 0x25, 0x49, 0x08, 0x70, 0x45, 0xE0, -0xFF, 0xF7, 0x72, 0xF9, 0x01, 0x28, 0x2E, 0xD1, -0x24, 0x48, 0x00, 0x78, 0x00, 0x28, 0x2A, 0xD1, -0x01, 0xF0, 0xAA, 0xFA, 0x22, 0x48, 0xC0, 0x78, -0x01, 0x28, 0x04, 0xD0, 0x21, 0x48, 0x40, 0x78, -0xC0, 0x07, 0xC0, 0x0F, 0x2D, 0xD0, 0x01, 0x25, +0x4D, 0x49, 0x08, 0x70, 0x02, 0xE0, 0x04, 0x20, +0x4B, 0x49, 0x08, 0x70, 0xFC, 0xF7, 0x54, 0xF9, +0x02, 0xE0, 0x01, 0x20, 0x48, 0x49, 0x08, 0x70, +0x8C, 0xE0, 0x00, 0xF0, 0xCF, 0xF9, 0x01, 0x28, +0x0B, 0xD1, 0x46, 0x48, 0x00, 0x78, 0x80, 0x21, +0x08, 0x42, 0x03, 0xD0, 0x01, 0x20, 0x42, 0x49, +0x08, 0x70, 0x02, 0xE0, 0x05, 0x20, 0x40, 0x49, +0x08, 0x70, 0x7B, 0xE0, 0x00, 0xF0, 0x96, 0xF8, +0x01, 0x28, 0x02, 0xD1, 0x06, 0x20, 0x3C, 0x49, +0x08, 0x70, 0x73, 0xE0, 0x3C, 0x48, 0x00, 0x78, +0x00, 0x28, 0x09, 0xD1, 0x01, 0x20, 0xFB, 0xF7, +0xC3, 0xFE, 0x00, 0x20, 0x39, 0x49, 0x08, 0x70, +0x01, 0x20, 0x35, 0x49, 0x08, 0x70, 0x65, 0xE0, +0xFF, 0xF7, 0x9C, 0xF9, 0x01, 0x28, 0x2F, 0xD1, +0x34, 0x48, 0x00, 0x78, 0x00, 0x28, 0x2B, 0xD1, +0x01, 0xF0, 0xA2, 0xFA, 0x32, 0x48, 0xC0, 0x78, +0x01, 0x28, 0x04, 0xD0, 0x31, 0x48, 0x40, 0x78, +0xC0, 0x07, 0xC0, 0x0F, 0x2E, 0xD0, 0x01, 0x25, 0xAD, 0x07, 0xA9, 0x14, 0x28, 0x68, 0x08, 0x40, -0x00, 0x0A, 0x0A, 0x21, 0x08, 0x43, 0x19, 0x49, -0xC8, 0x70, 0xB4, 0x24, 0x1A, 0x48, 0x00, 0x78, -0xFF, 0x28, 0x05, 0xDA, 0x18, 0x48, 0x00, 0x78, -0x40, 0x1C, 0x17, 0x49, 0x08, 0x70, 0x02, 0xE0, -0x01, 0x20, 0x15, 0x49, 0x08, 0x70, 0x14, 0x48, -0x00, 0x78, 0x10, 0x49, 0x08, 0x70, 0x00, 0x20, -0xFC, 0xF7, 0xB6, 0xFA, 0x0D, 0xE0, 0x00, 0x2C, -0x05, 0xD0, 0x60, 0x1E, 0xC4, 0xB2, 0x01, 0x20, -0xFC, 0xF7, 0xBE, 0xFB, 0x05, 0xE0, 0x00, 0x20, -0x08, 0x49, 0x08, 0x70, 0x01, 0x20, 0xFC, 0xF7, -0xA7, 0xFA, 0x01, 0x20, 0x02, 0x49, 0x08, 0x70, -0x00, 0xE0, 0x00, 0xBF, 0x00, 0xBF, 0x54, 0xE7, -0x80, 0x02, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, +0x00, 0x0A, 0x0A, 0x21, 0x08, 0x43, 0x29, 0x49, +0xC8, 0x70, 0x2B, 0x48, 0x04, 0x78, 0x2B, 0x48, +0x00, 0x78, 0xFF, 0x28, 0x05, 0xDA, 0x29, 0x48, +0x00, 0x78, 0x40, 0x1C, 0x27, 0x49, 0x08, 0x70, +0x02, 0xE0, 0x01, 0x20, 0x25, 0x49, 0x08, 0x70, +0x24, 0x48, 0x00, 0x78, 0x1F, 0x49, 0x08, 0x70, +0x00, 0x20, 0xFB, 0xF7, 0x89, 0xFE, 0x0D, 0xE0, +0x00, 0x2C, 0x05, 0xD0, 0x60, 0x1E, 0xC4, 0xB2, +0x01, 0x20, 0xFB, 0xF7, 0x91, 0xFF, 0x05, 0xE0, +0x00, 0x20, 0x18, 0x49, 0x08, 0x70, 0x01, 0x20, +0xFB, 0xF7, 0x7A, 0xFE, 0x1A, 0x48, 0x00, 0x78, +0x00, 0x28, 0x1A, 0xD0, 0x19, 0x48, 0x00, 0x78, +0x00, 0x28, 0x16, 0xD1, 0x18, 0x48, 0x00, 0x78, +0x01, 0x28, 0x12, 0xD1, 0x17, 0x48, 0x00, 0x78, +0x00, 0x28, 0x0E, 0xD1, 0x16, 0x48, 0x00, 0x78, +0x40, 0x1C, 0xC0, 0xB2, 0x14, 0x49, 0x08, 0x70, +0x0D, 0x49, 0x09, 0x78, 0x88, 0x42, 0x04, 0xDD, +0x00, 0x20, 0x11, 0x49, 0x08, 0x70, 0x0E, 0x49, +0x08, 0x70, 0x01, 0x20, 0x02, 0x49, 0x08, 0x70, +0x00, 0xE0, 0x00, 0xBF, 0x00, 0xBF, 0x34, 0xE7, +0x81, 0x02, 0x00, 0x20, 0x60, 0x07, 0x00, 0x20, 0xE4, 0x02, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, -0xA0, 0x04, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20, -0x84, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x18, 0x48, -0x00, 0x78, 0x03, 0x00, 0xFC, 0xF7, 0x42, 0xFA, +0x7C, 0x07, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20, +0xB7, 0x02, 0x00, 0x20, 0x86, 0x02, 0x00, 0x20, +0x11, 0x00, 0x00, 0x20, 0x50, 0x07, 0x00, 0x20, +0x12, 0x00, 0x00, 0x20, 0x13, 0x00, 0x00, 0x20, +0xCD, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x18, 0x48, +0x00, 0x78, 0x03, 0x00, 0xFB, 0xF7, 0xEA, 0xFD, 0x06, 0x28, 0x04, 0x0A, 0x16, 0x1C, 0x22, 0x28, -0x01, 0xF0, 0xD2, 0xF8, 0x02, 0x20, 0x12, 0x49, -0x08, 0x70, 0x1D, 0xE0, 0x01, 0xF0, 0xC0, 0xF8, +0x01, 0xF0, 0x9A, 0xF8, 0x02, 0x20, 0x12, 0x49, +0x08, 0x70, 0x1D, 0xE0, 0x01, 0xF0, 0x88, 0xF8, 0x00, 0x28, 0x03, 0xD0, 0x03, 0x20, 0x0E, 0x49, 0x08, 0x70, 0x02, 0xE0, 0x05, 0x20, 0x0C, 0x49, -0x08, 0x70, 0x11, 0xE0, 0x01, 0xF0, 0xB0, 0xF8, +0x08, 0x70, 0x11, 0xE0, 0xFC, 0xF7, 0x8C, 0xF8, 0x04, 0x20, 0x09, 0x49, 0x08, 0x70, 0x0B, 0xE0, -0x01, 0xF0, 0x14, 0xF9, 0x05, 0x20, 0x06, 0x49, -0x08, 0x70, 0x05, 0xE0, 0x01, 0xF0, 0x3E, 0xFA, +0x01, 0xF0, 0xDC, 0xF8, 0x05, 0x20, 0x06, 0x49, +0x08, 0x70, 0x05, 0xE0, 0x01, 0xF0, 0x0A, 0xFA, 0x01, 0x20, 0x03, 0x49, 0x08, 0x70, 0x10, 0xBD, 0x00, 0xBF, 0x00, 0x20, 0xFB, 0xE7, 0x00, 0x00, -0x0C, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x63, 0x4C, -0x20, 0x78, 0x00, 0x28, 0x41, 0xD0, 0x02, 0x20, -0xFC, 0xF7, 0x66, 0xFB, 0x62, 0x88, 0x21, 0x78, -0x5F, 0xA0, 0x00, 0xF0, 0x1F, 0xFD, 0x60, 0x88, -0x61, 0x4A, 0x00, 0x25, 0x90, 0x42, 0x02, 0xD1, -0xA5, 0x70, 0x28, 0x0A, 0xE0, 0x70, 0x20, 0x78, -0x5E, 0x4E, 0x5F, 0x49, 0x44, 0x28, 0x75, 0xD0, -0x13, 0xDC, 0x01, 0x22, 0x33, 0x28, 0x34, 0xD0, -0x08, 0xDC, 0x20, 0x28, 0x74, 0xD0, 0x30, 0x28, -0x28, 0xD0, 0x31, 0x28, 0x3D, 0xD0, 0x32, 0x28, -0x3B, 0xD1, 0x27, 0xE0, 0x34, 0x28, 0x39, 0xD0, -0x42, 0x28, 0x6F, 0xD0, 0x43, 0x28, 0x34, 0xD1, -0x90, 0xE0, 0x60, 0x28, 0x78, 0xD0, 0x07, 0xDC, -0x5D, 0x26, 0x5A, 0x28, 0x0B, 0xD0, 0x5C, 0x28, -0x0C, 0xD0, 0x5D, 0x28, 0x29, 0xD1, 0x0D, 0xE0, -0x62, 0x28, 0x65, 0xD0, 0x70, 0x28, 0x53, 0xD0, -0x71, 0x28, 0x7A, 0xD1, 0x71, 0xE0, 0xFF, 0xF7, -0xEF, 0xF8, 0x01, 0xE0, 0xFF, 0xF7, 0x02, 0xF9, -0x26, 0x70, 0x73, 0xE0, 0xC8, 0x78, 0x00, 0x28, -0x6F, 0xD0, 0x6F, 0xE0, 0x45, 0x49, 0x82, 0x20, -0x08, 0x70, 0x6B, 0xE0, 0x60, 0x88, 0x44, 0x49, -0x54, 0xE0, 0x60, 0x88, 0x08, 0x82, 0xA5, 0x70, -0x28, 0x0A, 0xE0, 0x70, 0x08, 0x8A, 0x04, 0x28, -0x5F, 0xD1, 0xFF, 0xF7, 0x8D, 0xF8, 0x00, 0x28, -0x02, 0xD1, 0x01, 0x20, 0xFC, 0xF7, 0xFC, 0xF9, -0x35, 0x70, 0x56, 0xE0, 0x60, 0x88, 0xC0, 0x07, -0x04, 0xD0, 0x48, 0x8A, 0x39, 0x4A, 0x80, 0x08, -0x80, 0x00, 0x15, 0xE0, 0x60, 0x88, 0x80, 0x07, -0x05, 0xD5, 0x48, 0x8A, 0x35, 0x4A, 0x80, 0x08, -0x80, 0x00, 0x52, 0x1C, 0x0C, 0xE0, 0x60, 0x88, -0xC0, 0x04, 0x48, 0x8A, 0x04, 0xD5, 0x80, 0x09, -0x30, 0x4A, 0x80, 0x01, 0xD2, 0x1C, 0x03, 0xE0, -0x80, 0x09, 0x80, 0x01, 0x01, 0x22, 0xD2, 0x03, -0x10, 0x43, 0x48, 0x82, 0x60, 0x88, 0xC0, 0x05, -0x03, 0xD5, 0x48, 0x8A, 0x08, 0x22, 0x10, 0x43, -0x48, 0x82, 0x0D, 0x72, 0xA5, 0x70, 0x28, 0x0A, -0xE0, 0x70, 0x2A, 0xE0, 0x30, 0xE0, 0x03, 0xE0, -0x25, 0x70, 0x25, 0x48, 0x05, 0x70, 0x25, 0xE0, -0x60, 0x88, 0x00, 0x28, 0xC8, 0x79, 0x03, 0xD0, -0x10, 0x43, 0x03, 0xE0, 0x20, 0xE0, 0x0B, 0xE0, -0x40, 0x08, 0x40, 0x00, 0xC8, 0x71, 0x18, 0xE0, -0xC8, 0x69, 0x02, 0x80, 0x60, 0x30, 0xC2, 0x83, -0x60, 0x88, 0x1C, 0x49, 0x08, 0x70, 0xE1, 0xE7, -0x48, 0x89, 0xA0, 0x70, 0x00, 0x0A, 0xE0, 0x70, -0x19, 0x48, 0x00, 0x68, 0x60, 0x30, 0x00, 0x78, -0x04, 0xE0, 0x71, 0x20, 0xA0, 0x70, 0x00, 0x20, -0xE0, 0x70, 0x0B, 0x20, 0x20, 0x71, 0x00, 0x0A, -0x60, 0x71, 0x25, 0x70, 0x01, 0x20, 0x70, 0xBD, -0x0A, 0x70, 0xFB, 0xE7, 0x0D, 0x70, 0xF9, 0xE7, -0x01, 0x20, 0xFC, 0xF7, 0x99, 0xF9, 0x35, 0x70, -0xF4, 0xE7, 0x00, 0x00, 0x88, 0x02, 0x00, 0x20, -0x43, 0x4D, 0x44, 0x3A, 0x30, 0x78, 0x25, 0x78, -0x2C, 0x30, 0x78, 0x25, 0x78, 0x0D, 0x0A, 0x00, -0xAA, 0x55, 0x00, 0x00, 0x90, 0x02, 0x00, 0x20, -0xE4, 0x02, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, -0x2B, 0x00, 0x00, 0x20, 0x01, 0x80, 0x00, 0x00, -0x80, 0x02, 0x00, 0x20, 0x86, 0x02, 0x00, 0x20, -0xB8, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x01, 0x20, -0x00, 0x06, 0xFC, 0xF7, 0x64, 0xF9, 0x10, 0xBD, -0x10, 0xB5, 0x00, 0x24, 0x00, 0xF0, 0x46, 0xFE, -0x01, 0xF0, 0x0E, 0xF8, 0x01, 0x20, 0xFE, 0xF7, -0x11, 0xFF, 0x04, 0x46, 0x00, 0x2C, 0x04, 0xD1, -0x05, 0xA0, 0x00, 0xF0, 0x33, 0xFC, 0x00, 0x20, -0x10, 0xBD, 0x06, 0x49, 0x06, 0x48, 0x81, 0x70, -0x09, 0x0A, 0xC1, 0x70, 0x01, 0x20, 0xF7, 0xE7, -0x49, 0x4E, 0x49, 0x54, 0x20, 0x4E, 0x47, 0x0D, -0x0A, 0x00, 0x00, 0x00, 0xAA, 0x55, 0x00, 0x00, -0x88, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x1C, 0x48, -0x00, 0x78, 0x01, 0x28, 0x02, 0xD0, 0x02, 0x28, -0x2E, 0xD1, 0x1E, 0xE0, 0xFE, 0xF7, 0xDE, 0xFF, -0xFD, 0xF7, 0x92, 0xFE, 0x17, 0x48, 0x00, 0x7A, -0x00, 0x28, 0x04, 0xD1, 0x01, 0x20, 0x16, 0x49, -0x08, 0x70, 0x00, 0x20, 0x10, 0xBD, 0xFC, 0xF7, -0xD9, 0xF9, 0xFE, 0xF7, 0x87, 0xFF, 0x13, 0x48, -0x00, 0x78, 0x80, 0x21, 0x08, 0x42, 0x04, 0xD0, -0x06, 0x20, 0x0F, 0x49, 0x08, 0x70, 0x00, 0x20, -0xF0, 0xE7, 0x02, 0x20, 0x0A, 0x49, 0x08, 0x70, -0x0E, 0xE0, 0xFE, 0xF7, 0xAB, 0xFE, 0x00, 0x28, -0x03, 0xD0, 0x01, 0x20, 0x06, 0x49, 0x08, 0x70, -0xE4, 0xE7, 0x01, 0x20, 0x04, 0x49, 0x08, 0x70, +0x0C, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x60, 0x4C, +0x20, 0x78, 0x00, 0x28, 0x3C, 0xD0, 0x02, 0x20, +0xFB, 0xF7, 0x0E, 0xFF, 0x60, 0x88, 0x5D, 0x4A, +0x00, 0x25, 0x90, 0x42, 0x02, 0xD1, 0xA5, 0x70, +0x28, 0x0A, 0xE0, 0x70, 0x20, 0x78, 0x5A, 0x4E, +0x5A, 0x49, 0x44, 0x28, 0x75, 0xD0, 0x13, 0xDC, +0x01, 0x22, 0x33, 0x28, 0x34, 0xD0, 0x08, 0xDC, +0x20, 0x28, 0x74, 0xD0, 0x30, 0x28, 0x28, 0xD0, +0x31, 0x28, 0x3D, 0xD0, 0x32, 0x28, 0x3B, 0xD1, +0x27, 0xE0, 0x34, 0x28, 0x39, 0xD0, 0x42, 0x28, +0x6F, 0xD0, 0x43, 0x28, 0x34, 0xD1, 0x90, 0xE0, +0x60, 0x28, 0x78, 0xD0, 0x07, 0xDC, 0x5D, 0x26, +0x5A, 0x28, 0x0B, 0xD0, 0x5C, 0x28, 0x0C, 0xD0, +0x5D, 0x28, 0x29, 0xD1, 0x0D, 0xE0, 0x62, 0x28, +0x65, 0xD0, 0x70, 0x28, 0x53, 0xD0, 0x71, 0x28, +0x7A, 0xD1, 0x71, 0xE0, 0xFF, 0xF7, 0xB4, 0xF8, +0x01, 0xE0, 0xFF, 0xF7, 0xC7, 0xF8, 0x26, 0x70, +0x73, 0xE0, 0xC8, 0x78, 0x00, 0x28, 0x6F, 0xD0, +0x6F, 0xE0, 0x41, 0x49, 0x82, 0x20, 0x08, 0x70, +0x6B, 0xE0, 0x60, 0x88, 0x3F, 0x49, 0x54, 0xE0, +0x60, 0x88, 0x88, 0x82, 0xA5, 0x70, 0x28, 0x0A, +0xE0, 0x70, 0x88, 0x8A, 0x04, 0x28, 0x5F, 0xD1, +0xFF, 0xF7, 0x90, 0xF8, 0x00, 0x28, 0x02, 0xD1, +0x01, 0x20, 0xFB, 0xF7, 0xA9, 0xFD, 0x35, 0x70, +0x56, 0xE0, 0x60, 0x88, 0xC0, 0x07, 0x04, 0xD0, +0x48, 0x8B, 0x35, 0x4A, 0x80, 0x08, 0x80, 0x00, +0x15, 0xE0, 0x60, 0x88, 0x80, 0x07, 0x05, 0xD5, +0x48, 0x8B, 0x31, 0x4A, 0x80, 0x08, 0x80, 0x00, +0x52, 0x1C, 0x0C, 0xE0, 0x60, 0x88, 0xC0, 0x04, +0x48, 0x8B, 0x04, 0xD5, 0x80, 0x09, 0x2C, 0x4A, +0x80, 0x01, 0xD2, 0x1C, 0x03, 0xE0, 0x80, 0x09, +0x80, 0x01, 0x01, 0x22, 0xD2, 0x03, 0x10, 0x43, +0x48, 0x83, 0x60, 0x88, 0xC0, 0x05, 0x03, 0xD5, +0x48, 0x8B, 0x08, 0x22, 0x10, 0x43, 0x48, 0x83, +0x0D, 0x72, 0xA5, 0x70, 0x28, 0x0A, 0xE0, 0x70, +0x2A, 0xE0, 0x30, 0xE0, 0x03, 0xE0, 0x25, 0x70, +0x20, 0x48, 0x05, 0x70, 0x25, 0xE0, 0x60, 0x88, +0x00, 0x28, 0xC8, 0x79, 0x03, 0xD0, 0x10, 0x43, +0x03, 0xE0, 0x20, 0xE0, 0x0B, 0xE0, 0x40, 0x08, +0x40, 0x00, 0xC8, 0x71, 0x18, 0xE0, 0x48, 0x6A, +0x02, 0x80, 0x60, 0x30, 0xC2, 0x83, 0x60, 0x88, +0x17, 0x49, 0x08, 0x70, 0xE1, 0xE7, 0xC8, 0x89, +0xA0, 0x70, 0x00, 0x0A, 0xE0, 0x70, 0x15, 0x48, +0x00, 0x68, 0x60, 0x30, 0x00, 0x78, 0x04, 0xE0, +0x71, 0x20, 0xA0, 0x70, 0x00, 0x20, 0xE0, 0x70, +0x0B, 0x20, 0x20, 0x71, 0x00, 0x0A, 0x60, 0x71, +0x25, 0x70, 0x01, 0x20, 0x70, 0xBD, 0x0A, 0x70, +0xFB, 0xE7, 0x0D, 0x70, 0xF9, 0xE7, 0x01, 0x20, +0xFB, 0xF7, 0x46, 0xFD, 0x35, 0x70, 0xF4, 0xE7, +0x88, 0x02, 0x00, 0x20, 0xAA, 0x55, 0x00, 0x00, +0x90, 0x02, 0x00, 0x20, 0xE4, 0x02, 0x00, 0x20, +0x60, 0x07, 0x00, 0x20, 0x35, 0x00, 0x00, 0x20, +0x01, 0x80, 0x00, 0x00, 0x81, 0x02, 0x00, 0x20, +0x8E, 0x02, 0x00, 0x20, 0xAC, 0x04, 0x00, 0x20, +0x10, 0xB5, 0x01, 0x20, 0x00, 0x06, 0xFB, 0xF7, +0x1A, 0xFD, 0x10, 0xBD, 0x10, 0xB5, 0x00, 0x24, +0x00, 0xF0, 0xBC, 0xFD, 0x00, 0xF0, 0xE4, 0xFF, +0x01, 0x20, 0xFE, 0xF7, 0xD7, 0xFE, 0x04, 0x46, +0x00, 0x2C, 0x00, 0xD1, 0x10, 0xBD, 0x03, 0x49, +0x03, 0x48, 0x81, 0x70, 0x09, 0x0A, 0xC1, 0x70, +0x01, 0x20, 0xF7, 0xE7, 0xAA, 0x55, 0x00, 0x00, +0x88, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x00, 0x24, +0x1B, 0x48, 0x00, 0x78, 0x01, 0x28, 0x02, 0xD0, +0x02, 0x28, 0x2D, 0xD1, 0x1C, 0xE0, 0xFF, 0xF7, +0x1F, 0xF8, 0xFD, 0xF7, 0x17, 0xFE, 0x17, 0x48, +0x00, 0x7A, 0x00, 0x28, 0x03, 0xD1, 0x01, 0x20, +0x15, 0x49, 0x08, 0x70, 0x0F, 0xE0, 0xFB, 0xF7, +0x39, 0xFD, 0xFE, 0xF7, 0x9D, 0xFF, 0x13, 0x48, +0x00, 0x78, 0x80, 0x21, 0x08, 0x42, 0x03, 0xD0, +0x06, 0x20, 0x0F, 0x49, 0x08, 0x70, 0x02, 0xE0, +0x02, 0x20, 0x0B, 0x49, 0x08, 0x70, 0x0F, 0xE0, +0xFE, 0xF7, 0x7C, 0xFE, 0x00, 0x28, 0x04, 0xD0, +0x01, 0x20, 0x07, 0x49, 0x08, 0x70, 0x01, 0x24, +0x05, 0xE0, 0x01, 0x20, 0x04, 0x49, 0x08, 0x70, 0x06, 0x20, 0x05, 0x49, 0x08, 0x70, 0x00, 0xBF, -0x00, 0xBF, 0x00, 0x20, 0xDA, 0xE7, 0x00, 0x00, -0x81, 0x02, 0x00, 0x20, 0x50, 0x02, 0x00, 0x20, -0x80, 0x02, 0x00, 0x20, 0x86, 0x02, 0x00, 0x20, +0x00, 0xBF, 0x20, 0x46, 0x10, 0xBD, 0x00, 0x00, +0x82, 0x02, 0x00, 0x20, 0x50, 0x02, 0x00, 0x20, +0x81, 0x02, 0x00, 0x20, 0x8E, 0x02, 0x00, 0x20, 0x0A, 0x07, 0x01, 0xD5, 0x40, 0x1C, 0x02, 0xE0, 0x4A, 0x07, 0x01, 0xD5, 0x40, 0x1E, 0x80, 0xB2, 0x07, 0x4A, 0xCB, 0x07, 0x12, 0x68, 0x02, 0xD0, 0x11, 0x7E, 0x40, 0x1A, 0x03, 0xE0, 0x89, 0x07, 0x02, 0xD5, 0x11, 0x7E, 0x08, 0x18, 0x80, 0xB2, 0x02, 0x49, 0x40, 0x00, 0x08, 0x5E, 0x70, 0x47, -0xB8, 0x02, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, +0xAC, 0x04, 0x00, 0x20, 0x2C, 0x06, 0x00, 0x20, 0xF0, 0xB5, 0x41, 0x18, 0x15, 0x4C, 0x16, 0x4D, 0x8C, 0x46, 0x0E, 0xE0, 0x29, 0x78, 0x01, 0x23, 0x22, 0x88, 0x05, 0xE0, 0x1E, 0x46, 0x8E, 0x40, @@ -2377,42 +2649,42 @@ const unsigned char u8_rad_fw_30[] = { 0x34, 0x27, 0x06, 0x46, 0x7E, 0x43, 0x0B, 0x4F, 0xF6, 0x19, 0x1F, 0x46, 0x31, 0x71, 0x8F, 0x40, 0x73, 0x72, 0x17, 0x43, 0x27, 0x80, 0x73, 0x71, -0x73, 0x89, 0x33, 0x82, 0xB2, 0x89, 0x72, 0x82, -0xB3, 0x85, 0xF2, 0x85, 0xFF, 0x22, 0x20, 0x36, -0x72, 0x75, 0xE3, 0xE7, 0x3E, 0x00, 0x00, 0x20, -0x31, 0x00, 0x00, 0x20, 0xA0, 0x04, 0x00, 0x20, +0xB3, 0x89, 0x73, 0x82, 0xF2, 0x89, 0xB2, 0x82, +0x73, 0x86, 0xB2, 0x86, 0xFF, 0x22, 0xF2, 0x72, +0xE4, 0xE7, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, +0x3B, 0x00, 0x00, 0x20, 0x7C, 0x07, 0x00, 0x20, 0xF0, 0xB5, 0x95, 0xB0, 0x00, 0x20, 0x0C, 0x90, -0x12, 0x90, 0x30, 0x21, 0x68, 0x46, 0xFC, 0xF7, -0x08, 0xF8, 0xC9, 0x48, 0x01, 0x78, 0x40, 0x78, -0x08, 0x18, 0x01, 0x06, 0x09, 0x0E, 0xC7, 0x48, +0x12, 0x90, 0x30, 0x21, 0x68, 0x46, 0xFB, 0xF7, +0xC8, 0xFB, 0xD3, 0x48, 0x01, 0x78, 0x40, 0x78, +0x08, 0x18, 0x01, 0x06, 0x09, 0x0E, 0xD1, 0x48, 0x0E, 0x91, 0x01, 0xD0, 0x00, 0x27, 0x99, 0xE0, 0x01, 0x78, 0x00, 0x20, 0xFF, 0xF7, 0xB4, 0xFF, 0x15, 0xB0, 0xF0, 0xBD, 0x34, 0x20, 0x39, 0x46, -0x41, 0x43, 0xC0, 0x48, 0x00, 0x25, 0x08, 0x18, +0x41, 0x43, 0xCA, 0x48, 0x00, 0x25, 0x08, 0x18, 0x0F, 0x90, 0x00, 0x79, 0xFF, 0x28, 0x7D, 0xD1, 0x38, 0x01, 0x12, 0x9E, 0x10, 0x90, 0x75, 0xE0, 0x10, 0x9A, 0x68, 0x46, 0x14, 0x18, 0x60, 0x19, 0xFF, 0x21, 0x13, 0x90, 0x41, 0x70, 0xA8, 0x00, -0x20, 0x18, 0xB7, 0x49, 0x11, 0x90, 0x41, 0x60, -0x0F, 0x98, 0x0C, 0x21, 0x41, 0x5E, 0x0A, 0x22, +0x20, 0x18, 0xC1, 0x49, 0x11, 0x90, 0x41, 0x60, +0x0F, 0x98, 0x0E, 0x21, 0x41, 0x5E, 0x0C, 0x22, 0x8C, 0x46, 0x82, 0x5E, 0x06, 0x20, 0x31, 0x46, -0x41, 0x43, 0xAF, 0x48, 0x00, 0x23, 0x08, 0x18, +0x41, 0x43, 0xB9, 0x48, 0x00, 0x23, 0x08, 0x18, 0xA0, 0x30, 0x02, 0x21, 0xC3, 0x5E, 0x41, 0x5E, -0x18, 0x46, 0x63, 0x46, 0x01, 0xF0, 0x52, 0xF8, -0x0D, 0x90, 0xAC, 0x48, 0x01, 0x88, 0x00, 0x29, -0x07, 0xD0, 0xAB, 0x48, 0x00, 0x88, 0x88, 0x42, -0x03, 0xD9, 0xFB, 0xF7, 0x7B, 0xFF, 0xC0, 0xB2, -0x00, 0xE0, 0x00, 0x20, 0x34, 0x21, 0xA2, 0x4A, -0x71, 0x43, 0x89, 0x18, 0xC9, 0x69, 0xA6, 0x4B, -0x4A, 0x00, 0xA4, 0x49, 0x1B, 0x78, 0x09, 0x78, +0x18, 0x46, 0x63, 0x46, 0x01, 0xF0, 0x36, 0xF8, +0x0D, 0x90, 0xB6, 0x48, 0x01, 0x88, 0x00, 0x29, +0x07, 0xD0, 0xB5, 0x48, 0x00, 0x88, 0x88, 0x42, +0x03, 0xD9, 0xFB, 0xF7, 0x3B, 0xFB, 0xC0, 0xB2, +0x00, 0xE0, 0x00, 0x20, 0x34, 0x21, 0xAC, 0x4A, +0x71, 0x43, 0x89, 0x18, 0x09, 0x6A, 0xB0, 0x4B, +0x4A, 0x00, 0xAE, 0x49, 0x1B, 0x78, 0x09, 0x78, 0x40, 0x1C, 0x59, 0x43, 0x41, 0x43, 0x50, 0x18, -0x9D, 0x49, 0x88, 0x42, 0x01, 0xD9, 0x08, 0x46, -0x03, 0xE0, 0xA0, 0x49, 0x88, 0x42, 0x00, 0xD2, -0x08, 0x46, 0x9F, 0x49, 0x09, 0x68, 0xB0, 0x31, -0x4A, 0x7A, 0x0B, 0x7A, 0x11, 0x02, 0x98, 0x4A, +0xA7, 0x49, 0x88, 0x42, 0x01, 0xD9, 0x08, 0x46, +0x03, 0xE0, 0xAA, 0x49, 0x88, 0x42, 0x00, 0xD2, +0x08, 0x46, 0xA9, 0x49, 0x09, 0x68, 0xB0, 0x31, +0x4A, 0x7A, 0x0B, 0x7A, 0x11, 0x02, 0xA2, 0x4A, 0x19, 0x43, 0x12, 0x88, 0x91, 0x42, 0x04, 0xD2, -0x91, 0x49, 0x09, 0x78, 0x01, 0x29, 0x00, 0xD1, -0x91, 0x48, 0x0D, 0x99, 0x81, 0x42, 0x1B, 0xD8, +0x9B, 0x49, 0x09, 0x78, 0x01, 0x29, 0x00, 0xD1, +0x9B, 0x48, 0x0D, 0x99, 0x81, 0x42, 0x1B, 0xD8, 0x08, 0x46, 0x11, 0x99, 0x6D, 0x1C, 0x48, 0x60, 0x13, 0x98, 0xED, 0xB2, 0x46, 0x70, 0x01, 0x2D, 0x12, 0xD9, 0x68, 0x1E, 0x0D, 0xE0, 0x81, 0x00, @@ -2421,10 +2693,10 @@ const unsigned char u8_rad_fw_30[] = { 0x22, 0x54, 0x62, 0x46, 0x52, 0x68, 0x62, 0x50, 0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, 0xEE, 0xD1, 0x76, 0x1C, 0xF6, 0xB2, 0x0E, 0x98, 0x86, 0x42, -0x86, 0xD3, 0x00, 0xE0, 0x08, 0xE0, 0x10, 0x98, -0x69, 0x46, 0x0D, 0x54, 0x00, 0x2D, 0x03, 0xD0, +0x86, 0xD3, 0x00, 0xE0, 0x08, 0xE0, 0x10, 0x99, +0x68, 0x46, 0x45, 0x54, 0x00, 0x2D, 0x03, 0xD0, 0x0C, 0x98, 0x40, 0x1C, 0xC0, 0xB2, 0x0C, 0x90, -0x7F, 0x1C, 0xFF, 0xB2, 0x77, 0x48, 0x00, 0x78, +0x7F, 0x1C, 0xFF, 0xB2, 0x81, 0x48, 0x00, 0x78, 0x84, 0x46, 0xB8, 0x42, 0x00, 0xD9, 0x65, 0xE7, 0x0C, 0x98, 0x00, 0x28, 0x7D, 0xD0, 0x01, 0x28, 0x59, 0xD9, 0x00, 0x24, 0x55, 0xE0, 0x60, 0x1C, @@ -2450,262 +2722,230 @@ const unsigned char u8_rad_fw_30[] = { 0x01, 0xD1, 0xFF, 0x20, 0x50, 0x70, 0x5B, 0x1C, 0xDB, 0xB2, 0x9C, 0x45, 0xAF, 0xD8, 0x0C, 0x98, 0xC4, 0xB2, 0xA4, 0x45, 0xA7, 0xD8, 0x00, 0x26, -0x6E, 0xE0, 0x32, 0x01, 0x68, 0x46, 0x10, 0x18, +0x80, 0xE0, 0x32, 0x01, 0x68, 0x46, 0x10, 0x18, 0x0D, 0x90, 0x40, 0x78, 0xFF, 0x28, 0x29, 0xD0, -0x34, 0x22, 0x50, 0x43, 0x3E, 0x4A, 0x12, 0x23, +0x34, 0x22, 0x50, 0x43, 0x48, 0x4A, 0x14, 0x23, 0x85, 0x18, 0x34, 0x20, 0x70, 0x43, 0x44, 0x18, -0x10, 0x22, 0xAA, 0x5E, 0x22, 0x82, 0xEB, 0x5E, -0x63, 0x82, 0x0C, 0x21, 0x0A, 0x20, 0x61, 0x5E, -0x20, 0x5E, 0x00, 0xF0, 0x6B, 0xFF, 0x0C, 0x90, -0x3D, 0x48, 0x07, 0x68, 0x38, 0x46, 0xA7, 0x30, -0x00, 0xE0, 0x52, 0xE0, 0xFB, 0xF7, 0xE5, 0xFE, +0x12, 0x22, 0xAA, 0x5E, 0x62, 0x82, 0xEB, 0x5E, +0xA3, 0x82, 0x0E, 0x21, 0x0C, 0x20, 0x61, 0x5E, +0x20, 0x5E, 0x00, 0xF0, 0x4F, 0xFF, 0x0C, 0x90, +0x47, 0x48, 0x07, 0x68, 0x38, 0x46, 0xA7, 0x30, +0x00, 0xE0, 0x65, 0xE0, 0xFB, 0xF7, 0xA5, 0xFA, 0x01, 0x46, 0x0C, 0x98, 0x81, 0x42, 0x0E, 0xD2, 0x38, 0x46, 0xB0, 0x30, 0xC1, 0x79, 0x82, 0x79, -0x08, 0x02, 0x31, 0x49, 0x10, 0x43, 0x09, 0x88, +0x08, 0x02, 0x3B, 0x49, 0x10, 0x43, 0x09, 0x88, 0x88, 0x42, 0x04, 0xD9, 0x01, 0x21, 0x30, 0x46, -0xFF, 0xF7, 0x82, 0xFE, 0x36, 0xE0, 0x0D, 0x98, +0xFF, 0xF7, 0x82, 0xFE, 0x48, 0xE0, 0x0D, 0x98, 0x41, 0x68, 0x68, 0x7A, 0xC8, 0x28, 0x01, 0xD2, -0x40, 0x1C, 0x60, 0x72, 0xE8, 0x69, 0x40, 0x18, -0x40, 0x08, 0xE0, 0x61, 0x28, 0x79, 0x20, 0x71, +0x40, 0x1C, 0x60, 0x72, 0x2A, 0x46, 0x20, 0x32, +0x20, 0x46, 0x13, 0x79, 0x20, 0x30, 0x0D, 0x90, +0x03, 0x71, 0x34, 0x48, 0x00, 0x78, 0x00, 0x28, +0x08, 0xD0, 0x60, 0x7A, 0x1E, 0x28, 0x05, 0xD1, +0x10, 0x7A, 0xC0, 0x07, 0x02, 0xD0, 0x0D, 0x98, +0x01, 0x22, 0x02, 0x71, 0x28, 0x6A, 0x40, 0x18, +0x40, 0x08, 0x20, 0x62, 0x28, 0x79, 0x20, 0x71, 0x03, 0x21, 0x61, 0x71, 0x80, 0x21, 0x08, 0x43, 0x28, 0x71, 0xA8, 0x79, 0xA0, 0x71, 0xE8, 0x79, 0xE0, 0x71, 0x28, 0x7A, 0x20, 0x72, 0x38, 0x46, -0xA3, 0x30, 0xFB, 0xF7, 0xB6, 0xFE, 0x01, 0x46, -0x0C, 0x98, 0x81, 0x42, 0x03, 0xD2, 0x60, 0x89, -0x20, 0x82, 0xA0, 0x89, 0x60, 0x82, 0x60, 0x7A, -0x02, 0x28, 0x07, 0xD1, 0x30, 0x20, 0x00, 0x5D, -0x00, 0x28, 0x03, 0xD1, 0x60, 0x89, 0xA8, 0x85, -0xA0, 0x89, 0xE8, 0x85, 0xA8, 0x8D, 0xA0, 0x85, -0xE8, 0x8D, 0xE0, 0x85, 0x20, 0x35, 0x68, 0x7D, -0x20, 0x34, 0x60, 0x75, 0x76, 0x1C, 0xF6, 0xB2, -0x0C, 0x49, 0x08, 0x78, 0xB0, 0x42, 0x8C, 0xD8, -0x8E, 0xE6, 0x00, 0x24, 0x09, 0x4D, 0x0B, 0xE0, -0x34, 0x20, 0x60, 0x43, 0x40, 0x19, 0x00, 0x79, +0xA3, 0x30, 0xFB, 0xF7, 0x62, 0xFA, 0x01, 0x46, +0x0C, 0x98, 0x81, 0x42, 0x03, 0xD2, 0xA0, 0x89, +0x60, 0x82, 0xE0, 0x89, 0xA0, 0x82, 0x60, 0x7A, +0x02, 0x28, 0x07, 0xD1, 0x0D, 0x98, 0x80, 0x7D, +0x00, 0x28, 0x03, 0xD1, 0xA0, 0x89, 0x68, 0x86, +0xE0, 0x89, 0xA8, 0x86, 0x68, 0x8E, 0x60, 0x86, +0xA8, 0x8E, 0xA0, 0x86, 0xE8, 0x7A, 0xE0, 0x72, +0x76, 0x1C, 0xF6, 0xB2, 0x0D, 0x49, 0x08, 0x78, +0xB0, 0x42, 0x00, 0xD9, 0x79, 0xE7, 0x7B, 0xE6, +0x00, 0x24, 0x0A, 0x4D, 0x0C, 0xE0, 0x20, 0x46, +0x34, 0x21, 0x48, 0x43, 0x40, 0x19, 0x00, 0x79, 0xFF, 0x28, 0x03, 0xD1, 0x01, 0x21, 0x20, 0x46, -0xFF, 0xF7, 0x36, 0xFE, 0x64, 0x1C, 0xE4, 0xB2, -0x28, 0x78, 0xA0, 0x42, 0xF0, 0xD8, 0x7B, 0xE6, -0x40, 0x05, 0x00, 0x20, 0xA0, 0x04, 0x00, 0x20, -0xFF, 0xFF, 0x07, 0x00, 0x36, 0x00, 0x00, 0x20, -0x34, 0x00, 0x00, 0x20, 0x2C, 0x00, 0x00, 0x20, -0x2D, 0x00, 0x00, 0x20, 0xFF, 0xFF, 0x00, 0x00, -0xB8, 0x02, 0x00, 0x20, 0xF0, 0xB5, 0x00, 0x24, -0x63, 0x49, 0xA9, 0xB0, 0x02, 0x20, 0x08, 0x70, -0x62, 0x48, 0x25, 0x46, 0x86, 0x78, 0x97, 0xE0, -0x34, 0x21, 0x60, 0x48, 0x71, 0x43, 0x09, 0x18, -0x08, 0x7A, 0x00, 0x28, 0x19, 0xD1, 0x5E, 0x4A, -0x0A, 0x20, 0x08, 0x5E, 0x12, 0x78, 0x90, 0x42, -0x11, 0xDB, 0x5C, 0x4F, 0x00, 0x23, 0xFB, 0x5E, -0x9A, 0x1A, 0x90, 0x42, 0x0B, 0xDC, 0x5A, 0x4A, -0x0C, 0x20, 0x08, 0x5E, 0x12, 0x78, 0x90, 0x42, -0x05, 0xDB, 0x58, 0x4F, 0x00, 0x23, 0xFB, 0x5E, -0x9A, 0x1A, 0x90, 0x42, 0x01, 0xDD, 0x02, 0x20, -0x08, 0x72, 0x88, 0x79, 0x4F, 0x4A, 0x00, 0x90, -0x00, 0x28, 0x06, 0xD0, 0x52, 0x4B, 0x52, 0x78, -0x1B, 0x78, 0x00, 0x20, 0x9C, 0x46, 0x96, 0x46, -0x66, 0xE0, 0x4F, 0x4F, 0x38, 0x78, 0x00, 0x19, -0x03, 0x28, 0x63, 0xD2, 0x01, 0x22, 0x8A, 0x71, -0x34, 0x22, 0x50, 0x43, 0xC0, 0x19, 0x09, 0x1D, -0x00, 0x1D, 0xFB, 0xF7, 0x05, 0xFE, 0x39, 0x78, -0x34, 0x22, 0x09, 0x19, 0x51, 0x43, 0x00, 0x20, -0xC9, 0x19, 0x48, 0x71, 0x45, 0x49, 0x64, 0x1C, -0x08, 0x70, 0x45, 0x49, 0xE4, 0xB2, 0x08, 0x70, -0x4C, 0xE0, 0x0A, 0x79, 0x80, 0x23, 0x1A, 0x43, +0xFF, 0xF7, 0x22, 0xFE, 0x64, 0x1C, 0xE4, 0xB2, +0x28, 0x78, 0xA0, 0x42, 0xEF, 0xD8, 0x67, 0xE6, +0x1C, 0x08, 0x00, 0x20, 0x7C, 0x07, 0x00, 0x20, +0xFF, 0xFF, 0x07, 0x00, 0x16, 0x00, 0x00, 0x20, +0x14, 0x00, 0x00, 0x20, 0x36, 0x00, 0x00, 0x20, +0x37, 0x00, 0x00, 0x20, 0xFF, 0xFF, 0x00, 0x00, +0xAC, 0x04, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20, +0xF0, 0xB5, 0x00, 0x24, 0x72, 0x49, 0xA9, 0xB0, +0x02, 0x20, 0x08, 0x70, 0x71, 0x48, 0x25, 0x46, +0x86, 0x78, 0xB5, 0xE0, 0x34, 0x21, 0x6F, 0x48, +0x71, 0x43, 0x09, 0x18, 0x08, 0x7A, 0x00, 0x28, +0x19, 0xD1, 0x6D, 0x4A, 0x0C, 0x20, 0x08, 0x5E, +0x12, 0x78, 0x90, 0x42, 0x11, 0xDB, 0x6B, 0x4F, +0x00, 0x23, 0xFB, 0x5E, 0x9A, 0x1A, 0x90, 0x42, +0x0B, 0xDC, 0x69, 0x4A, 0x0E, 0x20, 0x08, 0x5E, +0x12, 0x78, 0x90, 0x42, 0x05, 0xDB, 0x67, 0x4F, +0x00, 0x23, 0xFB, 0x5E, 0x9A, 0x1A, 0x90, 0x42, +0x01, 0xDD, 0x02, 0x20, 0x08, 0x72, 0x88, 0x79, +0x5E, 0x4A, 0x00, 0x90, 0x00, 0x28, 0x06, 0xD0, +0x61, 0x4B, 0x52, 0x78, 0x1B, 0x78, 0x00, 0x20, +0x9C, 0x46, 0x96, 0x46, 0x84, 0xE0, 0x5E, 0x4F, +0x38, 0x78, 0x00, 0x19, 0x03, 0x28, 0x22, 0xD2, +0x01, 0x22, 0x8A, 0x71, 0x34, 0x22, 0x50, 0x43, +0xC0, 0x19, 0x09, 0x1D, 0x00, 0x1D, 0xFB, 0xF7, +0xAF, 0xF9, 0x39, 0x78, 0x34, 0x22, 0x09, 0x19, +0x00, 0x20, 0x51, 0x43, 0xC9, 0x19, 0x48, 0x71, +0x69, 0xE0, 0x0A, 0x79, 0x80, 0x23, 0x1A, 0x43, 0x63, 0x46, 0x1B, 0x18, 0x34, 0x27, 0x7B, 0x43, -0x3D, 0x4F, 0xDB, 0x19, 0x1B, 0x79, 0x9A, 0x42, -0x3C, 0xD1, 0x3E, 0x48, 0x00, 0x78, 0x00, 0x28, -0x04, 0xD1, 0x0F, 0x46, 0x20, 0x37, 0x38, 0x7D, -0x46, 0x28, 0x02, 0xD9, 0x00, 0x20, 0x88, 0x71, -0x34, 0xE0, 0x00, 0x98, 0x40, 0x1C, 0xC0, 0xB2, -0x88, 0x71, 0x0A, 0x7E, 0x00, 0x2A, 0x05, 0xD0, -0x02, 0x28, 0x03, 0xD1, 0x4A, 0x89, 0x4A, 0x84, -0x8A, 0x89, 0x8A, 0x84, 0x28, 0x4A, 0x0B, 0x7A, -0x12, 0x78, 0x9A, 0x18, 0x90, 0x42, 0x0F, 0xD3, -0x00, 0x20, 0x88, 0x71, 0x83, 0x20, 0x38, 0x70, +0x4F, 0x4F, 0xDB, 0x19, 0x1B, 0x79, 0x9A, 0x42, +0x60, 0xD1, 0x4E, 0x48, 0x00, 0x78, 0x00, 0x28, +0x02, 0xD0, 0x00, 0x20, 0x88, 0x71, 0x5D, 0xE0, +0x00, 0x98, 0x40, 0x1C, 0xC0, 0xB2, 0x88, 0x71, +0x0F, 0x7F, 0x01, 0x2F, 0x0D, 0xD1, 0x40, 0x4A, +0x0B, 0x7A, 0x12, 0x78, 0x52, 0x1E, 0x9A, 0x18, +0x90, 0x42, 0x06, 0xD1, 0x8A, 0x89, 0x4A, 0x85, +0xCA, 0x89, 0x8A, 0x85, 0x8B, 0x7A, 0x27, 0x22, +0x53, 0x54, 0x39, 0x4A, 0x0B, 0x7A, 0x12, 0x78, +0x9A, 0x18, 0x90, 0x42, 0x30, 0xD1, 0x00, 0x20, +0x3D, 0x4A, 0x88, 0x71, 0x12, 0x78, 0x00, 0x2A, +0x08, 0xD0, 0x3C, 0x4A, 0x12, 0x78, 0x00, 0x2A, +0x04, 0xD1, 0x3B, 0x4A, 0x3B, 0x4B, 0x10, 0x70, +0x02, 0x22, 0x1A, 0x70, 0x3A, 0x4A, 0x12, 0x78, +0x13, 0x06, 0x05, 0xD5, 0x01, 0x2F, 0x03, 0xD1, +0x37, 0x4F, 0x01, 0x23, 0x1A, 0x43, 0x3A, 0x70, +0x0A, 0x46, 0x83, 0x23, 0x20, 0x32, 0x13, 0x72, +0x01, 0x23, 0x13, 0x71, 0x50, 0x71, 0x60, 0x46, +0x00, 0x28, 0x01, 0xD1, 0x31, 0x48, 0x03, 0x70, 0x34, 0x20, 0x68, 0x43, 0x01, 0xAA, 0x80, 0x18, 0x09, 0x1D, 0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7, -0xBF, 0xFD, 0x6D, 0x1C, 0xED, 0xB2, 0x11, 0xE0, +0x4B, 0xF9, 0x6D, 0x1C, 0xED, 0xB2, 0x11, 0xE0, 0x60, 0x46, 0x23, 0x4A, 0x00, 0x19, 0x34, 0x23, 0x58, 0x43, 0x80, 0x18, 0x09, 0x1D, 0x00, 0x1D, -0x1A, 0x46, 0xFB, 0xF7, 0xB1, 0xFD, 0x64, 0x1C, +0x1A, 0x46, 0xFB, 0xF7, 0x3D, 0xF9, 0x64, 0x1C, 0xE4, 0xB2, 0x03, 0xE0, 0x40, 0x1C, 0xC0, 0xB2, -0x86, 0x45, 0xB2, 0xD8, 0x76, 0x1C, 0xF6, 0xB2, +0x86, 0x45, 0x8E, 0xD8, 0x76, 0x1C, 0xF6, 0xB2, 0x14, 0x48, 0x01, 0x78, 0xB1, 0x42, 0x00, 0xD9, -0x62, 0xE7, 0x06, 0x46, 0xB0, 0x78, 0x2A, 0x46, +0x44, 0xE7, 0x06, 0x46, 0xB0, 0x78, 0x2A, 0x46, 0x41, 0x19, 0x31, 0x70, 0x34, 0x21, 0x48, 0x43, 0x4A, 0x43, 0x80, 0x19, 0x00, 0x1D, 0x02, 0xA9, -0xFB, 0xF7, 0x96, 0xFD, 0x10, 0x4D, 0x00, 0x20, +0xFB, 0xF7, 0x22, 0xF9, 0x10, 0x4D, 0x00, 0x20, 0x07, 0x46, 0x2B, 0x78, 0x0A, 0xE0, 0x19, 0x18, 0x34, 0x22, 0x51, 0x43, 0x4A, 0x19, 0x12, 0x79, 0x12, 0x06, 0x01, 0xD4, 0x89, 0x19, 0x8F, 0x71, 0x40, 0x1C, 0xC0, 0xB2, 0x71, 0x78, 0x81, 0x42, 0xF1, 0xD8, 0x74, 0x70, 0x29, 0xB0, 0xF0, 0xBD, -0x30, 0x00, 0x00, 0x20, 0xA0, 0x04, 0x00, 0x20, -0x2C, 0x00, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20, -0x2D, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, -0x40, 0x05, 0x00, 0x20, 0xCC, 0x00, 0x00, 0x20, -0xCD, 0x00, 0x00, 0x20, 0x73, 0x04, 0x00, 0x20, -0xF7, 0xB5, 0x00, 0x25, 0x84, 0xB0, 0x0C, 0x46, -0x16, 0x46, 0x61, 0x27, 0x5C, 0xE0, 0x25, 0x28, -0x54, 0xD1, 0x64, 0x1C, 0x00, 0x22, 0x20, 0x78, -0x13, 0x46, 0x00, 0x28, 0x57, 0xD0, 0x25, 0x28, -0x4C, 0xD0, 0x2D, 0x28, 0x01, 0xD1, 0x64, 0x1C, -0x01, 0x23, 0x02, 0x20, 0x21, 0x78, 0x30, 0x29, -0x07, 0xD1, 0x64, 0x1C, 0x03, 0x43, 0xF9, 0xE7, -0x0A, 0x21, 0x4A, 0x43, 0x30, 0x3A, 0x82, 0x18, -0x64, 0x1C, 0x20, 0x78, 0x01, 0x46, 0x30, 0x39, -0x09, 0x29, 0xF5, 0xD9, 0xC1, 0xB2, 0x73, 0x29, -0x0A, 0xD0, 0x64, 0x28, 0x10, 0xD0, 0x78, 0x28, -0x13, 0xD0, 0x58, 0x28, 0x19, 0xD0, 0x75, 0x28, -0x1F, 0xD0, 0x63, 0x28, 0x23, 0xD0, 0x2E, 0xE0, -0x02, 0xCE, 0x00, 0x29, 0x00, 0xD1, 0x1D, 0xA1, -0x04, 0x98, 0x00, 0xF0, 0x90, 0xF8, 0x0A, 0xE0, -0x68, 0x46, 0x8C, 0xC0, 0x02, 0xCE, 0x01, 0x23, -0x13, 0xE0, 0x68, 0x46, 0x8C, 0xC0, 0x08, 0xE0, -0x04, 0x98, 0x00, 0xF0, 0x3C, 0xF8, 0x45, 0x19, -0x19, 0xE0, 0x41, 0x20, 0x01, 0x93, 0x00, 0x92, -0x02, 0x90, 0x02, 0xCE, 0x00, 0x23, 0x10, 0x22, -0xF2, 0xE7, 0x68, 0x46, 0x8C, 0xC0, 0x02, 0xCE, -0x00, 0x23, 0x0A, 0x22, 0xEC, 0xE7, 0x02, 0xCE, -0x68, 0x46, 0x01, 0x73, 0x00, 0x21, 0x41, 0x73, -0x03, 0xA9, 0xD9, 0xE7, 0xC1, 0xB2, 0x04, 0x98, -0x00, 0xF0, 0x14, 0xF8, 0x6D, 0x1C, 0x64, 0x1C, -0x20, 0x78, 0x00, 0x28, 0x9F, 0xD1, 0x04, 0x98, -0x00, 0x28, 0x03, 0xD0, 0x04, 0x99, 0x00, 0x20, -0x09, 0x68, 0x08, 0x70, 0x28, 0x46, 0x07, 0xB0, -0xF0, 0xBD, 0x00, 0x00, 0x28, 0x6E, 0x75, 0x6C, -0x6C, 0x29, 0x00, 0x00, 0x10, 0xB5, 0x00, 0x28, -0x05, 0xD0, 0x02, 0x68, 0x11, 0x70, 0x01, 0x68, -0x49, 0x1C, 0x01, 0x60, 0x10, 0xBD, 0xC8, 0xB2, -0xFE, 0xF7, 0x64, 0xF9, 0x10, 0xBD, 0xFF, 0xB5, -0x00, 0x27, 0x83, 0xB0, 0x0C, 0x9D, 0x3E, 0x46, -0x08, 0x00, 0x3A, 0x46, 0x05, 0xD0, 0x00, 0x2B, -0x12, 0xD0, 0x05, 0x9B, 0x0A, 0x2B, 0x0B, 0xD0, -0x0E, 0xE0, 0x30, 0x20, 0x69, 0x46, 0x08, 0x70, -0x4A, 0x70, 0x2A, 0x46, 0x0D, 0x9B, 0x03, 0x98, -0x00, 0xF0, 0x31, 0xF8, 0x07, 0xB0, 0xF0, 0xBD, -0x00, 0x29, 0x01, 0xDA, 0x01, 0x27, 0x40, 0x42, -0x02, 0xAC, 0x69, 0x46, 0x03, 0x34, 0xCA, 0x72, -0x0A, 0xE0, 0x05, 0x99, 0xFB, 0xF7, 0x9A, 0xFC, -0x0A, 0x29, 0x02, 0xDB, 0x0E, 0x9A, 0x89, 0x18, -0x3A, 0x39, 0x30, 0x31, 0x64, 0x1E, 0x21, 0x70, -0x00, 0x28, 0xF2, 0xD1, 0x00, 0x2F, 0x0E, 0xD0, -0x00, 0x2D, 0x09, 0xD0, 0x0D, 0x98, 0x80, 0x07, -0x06, 0xD5, 0x2D, 0x21, 0x03, 0x98, 0xFF, 0xF7, -0xB9, 0xFF, 0x76, 0x1C, 0x6D, 0x1E, 0x02, 0xE0, -0x2D, 0x20, 0x64, 0x1E, 0x20, 0x70, 0x2A, 0x46, -0x21, 0x46, 0x0D, 0x9B, 0x03, 0x98, 0x00, 0xF0, -0x02, 0xF8, 0x80, 0x19, 0xCE, 0xE7, 0xFF, 0xB5, -0x00, 0x25, 0x20, 0x27, 0x81, 0xB0, 0x0E, 0x46, -0x14, 0x46, 0x00, 0x2A, 0x0E, 0xDD, 0x00, 0x20, -0x01, 0xE0, 0x40, 0x1C, 0x49, 0x1C, 0x0A, 0x78, -0x00, 0x2A, 0xFA, 0xD1, 0xA0, 0x42, 0x01, 0xDB, -0x00, 0x24, 0x00, 0xE0, 0x24, 0x1A, 0x98, 0x07, -0x00, 0xD5, 0x30, 0x27, 0xD8, 0x07, 0x06, 0xD0, -0x0D, 0xE0, 0x39, 0x46, 0x01, 0x98, 0xFF, 0xF7, -0x8D, 0xFF, 0x6D, 0x1C, 0x64, 0x1E, 0x00, 0x2C, -0xF7, 0xDC, 0x04, 0xE0, 0x01, 0x98, 0xFF, 0xF7, -0x85, 0xFF, 0x6D, 0x1C, 0x76, 0x1C, 0x31, 0x78, -0x00, 0x29, 0xF7, 0xD1, 0x05, 0xE0, 0x39, 0x46, -0x01, 0x98, 0xFF, 0xF7, 0x7B, 0xFF, 0x6D, 0x1C, -0x64, 0x1E, 0x00, 0x2C, 0xF7, 0xDC, 0x28, 0x46, -0x05, 0xB0, 0xF0, 0xBD, 0x0F, 0xB4, 0x10, 0xB5, -0x03, 0xAA, 0x00, 0x20, 0x02, 0x99, 0xFF, 0xF7, -0xF7, 0xFE, 0x10, 0xBC, 0x08, 0xBC, 0x04, 0xB0, -0x18, 0x47, 0x00, 0x00, 0xF0, 0xB5, 0xA9, 0xB0, -0x50, 0x49, 0x00, 0x24, 0x8C, 0x70, 0x9C, 0x21, -0x02, 0xA8, 0xFB, 0xF7, 0x6E, 0xFC, 0x00, 0x26, -0x66, 0xE0, 0x34, 0x21, 0x71, 0x43, 0x0D, 0x18, -0x29, 0x79, 0xFF, 0x29, 0x5E, 0xD0, 0x0A, 0x06, -0x36, 0xD5, 0x48, 0x4F, 0x00, 0x20, 0x3A, 0x78, -0x94, 0x46, 0x2E, 0xE0, 0x34, 0x22, 0x42, 0x43, -0xD2, 0x19, 0x12, 0x79, 0x80, 0x23, 0x1A, 0x43, -0x8A, 0x42, 0x24, 0xD1, 0x34, 0x21, 0x48, 0x43, -0x40, 0x49, 0x22, 0x46, 0x47, 0x18, 0x34, 0x20, -0x42, 0x43, 0x01, 0xA8, 0x10, 0x18, 0x00, 0x90, -0x39, 0x1D, 0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7, -0x2F, 0xFC, 0x28, 0x79, 0x64, 0x1C, 0x41, 0x06, -0x00, 0x98, 0x49, 0x0E, 0x01, 0x71, 0x00, 0x98, -0x69, 0x79, 0x41, 0x71, 0x78, 0x79, 0x80, 0x21, -0x08, 0x43, 0x78, 0x71, 0xA8, 0x79, 0xE4, 0xB2, -0x00, 0x28, 0x2F, 0xD1, 0x31, 0x48, 0x81, 0x78, -0x49, 0x1C, 0x81, 0x70, 0x2A, 0xE0, 0x40, 0x1C, -0xC0, 0xB2, 0x84, 0x45, 0xCE, 0xD8, 0x25, 0xE0, -0xA9, 0x79, 0x00, 0x29, 0x22, 0xD1, 0xE9, 0x79, -0x03, 0x29, 0x02, 0xD9, 0x00, 0x20, 0xE8, 0x71, -0x1C, 0xE0, 0x34, 0x20, 0x22, 0x46, 0x42, 0x43, -0x01, 0xA8, 0x17, 0x18, 0x29, 0x1D, 0x38, 0x1D, -0x34, 0x22, 0xFB, 0xF7, 0x01, 0xFC, 0x06, 0x20, -0x23, 0x49, 0x70, 0x43, 0x40, 0x18, 0xA0, 0x30, -0x01, 0x88, 0x79, 0x81, 0x40, 0x88, 0xB8, 0x81, -0x1E, 0x48, 0x64, 0x1C, 0x81, 0x78, 0xE4, 0xB2, +0x3A, 0x00, 0x00, 0x20, 0x7C, 0x07, 0x00, 0x20, +0x36, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, +0x37, 0x00, 0x00, 0x20, 0x46, 0x00, 0x00, 0x20, +0x1C, 0x08, 0x00, 0x20, 0x4F, 0x07, 0x00, 0x20, +0x11, 0x00, 0x00, 0x20, 0x12, 0x00, 0x00, 0x20, +0xCC, 0x00, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, +0xBD, 0x02, 0x00, 0x20, 0x10, 0x00, 0x00, 0x20, +0xF0, 0xB5, 0xA9, 0xB0, 0x53, 0x49, 0x00, 0x24, +0x8C, 0x70, 0x9C, 0x21, 0x02, 0xA8, 0xFB, 0xF7, +0x00, 0xF9, 0x00, 0x26, 0x6C, 0xE0, 0x34, 0x21, +0x71, 0x43, 0x0D, 0x18, 0x29, 0x79, 0xFF, 0x29, +0x64, 0xD0, 0x0A, 0x06, 0x35, 0xD5, 0x4B, 0x4F, +0x00, 0x20, 0x3A, 0x78, 0x94, 0x46, 0x2D, 0xE0, +0x34, 0x22, 0x42, 0x43, 0xD2, 0x19, 0x12, 0x79, +0x80, 0x23, 0x1A, 0x43, 0x8A, 0x42, 0x23, 0xD1, +0x34, 0x21, 0x48, 0x43, 0x43, 0x49, 0x22, 0x46, +0x41, 0x18, 0x34, 0x20, 0x42, 0x43, 0x01, 0xA8, +0x17, 0x18, 0x00, 0x91, 0x09, 0x1D, 0x38, 0x1D, +0x34, 0x22, 0xFB, 0xF7, 0xC1, 0xF8, 0x28, 0x79, +0x64, 0x1C, 0x40, 0x06, 0x40, 0x0E, 0x38, 0x71, +0x68, 0x79, 0x78, 0x71, 0x00, 0x99, 0x80, 0x20, +0x4A, 0x79, 0xE4, 0xB2, 0x02, 0x43, 0x4A, 0x71, +0xA8, 0x79, 0x00, 0x28, 0x36, 0xD1, 0x35, 0x48, +0x81, 0x78, 0x49, 0x1C, 0x81, 0x70, 0x31, 0xE0, +0x40, 0x1C, 0xC0, 0xB2, 0x84, 0x45, 0xCF, 0xD8, +0x2C, 0xE0, 0xA9, 0x79, 0x00, 0x29, 0x29, 0xD1, +0xE9, 0x79, 0x03, 0x29, 0x08, 0xD9, 0x00, 0x21, +0xE9, 0x71, 0x00, 0x78, 0x00, 0x28, 0x21, 0xD0, +0x2B, 0x49, 0x02, 0x20, 0x08, 0x70, 0x1D, 0xE0, +0x34, 0x20, 0x22, 0x46, 0x42, 0x43, 0x01, 0xA8, +0x17, 0x18, 0x29, 0x1D, 0x38, 0x1D, 0x34, 0x22, +0xFB, 0xF7, 0x8E, 0xF8, 0x06, 0x20, 0x31, 0x46, +0x41, 0x43, 0x24, 0x48, 0x64, 0x1C, 0x08, 0x18, +0xA0, 0x30, 0x01, 0x88, 0xB9, 0x81, 0x40, 0x88, +0xF8, 0x81, 0x1E, 0x48, 0xE4, 0xB2, 0x81, 0x78, 0x49, 0x1C, 0x81, 0x70, 0x28, 0x79, 0x80, 0x21, 0x08, 0x43, 0x28, 0x71, 0x76, 0x1C, 0xF6, 0xB2, -0x19, 0x48, 0x01, 0x78, 0x42, 0x78, 0x89, 0x18, -0xB1, 0x42, 0x92, 0xD8, 0x00, 0x25, 0x15, 0x4E, +0x1A, 0x48, 0x01, 0x78, 0x42, 0x78, 0x89, 0x18, +0xB1, 0x42, 0x8C, 0xD8, 0x00, 0x25, 0x15, 0x4E, 0x1A, 0xE0, 0x34, 0x20, 0x68, 0x43, 0x81, 0x19, 0x48, 0x79, 0x02, 0x06, 0x03, 0xD5, 0x40, 0x06, 0x40, 0x0E, 0x48, 0x71, 0x0E, 0xE0, 0x02, 0x2C, 0x11, 0xD0, 0x00, 0x20, 0x88, 0x71, 0x34, 0x20, 0x60, 0x43, 0x01, 0xAA, 0x80, 0x18, 0x09, 0x1D, -0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7, 0xCC, 0xFB, +0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7, 0x58, 0xF8, 0x64, 0x1C, 0xE4, 0xB2, 0x6D, 0x1C, 0xED, 0xB2, 0x30, 0x78, 0xA8, 0x42, 0xE1, 0xD8, 0x34, 0x70, -0x34, 0x20, 0x44, 0x43, 0x22, 0x46, 0x02, 0xA9, -0x30, 0x1D, 0xFB, 0xF7, 0xBD, 0xFB, 0x29, 0xB0, -0xF0, 0xBD, 0x00, 0x00, 0xA0, 0x04, 0x00, 0x20, -0x40, 0x05, 0x00, 0x20, 0xF8, 0xB5, 0x59, 0x4C, -0x59, 0x4D, 0x00, 0x26, 0xE6, 0x70, 0x16, 0x21, -0x28, 0x1D, 0xFB, 0xF7, 0xC6, 0xFB, 0x57, 0x48, -0x57, 0x49, 0x00, 0x78, 0x09, 0x78, 0xC0, 0x01, -0x08, 0x43, 0xC1, 0xB2, 0x55, 0x48, 0x01, 0x70, -0x55, 0x48, 0x02, 0x78, 0x01, 0x20, 0x00, 0x2A, -0x04, 0xD0, 0xAA, 0x7E, 0x8A, 0x42, 0x01, 0xD0, -0xA9, 0x76, 0xE0, 0x70, 0x51, 0x49, 0x0A, 0x78, -0x00, 0x2A, 0x10, 0xD1, 0x50, 0x4A, 0x12, 0x78, -0x18, 0x2A, 0x0C, 0xD1, 0x4F, 0x4A, 0x12, 0x78, -0x03, 0x2A, 0x08, 0xD3, 0xA9, 0x78, 0xC9, 0x07, -0x04, 0xD1, 0xE0, 0x70, 0xA8, 0x70, 0x6E, 0x70, -0x4B, 0x48, 0x06, 0x70, 0xF8, 0xBD, 0xAA, 0x78, -0xD3, 0x07, 0x4A, 0x4A, 0x07, 0xD0, 0xE0, 0x70, -0xAE, 0x70, 0x6E, 0x70, 0x2D, 0x20, 0x08, 0x70, -0x03, 0x20, 0x10, 0x70, 0xF8, 0xBD, 0x11, 0x78, -0x00, 0x29, 0xFB, 0xD1, 0x21, 0x78, 0x00, 0x29, -0x07, 0xD0, 0xE0, 0x70, 0x69, 0x70, 0x38, 0x48, -0x00, 0x24, 0x40, 0x1C, 0x40, 0x4F, 0x00, 0x90, -0x63, 0xE0, 0x69, 0x78, 0x00, 0x29, 0x00, 0xD0, -0xE0, 0x70, 0x6E, 0x70, 0xF8, 0xBD, 0x34, 0x20, -0x30, 0x49, 0x60, 0x43, 0x45, 0x18, 0x20, 0x46, +0x34, 0x21, 0x4C, 0x43, 0x22, 0x46, 0x02, 0xA9, +0x30, 0x1D, 0xFB, 0xF7, 0x49, 0xF8, 0x29, 0xB0, +0xF0, 0xBD, 0x00, 0x00, 0x7C, 0x07, 0x00, 0x20, +0x10, 0x00, 0x00, 0x20, 0x1C, 0x08, 0x00, 0x20, +0xF8, 0xB5, 0x76, 0x48, 0x00, 0x25, 0x07, 0x78, +0x75, 0x4E, 0x00, 0x2F, 0x04, 0xD0, 0x75, 0x48, +0x00, 0x78, 0x00, 0x28, 0x00, 0xD0, 0xB5, 0x70, +0x73, 0x4C, 0x16, 0x21, 0xE5, 0x70, 0x30, 0x1D, +0xFB, 0xF7, 0x47, 0xF8, 0x71, 0x48, 0x72, 0x49, +0x00, 0x78, 0x09, 0x78, 0xC0, 0x01, 0x08, 0x43, +0xC1, 0xB2, 0x70, 0x48, 0x70, 0x4A, 0x03, 0x78, +0x01, 0x20, 0x00, 0x2B, 0x03, 0xD0, 0x13, 0x78, +0x8B, 0x42, 0x00, 0xD0, 0xE0, 0x70, 0x11, 0x70, +0x6C, 0x4A, 0x3B, 0x00, 0x11, 0x78, 0x6C, 0x4F, +0x1E, 0xD0, 0x6C, 0x4B, 0x00, 0x29, 0x16, 0xD1, +0x6B, 0x4A, 0x11, 0x78, 0x00, 0x29, 0x12, 0xD0, +0x60, 0x49, 0x08, 0x70, 0x69, 0x49, 0x0D, 0x70, +0x69, 0x49, 0x09, 0x78, 0xB1, 0x70, 0x15, 0x70, +0x75, 0x70, 0x62, 0x4A, 0x25, 0x70, 0x05, 0x21, +0x11, 0x70, 0x06, 0x21, 0x39, 0x70, 0x65, 0x49, +0x0D, 0x70, 0x18, 0x70, 0x02, 0xE0, 0x19, 0x78, +0x00, 0x29, 0x00, 0xD0, 0xE0, 0x70, 0xF8, 0xBD, +0x61, 0x4B, 0x1B, 0x78, 0x00, 0x2B, 0x18, 0xD1, +0x60, 0x4B, 0x1B, 0x78, 0x18, 0x2B, 0x14, 0xD1, +0x5F, 0x4B, 0x1B, 0x78, 0x03, 0x2B, 0x10, 0xD9, +0xB1, 0x78, 0xC9, 0x07, 0xEF, 0xD1, 0x39, 0x78, +0x05, 0x29, 0xEC, 0xD0, 0xE0, 0x70, 0xB0, 0x70, +0x04, 0x21, 0x55, 0x48, 0xFA, 0xF7, 0xF9, 0xFF, +0x75, 0x70, 0x58, 0x48, 0x05, 0x70, 0x04, 0x20, +0x0B, 0xE0, 0xB3, 0x78, 0xDB, 0x07, 0x0A, 0xD0, +0xE0, 0x70, 0xB5, 0x70, 0x75, 0x70, 0x50, 0x48, +0x3C, 0x21, 0x01, 0x70, 0x03, 0x20, 0x10, 0x70, +0x05, 0x20, 0x38, 0x70, 0xF8, 0xBD, 0x00, 0x29, +0x04, 0xD0, 0x71, 0x78, 0x00, 0x29, 0xF9, 0xD0, +0x25, 0x70, 0x0A, 0xE0, 0x21, 0x78, 0x00, 0x29, +0x07, 0xD0, 0xE0, 0x70, 0x71, 0x70, 0x38, 0x48, +0x00, 0x24, 0x40, 0x1C, 0x48, 0x4F, 0x00, 0x90, +0x63, 0xE0, 0x71, 0x78, 0x00, 0x29, 0x00, 0xD0, +0xE0, 0x70, 0x75, 0x70, 0xF8, 0xBD, 0x34, 0x20, +0x33, 0x49, 0x60, 0x43, 0x45, 0x18, 0x20, 0x46, 0x0B, 0x26, 0x70, 0x43, 0x2A, 0x79, 0xC6, 0x18, -0x32, 0x71, 0x68, 0x89, 0x76, 0x1C, 0x30, 0x71, -0x00, 0x0A, 0x70, 0x71, 0xA8, 0x89, 0xB0, 0x71, -0x00, 0x0A, 0xF0, 0x71, 0xE8, 0x89, 0x30, 0x72, -0x00, 0x0A, 0x70, 0x72, 0x20, 0x20, 0x40, 0x5D, -0xC0, 0x07, 0x07, 0xD0, 0xE8, 0x8C, 0x30, 0x71, -0x00, 0x0A, 0x70, 0x71, 0x28, 0x8D, 0xB0, 0x71, +0x32, 0x71, 0xA8, 0x89, 0x76, 0x1C, 0x30, 0x71, +0x00, 0x0A, 0x70, 0x71, 0xE8, 0x89, 0xB0, 0x71, +0x00, 0x0A, 0xF0, 0x71, 0x28, 0x8A, 0x30, 0x72, +0x00, 0x0A, 0x70, 0x72, 0x28, 0x20, 0x40, 0x5D, +0xC0, 0x07, 0x07, 0xD0, 0xE8, 0x8D, 0x30, 0x71, +0x00, 0x0A, 0x70, 0x71, 0x28, 0x8E, 0xB0, 0x71, 0x00, 0x0A, 0xF0, 0x71, 0x08, 0x78, 0x01, 0x28, 0x2D, 0xD1, 0xC8, 0x79, 0x00, 0x28, 0x2A, 0xD0, 0x00, 0x98, 0xC1, 0x79, 0x80, 0x79, 0x09, 0x06, 0x0B, 0x14, 0x03, 0x43, 0x00, 0x98, 0x41, 0x79, 0x00, 0x79, 0x09, 0x06, 0x0A, 0x14, 0x02, 0x43, -0x22, 0x48, 0x00, 0x21, 0x41, 0x5E, 0x00, 0x20, -0x38, 0x5E, 0x00, 0xF0, 0xCB, 0xFB, 0x20, 0x49, +0x2A, 0x48, 0x00, 0x21, 0x41, 0x5E, 0x00, 0x20, +0x38, 0x5E, 0x00, 0xF0, 0x3F, 0xFC, 0x28, 0x49, 0x0A, 0x68, 0xB0, 0x32, 0x51, 0x7B, 0x12, 0x7B, 0x09, 0x02, 0x11, 0x43, 0x81, 0x42, 0x0E, 0xD2, 0x00, 0x98, 0x41, 0x79, 0x02, 0x79, 0x08, 0x06, 0x00, 0x14, 0x10, 0x43, 0x38, 0x80, 0x00, 0x98, 0xC2, 0x79, 0x81, 0x79, 0x10, 0x06, 0x00, 0x14, -0x08, 0x43, 0x14, 0x49, 0x08, 0x80, 0xA8, 0x8A, -0xB0, 0x72, 0x00, 0x0A, 0xF0, 0x72, 0xE8, 0x8A, +0x08, 0x43, 0x1C, 0x49, 0x08, 0x80, 0x28, 0x8B, +0xB0, 0x72, 0x00, 0x0A, 0xF0, 0x72, 0x68, 0x8B, 0x30, 0x73, 0x00, 0x0A, 0x70, 0x73, 0x64, 0x1C, 0xE4, 0xB2, 0x03, 0x4B, 0x58, 0x78, 0xA0, 0x42, -0x9D, 0xD8, 0xF8, 0xBD, 0xA0, 0x04, 0x00, 0x20, -0x90, 0x02, 0x00, 0x20, 0x74, 0x04, 0x00, 0x20, -0x85, 0x02, 0x00, 0x20, 0x34, 0x01, 0x00, 0x20, -0x2B, 0x00, 0x00, 0x20, 0x72, 0x04, 0x00, 0x20, -0x8E, 0x02, 0x00, 0x20, 0x73, 0x04, 0x00, 0x20, -0x83, 0x02, 0x00, 0x20, 0x75, 0x04, 0x00, 0x20, -0x38, 0x00, 0x00, 0x20, 0x3A, 0x00, 0x00, 0x20, -0xB8, 0x02, 0x00, 0x20, 0xF7, 0xB5, 0x00, 0x20, +0x9D, 0xD8, 0xF8, 0xBD, 0x11, 0x00, 0x00, 0x20, +0x90, 0x02, 0x00, 0x20, 0x12, 0x00, 0x00, 0x20, +0x7C, 0x07, 0x00, 0x20, 0x50, 0x07, 0x00, 0x20, +0x87, 0x02, 0x00, 0x20, 0x35, 0x00, 0x00, 0x20, +0x34, 0x01, 0x00, 0x20, 0x51, 0x07, 0x00, 0x20, +0x10, 0x00, 0x00, 0x20, 0x13, 0x00, 0x00, 0x20, +0xCC, 0x00, 0x00, 0x20, 0xCD, 0x00, 0x00, 0x20, +0x48, 0x00, 0x00, 0x20, 0xBD, 0x02, 0x00, 0x20, +0x4E, 0x07, 0x00, 0x20, 0xB5, 0x02, 0x00, 0x20, +0x4F, 0x07, 0x00, 0x20, 0x84, 0x02, 0x00, 0x20, +0x18, 0x00, 0x00, 0x20, 0x1A, 0x00, 0x00, 0x20, +0xAC, 0x04, 0x00, 0x20, 0xF7, 0xB5, 0x00, 0x20, 0x84, 0xB0, 0x03, 0x90, 0x32, 0x48, 0x00, 0x27, 0x01, 0x90, 0x38, 0x46, 0x56, 0xE0, 0x00, 0x24, 0x00, 0x29, 0x05, 0xD0, 0x40, 0x1E, 0x88, 0x42, @@ -2718,7 +2958,7 @@ const unsigned char u8_rad_fw_30[] = { 0x01, 0xD1, 0x08, 0x20, 0x04, 0x43, 0x00, 0x25, 0x21, 0x48, 0x41, 0x5D, 0x20, 0x46, 0x08, 0x42, 0x0F, 0xD1, 0x01, 0xE0, 0x04, 0x20, 0xF5, 0xE7, -0x38, 0x46, 0xFF, 0xF7, 0x69, 0xFA, 0x1B, 0x4A, +0x38, 0x46, 0xFF, 0xF7, 0xE9, 0xFA, 0x1B, 0x4A, 0x02, 0x99, 0x04, 0x2D, 0x51, 0x5E, 0x02, 0xD2, 0x81, 0x42, 0x17, 0xDB, 0x01, 0xE0, 0x81, 0x42, 0x04, 0xDD, 0x6D, 0x1C, 0xED, 0xB2, 0x08, 0x2D, @@ -2732,106 +2972,130 @@ const unsigned char u8_rad_fw_30[] = { 0x40, 0x1C, 0xC0, 0xB2, 0x08, 0x4A, 0x00, 0x90, 0x10, 0x68, 0x00, 0x99, 0x40, 0x7E, 0x88, 0x42, 0xA1, 0xD8, 0x03, 0x98, 0x07, 0xB0, 0xF0, 0xBD, -0x00, 0x80, 0xFF, 0xFF, 0x4C, 0x07, 0x00, 0x20, -0x8E, 0x5A, 0x00, 0x00, 0x28, 0x00, 0x00, 0x20, -0xB8, 0x02, 0x00, 0x20, 0x05, 0x48, 0x01, 0x78, +0x00, 0x80, 0xFF, 0xFF, 0x2C, 0x06, 0x00, 0x20, +0xD6, 0x62, 0x00, 0x00, 0x32, 0x00, 0x00, 0x20, +0xAC, 0x04, 0x00, 0x20, 0x05, 0x48, 0x01, 0x78, 0x82, 0x29, 0x05, 0xD1, 0xC0, 0x79, 0x01, 0x28, 0x02, 0xD1, 0x03, 0x49, 0xFF, 0x20, 0x08, 0x70, -0x70, 0x47, 0x00, 0x00, 0x84, 0x04, 0x00, 0x20, +0x70, 0x47, 0x00, 0x00, 0x60, 0x07, 0x00, 0x20, 0xEC, 0x02, 0x00, 0x20, 0x01, 0x20, 0x02, 0x49, 0x08, 0x70, 0x02, 0x49, 0x08, 0x70, 0x70, 0x47, -0x0C, 0x00, 0x00, 0x20, 0x81, 0x02, 0x00, 0x20, -0x70, 0xB5, 0x24, 0x4D, 0x00, 0x21, 0x28, 0x78, -0x00, 0x28, 0x2C, 0xD1, 0x22, 0x4A, 0x23, 0x4B, -0x10, 0x80, 0x04, 0x22, 0x1A, 0x70, 0x22, 0x4B, -0x1A, 0x70, 0x22, 0x4A, 0x10, 0x70, 0x22, 0x4A, -0x10, 0x70, 0x22, 0x4A, 0x10, 0x70, 0x22, 0x4A, -0x10, 0x70, 0x22, 0x4A, 0x10, 0x70, 0x1A, 0xE0, -0x34, 0x20, 0x48, 0x43, 0x40, 0x19, 0x02, 0x46, -0x20, 0x32, 0x13, 0x78, 0x1C, 0x06, 0x06, 0xD5, -0x5B, 0x06, 0x5B, 0x0E, 0x13, 0x70, 0x43, 0x89, -0xC3, 0x84, 0x83, 0x89, 0x03, 0x85, 0x13, 0x78, -0x9C, 0x07, 0x06, 0xD5, 0xFD, 0x24, 0x23, 0x40, -0x13, 0x70, 0x42, 0x89, 0x42, 0x84, 0x82, 0x89, -0x82, 0x84, 0x49, 0x1C, 0xC9, 0xB2, 0x28, 0x78, -0x88, 0x42, 0xE1, 0xD8, 0x12, 0x4E, 0x00, 0x24, -0x30, 0x70, 0x68, 0x78, 0x70, 0x70, 0x20, 0x46, -0x34, 0x21, 0x48, 0x43, 0x41, 0x19, 0x80, 0x19, -0x09, 0x1D, 0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7, -0x13, 0xFA, 0x64, 0x1C, 0xE4, 0xB2, 0x03, 0x2C, -0xF1, 0xD3, 0x70, 0xBD, 0xA0, 0x04, 0x00, 0x20, -0x34, 0x00, 0x00, 0x20, 0x4A, 0x00, 0x00, 0x20, -0x4B, 0x00, 0x00, 0x20, 0x49, 0x00, 0x00, 0x20, -0x2A, 0x00, 0x00, 0x20, 0x47, 0x00, 0x00, 0x20, -0x46, 0x00, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, -0x40, 0x05, 0x00, 0x20, 0x02, 0x49, 0x00, 0x20, -0x08, 0x70, 0x02, 0x49, 0x08, 0x80, 0x70, 0x47, -0x31, 0x00, 0x00, 0x20, 0x3E, 0x00, 0x00, 0x20, -0xF0, 0xB5, 0x3B, 0x4F, 0x00, 0x20, 0x38, 0x70, -0x01, 0x21, 0x3A, 0x4C, 0xC9, 0x03, 0x21, 0x80, -0x39, 0x4B, 0x3B, 0x49, 0x18, 0x80, 0x39, 0x4E, -0x0A, 0x88, 0x0D, 0xE0, 0x43, 0x00, 0x00, 0x25, -0xF1, 0x5E, 0x65, 0x5F, 0xA9, 0x42, 0x02, 0xDD, -0x33, 0x4D, 0x21, 0x80, 0x28, 0x80, 0x49, 0x08, -0x49, 0x00, 0x40, 0x1C, 0xF1, 0x52, 0x80, 0xB2, -0x90, 0x42, 0xEF, 0xD3, 0x00, 0x20, 0x20, 0x5E, -0x41, 0x00, 0x41, 0x18, 0xCB, 0x17, 0x5B, 0x0F, -0x59, 0x18, 0xCB, 0x10, 0x2D, 0x49, 0x09, 0x68, -0x90, 0x31, 0x4C, 0x7B, 0x0D, 0x7B, 0x21, 0x02, -0x29, 0x43, 0x81, 0x42, 0x0C, 0xDA, 0x00, 0x20, -0x08, 0xE0, 0x41, 0x00, 0x71, 0x5E, 0x99, 0x42, -0x02, 0xDD, 0x39, 0x78, 0x49, 0x1C, 0x39, 0x70, -0x40, 0x1C, 0x80, 0xB2, 0x90, 0x42, 0xF4, 0xD3, -0x23, 0x48, 0x24, 0x4A, 0x01, 0x78, 0x21, 0x48, -0x00, 0x68, 0x80, 0x30, 0x00, 0x29, 0x81, 0x7B, -0x02, 0xD0, 0xCB, 0x00, 0xC9, 0x18, 0xC9, 0x08, -0x11, 0x70, 0xC3, 0x7B, 0x1E, 0x49, 0x00, 0x20, -0x08, 0x5E, 0x83, 0x42, 0x0C, 0xDA, 0x1D, 0x49, -0x09, 0x78, 0x02, 0x29, 0x08, 0xD2, 0x1C, 0x49, -0x0B, 0x78, 0x11, 0x78, 0x49, 0x1E, 0x8B, 0x42, -0x02, 0xDD, 0x11, 0x78, 0x49, 0x1C, 0x39, 0x70, -0x18, 0x49, 0x00, 0x23, 0xCB, 0x5E, 0x59, 0x42, -0x81, 0x42, 0x01, 0xDD, 0x00, 0x20, 0x38, 0x70, -0x3B, 0x78, 0x12, 0x78, 0x14, 0x49, 0x93, 0x42, -0x08, 0x78, 0x06, 0xD9, 0x03, 0x28, 0x01, 0xD2, -0x40, 0x1C, 0x00, 0xE0, 0x0C, 0x20, 0x08, 0x70, -0xF0, 0xBD, 0x00, 0x28, 0xFC, 0xD0, 0x3A, 0x78, -0x03, 0x2A, 0xF9, 0xD2, 0x40, 0x1E, 0xF6, 0xE7, -0x0E, 0x00, 0x00, 0x20, 0x24, 0x00, 0x00, 0x20, -0x32, 0x00, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, -0x3C, 0x00, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, -0x70, 0x04, 0x00, 0x20, 0x0D, 0x00, 0x00, 0x20, -0x7C, 0x04, 0x00, 0x20, 0xA0, 0x04, 0x00, 0x20, -0x8F, 0x02, 0x00, 0x20, 0x7A, 0x04, 0x00, 0x20, -0x73, 0x04, 0x00, 0x20, 0x10, 0xB5, 0x06, 0x4C, -0x00, 0x20, 0x20, 0x80, 0x60, 0x80, 0xFF, 0xF7, -0x5D, 0xFF, 0x04, 0x48, 0xA0, 0x80, 0xE0, 0x80, -0x03, 0x49, 0x03, 0x20, 0x08, 0x70, 0x10, 0xBD, -0x34, 0x00, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, -0x82, 0x02, 0x00, 0x20, 0x10, 0xB5, 0xFF, 0xF7, -0x4D, 0xFF, 0x08, 0x49, 0x5A, 0x20, 0x08, 0x70, +0x0C, 0x00, 0x00, 0x20, 0x82, 0x02, 0x00, 0x20, +0x70, 0xB5, 0x3D, 0x4D, 0x3D, 0x48, 0x2A, 0x78, +0x00, 0x21, 0x00, 0x78, 0x00, 0x2A, 0x17, 0xD1, +0x3B, 0x4C, 0x00, 0x23, 0x23, 0x80, 0x3B, 0x4E, +0x04, 0x24, 0x34, 0x70, 0x3A, 0x4E, 0x00, 0x28, +0x34, 0x70, 0x3A, 0x4C, 0x23, 0x70, 0x3A, 0x4C, +0x23, 0x70, 0x01, 0xD1, 0x39, 0x4C, 0x23, 0x70, +0x39, 0x4C, 0x23, 0x70, 0x39, 0x4C, 0x23, 0x70, +0x39, 0x4C, 0x23, 0x70, 0x39, 0x4C, 0x23, 0x70, +0x39, 0x4B, 0x1B, 0x78, 0x00, 0x2B, 0x3F, 0xD0, +0x00, 0x2A, 0x3D, 0xD0, 0x00, 0x28, 0x3B, 0xD1, +0x36, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x37, 0xD1, +0x35, 0x48, 0x00, 0x78, 0x00, 0x28, 0x33, 0xD0, +0x34, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x2F, 0xD1, +0xE8, 0x78, 0x00, 0x28, 0x2C, 0xD1, 0x32, 0x48, +0x00, 0x78, 0x00, 0x28, 0x28, 0xD1, 0xE8, 0x79, +0x02, 0x28, 0x02, 0xD0, 0x68, 0x7A, 0x01, 0x28, +0x22, 0xD9, 0x2E, 0x4A, 0x02, 0x20, 0x10, 0x70, +0x01, 0x20, 0x50, 0x70, 0x2C, 0x4A, 0x10, 0x70, +0x1A, 0xE0, 0x34, 0x20, 0x48, 0x43, 0x40, 0x19, +0x02, 0x46, 0x20, 0x32, 0x13, 0x7A, 0x1C, 0x06, +0x06, 0xD5, 0x5B, 0x06, 0x5B, 0x0E, 0x13, 0x72, +0x83, 0x89, 0xC3, 0x85, 0xC3, 0x89, 0x03, 0x86, +0x13, 0x7A, 0x9C, 0x07, 0x06, 0xD5, 0xFD, 0x24, +0x23, 0x40, 0x13, 0x72, 0x82, 0x89, 0x42, 0x85, +0xC2, 0x89, 0x82, 0x85, 0x49, 0x1C, 0xC9, 0xB2, +0x28, 0x78, 0x88, 0x42, 0xE1, 0xD8, 0x1D, 0x4E, +0x00, 0x24, 0x30, 0x70, 0x68, 0x78, 0x70, 0x70, +0x20, 0x46, 0x34, 0x21, 0x48, 0x43, 0x41, 0x19, +0x80, 0x19, 0x09, 0x1D, 0x00, 0x1D, 0x34, 0x22, +0xFA, 0xF7, 0x22, 0xFE, 0x64, 0x1C, 0xE4, 0xB2, +0x03, 0x2C, 0xF1, 0xD3, 0x70, 0xBD, 0x00, 0x00, +0x7C, 0x07, 0x00, 0x20, 0x4F, 0x07, 0x00, 0x20, +0x14, 0x00, 0x00, 0x20, 0xCF, 0x00, 0x00, 0x20, +0xD0, 0x00, 0x00, 0x20, 0xCE, 0x00, 0x00, 0x20, +0x34, 0x00, 0x00, 0x20, 0xBA, 0x02, 0x00, 0x20, +0xA8, 0x04, 0x00, 0x20, 0xBF, 0x02, 0x00, 0x20, +0xCA, 0x02, 0x00, 0x20, 0xCB, 0x02, 0x00, 0x20, +0x11, 0x00, 0x00, 0x20, 0x8F, 0x02, 0x00, 0x20, +0x84, 0x02, 0x00, 0x20, 0xBD, 0x02, 0x00, 0x20, +0x51, 0x07, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, +0xCC, 0x00, 0x00, 0x20, 0x1C, 0x08, 0x00, 0x20, +0x02, 0x49, 0x00, 0x20, 0x08, 0x70, 0x02, 0x49, +0x08, 0x80, 0x70, 0x47, 0x3B, 0x00, 0x00, 0x20, +0x40, 0x00, 0x00, 0x20, 0xF0, 0xB5, 0x3F, 0x4F, +0x00, 0x20, 0x38, 0x70, 0x01, 0x21, 0x3E, 0x4C, +0xC9, 0x03, 0x21, 0x80, 0x3D, 0x49, 0x3F, 0x4A, +0x08, 0x80, 0x3D, 0x4E, 0x12, 0x88, 0x0D, 0xE0, +0x43, 0x00, 0x00, 0x25, 0xF1, 0x5E, 0x65, 0x5F, +0xA9, 0x42, 0x02, 0xDD, 0x37, 0x4D, 0x21, 0x80, +0x28, 0x80, 0x49, 0x08, 0x49, 0x00, 0x40, 0x1C, +0xF1, 0x52, 0x80, 0xB2, 0x90, 0x42, 0xEF, 0xD3, +0x00, 0x20, 0x20, 0x5E, 0x41, 0x00, 0x41, 0x18, +0xCB, 0x17, 0x5B, 0x0F, 0x59, 0x18, 0xCB, 0x10, +0x31, 0x49, 0x09, 0x68, 0x90, 0x31, 0x4C, 0x7B, +0x0D, 0x7B, 0x21, 0x02, 0x29, 0x43, 0x81, 0x42, +0x0C, 0xDA, 0x00, 0x20, 0x08, 0xE0, 0x41, 0x00, +0x71, 0x5E, 0x99, 0x42, 0x02, 0xDD, 0x39, 0x78, +0x49, 0x1C, 0x39, 0x70, 0x40, 0x1C, 0x80, 0xB2, +0x90, 0x42, 0xF4, 0xD3, 0x38, 0x78, 0x27, 0x49, +0x28, 0x4A, 0x08, 0x70, 0x26, 0x48, 0x01, 0x78, +0x23, 0x48, 0x00, 0x68, 0x80, 0x30, 0x00, 0x29, +0x81, 0x7B, 0x02, 0xD0, 0xCB, 0x00, 0xC9, 0x18, +0xC9, 0x08, 0x11, 0x70, 0xC3, 0x7B, 0x22, 0x49, +0x00, 0x20, 0x08, 0x5E, 0x83, 0x42, 0x11, 0xDA, +0x20, 0x49, 0x09, 0x78, 0x02, 0x29, 0x0D, 0xD2, +0x11, 0x78, 0x3B, 0x78, 0x49, 0x08, 0x99, 0x42, +0x08, 0xD2, 0x1D, 0x49, 0x0B, 0x78, 0x11, 0x78, +0x49, 0x1E, 0x8B, 0x42, 0x02, 0xDD, 0x11, 0x78, +0x49, 0x1C, 0x39, 0x70, 0x19, 0x49, 0x00, 0x23, +0xCB, 0x5E, 0x59, 0x42, 0x81, 0x42, 0x01, 0xDD, +0x00, 0x20, 0x38, 0x70, 0x3B, 0x78, 0x12, 0x78, +0x15, 0x49, 0x93, 0x42, 0x08, 0x78, 0x06, 0xD9, +0x03, 0x28, 0x01, 0xD2, 0x40, 0x1C, 0x00, 0xE0, +0x0C, 0x20, 0x08, 0x70, 0xF0, 0xBD, 0x00, 0x28, +0xFC, 0xD0, 0x3A, 0x78, 0x03, 0x2A, 0xF9, 0xD2, +0x40, 0x1E, 0xF6, 0xE7, 0x0E, 0x00, 0x00, 0x20, +0x30, 0x00, 0x00, 0x20, 0x3C, 0x00, 0x00, 0x20, +0x2C, 0x06, 0x00, 0x20, 0x3E, 0x00, 0x00, 0x20, +0xAC, 0x04, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, +0x4C, 0x07, 0x00, 0x20, 0x0D, 0x00, 0x00, 0x20, +0x58, 0x07, 0x00, 0x20, 0x7C, 0x07, 0x00, 0x20, +0xB6, 0x02, 0x00, 0x20, 0x56, 0x07, 0x00, 0x20, +0x4F, 0x07, 0x00, 0x20, 0x70, 0xB5, 0x0E, 0x4C, +0x00, 0x25, 0xA5, 0x80, 0xE5, 0x80, 0xFF, 0xF7, +0x53, 0xFF, 0x0C, 0x48, 0x00, 0x78, 0x01, 0x28, +0x0E, 0xD0, 0x02, 0x28, 0x0C, 0xD0, 0x0A, 0x48, +0x65, 0x70, 0x05, 0x70, 0xA5, 0x70, 0xE5, 0x70, +0x25, 0x70, 0x08, 0x48, 0x20, 0x81, 0x60, 0x81, +0x07, 0x49, 0x03, 0x20, 0x08, 0x70, 0x70, 0xBD, +0x20, 0x20, 0x60, 0x70, 0xF2, 0xE7, 0x00, 0x00, +0x10, 0x00, 0x00, 0x20, 0x87, 0x02, 0x00, 0x20, +0x4F, 0x07, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, +0x83, 0x02, 0x00, 0x20, 0x10, 0xB5, 0xFF, 0xF7, +0x2F, 0xFF, 0x08, 0x49, 0x78, 0x20, 0x08, 0x70, 0x07, 0x49, 0x00, 0x20, 0x08, 0x70, 0x08, 0x48, -0x06, 0x49, 0x81, 0x80, 0xC1, 0x80, 0x07, 0x49, +0x06, 0x49, 0x01, 0x81, 0x41, 0x81, 0x07, 0x49, 0x03, 0x20, 0x08, 0x70, 0x06, 0x49, 0x01, 0x20, -0x08, 0x70, 0x10, 0xBD, 0xB5, 0x02, 0x00, 0x20, -0x73, 0x04, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, -0x34, 0x00, 0x00, 0x20, 0x82, 0x02, 0x00, 0x20, -0x83, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x12, 0x4D, +0x08, 0x70, 0x10, 0xBD, 0xB7, 0x02, 0x00, 0x20, +0x4F, 0x07, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, +0x10, 0x00, 0x00, 0x20, 0x83, 0x02, 0x00, 0x20, +0x84, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x12, 0x4D, 0x06, 0x46, 0x2C, 0x78, 0xE0, 0x07, 0x07, 0xD0, -0x80, 0x22, 0x10, 0x49, 0x10, 0x48, 0xFB, 0xF7, -0x1F, 0xF9, 0x60, 0x08, 0x40, 0x00, 0x28, 0x70, +0x80, 0x22, 0x10, 0x49, 0x10, 0x48, 0xFA, 0xF7, +0xFB, 0xFC, 0x60, 0x08, 0x40, 0x00, 0x28, 0x70, 0x28, 0x78, 0x80, 0x07, 0x12, 0xD5, 0x00, 0x2E, 0x10, 0xD1, 0x0C, 0x49, 0x00, 0x20, 0x09, 0x4D, 0x09, 0x4C, 0x0E, 0x88, 0x08, 0xE0, 0x41, 0x00, 0x62, 0x5E, 0x6B, 0x5E, 0xD2, 0x18, 0x52, 0x10, 0x62, 0x52, 0x40, 0x1C, 0x6A, 0x52, 0x80, 0xB2, 0xB0, 0x42, 0xF4, 0xD3, 0x70, 0xBD, 0x00, 0x00, -0x82, 0x02, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, -0xF4, 0x05, 0x00, 0x20, 0x3C, 0x00, 0x00, 0x20, -0x10, 0xB5, 0xFB, 0xF7, 0x11, 0xFF, 0x10, 0xBD, +0x83, 0x02, 0x00, 0x20, 0x2C, 0x06, 0x00, 0x20, +0x28, 0x04, 0x00, 0x20, 0x3E, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x04, 0x48, 0x40, 0x78, 0x80, 0x06, -0x01, 0xD5, 0xFE, 0xF7, 0x53, 0xF9, 0xFB, 0xF7, -0x81, 0xFD, 0x10, 0xBD, 0x88, 0x02, 0x00, 0x20, +0x01, 0xD5, 0xFE, 0xF7, 0x55, 0xF9, 0xFB, 0xF7, +0x5D, 0xF9, 0x10, 0xBD, 0x88, 0x02, 0x00, 0x20, 0xF8, 0xB5, 0x22, 0x49, 0x00, 0x20, 0x03, 0x46, 0x1F, 0x4A, 0x0C, 0x88, 0x06, 0xE0, 0x41, 0x00, 0x55, 0x5E, 0x00, 0x2D, 0x00, 0xDA, 0x53, 0x52, @@ -2842,96 +3106,97 @@ const unsigned char u8_rad_fw_30[] = { 0xC3, 0x79, 0x84, 0x79, 0x18, 0x02, 0x20, 0x43, 0x90, 0x42, 0x01, 0xD8, 0x01, 0x29, 0x01, 0xD9, 0x03, 0x20, 0x28, 0x70, 0x00, 0x20, 0xFF, 0xF7, -0x99, 0xFF, 0xFF, 0xF7, 0xD1, 0xFE, 0xF8, 0xBD, +0x9D, 0xFF, 0xFF, 0xF7, 0xB7, 0xFE, 0xF8, 0xBD, 0x90, 0x34, 0x66, 0x7B, 0x27, 0x7B, 0x34, 0x02, 0x3C, 0x43, 0x0F, 0x4E, 0x00, 0x27, 0xF7, 0x5F, 0xBC, 0x42, 0x05, 0xDA, 0x00, 0x29, 0x03, 0xD1, 0x0C, 0x49, 0x09, 0x78, 0x01, 0x29, 0xE7, 0xD0, 0xC1, 0x79, 0x84, 0x79, 0x08, 0x02, 0x20, 0x43, 0x90, 0x42, 0xE3, 0xD2, 0x2B, 0x70, 0xE1, 0xE7, -0x4C, 0x07, 0x00, 0x20, 0x3C, 0x00, 0x00, 0x20, -0x82, 0x02, 0x00, 0x20, 0x40, 0x05, 0x00, 0x20, -0x34, 0x00, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, -0x78, 0x04, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, -0x10, 0xB5, 0xFE, 0xF7, 0xAD, 0xFD, 0xFF, 0xF7, -0x49, 0xFF, 0x10, 0xBD, 0xF8, 0xB5, 0x00, 0x26, -0xFB, 0xF7, 0xE8, 0xFB, 0x83, 0x4C, 0x20, 0x78, -0x01, 0x28, 0x2A, 0xD1, 0x60, 0x7A, 0x0A, 0x28, -0x27, 0xD2, 0x81, 0x4F, 0x81, 0x49, 0xB8, 0x5F, -0x88, 0x42, 0x22, 0xD0, 0x80, 0x4D, 0x0C, 0x23, -0x0A, 0x22, 0xE3, 0x5E, 0xA2, 0x5E, 0xA9, 0x5F, -0x00, 0xF0, 0x1C, 0xF9, 0x7D, 0x49, 0x0A, 0x68, -0xB0, 0x32, 0x51, 0x7B, 0x12, 0x7B, 0x09, 0x02, -0x11, 0x43, 0x81, 0x42, 0x11, 0xD3, 0xB8, 0x5F, -0x41, 0x00, 0x40, 0x18, 0x0A, 0x21, 0x61, 0x5E, -0x40, 0x18, 0x80, 0x10, 0x60, 0x81, 0xA9, 0x5F, -0x4A, 0x00, 0x89, 0x18, 0x0C, 0x22, 0xA2, 0x5E, -0x89, 0x18, 0x89, 0x10, 0xA1, 0x81, 0x60, 0x84, -0xA1, 0x84, 0x00, 0x25, 0xCC, 0xE0, 0x70, 0x4F, -0x34, 0x23, 0x00, 0x20, 0x6B, 0x43, 0x39, 0x78, -0x9C, 0x18, 0x0A, 0xE0, 0x22, 0x79, 0x80, 0x23, -0x1A, 0x43, 0x34, 0x23, 0x43, 0x43, 0xDB, 0x19, -0x1B, 0x79, 0x9A, 0x42, 0x03, 0xD0, 0x40, 0x1C, -0xC0, 0xB2, 0x81, 0x42, 0xF2, 0xD8, 0x81, 0x42, -0x7E, 0xD9, 0x34, 0x21, 0x48, 0x43, 0xC1, 0x19, -0x14, 0x20, 0x08, 0x5E, 0x14, 0x22, 0x43, 0x00, -0xC0, 0x18, 0xA2, 0x5E, 0x01, 0x23, 0x10, 0x18, -0x80, 0x03, 0xDB, 0x03, 0xC0, 0x18, 0x00, 0x14, -0xA0, 0x82, 0x16, 0x20, 0x08, 0x5E, 0x16, 0x22, -0x47, 0x00, 0xC0, 0x19, 0xA2, 0x5E, 0x27, 0x46, -0x10, 0x18, 0x80, 0x03, 0xC0, 0x18, 0x00, 0x14, -0xE0, 0x82, 0x20, 0x37, 0x20, 0x31, 0x0A, 0x22, -0x38, 0x46, 0xFB, 0xF7, 0x25, 0xF8, 0x38, 0x78, -0x40, 0x07, 0x07, 0xD5, 0x00, 0x20, 0x53, 0x49, -0x38, 0x70, 0x08, 0x70, 0x52, 0x49, 0x08, 0x70, -0x52, 0x49, 0x08, 0x70, 0x24, 0x23, 0x22, 0x22, -0x0C, 0x21, 0x0A, 0x20, 0xE3, 0x5E, 0xA2, 0x5E, -0x61, 0x5E, 0x20, 0x5E, 0x00, 0xF0, 0xB2, 0xF8, -0x39, 0x78, 0xCA, 0x07, 0x67, 0xD0, 0x62, 0x7A, -0x2D, 0x2A, 0x09, 0xD2, 0x48, 0x4A, 0x12, 0x78, -0x00, 0x2A, 0x05, 0xD0, 0x22, 0x7E, 0x00, 0x2A, -0x02, 0xD1, 0x02, 0x22, 0x11, 0x43, 0x39, 0x70, -0x40, 0x49, 0x0A, 0x68, 0x92, 0x21, 0x89, 0x5C, -0x81, 0x42, 0x01, 0xD2, 0x01, 0x26, 0x16, 0xE0, -0x10, 0x46, 0xB0, 0x30, 0x41, 0x7A, 0x03, 0x7A, -0x08, 0x02, 0x18, 0x43, 0x81, 0x08, 0x3E, 0x48, -0x00, 0x88, 0x81, 0x42, 0x07, 0xD2, 0x39, 0x48, -0x00, 0x78, 0x00, 0x28, 0x03, 0xD1, 0x3B, 0x48, -0x00, 0x78, 0x00, 0x28, 0x36, 0xD0, 0x00, 0x20, -0xB8, 0x72, 0x00, 0x2E, 0x4E, 0xD0, 0x38, 0x78, -0x04, 0x21, 0x08, 0x43, 0x38, 0x70, 0xB0, 0x32, -0x50, 0x7A, 0x12, 0x7A, 0x00, 0x02, 0x10, 0x43, -0x31, 0x4A, 0x79, 0x7D, 0x12, 0x88, 0x90, 0x42, -0x40, 0xD9, 0x26, 0x23, 0xE3, 0x5E, 0x30, 0x20, -0x2F, 0x4A, 0x9F, 0x01, 0x41, 0x43, 0x88, 0x18, -0x87, 0x60, 0xC7, 0x60, 0x2D, 0x4F, 0x00, 0xE0, -0x34, 0xE0, 0xBF, 0x88, 0x0A, 0x2F, 0x01, 0xD8, -0x0A, 0x23, 0xE3, 0x5E, 0x53, 0x50, 0x0A, 0x21, -0x61, 0x5E, 0x41, 0x60, 0x28, 0x21, 0x61, 0x5E, -0x8A, 0x01, 0x82, 0x61, 0xC2, 0x61, 0x25, 0x4A, -0x92, 0x88, 0x0A, 0x2A, 0x01, 0xD8, 0x0C, 0x21, -0x61, 0x5E, 0x01, 0x61, 0x0C, 0x21, 0x61, 0x5E, -0x41, 0x61, 0x1B, 0xE0, 0xB8, 0x7A, 0x40, 0x1C, -0xC0, 0xB2, 0xB8, 0x72, 0x02, 0x28, 0xC4, 0xD9, -0x00, 0x20, 0xB8, 0x72, 0xAA, 0xE7, 0x00, 0x29, -0x10, 0xD1, 0x12, 0x49, 0x09, 0x68, 0x80, 0x31, -0x89, 0x7C, 0x49, 0x08, 0x81, 0x42, 0x11, 0xD9, -0x78, 0x78, 0x40, 0x1C, 0xC0, 0xB2, 0x78, 0x70, -0x0A, 0x28, 0x03, 0xD9, 0x00, 0x20, 0x78, 0x70, -0x83, 0x20, 0x38, 0x70, 0x6D, 0x1C, 0xED, 0xB2, -0x04, 0x4A, 0x10, 0x78, 0xA8, 0x42, 0x00, 0xD9, -0x2D, 0xE7, 0xF8, 0xBD, 0x00, 0x20, 0x78, 0x70, -0x02, 0x20, 0xF2, 0xE7, 0xA0, 0x04, 0x00, 0x20, -0x38, 0x00, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, -0x3A, 0x00, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, -0x40, 0x05, 0x00, 0x20, 0x75, 0x04, 0x00, 0x20, -0x47, 0x00, 0x00, 0x20, 0x46, 0x00, 0x00, 0x20, -0x34, 0x00, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, -0x38, 0x01, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, +0x2C, 0x06, 0x00, 0x20, 0x3E, 0x00, 0x00, 0x20, +0x83, 0x02, 0x00, 0x20, 0x1C, 0x08, 0x00, 0x20, +0x14, 0x00, 0x00, 0x20, 0xAC, 0x04, 0x00, 0x20, +0x54, 0x07, 0x00, 0x20, 0x60, 0x07, 0x00, 0x20, +0x10, 0xB5, 0xFE, 0xF7, 0xB9, 0xFD, 0xFF, 0xF7, +0x4D, 0xFF, 0x10, 0xBD, 0xF8, 0xB5, 0x89, 0x4C, +0x20, 0x78, 0x01, 0x28, 0x2E, 0xD1, 0x60, 0x7A, +0x0A, 0x28, 0x2B, 0xD2, 0x86, 0x4E, 0x00, 0x20, +0x30, 0x5E, 0x86, 0x49, 0x88, 0x42, 0x25, 0xD0, +0x85, 0x4D, 0x0E, 0x23, 0x0C, 0x22, 0x00, 0x21, +0xE3, 0x5E, 0xA2, 0x5E, 0x69, 0x5E, 0x00, 0xF0, +0x21, 0xF9, 0x82, 0x49, 0x09, 0x68, 0xB0, 0x31, +0x4A, 0x7B, 0x0B, 0x7B, 0x11, 0x02, 0x19, 0x43, +0x81, 0x42, 0x13, 0xD3, 0x00, 0x20, 0x30, 0x5E, +0x41, 0x00, 0x40, 0x18, 0x0C, 0x21, 0x61, 0x5E, +0x40, 0x18, 0x80, 0x10, 0xA0, 0x81, 0x00, 0x21, +0x69, 0x5E, 0x4A, 0x00, 0x89, 0x18, 0x0E, 0x22, +0xA2, 0x5E, 0x89, 0x18, 0x89, 0x10, 0xE1, 0x81, +0x60, 0x85, 0xA1, 0x85, 0x00, 0x25, 0xD5, 0xE0, +0x73, 0x4E, 0x34, 0x23, 0x00, 0x20, 0x80, 0x27, +0x6B, 0x43, 0x31, 0x78, 0x9C, 0x18, 0x09, 0xE0, +0x34, 0x23, 0x43, 0x43, 0x22, 0x79, 0x9B, 0x19, +0x1B, 0x79, 0x3A, 0x43, 0x9A, 0x42, 0x03, 0xD0, +0x40, 0x1C, 0xC0, 0xB2, 0x81, 0x42, 0xF3, 0xD8, +0x81, 0x42, 0x7D, 0xD9, 0x34, 0x22, 0x50, 0x43, +0x87, 0x19, 0x18, 0x20, 0x38, 0x5E, 0x18, 0x21, +0x42, 0x00, 0x80, 0x18, 0x61, 0x5E, 0x01, 0x22, +0x08, 0x18, 0x80, 0x03, 0xD2, 0x03, 0x80, 0x18, +0x00, 0x14, 0x20, 0x83, 0x1A, 0x20, 0x38, 0x5E, +0x1A, 0x21, 0x61, 0x5E, 0x43, 0x00, 0xC0, 0x18, +0x08, 0x18, 0x80, 0x03, 0x80, 0x18, 0x00, 0x14, +0x60, 0x83, 0x39, 0x46, 0x20, 0x46, 0x28, 0x31, +0x28, 0x30, 0x0A, 0x22, 0xFA, 0xF7, 0x04, 0xFC, +0x26, 0x46, 0x20, 0x36, 0x30, 0x7A, 0x40, 0x07, +0x03, 0xD5, 0x00, 0x20, 0x55, 0x49, 0x30, 0x72, +0x08, 0x70, 0x30, 0x7A, 0xC1, 0x07, 0x66, 0xD0, +0x2C, 0x23, 0x2A, 0x22, 0x0E, 0x21, 0x0C, 0x20, +0xE3, 0x5E, 0xA2, 0x5E, 0x79, 0x5E, 0x38, 0x5E, +0x00, 0xF0, 0xB4, 0xF8, 0x00, 0x90, 0x30, 0x23, +0x2E, 0x22, 0x0E, 0x21, 0x0C, 0x20, 0xE3, 0x5E, +0xA2, 0x5E, 0x79, 0x5E, 0x38, 0x5E, 0x00, 0xF0, +0xA9, 0xF8, 0x46, 0x49, 0x92, 0x22, 0x09, 0x68, +0x53, 0x5C, 0x00, 0x9A, 0x93, 0x42, 0x1E, 0xD3, +0xFD, 0x28, 0x03, 0xD9, 0x44, 0x48, 0x00, 0x78, +0x00, 0x28, 0x18, 0xD0, 0x08, 0x46, 0xB0, 0x30, +0x42, 0x7A, 0x03, 0x7A, 0x10, 0x02, 0x18, 0x43, +0x82, 0x08, 0x40, 0x48, 0x00, 0x88, 0x82, 0x42, +0x03, 0xD2, 0x3C, 0x48, 0x00, 0x78, 0x00, 0x28, +0x09, 0xD0, 0x30, 0x79, 0x01, 0x28, 0x5B, 0xD1, +0xB0, 0x79, 0x00, 0x28, 0x58, 0xD1, 0x30, 0x7A, +0x02, 0x21, 0x08, 0x43, 0x53, 0xE0, 0x00, 0x20, +0x30, 0x71, 0x30, 0x7A, 0x04, 0x22, 0x10, 0x43, +0x30, 0x72, 0x35, 0x4A, 0x03, 0x20, 0x10, 0x70, +0xB0, 0x31, 0x48, 0x7A, 0x09, 0x7A, 0x00, 0x02, +0x08, 0x43, 0x30, 0x49, 0xE2, 0x7A, 0x09, 0x88, +0x88, 0x42, 0x41, 0xD9, 0x2E, 0x20, 0x00, 0xE0, +0x39, 0xE0, 0x38, 0x5E, 0x2D, 0x4B, 0x81, 0x01, +0x30, 0x20, 0x42, 0x43, 0xD0, 0x18, 0x81, 0x60, +0xC1, 0x60, 0x0C, 0x21, 0x61, 0x5E, 0x99, 0x50, +0x41, 0x60, 0x30, 0x21, 0x79, 0x5E, 0x89, 0x01, +0x81, 0x61, 0xC1, 0x61, 0x0E, 0x21, 0x61, 0x5E, +0x01, 0x61, 0x41, 0x61, 0x28, 0xE0, 0x00, 0x28, +0x26, 0xD1, 0x21, 0x4F, 0x03, 0x20, 0x38, 0x70, +0x2C, 0x23, 0x2A, 0x22, 0x0E, 0x21, 0x0C, 0x20, +0xE3, 0x5E, 0xA2, 0x5E, 0x61, 0x5E, 0x20, 0x5E, +0x00, 0xF0, 0x48, 0xF8, 0x15, 0x49, 0x09, 0x68, +0x80, 0x31, 0x89, 0x7C, 0x49, 0x08, 0x81, 0x42, +0x0E, 0xD9, 0x70, 0x7A, 0x40, 0x1C, 0xC0, 0xB2, +0x70, 0x72, 0x0A, 0x28, 0x0C, 0xD9, 0x00, 0x20, +0x70, 0x72, 0x83, 0x21, 0x31, 0x72, 0x01, 0x21, +0x31, 0x71, 0x70, 0x71, 0x39, 0x70, 0x03, 0xE0, +0x00, 0x20, 0x70, 0x72, 0x02, 0x20, 0x30, 0x72, +0x6D, 0x1C, 0xED, 0xB2, 0x03, 0x4A, 0x10, 0x78, +0xA8, 0x42, 0x00, 0xD9, 0x24, 0xE7, 0xFA, 0xF7, +0xB9, 0xFE, 0xF8, 0xBD, 0x7C, 0x07, 0x00, 0x20, +0x18, 0x00, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, +0x1A, 0x00, 0x00, 0x20, 0xAC, 0x04, 0x00, 0x20, +0x1C, 0x08, 0x00, 0x20, 0x51, 0x07, 0x00, 0x20, +0x34, 0x00, 0x00, 0x20, 0x14, 0x00, 0x00, 0x20, +0x10, 0x00, 0x00, 0x20, 0x38, 0x01, 0x00, 0x20, 0x10, 0xB5, 0x03, 0x48, 0x00, 0x78, 0x00, 0x06, -0x01, 0xD4, 0xFF, 0xF7, 0xAF, 0xFB, 0x10, 0xBD, -0x86, 0x02, 0x00, 0x20, 0x10, 0xB5, 0xFF, 0xF7, -0x07, 0xFD, 0x10, 0xBD, 0x10, 0x1A, 0x59, 0x1A, +0x01, 0xD4, 0xFF, 0xF7, 0x01, 0xFB, 0x10, 0xBD, +0x8E, 0x02, 0x00, 0x20, 0x10, 0xB5, 0xFF, 0xF7, +0xA3, 0xFC, 0x10, 0xBD, 0x10, 0x1A, 0x59, 0x1A, 0x40, 0x43, 0x49, 0x43, 0xC2, 0x17, 0xCB, 0x17, 0x08, 0x18, 0x53, 0x41, 0x00, 0x22, 0xD2, 0x43, 0x00, 0x21, 0x12, 0x1A, 0x99, 0x41, 0x01, 0xD2, @@ -2959,17 +3224,19 @@ const unsigned char u8_rad_fw_30[] = { 0x04, 0x00, 0x00, 0x40, 0x10, 0x32, 0x00, 0x00, 0x1C, 0x0E, 0x00, 0x50, 0x03, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x50, 0x14, 0x07, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, +0x28, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x58, 0x10, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00, 0x04, 0x11, 0x00, 0x50, 0x78, 0x11, 0x00, 0x00, -0x0C, 0x11, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00, +0x0C, 0x11, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x00, 0x50, 0x78, 0x01, 0x00, 0x00, 0x14, 0x11, 0x00, 0x50, 0xC8, 0x03, 0x60, 0x00, +0x18, 0x10, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, +0x1C, 0x10, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x50, 0x31, 0x00, 0x00, 0x00, 0x3C, 0x10, 0x00, 0x50, 0x00, 0x00, 0x10, 0x00, -0xB4, 0x10, 0x00, 0x50, 0x00, 0x26, 0x31, 0x00, +0xB4, 0x10, 0x00, 0x50, 0x00, 0x26, 0x31, 0x08, 0xC0, 0x10, 0x00, 0x50, 0x33, 0x03, 0x33, 0x03, 0xC4, 0x10, 0x00, 0x50, 0x33, 0x03, 0x33, 0x03, 0xC8, 0x10, 0x00, 0x50, 0x0C, 0x0A, 0x00, 0x00, @@ -2991,12 +3258,2321 @@ const unsigned char u8_rad_fw_30[] = { 0x04, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, -0x78, 0x5C, 0x00, 0x00, 0x04, 0x00, 0x00, 0x20, +0xC0, 0x64, 0x00, 0x00, 0x04, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, 0x5C, 0x0A, 0x00, 0x00, -0x80, 0x5C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x20, -0xC4, 0x0C, 0x00, 0x00, 0x6A, 0x0A, 0x00, 0x00, +0xC8, 0x64, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x20, +0xC4, 0x0D, 0x00, 0x00, 0x6A, 0x0A, 0x00, 0x00, +0xFF, 0x00, 0x00, 0x00, 0x78, 0x7F, 0x00, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0x9C, 0xD7, 0x90, 0x66, +}; +const unsigned char u8_rad_para_30[] = { +0xA2, 0x00, 0x03, 0xF3, 0x02, 0x02, 0x00, 0x04, +0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, +0x40, 0x01, 0x68, 0x01, 0xFA, 0x00, 0x18, 0x01, +0x05, 0x05, 0x00, 0x00, 0x01, 0x05, 0x41, 0x06, +0x0A, 0x0B, 0x41, 0x0F, 0x10, 0x14, 0x41, 0x15, +0x02, 0x07, 0x41, 0x16, 0x41, 0x41, 0x41, 0x41, +0x41, 0x41, 0x41, 0x04, 0x03, 0x09, 0x41, 0x08, +0x0E, 0x0D, 0x41, 0x13, 0x12, 0x18, 0x41, 0x17, +0x0C, 0x11, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, +0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, +0x0C, 0x3E, 0x0C, 0x3E, 0x06, 0x06, 0x02, 0x02, +0x14, 0x03, 0x06, 0x06, 0x01, 0x01, 0xE6, 0x00, +0x1F, 0x01, 0x08, 0x14, 0x82, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, 0x01, +0xB2, 0x00, 0x73, 0x00, 0x3C, 0x00, 0x09, 0x78, +0x0F, 0x08, 0x1D, 0x40, 0x48, 0x40, 0x48, 0x00, +0x00, 0x00, 0x88, 0x00, 0x62, 0x00, 0x24, 0x00, +0x06, 0x32, 0x6C, 0x41, 0x0F, 0x00, 0x00, 0x45, +0x4C, 0x00, 0x00, 0x06, 0x00, 0xA0, 0x00, 0x78, +0x00, 0x64, 0xCE, 0x15, 0xF4, 0x0D, 0x7D, 0x03, +0xDF, 0x00, 0x16, 0x0B, 0xD8, 0x01, 0x7C, 0x06, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x55, 0xAA, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xDE, 0x68, 0xE3, 0xA1, 0xE3, 0x4C, 0x06, 0xD3, +}; +const unsigned char u8_rad_testfw_30[] = { +0xA0, 0x0F, 0x00, 0x20, 0xB1, 0x08, 0x00, 0x00, +0xB9, 0x08, 0x00, 0x00, 0x95, 0x08, 0x00, 0x00, +0xBD, 0x08, 0x00, 0x00, 0xBF, 0x08, 0x00, 0x00, +0xC1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xC3, 0x08, 0x00, 0x00, +0xC5, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xC7, 0x08, 0x00, 0x00, 0x81, 0x0F, 0x00, 0x00, +0xE1, 0x0C, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00, +0xCB, 0x08, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x00, +0xA1, 0x0D, 0x00, 0x00, 0x41, 0x0A, 0x00, 0x00, +0xCB, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, +0xCB, 0x08, 0x00, 0x00, 0x41, 0x0B, 0x00, 0x00, +0xE1, 0x0A, 0x00, 0x00, 0xA1, 0x0A, 0x00, 0x00, +0xC1, 0x0A, 0x00, 0x00, 0x81, 0x0A, 0x00, 0x00, +0xCB, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, +0x03, 0x48, 0x85, 0x46, 0x00, 0xF0, 0x80, 0xF8, +0x00, 0x48, 0x00, 0x47, 0xE9, 0x33, 0x00, 0x00, +0xA0, 0x0F, 0x00, 0x20, 0x04, 0x20, 0x71, 0x46, +0x08, 0x42, 0x02, 0xD0, 0xEF, 0xF3, 0x09, 0x80, +0x01, 0xE0, 0xEF, 0xF3, 0x08, 0x80, 0x71, 0x46, +0x00, 0x4A, 0x10, 0x47, 0x01, 0x33, 0x00, 0x00, +0x06, 0x48, 0x80, 0x47, 0x06, 0x48, 0x00, 0x47, +0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, +0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, +0xFE, 0xE7, 0xFE, 0xE7, 0x15, 0x0A, 0x00, 0x00, +0x81, 0x08, 0x00, 0x00, 0x30, 0xB5, 0x0B, 0x46, +0x01, 0x46, 0x00, 0x20, 0x20, 0x22, 0x01, 0x24, +0x09, 0xE0, 0x0D, 0x46, 0xD5, 0x40, 0x9D, 0x42, +0x05, 0xD3, 0x1D, 0x46, 0x95, 0x40, 0x49, 0x1B, +0x25, 0x46, 0x95, 0x40, 0x40, 0x19, 0x15, 0x46, +0x52, 0x1E, 0x00, 0x2D, 0xF1, 0xDC, 0x30, 0xBD, +0x70, 0xB5, 0x00, 0x24, 0x25, 0x46, 0x00, 0x28, +0x01, 0xDA, 0x01, 0x24, 0x40, 0x42, 0x00, 0x29, +0x01, 0xDA, 0x01, 0x25, 0x49, 0x42, 0xFF, 0xF7, +0xDD, 0xFF, 0xAC, 0x42, 0x00, 0xD0, 0x40, 0x42, +0x00, 0x2C, 0x00, 0xD0, 0x49, 0x42, 0x70, 0xBD, +0x03, 0x46, 0x0B, 0x43, 0x9B, 0x07, 0x03, 0xD0, +0x09, 0xE0, 0x08, 0xC9, 0x12, 0x1F, 0x08, 0xC0, +0x04, 0x2A, 0xFA, 0xD2, 0x03, 0xE0, 0x0B, 0x78, +0x49, 0x1C, 0x03, 0x70, 0x40, 0x1C, 0x52, 0x1E, +0xF9, 0xD2, 0x70, 0x47, 0xD2, 0xB2, 0x01, 0xE0, +0x02, 0x70, 0x40, 0x1C, 0x49, 0x1E, 0xFB, 0xD2, +0x70, 0x47, 0x00, 0x22, 0xF6, 0xE7, 0x10, 0xB5, +0x04, 0x46, 0x08, 0x46, 0x11, 0x46, 0x02, 0x46, +0x20, 0x46, 0xFF, 0xF7, 0xEF, 0xFF, 0x20, 0x46, +0x10, 0xBD, 0x00, 0x1D, 0x03, 0x21, 0x40, 0x1E, +0x03, 0x78, 0x12, 0x02, 0x1A, 0x43, 0x49, 0x1E, +0xF9, 0xD5, 0x10, 0x46, 0x70, 0x47, 0x00, 0x00, +0x06, 0x4C, 0x01, 0x25, 0x06, 0x4E, 0x05, 0xE0, +0x20, 0x46, 0xE3, 0x68, 0x07, 0xC8, 0x2B, 0x43, +0x98, 0x47, 0x10, 0x34, 0xB4, 0x42, 0xF7, 0xD3, +0xFF, 0xF7, 0x72, 0xFF, 0x9C, 0x36, 0x00, 0x00, +0xBC, 0x36, 0x00, 0x00, 0xC1, 0x06, 0xC9, 0x0E, +0x01, 0x20, 0x88, 0x40, 0x01, 0x49, 0x08, 0x60, +0x70, 0x47, 0x00, 0x00, 0x00, 0xE1, 0x00, 0xE0, +0x0B, 0x49, 0x10, 0xB5, 0x88, 0x42, 0x01, 0xD9, +0x01, 0x20, 0x10, 0xBD, 0x01, 0x02, 0x09, 0x0A, +0x08, 0x48, 0x49, 0x1E, 0x41, 0x61, 0x08, 0x49, +0x07, 0x23, 0xCA, 0x69, 0x12, 0x02, 0x12, 0x0A, +0x04, 0x04, 0x22, 0x43, 0xCA, 0x61, 0x00, 0x21, +0x81, 0x61, 0x03, 0x61, 0x08, 0x46, 0x10, 0xBD, +0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xE0, 0x00, 0xE0, +0x04, 0xED, 0x00, 0xE0, 0x70, 0x47, 0x00, 0x00, +0x03, 0x49, 0x02, 0x20, 0x08, 0x60, 0x02, 0x49, +0x80, 0x39, 0x08, 0x60, 0x70, 0x47, 0x00, 0x00, +0x80, 0xE1, 0x00, 0xE0, 0x62, 0xB6, 0x02, 0x48, +0x00, 0x21, 0x01, 0x60, 0x70, 0x47, 0x00, 0x00, +0x0C, 0x00, 0x00, 0x40, 0x30, 0xB4, 0x74, 0x46, +0x64, 0x1E, 0x25, 0x78, 0x64, 0x1C, 0xAB, 0x42, +0x00, 0xD2, 0x1D, 0x46, 0x63, 0x5D, 0x5B, 0x00, +0xE3, 0x18, 0x30, 0xBC, 0x18, 0x47, 0x00, 0x00, +0x05, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, +0x01, 0x20, 0x80, 0x07, 0x01, 0x6A, 0x03, 0x22, +0xD2, 0x03, 0x11, 0x43, 0x01, 0x62, 0x70, 0x47, +0x40, 0x00, 0x00, 0x20, 0x02, 0xE0, 0x08, 0xC8, +0x12, 0x1F, 0x08, 0xC1, 0x00, 0x2A, 0xFA, 0xD1, +0x70, 0x47, 0x00, 0x20, 0x01, 0xE0, 0x01, 0xC1, +0x12, 0x1F, 0x00, 0x2A, 0xFB, 0xD1, 0x70, 0x47, +0x10, 0xB5, 0x00, 0xF0, 0xDF, 0xF9, 0x10, 0xBD, +0x01, 0x20, 0x80, 0x07, 0x41, 0x6A, 0x82, 0x13, +0x11, 0x43, 0x41, 0x62, 0x41, 0x6A, 0xC2, 0x13, +0x11, 0x43, 0x41, 0x62, 0x70, 0x47, 0x10, 0xB5, +0x02, 0xF0, 0x66, 0xF8, 0x10, 0xBD, 0x00, 0x00, +0x03, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, +0x02, 0x49, 0x01, 0x20, 0xC8, 0x60, 0x70, 0x47, +0x64, 0x01, 0x00, 0x20, 0x00, 0x03, 0x00, 0x50, +0x10, 0xB5, 0x00, 0xF0, 0xE7, 0xFA, 0x10, 0xBD, +0x70, 0x47, 0x00, 0x00, 0x04, 0x49, 0x06, 0x22, +0x00, 0x28, 0x08, 0x68, 0x01, 0xD0, 0x10, 0x43, +0x00, 0xE0, 0x90, 0x43, 0x08, 0x60, 0x70, 0x47, +0x00, 0x09, 0x00, 0x50, 0x00, 0x20, 0x70, 0x47, +0x70, 0x47, 0x00, 0x00, 0x01, 0x28, 0x05, 0xD0, +0x02, 0x28, 0x05, 0xD0, 0x04, 0x28, 0x06, 0xD0, +0x00, 0x20, 0x70, 0x47, 0x03, 0x48, 0x70, 0x47, +0x02, 0x48, 0xC0, 0x30, 0x70, 0x47, 0x02, 0x48, +0x70, 0x47, 0x00, 0x00, 0x78, 0x7C, 0x00, 0x00, +0xF8, 0x7D, 0x00, 0x00, 0x70, 0xB5, 0x05, 0x46, +0x06, 0x4C, 0x01, 0xF0, 0xE3, 0xFC, 0x00, 0x21, +0x8A, 0x00, 0x49, 0x1C, 0xA0, 0x58, 0xC9, 0xB2, +0xA8, 0x50, 0x18, 0x29, 0xF8, 0xD3, 0x01, 0x20, +0x70, 0xBD, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, +0x01, 0x20, 0x80, 0x07, 0x41, 0x68, 0x82, 0x14, +0x11, 0x43, 0x41, 0x60, 0x70, 0x47, 0x00, 0x00, +0x30, 0xB5, 0x1E, 0x4B, 0x58, 0x68, 0x99, 0x68, +0x00, 0x28, 0x01, 0xDA, 0xDA, 0x04, 0x5A, 0x60, +0x1B, 0x4A, 0x15, 0x68, 0x01, 0x24, 0x05, 0x40, +0x00, 0x2D, 0x02, 0xD1, 0x50, 0x68, 0x08, 0x42, +0x0F, 0xD0, 0x18, 0x48, 0x04, 0x70, 0x00, 0xF0, +0xAF, 0xF8, 0x14, 0x72, 0x16, 0x48, 0x00, 0x78, +0x00, 0x28, 0x06, 0xD0, 0x18, 0x68, 0x01, 0x21, +0x00, 0x09, 0x00, 0x01, 0x89, 0x02, 0x08, 0x43, +0x18, 0x60, 0x58, 0x68, 0x80, 0x02, 0x02, 0xD5, +0x01, 0x20, 0x40, 0x05, 0x58, 0x60, 0x58, 0x68, +0x00, 0x04, 0x03, 0xD5, 0x01, 0x20, 0xC0, 0x03, +0x58, 0x60, 0x94, 0x72, 0x58, 0x68, 0x80, 0x00, +0x03, 0xD5, 0x01, 0x20, 0x40, 0x07, 0x58, 0x60, +0xD4, 0x72, 0x58, 0x68, 0x40, 0x00, 0x03, 0xD5, +0x01, 0x20, 0x80, 0x07, 0x58, 0x60, 0x94, 0x72, +0x30, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, +0x24, 0x00, 0x00, 0x20, 0x37, 0x00, 0x00, 0x20, +0x35, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x02, 0x46, +0x07, 0x49, 0x60, 0x32, 0x11, 0x80, 0x00, 0x21, +0x51, 0x80, 0x4C, 0x00, 0x53, 0x88, 0x04, 0x5B, +0x49, 0x1C, 0x1B, 0x19, 0xC9, 0xB2, 0x53, 0x80, +0x31, 0x29, 0xF6, 0xD3, 0x10, 0xBD, 0x00, 0x00, +0xAA, 0x55, 0x00, 0x00, 0x10, 0xB5, 0x00, 0xF0, +0xB9, 0xF9, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00, +0x70, 0x47, 0x00, 0x00, 0xF8, 0xB5, 0x06, 0x46, +0x0D, 0x46, 0x30, 0x23, 0xFF, 0xF7, 0x6A, 0xFF, +0x04, 0x00, 0x28, 0xD0, 0x19, 0x4F, 0x00, 0x2A, +0x21, 0xD0, 0x00, 0x20, 0xFF, 0xF7, 0x52, 0xFF, +0x02, 0xE0, 0x98, 0x00, 0x29, 0x58, 0x21, 0x50, +0x18, 0x46, 0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, +0xF7, 0xD1, 0x38, 0x68, 0x01, 0x88, 0x31, 0x43, +0x01, 0x80, 0x11, 0x49, 0x09, 0x68, 0xCA, 0x79, +0x8B, 0x79, 0x11, 0x02, 0x19, 0x43, 0x41, 0x80, +0xC1, 0x21, 0x89, 0x00, 0x0D, 0x48, 0x00, 0xF0, +0x1D, 0xF8, 0x0D, 0x49, 0xC8, 0x63, 0x01, 0x20, +0xFF, 0xF7, 0x34, 0xFF, 0x0C, 0xE0, 0x38, 0x68, +0x00, 0x88, 0x30, 0x40, 0x03, 0xD1, 0xF8, 0xBD, +0x98, 0x00, 0x21, 0x58, 0x29, 0x50, 0x18, 0x46, +0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, 0xF7, 0xD1, +0x01, 0x20, 0xF8, 0xBD, 0x08, 0x00, 0x00, 0x20, +0x80, 0x02, 0x00, 0x20, 0x78, 0x7C, 0x00, 0x00, +0x40, 0x7F, 0x00, 0x00, 0x70, 0xB5, 0x41, 0x18, +0x49, 0x1E, 0x64, 0x24, 0x09, 0x04, 0x0B, 0x4D, +0x01, 0x43, 0x69, 0x63, 0xE8, 0x68, 0x81, 0x21, +0x09, 0x06, 0x08, 0x43, 0xE8, 0x60, 0x02, 0xE0, +0x01, 0x20, 0xFF, 0xF7, 0xA3, 0xFF, 0xE8, 0x68, +0xC0, 0x01, 0x04, 0xD5, 0x20, 0x46, 0x64, 0x1E, +0xA4, 0xB2, 0x00, 0x28, 0xF4, 0xD1, 0xA8, 0x6B, +0x70, 0xBD, 0x00, 0x00, 0x40, 0x09, 0x00, 0x50, +0x03, 0x48, 0x02, 0x49, 0x41, 0x60, 0x03, 0x49, +0x81, 0x60, 0x70, 0x47, 0x1F, 0x1F, 0x5F, 0x1F, +0x00, 0x10, 0x00, 0x50, 0x1F, 0x1F, 0x1F, 0x1F, +0x70, 0xB5, 0x01, 0x25, 0xAD, 0x07, 0xE8, 0x68, +0x40, 0x08, 0x40, 0x00, 0xE8, 0x60, 0x1D, 0x48, +0x41, 0x68, 0x01, 0x26, 0x49, 0x07, 0x00, 0x29, +0x2E, 0xDA, 0x42, 0x68, 0x04, 0x21, 0x0A, 0x43, +0x42, 0x60, 0x19, 0x48, 0x00, 0x68, 0x19, 0x4A, +0x40, 0x05, 0x40, 0x0F, 0x01, 0x28, 0x07, 0xD0, +0x00, 0x23, 0x17, 0x4C, 0x13, 0x70, 0x20, 0x73, +0x20, 0x7B, 0x06, 0x28, 0x03, 0xD0, 0x03, 0xE0, +0x03, 0x20, 0x10, 0x70, 0x18, 0xE0, 0x21, 0x73, +0x20, 0x7B, 0x02, 0x28, 0x18, 0xD0, 0x20, 0x7B, +0x06, 0x28, 0x05, 0xD8, 0x20, 0x7B, 0x04, 0x28, +0x02, 0xD3, 0x01, 0x20, 0x01, 0xF0, 0x4E, 0xF8, +0x20, 0x7B, 0x0C, 0x49, 0x09, 0x78, 0x88, 0x42, +0x06, 0xD0, 0x0B, 0x48, 0x01, 0x78, 0x31, 0x43, +0x01, 0x70, 0x0A, 0x49, 0x81, 0x20, 0x08, 0x70, +0xE8, 0x68, 0x30, 0x43, 0xE8, 0x60, 0x70, 0xBD, +0x00, 0x20, 0xEB, 0xE7, 0x40, 0x00, 0x00, 0x50, +0x00, 0x11, 0x00, 0x50, 0x38, 0x00, 0x00, 0x20, +0x24, 0x00, 0x00, 0x20, 0x39, 0x01, 0x00, 0x20, +0x34, 0x00, 0x00, 0x20, 0xE0, 0x08, 0x00, 0x20, +0x10, 0xB5, 0x01, 0x22, 0x92, 0x07, 0x13, 0x68, +0x03, 0x4C, 0x23, 0x43, 0x13, 0x60, 0x89, 0x04, +0x09, 0x0C, 0x01, 0xF0, 0x6B, 0xF8, 0x10, 0xBD, +0x10, 0x01, 0x42, 0x88, 0x00, 0x00, 0x00, 0x00, +0x10, 0xB5, 0x14, 0x48, 0x80, 0x69, 0x40, 0x04, +0x23, 0xD5, 0x13, 0x4C, 0xE0, 0x68, 0xA2, 0x68, +0x02, 0x40, 0xE0, 0x68, 0xA1, 0x68, 0x88, 0x43, +0xE0, 0x60, 0x10, 0x07, 0x03, 0xD5, 0x08, 0x20, +0xE0, 0x60, 0x02, 0xF0, 0x49, 0xFB, 0x50, 0x07, +0x0E, 0xD5, 0x04, 0x20, 0xE0, 0x60, 0x02, 0xF0, +0x43, 0xFB, 0x02, 0xF0, 0x56, 0xFA, 0x00, 0x28, +0x06, 0xD1, 0x08, 0x48, 0x00, 0x78, 0x00, 0x28, +0x02, 0xD0, 0x01, 0x20, 0xFF, 0xF7, 0x48, 0xFE, +0xFF, 0x20, 0xF3, 0x30, 0xE0, 0x60, 0x01, 0x20, +0xE0, 0x60, 0x10, 0xBD, 0x00, 0x09, 0x00, 0x50, +0x00, 0x05, 0x00, 0x50, 0x90, 0x02, 0x00, 0x20, +0x70, 0xB5, 0x0C, 0x49, 0x00, 0x20, 0x0C, 0x4B, +0x89, 0x25, 0x0C, 0x68, 0xAD, 0x00, 0x21, 0x18, +0xC9, 0x7E, 0x41, 0x29, 0x08, 0xD0, 0x42, 0x00, +0x99, 0x5A, 0x49, 0x05, 0x49, 0x0D, 0x99, 0x52, +0x4E, 0x05, 0x01, 0xD5, 0x49, 0x1B, 0x99, 0x52, +0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, 0xEE, 0xD3, +0x01, 0x20, 0x70, 0xBD, 0x80, 0x02, 0x00, 0x20, +0xAC, 0x03, 0x00, 0x20, 0x01, 0x21, 0x89, 0x07, +0x0A, 0x15, 0x00, 0x28, 0x48, 0x69, 0x02, 0xD0, +0x10, 0x43, 0x48, 0x61, 0x70, 0x47, 0x90, 0x43, +0xFB, 0xE7, 0x00, 0x00, 0x01, 0x20, 0x01, 0x49, +0x08, 0x70, 0x70, 0x47, 0xD2, 0x00, 0x00, 0x20, +0xF8, 0xB5, 0x3C, 0x48, 0x80, 0x69, 0x40, 0x04, +0x72, 0xD5, 0x3B, 0x4D, 0xA8, 0x6A, 0xE9, 0x68, +0xAC, 0x68, 0x0C, 0x40, 0xE9, 0x68, 0xAA, 0x68, +0x91, 0x43, 0xE9, 0x60, 0x37, 0x4E, 0xC0, 0x07, +0x15, 0xD0, 0x68, 0x69, 0x89, 0x27, 0xC0, 0xB2, +0xEF, 0x60, 0xAA, 0x6A, 0x01, 0x21, 0x0A, 0x43, +0xAA, 0x62, 0x0B, 0x28, 0x03, 0xD2, 0x31, 0x70, +0x00, 0xF0, 0x1E, 0xFD, 0x03, 0xE0, 0x00, 0x21, +0x31, 0x70, 0x00, 0xF0, 0xF5, 0xFC, 0xBC, 0x43, +0x01, 0x20, 0xFF, 0xF7, 0xE5, 0xFD, 0x2C, 0x48, +0xA1, 0x04, 0x04, 0xD5, 0x29, 0x6A, 0x81, 0x43, +0x29, 0x62, 0x41, 0x14, 0xE9, 0x60, 0xE1, 0x04, +0x16, 0xD5, 0x29, 0x6A, 0x81, 0x43, 0x29, 0x62, +0x01, 0x20, 0x00, 0x03, 0xE8, 0x60, 0xE8, 0x6B, +0x24, 0x4F, 0x00, 0x04, 0x79, 0x78, 0x00, 0x0E, +0x02, 0x29, 0x02, 0xD0, 0x00, 0x28, 0x02, 0xD0, +0x06, 0xE0, 0x03, 0x28, 0x04, 0xD1, 0x01, 0x20, +0xFF, 0xF7, 0xC6, 0xFD, 0x00, 0x20, 0x38, 0x70, +0xA0, 0x05, 0x02, 0xD5, 0x01, 0x20, 0x40, 0x02, +0xE8, 0x60, 0x20, 0x07, 0x09, 0xD5, 0x30, 0x78, +0x01, 0x28, 0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, +0xC3, 0xFC, 0x64, 0x08, 0x64, 0x00, 0x09, 0x20, +0xE8, 0x60, 0x60, 0x07, 0x17, 0xD5, 0x30, 0x78, +0x01, 0x28, 0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, +0xB7, 0xFC, 0x05, 0x20, 0xE8, 0x60, 0xA8, 0x69, +0x0F, 0x49, 0x88, 0x42, 0x08, 0xD0, 0xA9, 0x69, +0x0D, 0x48, 0x40, 0x1C, 0x81, 0x42, 0x03, 0xD0, +0xA9, 0x69, 0xC0, 0x1C, 0x81, 0x42, 0x02, 0xD1, +0x01, 0x20, 0xFF, 0xF7, 0x99, 0xFD, 0xFF, 0x20, +0xF3, 0x30, 0xE8, 0x60, 0x01, 0x20, 0xE8, 0x60, +0xF8, 0xBD, 0x00, 0x00, 0x00, 0x09, 0x00, 0x50, +0x00, 0x06, 0x00, 0x50, 0xD0, 0x00, 0x00, 0x20, +0x00, 0x00, 0x00, 0x40, 0x90, 0x02, 0x00, 0x20, +0x88, 0x02, 0x00, 0x20, 0x03, 0x49, 0x0A, 0x68, +0x10, 0x18, 0x0A, 0x68, 0x90, 0x42, 0xFC, 0xD1, +0x70, 0x47, 0x00, 0x00, 0x64, 0x01, 0x00, 0x20, +0x02, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, +0x70, 0x47, 0x00, 0x00, 0xDC, 0x08, 0x00, 0x20, +0x10, 0xB5, 0x02, 0xF0, 0xC9, 0xF9, 0x02, 0xF0, +0x83, 0xFA, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00, +0x05, 0x20, 0x00, 0x07, 0x82, 0x69, 0x0C, 0x49, +0x00, 0x2A, 0x07, 0xDA, 0x82, 0x69, 0xC3, 0x00, +0x92, 0x00, 0x92, 0x08, 0xD2, 0x18, 0x82, 0x61, +0x01, 0x22, 0x4A, 0x72, 0x82, 0x69, 0x52, 0x00, +0x08, 0xD5, 0x82, 0x69, 0x01, 0x23, 0x92, 0x00, +0x92, 0x08, 0x9B, 0x07, 0xD2, 0x18, 0x82, 0x61, +0x00, 0x20, 0x48, 0x72, 0x70, 0x47, 0x00, 0x00, +0xE0, 0x08, 0x00, 0x20, 0x70, 0xB5, 0x1D, 0x4D, +0x68, 0x78, 0x00, 0x06, 0x2A, 0xD5, 0x05, 0x20, +0xFF, 0xF7, 0x04, 0xFE, 0x01, 0x20, 0xFF, 0xF7, +0x43, 0xFD, 0x68, 0x78, 0x40, 0x06, 0x40, 0x0E, +0x68, 0x70, 0x68, 0x78, 0x16, 0x4C, 0x10, 0x38, +0x03, 0x00, 0xFF, 0xF7, 0x0F, 0xFD, 0x05, 0x04, +0x06, 0x0C, 0x1C, 0x1E, 0x0E, 0x00, 0x06, 0x20, +0x06, 0xE0, 0x01, 0x20, 0x20, 0x70, 0x11, 0xA0, +0xFF, 0xF7, 0x60, 0xFD, 0x01, 0xE0, 0x02, 0x20, +0x20, 0x70, 0x68, 0x78, 0x80, 0x21, 0x08, 0x43, +0x28, 0x71, 0x00, 0x0A, 0x68, 0x71, 0x00, 0x20, +0xFF, 0xF7, 0x22, 0xFD, 0xA9, 0x88, 0x0C, 0xA0, +0xFF, 0xF7, 0x50, 0xFD, 0x70, 0xBD, 0x03, 0x20, +0xEE, 0xE7, 0x0D, 0x48, 0x69, 0x78, 0x82, 0x88, +0x0C, 0xA0, 0xFF, 0xF7, 0x47, 0xFD, 0x05, 0x20, +0xE6, 0xE7, 0x00, 0x00, 0x88, 0x02, 0x00, 0x20, +0x98, 0x01, 0x00, 0x20, 0x43, 0x4D, 0x44, 0x5F, +0x49, 0x4E, 0x49, 0x54, 0x0D, 0x0A, 0x00, 0x00, +0x43, 0x4D, 0x44, 0x20, 0x45, 0x78, 0x69, 0x74, +0x5B, 0x25, 0x78, 0x5D, 0x3D, 0x0D, 0x0A, 0x00, +0x74, 0x06, 0x00, 0x20, 0x43, 0x4D, 0x44, 0x3D, +0x30, 0x78, 0x25, 0x78, 0x3A, 0x25, 0x78, 0x0D, +0x0A, 0x00, 0x00, 0x00, 0xF8, 0xB5, 0x1A, 0x4C, +0x01, 0x20, 0x20, 0x70, 0x19, 0x4E, 0x00, 0x20, +0x30, 0x71, 0x70, 0x71, 0x18, 0x48, 0xB0, 0x70, +0x00, 0x0A, 0xF0, 0x70, 0x00, 0x20, 0x02, 0xF0, +0x31, 0xF8, 0x16, 0x4F, 0x04, 0x25, 0x20, 0x78, +0x03, 0x00, 0xFF, 0xF7, 0xB7, 0xFC, 0x07, 0x14, +0x05, 0x12, 0x12, 0x16, 0x19, 0x1F, 0x14, 0x00, +0x37, 0x71, 0x38, 0x0A, 0x70, 0x71, 0x25, 0x70, +0x70, 0x20, 0x30, 0x70, 0x0E, 0x48, 0x30, 0x71, +0x00, 0x0A, 0x70, 0x71, 0x00, 0xF0, 0x1A, 0xFB, +0xE9, 0xE7, 0x00, 0xF0, 0x17, 0xFB, 0x25, 0x70, +0xE5, 0xE7, 0xFF, 0xF7, 0x7B, 0xFF, 0xE2, 0xE7, +0x08, 0xA0, 0xFF, 0xF7, 0xF7, 0xFC, 0x00, 0xF0, +0x11, 0xF8, 0xF2, 0xE7, 0xF8, 0xBD, 0x00, 0x00, +0x98, 0x01, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20, +0xCC, 0x55, 0x00, 0x00, 0x55, 0xAA, 0x00, 0x00, +0x11, 0x5A, 0x00, 0x00, 0x5B, 0x53, 0x54, 0x5D, +0x0D, 0x0A, 0x00, 0x00, 0x70, 0xB5, 0x1F, 0x48, +0x00, 0x25, 0x84, 0x88, 0x01, 0x21, 0x00, 0x2C, +0x1A, 0xD0, 0x0B, 0x00, 0xFF, 0xF7, 0x7E, 0xFC, +0x07, 0x16, 0x05, 0x13, 0x1A, 0x16, 0x0F, 0x1F, +0x16, 0x00, 0x00, 0xF0, 0x55, 0xF8, 0x00, 0xF0, +0x91, 0xF9, 0x01, 0x28, 0x01, 0xD1, 0x05, 0x21, +0x08, 0xE0, 0x06, 0x21, 0x06, 0xE0, 0x01, 0xF0, +0xB5, 0xFF, 0x02, 0x21, 0x02, 0xE0, 0x00, 0xF0, +0xC5, 0xFD, 0x03, 0x21, 0x00, 0x2C, 0xE4, 0xD1, +0x28, 0x46, 0x70, 0xBD, 0x01, 0xF0, 0x18, 0xFF, +0x00, 0xF0, 0x84, 0xFD, 0xED, 0xE7, 0x0C, 0xA0, +0xFF, 0xF7, 0xB8, 0xFC, 0x0E, 0x48, 0xFF, 0xF7, +0x2D, 0xFD, 0x0E, 0x48, 0xFF, 0xF7, 0x2A, 0xFD, +0x0D, 0x48, 0xFF, 0xF7, 0x27, 0xFD, 0x0D, 0x48, +0xFF, 0xF7, 0x24, 0xFD, 0x00, 0xF0, 0x2E, 0xFA, +0x0B, 0x48, 0x01, 0x68, 0x0B, 0xA0, 0xFF, 0xF7, +0xA5, 0xFC, 0xE1, 0xE7, 0x74, 0x06, 0x00, 0x20, +0x54, 0x45, 0x53, 0x54, 0x5F, 0x43, 0x4D, 0x44, +0x5F, 0x45, 0x58, 0x49, 0x54, 0x0D, 0x0A, 0x00, +0xE4, 0x02, 0x00, 0x20, 0x48, 0x03, 0x00, 0x20, +0xAC, 0x03, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20, +0x0C, 0x05, 0x00, 0x20, 0x67, 0x5F, 0x75, 0x33, +0x32, 0x5F, 0x77, 0x65, 0x61, 0x72, 0x61, 0x62, +0x6C, 0x65, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x5F, +0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x3A, 0x20, +0x30, 0x78, 0x25, 0x78, 0x0D, 0x0A, 0x00, 0x00, +0x10, 0xB5, 0xC8, 0x21, 0x0E, 0x48, 0xFF, 0xF7, +0xB8, 0xFB, 0xC8, 0x21, 0x0D, 0x48, 0xFF, 0xF7, +0xB4, 0xFB, 0xC8, 0x21, 0x0C, 0x48, 0xFF, 0xF7, +0xB0, 0xFB, 0xC8, 0x21, 0x0B, 0x48, 0xFF, 0xF7, +0xAC, 0xFB, 0x64, 0x21, 0x0A, 0x48, 0xFF, 0xF7, +0xA8, 0xFB, 0x32, 0x21, 0x09, 0x48, 0xFF, 0xF7, +0xA4, 0xFB, 0x09, 0x49, 0x00, 0x20, 0x08, 0x60, +0x08, 0xA0, 0xFF, 0xF7, 0x5F, 0xFC, 0x10, 0xBD, +0xE4, 0x02, 0x00, 0x20, 0x48, 0x03, 0x00, 0x20, +0xAC, 0x03, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20, +0x74, 0x04, 0x00, 0x20, 0xD8, 0x04, 0x00, 0x20, +0x0C, 0x05, 0x00, 0x20, 0x46, 0x54, 0x20, 0x52, +0x65, 0x73, 0x75, 0x6C, 0x74, 0x20, 0x49, 0x6E, +0x69, 0x74, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0xF3, 0xB5, 0xC1, 0xB0, 0x42, 0x98, 0x02, 0x28, +0x01, 0xD2, 0x02, 0x20, 0x42, 0x90, 0xC0, 0x21, +0x68, 0x46, 0xFF, 0xF7, 0x7A, 0xFB, 0x01, 0xF0, +0x57, 0xFB, 0x01, 0xF0, 0xFD, 0xFD, 0x00, 0xF0, +0xB5, 0xFF, 0x00, 0x24, 0x6E, 0x46, 0x1A, 0xE0, +0x01, 0xF0, 0x76, 0xFC, 0x01, 0xF0, 0xF4, 0xFD, +0x00, 0xF0, 0xAC, 0xFF, 0x18, 0x49, 0x00, 0x20, +0x0A, 0x68, 0x18, 0x49, 0x0F, 0x68, 0x39, 0x18, +0xC9, 0x7E, 0x41, 0x29, 0x05, 0xD0, 0x81, 0x00, +0x45, 0x00, 0x73, 0x58, 0x55, 0x5F, 0x5B, 0x19, +0x73, 0x50, 0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, +0xF1, 0xD3, 0x64, 0x1C, 0xE4, 0xB2, 0x42, 0x98, +0x84, 0x42, 0xE1, 0xD3, 0x01, 0xF0, 0xD8, 0xFD, +0x01, 0xF0, 0x6E, 0xFC, 0x0B, 0x4D, 0x00, 0x24, +0x28, 0x68, 0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, +0x07, 0xD0, 0xA0, 0x00, 0x30, 0x58, 0x42, 0x99, +0xFF, 0xF7, 0x16, 0xFB, 0x41, 0x99, 0x62, 0x00, +0x88, 0x52, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C, +0xEE, 0xD3, 0x01, 0x20, 0x43, 0xB0, 0xF0, 0xBD, +0x4C, 0x01, 0x00, 0x20, 0x80, 0x02, 0x00, 0x20, +0x70, 0xB5, 0x14, 0x48, 0x78, 0x24, 0x41, 0x6B, +0x03, 0x22, 0x12, 0x05, 0x91, 0x43, 0x01, 0x22, +0x12, 0x05, 0x89, 0x18, 0x41, 0x63, 0x10, 0x4D, +0x10, 0x48, 0xA9, 0x7E, 0xFF, 0xF7, 0xA0, 0xFF, +0x0C, 0x49, 0x0F, 0x48, 0x40, 0x31, 0x88, 0x60, +0xA9, 0x7E, 0x0E, 0x48, 0xFF, 0xF7, 0x98, 0xFF, +0x0D, 0x49, 0x0C, 0x4B, 0x0A, 0x68, 0x00, 0x20, +0x11, 0x18, 0xC9, 0x7E, 0x41, 0x29, 0x03, 0xD0, +0x41, 0x00, 0x5D, 0x5A, 0x2D, 0x1B, 0x5D, 0x52, +0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, 0xF3, 0xD3, +0x01, 0x20, 0x70, 0xBD, 0x80, 0x10, 0x00, 0x50, +0xA8, 0x06, 0x00, 0x20, 0x74, 0x04, 0x00, 0x20, +0x24, 0x0A, 0x00, 0x00, 0x48, 0x03, 0x00, 0x20, +0x80, 0x02, 0x00, 0x20, 0xF8, 0xB5, 0x3A, 0x48, +0x00, 0x24, 0x60, 0x21, 0x00, 0x68, 0xFF, 0xF7, +0xF8, 0xFA, 0x38, 0x4E, 0x38, 0x48, 0x00, 0x68, +0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x5A, 0xD0, +0xA5, 0x07, 0xAD, 0x0F, 0xA7, 0x08, 0x0C, 0x21, +0x20, 0x46, 0xFF, 0xF7, 0xA7, 0xFA, 0x88, 0x08, +0x00, 0x21, 0xC9, 0x43, 0x71, 0x62, 0x09, 0x0C, +0xB1, 0x62, 0x20, 0x2C, 0x05, 0xD2, 0x72, 0x6A, +0x01, 0x21, 0xA1, 0x40, 0x8A, 0x43, 0x72, 0x62, +0x06, 0xE0, 0xB3, 0x6A, 0x21, 0x46, 0x20, 0x39, +0x01, 0x22, 0x8A, 0x40, 0x93, 0x43, 0xB3, 0x62, +0x03, 0x2D, 0x02, 0xD2, 0x69, 0x1C, 0xC9, 0xB2, +0x00, 0xE0, 0x08, 0x21, 0xF2, 0x6A, 0x25, 0x4B, +0x1A, 0x40, 0x01, 0x23, 0xBB, 0x40, 0x1B, 0x05, +0x1B, 0x09, 0x1A, 0x43, 0xF2, 0x62, 0x72, 0x6B, +0x0E, 0x23, 0x9A, 0x43, 0x01, 0x23, 0x83, 0x40, +0x5B, 0x07, 0x1B, 0x0F, 0x1A, 0x43, 0x72, 0x63, +0x40, 0x1C, 0x82, 0x07, 0x92, 0x0F, 0x90, 0x05, +0x13, 0x05, 0x18, 0x43, 0x93, 0x04, 0x18, 0x43, +0x12, 0x04, 0x16, 0x4F, 0x10, 0x43, 0x40, 0x37, +0xFA, 0x6A, 0xFF, 0x23, 0x1B, 0x04, 0x9A, 0x43, +0x10, 0x43, 0xF8, 0x62, 0xF0, 0x6B, 0x09, 0x07, +0xF4, 0x22, 0x09, 0x0E, 0x90, 0x43, 0x09, 0x1D, +0x08, 0x43, 0xF0, 0x63, 0xB0, 0x6B, 0x74, 0x21, +0x88, 0x43, 0x6D, 0x1C, 0x69, 0x07, 0x49, 0x0E, +0x09, 0x1D, 0x08, 0x43, 0xB0, 0x63, 0x01, 0xF0, +0x9F, 0xFB, 0x01, 0xF0, 0x1D, 0xFD, 0x64, 0x1C, +0xE4, 0xB2, 0x30, 0x2C, 0x9A, 0xD3, 0x04, 0x48, +0x60, 0x22, 0x01, 0x68, 0x06, 0x48, 0xFF, 0xF7, +0x73, 0xFA, 0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, +0x4C, 0x01, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, +0x80, 0x02, 0x00, 0x20, 0xFF, 0xFF, 0x00, 0xF0, +0xE4, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x0E, 0x48, +0x00, 0x68, 0x0E, 0x4C, 0x40, 0x05, 0x40, 0x0F, +0x20, 0x70, 0x00, 0x20, 0x01, 0xF0, 0xF2, 0xFE, +0x0B, 0x49, 0x01, 0x20, 0x08, 0x70, 0x08, 0x20, +0x20, 0x70, 0x0A, 0x49, 0x06, 0x20, 0x08, 0x70, +0x01, 0x20, 0x09, 0x49, 0x80, 0x02, 0x08, 0x80, +0x01, 0xF0, 0x50, 0xFE, 0x01, 0xF0, 0xEC, 0xFC, +0x01, 0xF0, 0x82, 0xFB, 0x01, 0x20, 0x10, 0xBD, +0x00, 0x11, 0x00, 0x50, 0x39, 0x01, 0x00, 0x20, +0x3B, 0x01, 0x00, 0x20, 0xCD, 0x08, 0x00, 0x20, +0x46, 0x01, 0x00, 0x20, 0xF0, 0xB5, 0x3C, 0x49, +0x00, 0x20, 0x48, 0x80, 0x3B, 0x49, 0x42, 0x1E, +0xCA, 0x61, 0x12, 0x0C, 0x0A, 0x62, 0x3A, 0x4A, +0x4A, 0x62, 0x8A, 0x62, 0xCA, 0x62, 0x0A, 0x06, +0x13, 0x68, 0x14, 0x13, 0xA3, 0x43, 0x13, 0x60, +0x36, 0x4A, 0x01, 0x27, 0x97, 0x61, 0xD7, 0x61, +0x34, 0x4B, 0x40, 0x3B, 0xD8, 0x63, 0x10, 0x60, +0x90, 0x60, 0x33, 0x4D, 0x2C, 0x7F, 0xED, 0x7E, +0x23, 0x04, 0x2D, 0x02, 0x2B, 0x43, 0x0C, 0x33, +0x4B, 0x60, 0x8B, 0x60, 0x08, 0x61, 0x48, 0x61, +0x1F, 0x21, 0x09, 0x04, 0x11, 0x62, 0x29, 0x49, +0x2C, 0x4B, 0xC0, 0x31, 0x8B, 0x61, 0xCB, 0x61, +0x26, 0x4E, 0x40, 0x3E, 0xF0, 0x60, 0x1B, 0x06, +0x33, 0x61, 0x14, 0x23, 0x8B, 0x62, 0x34, 0x23, +0xCB, 0x63, 0x24, 0x4B, 0x54, 0x25, 0x80, 0x3B, +0x1D, 0x61, 0x74, 0x25, 0x5D, 0x62, 0xD0, 0x25, +0xCD, 0x62, 0xF0, 0x25, 0x1D, 0x60, 0xFF, 0x25, +0x11, 0x35, 0x5D, 0x61, 0xFF, 0x25, 0x31, 0x35, +0x9D, 0x62, 0x08, 0x62, 0x1E, 0x4B, 0x4B, 0x62, +0x1E, 0x49, 0x09, 0x68, 0x50, 0x31, 0xCB, 0x7B, +0x8D, 0x7B, 0x19, 0x02, 0x29, 0x43, 0xC9, 0x1C, +0x89, 0x05, 0x1B, 0x4B, 0x89, 0x0D, 0xC9, 0x18, +0x91, 0x62, 0x1A, 0x49, 0x1A, 0x4B, 0x0A, 0x68, +0x10, 0x49, 0x40, 0x31, 0x9A, 0x42, 0x01, 0xD1, +0x18, 0x4A, 0x00, 0xE0, 0x18, 0x4A, 0x4A, 0x63, +0x0C, 0x3C, 0x05, 0x22, 0xE1, 0xB2, 0x52, 0x02, +0x89, 0x18, 0x0A, 0x4A, 0x80, 0x32, 0x91, 0x60, +0x15, 0x49, 0x14, 0x4A, 0x0A, 0x60, 0x48, 0x60, +0xF1, 0x68, 0x14, 0x48, 0x01, 0x60, 0x31, 0x69, +0x41, 0x60, 0x13, 0x48, 0x07, 0x70, 0x05, 0x20, +0x00, 0x02, 0x30, 0x60, 0xF0, 0xBD, 0x00, 0x00, +0x98, 0x01, 0x00, 0x20, 0x40, 0x10, 0x00, 0x50, +0x55, 0x55, 0x55, 0x55, 0xC0, 0x11, 0x00, 0x50, +0xA8, 0x06, 0x00, 0x20, 0x03, 0x00, 0x03, 0x00, +0x00, 0x3F, 0x3F, 0x3F, 0x80, 0x02, 0x00, 0x20, +0x00, 0x30, 0xBC, 0x00, 0xE4, 0x06, 0x00, 0x20, +0xA1, 0x00, 0x03, 0xF3, 0x00, 0x26, 0x30, 0x00, +0x00, 0x26, 0x30, 0x08, 0x21, 0x20, 0x00, 0x00, +0x00, 0x19, 0x00, 0x50, 0x24, 0x00, 0x00, 0x20, +0x35, 0x00, 0x00, 0x20, 0x08, 0x49, 0x10, 0xB5, +0x5A, 0x20, 0x08, 0x74, 0x00, 0x20, 0x0A, 0x46, +0x48, 0x74, 0x20, 0x3A, 0x14, 0x5C, 0x4B, 0x7C, +0x40, 0x1C, 0x1B, 0x19, 0xC0, 0xB2, 0x4B, 0x74, +0x31, 0x28, 0xF7, 0xD3, 0x10, 0xBD, 0x00, 0x00, +0xF8, 0x04, 0x00, 0x20, 0x70, 0xB5, 0x2F, 0x49, +0x2D, 0x48, 0x08, 0x60, 0x2F, 0x4B, 0x2E, 0x48, +0x58, 0x60, 0x2E, 0x4C, 0x2E, 0x48, 0x80, 0x34, +0xA0, 0x60, 0x2E, 0x48, 0x40, 0x78, 0x41, 0x1E, +0xC9, 0x05, 0xC9, 0x0D, 0x19, 0x61, 0x2C, 0x4A, +0x00, 0x21, 0x11, 0x62, 0x2B, 0x4A, 0x2C, 0x4E, +0x15, 0x68, 0x26, 0x4A, 0x40, 0x32, 0xB5, 0x42, +0x01, 0xD1, 0x2A, 0x4D, 0x00, 0xE0, 0x2A, 0x4D, +0x55, 0x63, 0x05, 0x02, 0x29, 0x48, 0x6D, 0x1C, +0x05, 0x60, 0x41, 0x60, 0x09, 0x25, 0x1F, 0x48, +0x2D, 0x05, 0x40, 0x38, 0x05, 0x63, 0x41, 0x63, +0x81, 0x63, 0xD9, 0x63, 0x11, 0x60, 0xA1, 0x62, +0x23, 0x4B, 0x19, 0x70, 0x04, 0x23, 0x03, 0x60, +0x83, 0x02, 0xC3, 0x60, 0x01, 0x61, 0xC3, 0x68, +0x20, 0x49, 0x0B, 0x60, 0x03, 0x69, 0x4B, 0x60, +0x03, 0x21, 0x1F, 0x4B, 0x09, 0x06, 0x19, 0x60, +0x01, 0x21, 0x49, 0x02, 0x11, 0x62, 0x81, 0x04, +0xD1, 0x62, 0x81, 0x69, 0x03, 0x23, 0x9B, 0x03, +0x19, 0x43, 0x81, 0x61, 0xC1, 0x69, 0x19, 0x4B, +0x19, 0x43, 0xC1, 0x61, 0xD0, 0x21, 0x11, 0x63, +0xC1, 0x6A, 0x10, 0x22, 0x11, 0x43, 0xC1, 0x62, +0x55, 0x20, 0xE0, 0x62, 0x60, 0x21, 0x14, 0x48, +0xFF, 0xF7, 0x4F, 0xF9, 0x12, 0x48, 0x60, 0x21, +0x60, 0x30, 0xFF, 0xF7, 0x4A, 0xF9, 0x70, 0xBD, +0x00, 0x00, 0x04, 0x20, 0x4C, 0x01, 0x00, 0x20, +0x0C, 0x0D, 0xF2, 0x00, 0x40, 0x10, 0x00, 0x50, +0xE6, 0x0A, 0x00, 0x00, 0xC8, 0x06, 0x00, 0x20, +0xC0, 0x11, 0x00, 0x50, 0xE4, 0x06, 0x00, 0x20, +0xA1, 0x00, 0x03, 0xF3, 0x00, 0x36, 0x30, 0x04, +0x00, 0x36, 0x30, 0x0C, 0x00, 0x19, 0x00, 0x50, +0x35, 0x00, 0x00, 0x20, 0x24, 0x00, 0x00, 0x20, +0x00, 0x07, 0x00, 0x50, 0x00, 0xC0, 0x00, 0xC0, +0x00, 0x20, 0x00, 0x50, 0x10, 0xB5, 0x1C, 0x48, +0x64, 0x24, 0x41, 0x79, 0x02, 0x79, 0x09, 0x02, +0x11, 0x43, 0x80, 0x22, 0x91, 0x43, 0x01, 0x71, +0x09, 0x0A, 0x41, 0x71, 0x41, 0x79, 0x02, 0x79, +0x09, 0x02, 0x11, 0x43, 0x2D, 0x22, 0x52, 0x02, +0x11, 0x43, 0x01, 0x71, 0x09, 0x0A, 0x41, 0x71, +0x81, 0x88, 0x12, 0xA0, 0xFF, 0xF7, 0xCE, 0xF9, +0x06, 0xE0, 0x21, 0x46, 0x16, 0xA0, 0xFF, 0xF7, +0xC9, 0xF9, 0x01, 0x20, 0xFF, 0xF7, 0x52, 0xFA, +0x01, 0xF0, 0x97, 0xFD, 0x00, 0x28, 0x04, 0xD1, +0x20, 0x46, 0x64, 0x1E, 0x24, 0xB2, 0x00, 0x28, +0xEF, 0xD1, 0x64, 0x1C, 0x08, 0xD1, 0x01, 0x20, +0xFF, 0xF7, 0x86, 0xF9, 0x10, 0xA0, 0xFF, 0xF7, +0xB5, 0xF9, 0x0A, 0x20, 0xFF, 0xF7, 0x3E, 0xFA, +0x00, 0x20, 0xFF, 0xF7, 0x7D, 0xF9, 0x10, 0xBD, +0x88, 0x02, 0x00, 0x20, 0x46, 0x54, 0x20, 0x55, +0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x53, 0x74, +0x61, 0x74, 0x75, 0x73, 0x20, 0x28, 0x30, 0x78, +0x25, 0x34, 0x78, 0x29, 0x0D, 0x0A, 0x00, 0x00, +0x49, 0x4E, 0x54, 0x20, 0x4C, 0x6F, 0x77, 0x21, +0x21, 0x28, 0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, +0x49, 0x4E, 0x54, 0x20, 0x70, 0x61, 0x64, 0x20, +0x69, 0x73, 0x20, 0x6C, 0x6F, 0x77, 0x20, 0x6C, +0x65, 0x76, 0x65, 0x6C, 0x20, 0x21, 0x21, 0x00, +0x10, 0xB5, 0x16, 0x4C, 0x01, 0x21, 0xE0, 0x89, +0x89, 0x02, 0x88, 0x42, 0x01, 0xD0, 0x03, 0x20, +0x60, 0x71, 0x12, 0x48, 0x30, 0x21, 0x30, 0x30, +0xFF, 0xF7, 0xBB, 0xF8, 0x00, 0x20, 0x60, 0x72, +0xA0, 0x71, 0xE0, 0x62, 0x1E, 0x21, 0x21, 0x72, +0x0D, 0x49, 0x20, 0x71, 0x08, 0x70, 0x0D, 0x49, +0x09, 0x78, 0xA1, 0x81, 0x60, 0x81, 0x60, 0x8A, +0x09, 0x21, 0x09, 0x03, 0x08, 0x43, 0x60, 0x82, +0x03, 0x20, 0x40, 0x02, 0xE0, 0x61, 0x0D, 0x20, +0xC0, 0x01, 0x20, 0x62, 0x07, 0x20, 0x00, 0x02, +0x60, 0x62, 0x0F, 0x20, 0xC0, 0x01, 0xA0, 0x62, +0x10, 0xBD, 0x00, 0x00, 0x38, 0x01, 0x00, 0x20, +0x7E, 0x02, 0x00, 0x20, 0x7F, 0x02, 0x00, 0x20, +0x10, 0x48, 0xC0, 0x6B, 0x10, 0x49, 0x08, 0x60, +0x10, 0x48, 0x03, 0x21, 0x41, 0x71, 0x10, 0x49, +0x41, 0x61, 0x0F, 0x49, 0x60, 0x31, 0x81, 0x61, +0x01, 0x21, 0x01, 0x70, 0x07, 0x22, 0x42, 0x70, +0x0C, 0x4B, 0x05, 0x22, 0x1A, 0x70, 0x0C, 0x4B, +0x1A, 0x70, 0x0C, 0x4B, 0x55, 0x22, 0xDA, 0x70, +0x04, 0x22, 0x02, 0x82, 0x00, 0x22, 0xC2, 0x70, +0x09, 0x22, 0x12, 0x03, 0x42, 0x82, 0x81, 0x70, +0x70, 0x47, 0x00, 0x00, 0x80, 0x09, 0x00, 0x50, +0xE4, 0x06, 0x00, 0x20, 0x38, 0x01, 0x00, 0x20, +0x00, 0x00, 0x04, 0x20, 0x7D, 0x02, 0x00, 0x20, +0xCD, 0x08, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, +0x04, 0x22, 0x0F, 0x49, 0x0C, 0x28, 0x10, 0xD0, +0x8B, 0x05, 0x0D, 0x28, 0x08, 0x6A, 0x10, 0xD0, +0x18, 0x43, 0x08, 0x62, 0x88, 0x6A, 0x10, 0x43, +0x88, 0x62, 0x0A, 0x4A, 0x01, 0x20, 0x10, 0x70, +0xC8, 0x68, 0xC8, 0x60, 0x88, 0x6A, 0x88, 0x62, +0x70, 0x47, 0x08, 0x6A, 0x40, 0x00, 0x40, 0x08, +0x00, 0xE0, 0x18, 0x43, 0x08, 0x62, 0x88, 0x6A, +0x90, 0x43, 0x88, 0x62, 0xF0, 0xE7, 0x00, 0x00, +0x00, 0x06, 0x00, 0x50, 0xD0, 0x00, 0x00, 0x20, +0x00, 0xB5, 0x08, 0x49, 0x0A, 0x28, 0x05, 0xD0, +0x07, 0x48, 0x00, 0x0C, 0x48, 0x63, 0x07, 0x48, +0x08, 0x63, 0x00, 0xBD, 0x06, 0x48, 0x00, 0x68, +0x08, 0x62, 0x0D, 0x20, 0xFF, 0xF7, 0xCC, 0xFF, +0x00, 0xBD, 0x00, 0x00, 0x00, 0x06, 0x00, 0x50, +0xBC, 0x02, 0x00, 0x20, 0xCC, 0x02, 0x00, 0x20, +0xC0, 0x02, 0x00, 0x20, 0x00, 0xB5, 0x1B, 0x48, +0x00, 0x21, 0x0B, 0x00, 0xFF, 0xF7, 0x86, 0xF8, +0x0C, 0x07, 0x0A, 0x0E, 0x11, 0x2A, 0x15, 0x18, +0x1B, 0x1E, 0x21, 0x24, 0x27, 0x2A, 0x16, 0x4A, +0x02, 0x80, 0x22, 0xE0, 0x14, 0x4A, 0x12, 0x1D, +0x42, 0x80, 0x1E, 0xE0, 0x13, 0x4A, 0x82, 0x80, +0x1B, 0xE0, 0x11, 0x4A, 0x10, 0x32, 0xC2, 0x80, +0x17, 0xE0, 0x11, 0x4A, 0x42, 0x81, 0x14, 0xE0, +0x10, 0x4A, 0x82, 0x81, 0x11, 0xE0, 0x10, 0x4A, +0xC2, 0x81, 0x0E, 0xE0, 0x0F, 0x4A, 0x02, 0x82, +0x0B, 0xE0, 0x0F, 0x4A, 0x42, 0x82, 0x08, 0xE0, +0x0E, 0x4A, 0x82, 0x82, 0x05, 0xE0, 0x0E, 0x4A, +0xC2, 0x82, 0x02, 0xE0, 0x0D, 0x4A, 0x4B, 0x00, +0xC2, 0x52, 0x49, 0x1C, 0x89, 0xB2, 0x0C, 0x29, +0xCB, 0xD3, 0x00, 0xBD, 0xCC, 0x02, 0x00, 0x20, +0x90, 0x02, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20, +0x34, 0x01, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20, +0x10, 0x05, 0x00, 0x20, 0xD8, 0x06, 0x00, 0x20, +0xC0, 0x02, 0x00, 0x20, 0xBC, 0x02, 0x00, 0x20, +0x9C, 0x01, 0x00, 0x20, 0xC4, 0x02, 0x00, 0x20, +0x10, 0xB5, 0x27, 0x4A, 0x11, 0x68, 0x26, 0x48, +0x40, 0x30, 0x01, 0x61, 0x51, 0x68, 0x41, 0x61, +0x91, 0x68, 0x81, 0x61, 0xD1, 0x68, 0xC1, 0x61, +0x83, 0x69, 0xF0, 0x21, 0x8B, 0x43, 0x83, 0x61, +0x83, 0x69, 0x0B, 0x43, 0x83, 0x61, 0x1F, 0x49, +0x1F, 0x4B, 0x09, 0x68, 0x99, 0x42, 0x07, 0xD1, +0x43, 0x69, 0x5B, 0x04, 0x04, 0xD5, 0x43, 0x69, +0x01, 0x24, 0x64, 0x03, 0x1B, 0x1B, 0x43, 0x61, +0x17, 0x48, 0x40, 0x38, 0x03, 0x68, 0x01, 0x24, +0x24, 0x06, 0x23, 0x43, 0x03, 0x60, 0x16, 0x48, +0x40, 0x1C, 0x81, 0x42, 0x09, 0xD1, 0x05, 0x20, +0x00, 0x07, 0x41, 0x69, 0x52, 0x68, 0xD2, 0x0A, +0x12, 0x1F, 0x12, 0x07, 0x12, 0x0A, 0x11, 0x43, +0x41, 0x61, 0x30, 0x21, 0x0F, 0x48, 0xFF, 0xF7, +0xB3, 0xF9, 0x0E, 0x48, 0x40, 0x21, 0xC0, 0x30, +0xFF, 0xF7, 0xAE, 0xF9, 0x0B, 0x20, 0xFE, 0xF7, +0xC1, 0xFF, 0x03, 0x20, 0xFE, 0xF7, 0xBE, 0xFF, +0x00, 0x20, 0xFE, 0xF7, 0xBB, 0xFF, 0x05, 0x20, +0xFE, 0xF7, 0xB8, 0xFF, 0x09, 0x20, 0xFE, 0xF7, +0xB5, 0xFF, 0x01, 0x20, 0x10, 0xBD, 0x00, 0x00, +0x40, 0x14, 0x00, 0x50, 0xE4, 0x06, 0x00, 0x20, +0xA1, 0x00, 0x03, 0xF3, 0xDC, 0x34, 0x00, 0x00, +0xFE, 0xB5, 0x20, 0x48, 0x0A, 0x25, 0x45, 0x5F, +0x01, 0x26, 0x29, 0x46, 0x1E, 0xA0, 0xFF, 0xF7, +0x39, 0xF8, 0x23, 0x48, 0x72, 0x04, 0x01, 0x68, +0x22, 0x4F, 0x91, 0x43, 0x00, 0x24, 0x01, 0x60, +0x21, 0x48, 0x00, 0x68, 0x00, 0x19, 0xC0, 0x7E, +0x41, 0x28, 0x18, 0xD0, 0x1F, 0x49, 0x60, 0x00, +0x0A, 0x5E, 0x1F, 0x49, 0x0B, 0x5A, 0xD0, 0x1A, +0x00, 0xB2, 0xA8, 0x42, 0x0B, 0xDA, 0x00, 0x90, +0x21, 0x46, 0x01, 0x95, 0x1B, 0xA0, 0xFF, 0xF7, +0x1D, 0xF8, 0x38, 0x5D, 0x20, 0x21, 0x08, 0x43, +0x38, 0x55, 0x00, 0x26, 0x03, 0xE0, 0x38, 0x5D, +0xDF, 0x21, 0x08, 0x40, 0x38, 0x55, 0x64, 0x1C, +0xE4, 0xB2, 0x30, 0x2C, 0xDC, 0xD3, 0x01, 0x2E, +0x0A, 0xD0, 0x0D, 0x4A, 0x01, 0x21, 0x10, 0x68, +0x49, 0x04, 0x08, 0x43, 0x10, 0x60, 0x1C, 0xA0, +0xFF, 0xF7, 0x04, 0xF8, 0x30, 0x46, 0xFE, 0xBD, +0x1F, 0xA0, 0xF9, 0xE7, 0x84, 0x06, 0x00, 0x20, +0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x43, 0x20, +0x43, 0x68, 0x65, 0x63, 0x6B, 0x20, 0x54, 0x48, +0x44, 0x3D, 0x25, 0x64, 0x20, 0x0D, 0x0A, 0x00, +0x0C, 0x05, 0x00, 0x20, 0xD8, 0x04, 0x00, 0x20, +0x80, 0x02, 0x00, 0x20, 0xAC, 0x03, 0x00, 0x20, +0x4C, 0x07, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, +0x20, 0x43, 0x43, 0x20, 0x4E, 0x47, 0x20, 0x50, +0x69, 0x6E, 0x5B, 0x25, 0x64, 0x5D, 0x2C, 0x20, +0x44, 0x69, 0x66, 0x66, 0x3D, 0x25, 0x64, 0x2D, +0x25, 0x64, 0x3D, 0x25, 0x64, 0x2C, 0x20, 0x4C, +0x6F, 0x77, 0x54, 0x48, 0x44, 0x20, 0x3D, 0x20, +0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x43, +0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x5B, +0x4E, 0x47, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, +0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x43, +0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x5B, +0x50, 0x61, 0x73, 0x73, 0x5D, 0x0D, 0x0A, 0x00, +0xF8, 0xB5, 0x28, 0x48, 0x02, 0x21, 0x0A, 0x22, +0x41, 0x5E, 0x82, 0x5E, 0x03, 0x20, 0x80, 0x03, +0x42, 0x43, 0x10, 0x14, 0x86, 0x46, 0x24, 0x48, +0x01, 0x24, 0x02, 0x68, 0xA2, 0x43, 0x02, 0x60, +0x22, 0x48, 0x00, 0x22, 0x00, 0x68, 0x84, 0x46, +0x60, 0x46, 0x80, 0x18, 0xC0, 0x7E, 0x41, 0x28, +0x23, 0xD0, 0x53, 0x00, 0x1E, 0x4F, 0x1F, 0x4D, +0xF8, 0x5E, 0xEE, 0x5A, 0x00, 0x25, 0x86, 0x1B, +0x36, 0xB2, 0xFE, 0x52, 0x8E, 0x42, 0x00, 0xDA, +0x01, 0x25, 0x00, 0x28, 0x07, 0xD1, 0x1A, 0x48, +0x1A, 0x4E, 0xC0, 0x5A, 0xF3, 0x5A, 0xC0, 0x1A, +0x00, 0xB2, 0x70, 0x45, 0x01, 0xDB, 0x00, 0x2D, +0x06, 0xD0, 0x17, 0x48, 0x01, 0x24, 0x83, 0x5C, +0x23, 0x43, 0x83, 0x54, 0x00, 0x24, 0x04, 0xE0, +0x13, 0x48, 0x83, 0x5C, 0x5B, 0x08, 0x5B, 0x00, +0x83, 0x54, 0x52, 0x1C, 0xD2, 0xB2, 0x30, 0x2A, +0xD2, 0xD3, 0x01, 0x2C, 0x09, 0xD0, 0x0F, 0xA0, +0xFE, 0xF7, 0x70, 0xFF, 0x06, 0x48, 0x01, 0x22, +0x01, 0x68, 0x11, 0x43, 0x01, 0x60, 0x20, 0x46, +0xF8, 0xBD, 0x11, 0xA0, 0xFE, 0xF7, 0x66, 0xFF, +0xF9, 0xE7, 0x00, 0x00, 0x84, 0x06, 0x00, 0x20, +0x0C, 0x05, 0x00, 0x20, 0x80, 0x02, 0x00, 0x20, +0x48, 0x03, 0x00, 0x20, 0x74, 0x04, 0x00, 0x20, +0xAC, 0x03, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, +0xD8, 0x04, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, +0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x54, 0x48, +0x44, 0x3A, 0x25, 0x64, 0x29, 0x20, 0x5B, 0x4E, +0x47, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, +0x4F, 0x70, 0x65, 0x6E, 0x20, 0x54, 0x65, 0x73, +0x74, 0x28, 0x54, 0x48, 0x44, 0x3A, 0x25, 0x64, +0x29, 0x20, 0x5B, 0x50, 0x61, 0x73, 0x73, 0x5D, +0x0D, 0x0A, 0x00, 0x00, 0xF0, 0xB5, 0x17, 0x49, +0x00, 0x20, 0x7D, 0x27, 0x09, 0x68, 0xFF, 0x00, +0x02, 0x46, 0x89, 0x07, 0x24, 0xD5, 0x14, 0x49, +0x14, 0x4D, 0x09, 0x68, 0xCE, 0x26, 0x0B, 0x18, +0xDB, 0x7E, 0x41, 0x2B, 0x0A, 0xD0, 0x12, 0x4B, +0x44, 0x00, 0x1B, 0x5F, 0xBB, 0x42, 0x00, 0xDA, +0x01, 0x22, 0x2B, 0x5C, 0x9C, 0x07, 0x01, 0xD5, +0x33, 0x40, 0x2B, 0x54, 0x40, 0x1C, 0xC0, 0xB2, +0x30, 0x28, 0xEC, 0xD3, 0x00, 0x2A, 0x0B, 0xD0, +0x00, 0x20, 0x0A, 0x18, 0xD2, 0x7E, 0x41, 0x2A, +0x02, 0xD0, 0x2A, 0x5C, 0x32, 0x40, 0x2A, 0x54, +0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, 0xF4, 0xD3, +0x01, 0x20, 0xF0, 0xBD, 0x0C, 0x05, 0x00, 0x20, +0x80, 0x02, 0x00, 0x20, 0xD8, 0x04, 0x00, 0x20, +0xE4, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x20, 0x48, +0x01, 0x25, 0xC4, 0x7D, 0x1F, 0x48, 0x80, 0x88, +0x40, 0x07, 0x01, 0xD4, 0x01, 0x20, 0x70, 0xBD, +0xFF, 0xF7, 0xDC, 0xFB, 0x1C, 0x48, 0xFE, 0xF7, +0x09, 0xFF, 0x00, 0x28, 0x0F, 0xD1, 0x1B, 0xA0, +0xFE, 0xF7, 0xEC, 0xFE, 0x01, 0x20, 0x20, 0x49, +0x00, 0x25, 0x08, 0x70, 0x00, 0x2C, 0x06, 0xD0, +0x64, 0x1E, 0xE4, 0xB2, 0x21, 0x46, 0x1D, 0xA0, +0xFE, 0xF7, 0xE0, 0xFE, 0xE8, 0xE7, 0xFF, 0xF7, +0x6F, 0xF8, 0xFF, 0xF7, 0xE5, 0xFA, 0xFF, 0xF7, +0x23, 0xFF, 0x00, 0x28, 0x07, 0xD1, 0x00, 0x25, +0x00, 0x2C, 0x04, 0xD0, 0x64, 0x1E, 0xE4, 0xB2, +0x21, 0x46, 0x1C, 0xA0, 0x0A, 0xE0, 0xFF, 0xF7, +0x8B, 0xFE, 0x00, 0x28, 0x0A, 0xD1, 0x00, 0x25, +0x00, 0x2C, 0x07, 0xD0, 0x64, 0x1E, 0xE4, 0xB2, +0x21, 0x46, 0x1C, 0xA0, 0xFE, 0xF7, 0xC2, 0xFE, +0x01, 0x25, 0xC9, 0xE7, 0x28, 0x46, 0x70, 0xBD, +0xA8, 0x06, 0x00, 0x20, 0x74, 0x06, 0x00, 0x20, +0xAC, 0x03, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, +0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x61, +0x6C, 0x69, 0x62, 0x72, 0x61, 0x74, 0x69, 0x6F, +0x6E, 0x20, 0x4E, 0x47, 0x21, 0x0D, 0x0A, 0x00, +0xD8, 0x04, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, +0x20, 0x43, 0x61, 0x6C, 0x69, 0x62, 0x72, 0x61, +0x74, 0x69, 0x6F, 0x6E, 0x20, 0x52, 0x65, 0x74, +0x72, 0x79, 0x20, 0x28, 0x25, 0x64, 0x29, 0x0D, +0x0A, 0x00, 0x00, 0x00, 0x4F, 0x70, 0x65, 0x6E, +0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x52, 0x65, +0x74, 0x72, 0x79, 0x20, 0x28, 0x25, 0x64, 0x29, +0x0D, 0x0A, 0x00, 0x00, 0x4F, 0x70, 0x65, 0x6E, +0x20, 0x43, 0x43, 0x20, 0x52, 0x65, 0x74, 0x72, +0x79, 0x20, 0x28, 0x25, 0x64, 0x29, 0x0D, 0x0A, +0x00, 0x00, 0x00, 0x00, 0x10, 0xB5, 0x15, 0x4A, +0x11, 0x78, 0x81, 0x42, 0x25, 0xD0, 0x14, 0x49, +0x0B, 0x68, 0x01, 0x24, 0xA4, 0x02, 0x23, 0x43, +0x0B, 0x60, 0x10, 0x70, 0x05, 0x22, 0x40, 0x24, +0x10, 0x49, 0x12, 0x07, 0x80, 0x23, 0x00, 0x28, +0x09, 0xD0, 0x08, 0x68, 0x40, 0x06, 0x0C, 0xD4, +0x08, 0x68, 0x20, 0x43, 0x08, 0x60, 0xD0, 0x68, +0x98, 0x43, 0xD0, 0x60, 0x05, 0xE0, 0xD0, 0x68, +0x18, 0x43, 0xD0, 0x60, 0x08, 0x68, 0xA0, 0x43, +0x08, 0x60, 0x08, 0x68, 0x18, 0x43, 0x08, 0x60, +0xD0, 0x68, 0x01, 0x21, 0xC9, 0x03, 0x08, 0x43, +0xD0, 0x60, 0x10, 0xBD, 0x04, 0x00, 0x00, 0x20, +0x00, 0x10, 0x00, 0x50, 0x00, 0x11, 0x00, 0x50, +0x06, 0x49, 0x83, 0x20, 0x08, 0x70, 0x06, 0x49, +0x00, 0x20, 0x08, 0x70, 0x05, 0x48, 0x00, 0x68, +0x05, 0x49, 0x40, 0x05, 0x40, 0x0F, 0x08, 0x73, +0x70, 0x47, 0x00, 0x00, 0xE0, 0x08, 0x00, 0x20, +0x34, 0x00, 0x00, 0x20, 0x00, 0x11, 0x00, 0x50, +0x24, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x04, 0x46, +0x05, 0x22, 0x01, 0x20, 0x52, 0x04, 0xCB, 0x07, +0x01, 0xD0, 0x00, 0x20, 0x10, 0xBD, 0x07, 0x4B, +0x9C, 0x61, 0x09, 0x02, 0x49, 0x1C, 0x59, 0x61, +0x02, 0xE0, 0x52, 0x1E, 0x00, 0xD1, 0x00, 0x20, +0x59, 0x69, 0xC9, 0x07, 0xF2, 0xD0, 0x00, 0x2A, +0xF7, 0xD1, 0x10, 0xBD, 0x40, 0x14, 0x00, 0x50, +0xF0, 0xB5, 0x90, 0xB0, 0xAD, 0x49, 0x09, 0x91, +0xAC, 0x49, 0xAD, 0x4A, 0x60, 0x31, 0x07, 0x92, +0x0A, 0x91, 0xAC, 0x49, 0xAC, 0x4A, 0x08, 0x91, +0x12, 0x68, 0x0C, 0x92, 0xAB, 0x4A, 0xAC, 0x4E, +0x12, 0x68, 0x0D, 0x92, 0x00, 0x22, 0x17, 0x46, +0x14, 0x46, 0x00, 0x92, 0x07, 0x9A, 0x06, 0x91, +0xA8, 0x4D, 0x05, 0x92, 0x00, 0x21, 0x8A, 0x00, +0x05, 0x9B, 0x49, 0x1C, 0x9E, 0x50, 0x06, 0x9B, +0x89, 0xB2, 0x9D, 0x50, 0x18, 0x29, 0xF6, 0xD3, +0x01, 0x21, 0x89, 0x02, 0x03, 0x91, 0x01, 0x46, +0x04, 0x90, 0x50, 0x31, 0x50, 0x38, 0x09, 0xB2, +0x00, 0xB2, 0x0E, 0x91, 0x0F, 0x90, 0x00, 0xF0, +0x0B, 0xFD, 0x00, 0xF0, 0xB1, 0xFF, 0x00, 0xF0, +0x69, 0xF9, 0x9B, 0x48, 0x00, 0x78, 0xC0, 0x07, +0x20, 0xD1, 0x00, 0x26, 0x00, 0x21, 0x09, 0x9A, +0x88, 0x00, 0x13, 0x58, 0x0C, 0x9A, 0x49, 0x1C, +0x13, 0x50, 0x0A, 0x9A, 0x89, 0xB2, 0x13, 0x58, +0x0D, 0x9A, 0x18, 0x29, 0x13, 0x50, 0x05, 0x9A, +0x13, 0x58, 0x09, 0x9A, 0x13, 0x50, 0x06, 0x9A, +0x13, 0x58, 0x0A, 0x9A, 0x13, 0x50, 0xEA, 0xD3, +0x00, 0xF0, 0x12, 0xFE, 0x00, 0xF0, 0x90, 0xFF, +0x00, 0xF0, 0x48, 0xF9, 0x8A, 0x48, 0x00, 0x78, +0xC0, 0x07, 0x02, 0xD0, 0x00, 0x20, 0x10, 0xB0, +0xF0, 0xBD, 0x00, 0x25, 0x3C, 0xE0, 0x00, 0x2D, +0x3A, 0xD0, 0x02, 0x91, 0x01, 0x90, 0x05, 0x2E, +0x7A, 0xD9, 0x30, 0x2F, 0x78, 0xD2, 0x08, 0x98, +0x82, 0x4F, 0x0B, 0x90, 0x78, 0x6B, 0x03, 0x21, +0x09, 0x05, 0x88, 0x43, 0x01, 0x21, 0x49, 0x05, +0x40, 0x18, 0x78, 0x63, 0x00, 0xF0, 0xF0, 0xFD, +0x00, 0xF0, 0x6E, 0xFF, 0x00, 0xF0, 0x26, 0xF9, +0x79, 0x6B, 0x03, 0x20, 0x00, 0x05, 0x81, 0x43, +0x01, 0x20, 0x00, 0x05, 0x08, 0x18, 0x78, 0x63, +0x00, 0x27, 0x00, 0x20, 0x84, 0x46, 0x76, 0x48, +0x01, 0x68, 0x60, 0x46, 0x08, 0x18, 0xC0, 0x7E, +0x41, 0x28, 0x1F, 0xD0, 0x60, 0x46, 0x41, 0x00, +0x0B, 0x98, 0x0A, 0x2E, 0x40, 0x5A, 0x23, 0xD2, +0x70, 0x4A, 0x12, 0x68, 0x53, 0x5E, 0x04, 0x9A, +0x93, 0x42, 0x01, 0xDA, 0x03, 0x9A, 0x90, 0x43, +0x03, 0x9A, 0x52, 0x08, 0x10, 0x43, 0x7D, 0xE0, +0x6B, 0x48, 0x00, 0x68, 0x01, 0x90, 0x62, 0x48, +0x00, 0x68, 0x02, 0x90, 0x00, 0x98, 0x30, 0x28, +0x77, 0xD0, 0x07, 0x98, 0x0B, 0x90, 0x00, 0x20, +0x00, 0x90, 0xD6, 0xE7, 0x00, 0x2D, 0x02, 0xD0, +0x7F, 0x1C, 0xBF, 0xB2, 0x88, 0xE0, 0x00, 0x98, +0x40, 0x1C, 0x80, 0xB2, 0x00, 0x90, 0x83, 0xE0, +0x10, 0x2E, 0x13, 0xD2, 0x5D, 0x4A, 0x01, 0x23, +0x12, 0x68, 0x9B, 0x02, 0x54, 0x5E, 0xE2, 0x1D, +0xFF, 0x32, 0xFA, 0x32, 0x9A, 0x42, 0x09, 0xD9, +0x00, 0x2C, 0x00, 0xDA, 0x64, 0x42, 0x22, 0xB2, +0x40, 0x32, 0xD3, 0x17, 0x5B, 0x0E, 0x9A, 0x18, +0xD4, 0x11, 0x00, 0xE0, 0x01, 0x24, 0x0B, 0x2E, +0x24, 0xD9, 0x01, 0x9A, 0x52, 0x5E, 0x96, 0x46, +0x00, 0x2A, 0x0E, 0xD0, 0x4F, 0x4B, 0x1B, 0x68, +0x5B, 0x5E, 0x5A, 0x43, 0x00, 0x2A, 0x19, 0xDA, +0x02, 0x9A, 0x54, 0x5A, 0x22, 0x1A, 0x01, 0x2A, +0x05, 0xD0, 0x52, 0x1C, 0x03, 0xD0, 0x10, 0xE0, +0x61, 0xE0, 0x00, 0x24, 0x0E, 0xE0, 0x72, 0x46, +0x00, 0x2A, 0x00, 0xDA, 0x52, 0x42, 0x00, 0x2B, +0x00, 0xDA, 0x5B, 0x42, 0x9A, 0x42, 0x04, 0xDA, +0x20, 0x46, 0x01, 0x9A, 0x00, 0x24, 0x54, 0x52, +0x00, 0xE0, 0x01, 0x24, 0x3F, 0x4A, 0x0F, 0x9B, +0x12, 0x68, 0x52, 0x5E, 0x9A, 0x42, 0x0F, 0xDA, +0x02, 0x05, 0x06, 0xD5, 0x00, 0x1B, 0x01, 0x22, +0x80, 0xB2, 0xD2, 0x02, 0x90, 0x42, 0x10, 0xD3, +0x16, 0xE0, 0xA0, 0x42, 0x02, 0xDD, 0x00, 0x1B, +0x80, 0xB2, 0x11, 0xE0, 0x00, 0x20, 0x0F, 0xE0, +0x0E, 0x9B, 0x9A, 0x42, 0x12, 0xDD, 0x02, 0x05, +0x05, 0xD5, 0x00, 0x19, 0x80, 0xB2, 0x33, 0x4A, +0x04, 0xE0, 0x10, 0x46, 0x04, 0xE0, 0x00, 0x19, +0x31, 0x4A, 0x80, 0xB2, 0x90, 0x42, 0xF8, 0xD8, +0x00, 0x2C, 0x04, 0xD0, 0x00, 0x2D, 0x0F, 0xD1, +0x0A, 0xE0, 0x20, 0xE0, 0x00, 0x24, 0x00, 0x2D, +0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, 0x07, 0xE0, +0x00, 0x9A, 0x52, 0x1C, 0x92, 0xB2, 0x00, 0x92, +0x07, 0x9A, 0x05, 0x2E, 0x50, 0x52, 0x04, 0xD8, +0x01, 0x22, 0xD2, 0x02, 0x10, 0x43, 0x08, 0x9A, +0x50, 0x52, 0x00, 0x2C, 0x04, 0xD0, 0x1F, 0x48, +0x01, 0x9A, 0x00, 0x68, 0x40, 0x5A, 0x50, 0x52, +0x60, 0x46, 0x40, 0x1C, 0x80, 0xB2, 0x84, 0x46, +0x30, 0x28, 0x00, 0xD2, 0x43, 0xE7, 0x13, 0x48, +0x1C, 0x4A, 0x01, 0x68, 0x10, 0x68, 0x6D, 0x1C, +0xED, 0xB2, 0x02, 0x2D, 0x00, 0xD2, 0x16, 0xE7, +0x03, 0x98, 0x40, 0x08, 0x03, 0x90, 0x00, 0x98, +0x30, 0x28, 0x02, 0xD1, 0x30, 0x2F, 0x00, 0xD1, +0x40, 0x26, 0x76, 0x1C, 0xB6, 0xB2, 0x40, 0x2E, +0x00, 0xD8, 0xE3, 0xE6, 0x0B, 0x49, 0x48, 0x6B, +0x03, 0x22, 0x12, 0x05, 0x10, 0x43, 0x48, 0x63, +0x01, 0x20, 0xFC, 0xE6, 0x00, 0x20, 0x00, 0x50, +0x9C, 0x09, 0x00, 0x20, 0x00, 0x00, 0x01, 0x20, +0x5C, 0x01, 0x00, 0x20, 0x60, 0x01, 0x00, 0x20, +0x00, 0x04, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x0C, +0x34, 0x00, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, +0x80, 0x02, 0x00, 0x20, 0x4C, 0x01, 0x00, 0x20, +0x54, 0x01, 0x00, 0x20, 0xFF, 0x0F, 0x00, 0x00, +0xFF, 0x07, 0x00, 0x00, 0x58, 0x01, 0x00, 0x20, +0x10, 0xB5, 0x0B, 0x49, 0x30, 0x24, 0x00, 0x28, +0x0A, 0x4A, 0x0B, 0x4B, 0x08, 0x68, 0x06, 0xD0, +0x20, 0x43, 0x08, 0x60, 0x09, 0x48, 0x10, 0x60, +0x08, 0x48, 0x60, 0x30, 0x05, 0xE0, 0xA0, 0x43, +0x08, 0x60, 0x07, 0x48, 0x10, 0x60, 0x06, 0x48, +0x60, 0x30, 0x18, 0x60, 0x10, 0xBD, 0x00, 0x00, +0x00, 0x10, 0x00, 0x50, 0x4C, 0x01, 0x00, 0x20, +0x50, 0x01, 0x00, 0x20, 0x00, 0x00, 0x04, 0x20, +0x00, 0x10, 0x04, 0x20, 0xF0, 0xB5, 0x11, 0x48, +0x01, 0x68, 0x11, 0x4D, 0x89, 0x06, 0x30, 0x22, +0x60, 0x35, 0x00, 0x29, 0x0E, 0x4B, 0x0F, 0x4C, +0x0F, 0x4E, 0x10, 0x4F, 0x01, 0x68, 0x0A, 0xDA, +0x91, 0x43, 0x01, 0x60, 0x23, 0x60, 0x0E, 0x48, +0x35, 0x60, 0x07, 0x60, 0x0B, 0x48, 0x0D, 0x49, +0x60, 0x30, 0x08, 0x60, 0xF0, 0xBD, 0x11, 0x43, +0x01, 0x60, 0x08, 0x48, 0x27, 0x60, 0x60, 0x30, +0x30, 0x60, 0x07, 0x48, 0x03, 0x60, 0x07, 0x48, +0x05, 0x60, 0xF0, 0xBD, 0x00, 0x10, 0x00, 0x50, +0x00, 0x00, 0x04, 0x20, 0x44, 0x00, 0x00, 0x20, +0x48, 0x00, 0x00, 0x20, 0x00, 0x10, 0x04, 0x20, +0x4C, 0x01, 0x00, 0x20, 0x50, 0x01, 0x00, 0x20, +0x00, 0xB5, 0x09, 0x48, 0x00, 0x78, 0x00, 0x28, +0x08, 0xD0, 0x01, 0x28, 0x09, 0xD0, 0x02, 0x28, +0x03, 0xD1, 0x06, 0x49, 0x04, 0x20, 0x00, 0xF0, +0x55, 0xF8, 0x00, 0xBD, 0x04, 0x49, 0x01, 0x20, +0xF9, 0xE7, 0x04, 0x49, 0x02, 0x20, 0xF6, 0xE7, +0xCD, 0x08, 0x00, 0x20, 0x6C, 0x08, 0x00, 0x20, +0x1C, 0x02, 0x00, 0x20, 0x0C, 0x08, 0x00, 0x20, +0xF8, 0xB5, 0x07, 0x46, 0x00, 0xF0, 0x4C, 0xFB, +0x1C, 0x4D, 0x60, 0x21, 0x28, 0x46, 0xFE, 0xF7, +0x68, 0xFB, 0x00, 0x24, 0x00, 0xF0, 0xEC, 0xFD, +0xFF, 0xF7, 0xA4, 0xFF, 0xFE, 0xF7, 0xFF, 0xFB, +0x00, 0x2C, 0x0C, 0xD0, 0x16, 0x48, 0x00, 0x22, +0x06, 0x68, 0x50, 0x00, 0x43, 0x19, 0x19, 0x88, +0x30, 0x5A, 0x52, 0x1C, 0x08, 0x18, 0xD2, 0xB2, +0x18, 0x80, 0x30, 0x2A, 0xF5, 0xD3, 0x64, 0x1C, +0xE4, 0xB2, 0x05, 0x2C, 0xE6, 0xD3, 0x00, 0x20, +0x29, 0x46, 0x42, 0x00, 0x52, 0x18, 0x00, 0x23, +0xD3, 0x5E, 0x40, 0x1C, 0x9B, 0x10, 0xC0, 0xB2, +0x13, 0x80, 0x30, 0x28, 0xF5, 0xD3, 0x09, 0x4C, +0x20, 0x78, 0x38, 0x42, 0x07, 0xD1, 0x08, 0x48, +0x60, 0x22, 0x00, 0x68, 0xFE, 0xF7, 0x20, 0xFB, +0x20, 0x78, 0x38, 0x43, 0x20, 0x70, 0x01, 0x20, +0xF8, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, +0x4C, 0x01, 0x00, 0x20, 0x36, 0x00, 0x00, 0x20, +0xCC, 0x00, 0x00, 0x20, 0xF0, 0xB5, 0x22, 0x48, +0x00, 0x22, 0x00, 0x68, 0x14, 0x46, 0x21, 0x4B, +0x98, 0x42, 0x28, 0xD8, 0x20, 0x48, 0x00, 0x78, +0x04, 0x28, 0x27, 0xD1, 0x1F, 0x4B, 0x20, 0x4D, +0x1E, 0x68, 0x00, 0x20, 0x33, 0x18, 0xDB, 0x7E, +0x41, 0x2B, 0x08, 0xD0, 0x42, 0x00, 0x57, 0x19, +0x00, 0x23, 0xFB, 0x5E, 0x8A, 0x5E, 0x9B, 0x1A, +0x00, 0xD5, 0x5B, 0x42, 0x1A, 0xB2, 0x94, 0x42, +0x00, 0xDA, 0x14, 0x46, 0x40, 0x1C, 0xC0, 0xB2, +0x30, 0x28, 0xEB, 0xD3, 0x60, 0x20, 0x80, 0x5D, +0x90, 0x36, 0x40, 0x1C, 0x72, 0x7B, 0x60, 0x43, +0x33, 0x7B, 0x00, 0x11, 0x12, 0x02, 0x00, 0xB2, +0x1A, 0x43, 0x82, 0x42, 0x03, 0xDA, 0x0F, 0x49, +0x00, 0x20, 0x08, 0x70, 0xF0, 0xBD, 0x00, 0x20, +0x42, 0x00, 0x8B, 0x5E, 0x00, 0x26, 0xDC, 0x00, +0xE3, 0x1A, 0x54, 0x19, 0xA6, 0x5F, 0x40, 0x1C, +0x9B, 0x19, 0xDB, 0x10, 0xC0, 0xB2, 0x8B, 0x52, +0x30, 0x28, 0xF1, 0xD3, 0xF0, 0xBD, 0x00, 0x00, +0x64, 0x01, 0x00, 0x20, 0x40, 0x77, 0x1B, 0x00, +0x7E, 0x02, 0x00, 0x20, 0x80, 0x02, 0x00, 0x20, +0x00, 0x00, 0x01, 0x20, 0x36, 0x00, 0x00, 0x20, +0xF8, 0xB5, 0x27, 0x48, 0x27, 0x49, 0x00, 0x78, +0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x08, 0xD0, +0x02, 0x28, 0x20, 0xD0, 0x05, 0x28, 0x36, 0xD1, +0x3F, 0xE0, 0x01, 0x25, 0x2C, 0x46, 0x22, 0x48, +0x02, 0xE0, 0x02, 0x25, 0x21, 0x48, 0x2C, 0x46, +0x08, 0x60, 0x00, 0x22, 0x20, 0x49, 0x28, 0x46, +0xFE, 0xF7, 0x14, 0xFC, 0x03, 0x27, 0x1F, 0x4E, +0x3F, 0x05, 0x00, 0x28, 0x03, 0xD0, 0x1E, 0x48, +0x00, 0x78, 0x00, 0x28, 0x0B, 0xD0, 0x01, 0x22, +0x21, 0x46, 0x28, 0x46, 0x00, 0xF0, 0x3C, 0xF8, +0x00, 0x28, 0x1E, 0xD0, 0x17, 0xE0, 0x04, 0x25, +0x2C, 0x46, 0x18, 0x48, 0xE4, 0xE7, 0x70, 0x6B, +0x38, 0x43, 0x70, 0x63, 0x16, 0x48, 0x00, 0xF0, +0xD1, 0xFA, 0x00, 0x28, 0x0B, 0xD1, 0x20, 0x46, +0xFF, 0xF7, 0x2E, 0xFF, 0x00, 0x28, 0x06, 0xD1, +0x01, 0x22, 0x21, 0x46, 0x28, 0x46, 0x00, 0xF0, +0x23, 0xF8, 0x00, 0x28, 0x04, 0xD0, 0x0F, 0x48, +0x00, 0x78, 0xC0, 0x07, 0x05, 0xD0, 0x00, 0x20, +0xF8, 0xBD, 0x70, 0x6B, 0x38, 0x43, 0x70, 0x63, +0xF9, 0xE7, 0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, +0x7D, 0x02, 0x00, 0x20, 0xCC, 0x00, 0x00, 0x20, +0x1C, 0x02, 0x00, 0x20, 0x0C, 0x08, 0x00, 0x20, +0x00, 0x20, 0x00, 0x50, 0x80, 0x10, 0x00, 0x50, +0x3B, 0x01, 0x00, 0x20, 0x6C, 0x08, 0x00, 0x20, +0x00, 0x00, 0x01, 0x20, 0x34, 0x00, 0x00, 0x20, +0xF7, 0xB5, 0x07, 0x46, 0x14, 0x48, 0x00, 0x25, +0x05, 0x70, 0x78, 0x07, 0x1D, 0xD0, 0x01, 0x21, +0x02, 0x20, 0x00, 0xF0, 0x9F, 0xFB, 0x11, 0x4C, +0x60, 0x6B, 0x03, 0x26, 0x36, 0x05, 0x01, 0x21, +0xB0, 0x43, 0x09, 0x05, 0x40, 0x18, 0x60, 0x63, +0x00, 0x20, 0xFF, 0xF7, 0x01, 0xFD, 0x00, 0x28, +0x0D, 0xD0, 0x01, 0x98, 0xFF, 0xF7, 0xE8, 0xFE, +0x01, 0x22, 0x09, 0x49, 0x38, 0x46, 0xFE, 0xF7, +0xA9, 0xFB, 0x08, 0x48, 0x05, 0x70, 0x08, 0x48, +0x05, 0x70, 0x01, 0x20, 0xFE, 0xBD, 0x60, 0x6B, +0x30, 0x43, 0x60, 0x63, 0x00, 0x20, 0xFE, 0xBD, +0xD3, 0x00, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, +0x00, 0x20, 0x00, 0x50, 0x3B, 0x01, 0x00, 0x20, +0x3C, 0x01, 0x00, 0x20, 0x70, 0xB5, 0x10, 0x4C, +0x60, 0x6B, 0x03, 0x25, 0x2D, 0x05, 0x01, 0x26, +0xA8, 0x43, 0x36, 0x05, 0x80, 0x19, 0x60, 0x63, +0x0C, 0x49, 0x02, 0x20, 0x08, 0x5E, 0xFF, 0xF7, +0xD3, 0xFC, 0x00, 0x28, 0x60, 0x6B, 0x09, 0xD0, +0xA8, 0x43, 0x80, 0x19, 0x60, 0x63, 0x08, 0x49, +0x00, 0x20, 0x08, 0x70, 0x07, 0x49, 0x08, 0x70, +0x01, 0x20, 0x70, 0xBD, 0xA8, 0x43, 0x80, 0x19, +0x60, 0x63, 0x00, 0x20, 0x70, 0xBD, 0x00, 0x00, +0x80, 0x10, 0x00, 0x50, 0x98, 0x01, 0x00, 0x20, +0x3B, 0x01, 0x00, 0x20, 0x3C, 0x01, 0x00, 0x20, +0xF0, 0xB5, 0x01, 0x24, 0xEE, 0x4B, 0xA4, 0x02, +0x23, 0x20, 0x13, 0x21, 0x29, 0x22, 0x1C, 0x60, +0x40, 0x01, 0x89, 0x01, 0x52, 0x01, 0x1B, 0x1D, +0x07, 0xC3, 0xEA, 0x4B, 0x1D, 0x68, 0x2B, 0x46, +0x40, 0x33, 0xAC, 0x46, 0x5D, 0x7C, 0x1E, 0x7C, +0x2D, 0x04, 0x36, 0x02, 0x35, 0x43, 0x0C, 0x35, +0x26, 0x46, 0x35, 0x60, 0x5D, 0x7C, 0x1E, 0x7C, +0x2D, 0x04, 0x36, 0x02, 0x35, 0x43, 0x0C, 0x35, +0x26, 0x46, 0x75, 0x60, 0x01, 0x25, 0xB5, 0x60, +0xF5, 0x60, 0x1D, 0x7D, 0x6D, 0x1E, 0xED, 0x05, +0xED, 0x09, 0x6D, 0x1C, 0x35, 0x61, 0xDC, 0x4D, +0x75, 0x61, 0x03, 0x25, 0xB5, 0x61, 0x00, 0x25, +0xF5, 0x61, 0x03, 0x25, 0x2D, 0x02, 0x35, 0x62, +0x9E, 0x7E, 0x1D, 0x7E, 0x77, 0x00, 0xF6, 0x19, +0xAE, 0x19, 0x36, 0x04, 0x2E, 0x43, 0x25, 0x46, +0xEE, 0x62, 0x9D, 0x7E, 0x1E, 0x7E, 0x6F, 0x00, +0xEF, 0x19, 0xBF, 0x1C, 0xF7, 0x19, 0x7F, 0x05, +0x7F, 0x09, 0x75, 0x19, 0x2F, 0x43, 0x25, 0x46, +0x2F, 0x63, 0x1E, 0x7E, 0x9D, 0x7E, 0x6F, 0x00, +0xEF, 0x19, 0x3F, 0x1D, 0xF7, 0x19, 0x6D, 0x00, +0x7F, 0x05, 0x75, 0x19, 0x7F, 0x09, 0x2F, 0x43, +0x25, 0x46, 0x6F, 0x63, 0x00, 0x25, 0x26, 0x46, +0xB5, 0x63, 0x9E, 0x7E, 0x1D, 0x7E, 0x77, 0x00, +0xF6, 0x19, 0xBC, 0x36, 0xAE, 0x19, 0x36, 0x04, +0xBC, 0x35, 0x2E, 0x43, 0x25, 0x46, 0xEE, 0x63, +0x9D, 0x7E, 0x1E, 0x7E, 0x6F, 0x00, 0xEF, 0x19, +0xBE, 0x37, 0xF7, 0x19, 0x7F, 0x05, 0x75, 0x19, +0x7F, 0x09, 0xBC, 0x35, 0x2F, 0x43, 0x25, 0x46, +0x2F, 0x64, 0x9D, 0x7E, 0x1E, 0x7E, 0x6F, 0x00, +0xEF, 0x19, 0xC0, 0x37, 0xF7, 0x19, 0x6D, 0x00, +0xBC, 0x35, 0x7F, 0x05, 0x75, 0x19, 0x7F, 0x09, +0x2F, 0x43, 0x25, 0x46, 0x6F, 0x64, 0x00, 0x25, +0x26, 0x46, 0xB5, 0x64, 0x75, 0x62, 0xB1, 0x4D, +0xB5, 0x62, 0x65, 0x46, 0x50, 0x35, 0xEE, 0x7B, +0xAF, 0x7B, 0x35, 0x02, 0x3D, 0x43, 0xED, 0x1C, +0xAE, 0x05, 0xAD, 0x4D, 0xB6, 0x0D, 0x75, 0x19, +0x26, 0x46, 0xF5, 0x64, 0xAB, 0x4D, 0x2E, 0x68, +0xAB, 0x4D, 0xAE, 0x42, 0x01, 0xD1, 0xAB, 0x4D, +0x00, 0xE0, 0xAB, 0x4D, 0x25, 0x65, 0x5D, 0x7C, +0xA2, 0x4E, 0x0C, 0x3D, 0xEF, 0xB2, 0x05, 0x25, +0x6D, 0x02, 0x7D, 0x19, 0x65, 0x65, 0x1D, 0x7D, +0x00, 0x27, 0x2D, 0x02, 0x21, 0x35, 0xA5, 0x65, +0x02, 0x25, 0xE5, 0x65, 0x5C, 0x7C, 0x1B, 0x7C, +0x24, 0x04, 0x1B, 0x02, 0x1C, 0x43, 0x0C, 0x34, +0x04, 0x60, 0x34, 0x68, 0x23, 0x46, 0x40, 0x33, +0xA4, 0x46, 0x5C, 0x7C, 0x1D, 0x7C, 0x24, 0x04, +0x2D, 0x02, 0x2C, 0x43, 0x0C, 0x34, 0x44, 0x60, +0x01, 0x24, 0x84, 0x60, 0xC4, 0x60, 0x5C, 0x7D, +0x64, 0x1E, 0xE4, 0x05, 0xE4, 0x09, 0x64, 0x1C, +0x04, 0x61, 0x8F, 0x4C, 0x44, 0x61, 0x03, 0x24, +0xC7, 0x61, 0x84, 0x61, 0x24, 0x02, 0x04, 0x62, +0xDD, 0x7E, 0x1C, 0x7E, 0x6E, 0x00, 0xAD, 0x19, +0x65, 0x19, 0x2D, 0x04, 0x25, 0x43, 0xC5, 0x62, +0xDC, 0x7E, 0x1D, 0x7E, 0x66, 0x00, 0xA6, 0x19, +0xB6, 0x1C, 0xAE, 0x19, 0x76, 0x05, 0x76, 0x09, +0x2C, 0x19, 0x26, 0x43, 0x06, 0x63, 0xDC, 0x7E, +0x1D, 0x7E, 0x66, 0x00, 0xA6, 0x19, 0x36, 0x1D, +0xAE, 0x19, 0x76, 0x05, 0x64, 0x00, 0x76, 0x09, +0x2C, 0x19, 0x26, 0x43, 0x87, 0x63, 0x46, 0x63, +0xDD, 0x7E, 0x1C, 0x7E, 0x6E, 0x00, 0xAD, 0x19, +0xBC, 0x35, 0x65, 0x19, 0x2D, 0x04, 0xBC, 0x34, +0x25, 0x43, 0xC5, 0x63, 0xDC, 0x7E, 0x1D, 0x7E, +0x66, 0x00, 0xA6, 0x19, 0xBE, 0x36, 0xAE, 0x19, +0x76, 0x05, 0x2C, 0x19, 0x76, 0x09, 0xBC, 0x34, +0x26, 0x43, 0x06, 0x64, 0xDC, 0x7E, 0x1D, 0x7E, +0x66, 0x00, 0xA6, 0x19, 0xC0, 0x36, 0xAE, 0x19, +0x64, 0x00, 0x76, 0x05, 0xBC, 0x34, 0x76, 0x09, +0x2C, 0x19, 0x26, 0x43, 0x87, 0x64, 0x47, 0x62, +0x6A, 0x4C, 0x46, 0x64, 0x84, 0x62, 0x64, 0x46, +0x50, 0x34, 0xE5, 0x7B, 0xA6, 0x7B, 0x2C, 0x02, +0x34, 0x43, 0xE4, 0x1C, 0xA5, 0x05, 0x66, 0x4C, +0xAD, 0x0D, 0x2C, 0x19, 0xC4, 0x64, 0x65, 0x4C, +0x25, 0x68, 0x65, 0x4C, 0xA5, 0x42, 0x01, 0xD1, +0x64, 0x4C, 0x00, 0xE0, 0x64, 0x4C, 0x04, 0x65, +0x5C, 0x7C, 0x5C, 0x4E, 0x0C, 0x3C, 0xE5, 0xB2, +0x05, 0x24, 0x64, 0x02, 0x2D, 0x19, 0x45, 0x65, +0x5D, 0x7D, 0x2D, 0x02, 0x21, 0x35, 0x85, 0x65, +0x02, 0x25, 0xC5, 0x65, 0xD8, 0x7C, 0x9B, 0x7C, +0x00, 0x04, 0x1B, 0x02, 0x18, 0x43, 0x0C, 0x30, +0x08, 0x60, 0x30, 0x68, 0x40, 0x30, 0xC3, 0x7C, +0x85, 0x7C, 0x1B, 0x04, 0x2D, 0x02, 0x2B, 0x43, +0x0C, 0x33, 0x4B, 0x60, 0x83, 0x7D, 0x55, 0x4D, +0x5B, 0x1E, 0xDB, 0x05, 0xDB, 0x0D, 0x8B, 0x60, +0x83, 0x7D, 0x5B, 0x08, 0x5B, 0x1E, 0xDB, 0x05, +0xDB, 0x0D, 0xCB, 0x60, 0x0F, 0x61, 0x4D, 0x61, +0xCF, 0x61, 0x01, 0x25, 0x8D, 0x61, 0x2D, 0x02, +0x0D, 0x62, 0x45, 0x7E, 0xCD, 0x62, 0x45, 0x7E, +0x06, 0x7F, 0x3B, 0x46, 0xAD, 0x19, 0x0D, 0x63, +0x06, 0x7F, 0x45, 0x7E, 0x8B, 0x63, 0xCB, 0x63, +0x0B, 0x64, 0x40, 0x4F, 0x4B, 0x64, 0x76, 0x00, +0x8B, 0x64, 0x3F, 0x37, 0xAD, 0x19, 0x8F, 0x62, +0x4D, 0x63, 0x3D, 0x4D, 0x4B, 0x62, 0xED, 0x1C, +0xCD, 0x64, 0x3C, 0x4D, 0x3C, 0x4E, 0x2D, 0x68, +0xB5, 0x42, 0x01, 0xD1, 0x3B, 0x4D, 0x00, 0xE0, +0x3B, 0x4D, 0x0D, 0x65, 0xC5, 0x7C, 0x0C, 0x3D, +0xED, 0xB2, 0x2D, 0x19, 0x4D, 0x65, 0x85, 0x7D, +0x6D, 0x08, 0x2D, 0x02, 0x21, 0x35, 0x8D, 0x65, +0xCB, 0x65, 0xC1, 0x7C, 0x80, 0x7C, 0x09, 0x04, +0x00, 0x02, 0x01, 0x43, 0x0C, 0x31, 0x2B, 0x48, +0x11, 0x60, 0x00, 0x68, 0x40, 0x30, 0xC1, 0x7C, +0x85, 0x7C, 0x09, 0x04, 0x2D, 0x02, 0x29, 0x43, +0x0C, 0x31, 0x51, 0x60, 0xC1, 0x7D, 0x49, 0x1E, +0xC9, 0x05, 0xC9, 0x0D, 0x91, 0x60, 0xC1, 0x7D, +0x13, 0x61, 0x49, 0x08, 0x49, 0x1E, 0xC9, 0x05, +0xC9, 0x0D, 0xD1, 0x60, 0x27, 0x49, 0x51, 0x61, +0x01, 0x21, 0xD3, 0x61, 0x91, 0x61, 0x09, 0x02, +0x11, 0x62, 0x41, 0x7E, 0xD1, 0x62, 0x41, 0x7E, +0x45, 0x7F, 0x49, 0x19, 0x11, 0x63, 0x45, 0x7F, +0x41, 0x7E, 0x6D, 0x00, 0x49, 0x19, 0x93, 0x63, +0x51, 0x63, 0x41, 0x7E, 0xBC, 0x31, 0xD1, 0x63, +0x41, 0x7E, 0x45, 0x7F, 0x49, 0x19, 0xBC, 0x31, +0x11, 0x64, 0x45, 0x7F, 0x41, 0x7E, 0x6D, 0x00, +0xBC, 0x35, 0x49, 0x19, 0x93, 0x64, 0x51, 0x64, +0x01, 0x21, 0x89, 0x04, 0x97, 0x62, 0x51, 0x62, +0x0F, 0x49, 0xC9, 0x1C, 0xD1, 0x64, 0x0F, 0x49, +0x09, 0x68, 0xB1, 0x42, 0x01, 0xD1, 0x0F, 0x49, +0x00, 0xE0, 0x0F, 0x49, 0x11, 0x65, 0xC1, 0x7C, +0x0C, 0x39, 0xC9, 0xB2, 0x09, 0x19, 0x51, 0x65, +0xC0, 0x7D, 0xD3, 0x65, 0x40, 0x08, 0x00, 0x02, +0x21, 0x30, 0x90, 0x65, 0xF0, 0xBD, 0x00, 0x00, +0x2C, 0x06, 0x00, 0x20, 0x80, 0x02, 0x00, 0x20, +0x03, 0x00, 0x03, 0x00, 0x00, 0x3F, 0x3F, 0x3F, +0x00, 0x30, 0xBC, 0x00, 0xE4, 0x06, 0x00, 0x20, +0xA1, 0x00, 0x03, 0xF3, 0x00, 0x26, 0x31, 0x00, +0x00, 0x26, 0x31, 0x08, 0x01, 0x00, 0x01, 0x00, +0x00, 0xB5, 0x00, 0xF0, 0xA5, 0xFA, 0x00, 0xF0, +0x3B, 0xF9, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, +0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x00, 0x20, +0xFF, 0xF7, 0x32, 0xFC, 0x09, 0x48, 0x01, 0x78, +0x09, 0x48, 0x00, 0x29, 0x04, 0xD0, 0x01, 0x68, +0xFF, 0x22, 0x01, 0x32, 0x11, 0x43, 0x01, 0x60, +0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x91, 0x43, +0x01, 0x60, 0x04, 0x49, 0x02, 0x20, 0x08, 0x72, +0x00, 0xBD, 0x00, 0x00, 0x35, 0x00, 0x00, 0x20, +0x00, 0x10, 0x00, 0x50, 0x24, 0x00, 0x00, 0x20, +0x10, 0xB5, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, +0x04, 0x24, 0x21, 0x43, 0x41, 0x60, 0x0B, 0x48, +0x09, 0x49, 0x41, 0x60, 0x0A, 0x49, 0x81, 0x60, +0xFF, 0xF7, 0xD6, 0xFD, 0x05, 0x20, 0x00, 0x07, +0x81, 0x69, 0x21, 0x43, 0x81, 0x61, 0x80, 0x21, +0x06, 0x48, 0xFD, 0xF7, 0xE2, 0xFF, 0x80, 0x21, +0x05, 0x48, 0xFD, 0xF7, 0xDE, 0xFF, 0x10, 0xBD, +0x1F, 0x1F, 0x5F, 0x1F, 0x00, 0x10, 0x00, 0x50, +0x1F, 0x1F, 0x1F, 0x1F, 0x1C, 0x0A, 0x00, 0x20, +0x9C, 0x01, 0x00, 0x20, 0x10, 0xB5, 0x0F, 0x49, +0x0A, 0x78, 0x0F, 0x49, 0x00, 0x2A, 0x09, 0x78, +0x04, 0xD0, 0x01, 0x2A, 0x07, 0xD0, 0x02, 0x2A, +0x12, 0xD1, 0x09, 0xE0, 0xC9, 0x07, 0x0F, 0xD0, +0x60, 0x22, 0x0A, 0x49, 0x08, 0xE0, 0x89, 0x07, +0x0A, 0xD5, 0x60, 0x22, 0x08, 0x49, 0x03, 0xE0, +0x49, 0x07, 0x05, 0xD5, 0x60, 0x22, 0x07, 0x49, +0xFD, 0xF7, 0x9E, 0xFF, 0x01, 0x20, 0x10, 0xBD, +0x00, 0x20, 0x10, 0xBD, 0x7D, 0x02, 0x00, 0x20, +0x36, 0x00, 0x00, 0x20, 0x1C, 0x02, 0x00, 0x20, +0x0C, 0x08, 0x00, 0x20, 0x6C, 0x08, 0x00, 0x20, +0x10, 0xB5, 0x13, 0x49, 0x13, 0x4B, 0x09, 0x68, +0x13, 0x4A, 0x40, 0x31, 0x00, 0x28, 0x0F, 0xD0, +0x01, 0x28, 0x12, 0xD0, 0x02, 0x28, 0x15, 0xD0, +0x03, 0x28, 0x08, 0xD1, 0x48, 0x7E, 0x84, 0x1E, +0x1C, 0x80, 0x49, 0x7F, 0x4B, 0x00, 0xC9, 0x18, +0x89, 0x1C, 0x40, 0x18, 0x10, 0x80, 0x10, 0xBD, +0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, 0x89, 0x7E, +0xF4, 0xE7, 0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, +0xC9, 0x7E, 0xEF, 0xE7, 0x48, 0x7E, 0x84, 0x1E, +0x1C, 0x80, 0x09, 0x7F, 0xEA, 0xE7, 0x00, 0x00, +0x80, 0x02, 0x00, 0x20, 0x3A, 0x00, 0x00, 0x20, +0x3C, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x1C, 0x4A, +0x00, 0x21, 0x11, 0x60, 0x51, 0x60, 0x1B, 0x4A, +0x01, 0x21, 0x11, 0x70, 0x1A, 0x49, 0x1B, 0x4A, +0x09, 0x78, 0x00, 0x29, 0x06, 0xD0, 0x3C, 0x24, +0x0C, 0x23, 0x01, 0x29, 0x0A, 0xD0, 0x02, 0x29, +0x20, 0xD1, 0x11, 0xE0, 0x78, 0x21, 0x00, 0x28, +0x11, 0x70, 0x01, 0xD0, 0x00, 0x20, 0x08, 0xE0, +0x01, 0x20, 0x06, 0xE0, 0x00, 0x28, 0x02, 0xD0, +0x14, 0x70, 0x02, 0x20, 0x01, 0xE0, 0x13, 0x70, +0x03, 0x20, 0x00, 0xF0, 0x83, 0xF9, 0x0D, 0xE0, +0x00, 0x28, 0x02, 0xD0, 0x14, 0x70, 0x02, 0x20, +0x01, 0xE0, 0x13, 0x70, 0x03, 0x20, 0x00, 0xF0, +0x79, 0xF9, 0x09, 0x48, 0x81, 0x6A, 0x09, 0x4A, +0x11, 0x40, 0x81, 0x62, 0x05, 0x20, 0x08, 0x49, +0x00, 0x02, 0x08, 0x60, 0x10, 0xBD, 0x00, 0x00, +0x24, 0x00, 0x00, 0x20, 0x35, 0x00, 0x00, 0x20, +0x7D, 0x02, 0x00, 0x20, 0x7F, 0x02, 0x00, 0x20, +0xC0, 0x11, 0x00, 0x50, 0xFF, 0xFF, 0x00, 0xF8, +0x00, 0x10, 0x00, 0x50, 0x10, 0xB5, 0x17, 0x4C, +0x17, 0x49, 0x20, 0x70, 0x01, 0x20, 0x08, 0x70, +0x00, 0xF0, 0xB2, 0xF9, 0x00, 0xF0, 0x48, 0xF8, +0x01, 0x20, 0xFD, 0xF7, 0xB5, 0xFF, 0xFF, 0xF7, +0x97, 0xFB, 0x01, 0x20, 0xFF, 0xF7, 0xA6, 0xFF, +0x00, 0xF0, 0xBC, 0xFB, 0xFE, 0xF7, 0x54, 0xFE, +0x20, 0x78, 0x0E, 0x49, 0x05, 0x28, 0x08, 0x70, +0x0F, 0xD0, 0xFF, 0xF7, 0x3D, 0xFC, 0x00, 0x28, +0x0C, 0xD0, 0x0B, 0x48, 0x00, 0x7A, 0x00, 0x28, +0x03, 0xD1, 0x00, 0x21, 0x02, 0x20, 0x00, 0xF0, +0x41, 0xF8, 0x08, 0x48, 0x00, 0x78, 0xC0, 0x07, +0x01, 0xD0, 0x00, 0x20, 0x10, 0xBD, 0x01, 0x20, +0x10, 0xBD, 0x00, 0x00, 0x7D, 0x02, 0x00, 0x20, +0x7C, 0x02, 0x00, 0x20, 0xCD, 0x08, 0x00, 0x20, +0x24, 0x00, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20, +0x08, 0x49, 0x02, 0x20, 0x08, 0x72, 0x08, 0x48, +0x01, 0x78, 0x08, 0x48, 0x00, 0x29, 0x01, 0x68, +0x04, 0xD0, 0x01, 0x22, 0x92, 0x02, 0x91, 0x43, +0x01, 0x60, 0x70, 0x47, 0x01, 0x22, 0x11, 0x43, +0xFA, 0xE7, 0x00, 0x00, 0x24, 0x00, 0x00, 0x20, +0x35, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, +0x10, 0xB5, 0x08, 0x48, 0x01, 0x68, 0x01, 0x22, +0x92, 0x02, 0x11, 0x43, 0x01, 0x60, 0x00, 0x68, +0x05, 0x4C, 0xC0, 0x07, 0x03, 0xD0, 0x02, 0x20, +0x20, 0x72, 0x00, 0xF0, 0x59, 0xF9, 0x00, 0x20, +0x20, 0x72, 0x10, 0xBD, 0x00, 0x10, 0x00, 0x50, +0x24, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x00, +0x0E, 0x46, 0x16, 0xD0, 0xFF, 0xF7, 0xA4, 0xFE, +0x00, 0x24, 0x6D, 0x1E, 0x07, 0xE0, 0x00, 0xF0, +0x47, 0xF9, 0xFF, 0xF7, 0xFF, 0xFA, 0xFF, 0xF7, +0xC3, 0xFF, 0x64, 0x1C, 0xE4, 0xB2, 0xAC, 0x42, +0xF5, 0xDB, 0x00, 0xF0, 0x3D, 0xF9, 0xFF, 0xF7, +0xF5, 0xFA, 0x00, 0x2E, 0x02, 0xD0, 0xFF, 0xF7, +0xCF, 0xFF, 0x70, 0xBD, 0xFF, 0xF7, 0xB4, 0xFF, +0x70, 0xBD, 0x00, 0x00, 0xF0, 0xB5, 0x01, 0x21, +0x31, 0x4A, 0x89, 0x07, 0x05, 0x27, 0x0C, 0x13, +0x04, 0x25, 0x3F, 0x07, 0x12, 0x68, 0x2F, 0x4B, +0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x04, 0xD0, +0x02, 0x28, 0x1A, 0xD0, 0x03, 0x28, 0x40, 0xD1, +0x17, 0xE0, 0x08, 0x68, 0xA0, 0x43, 0x08, 0x60, +0x01, 0x20, 0x98, 0x61, 0xD8, 0x61, 0x27, 0x49, +0x00, 0x20, 0x40, 0x39, 0xC8, 0x63, 0x18, 0x60, +0x98, 0x60, 0x25, 0x48, 0x82, 0x42, 0x30, 0xD1, +0x88, 0x6B, 0xA8, 0x43, 0x88, 0x63, 0x78, 0x69, +0x01, 0x21, 0x09, 0x04, 0x88, 0x43, 0x78, 0x61, +0x27, 0xE0, 0x0E, 0x68, 0x26, 0x43, 0x0E, 0x60, +0x62, 0x21, 0x99, 0x61, 0xD9, 0x61, 0x1B, 0x4C, +0x1C, 0x49, 0x40, 0x3C, 0xE1, 0x63, 0x1C, 0x49, +0x09, 0x68, 0x40, 0x31, 0x02, 0x28, 0x48, 0x7E, +0x23, 0xD0, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D, +0x18, 0x60, 0x4E, 0x7E, 0x48, 0x7F, 0x41, 0x00, +0x40, 0x18, 0x80, 0x1C, 0x30, 0x18, 0x40, 0x05, +0x40, 0x0D, 0x98, 0x60, 0x10, 0x48, 0x82, 0x42, +0x07, 0xD1, 0xA0, 0x6B, 0x28, 0x43, 0xA0, 0x63, +0x79, 0x69, 0x01, 0x20, 0x00, 0x04, 0x01, 0x43, +0x79, 0x61, 0x19, 0x68, 0x0D, 0x48, 0x41, 0x63, +0x19, 0x68, 0x81, 0x63, 0x99, 0x68, 0x0B, 0x48, +0x40, 0x30, 0x41, 0x60, 0x99, 0x68, 0x81, 0x60, +0xF0, 0xBD, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D, +0x18, 0x60, 0x4E, 0x7E, 0x08, 0x7F, 0xDA, 0xE7, +0xE4, 0x06, 0x00, 0x20, 0xC0, 0x11, 0x00, 0x50, +0xA2, 0x00, 0x03, 0xF3, 0x01, 0x00, 0x01, 0x00, +0x80, 0x02, 0x00, 0x20, 0x80, 0x13, 0x00, 0x50, +0x2F, 0x4A, 0x90, 0x6A, 0x2F, 0x49, 0x08, 0x60, +0xD0, 0x6B, 0x48, 0x60, 0x2C, 0x48, 0x40, 0x30, +0x03, 0x69, 0x8B, 0x60, 0x43, 0x6A, 0xCB, 0x60, +0xD3, 0x6A, 0x0B, 0x61, 0x03, 0x68, 0x4B, 0x61, +0x43, 0x69, 0x8B, 0x61, 0x83, 0x6A, 0xCB, 0x61, +0x93, 0x6A, 0x0B, 0x62, 0xD3, 0x6B, 0x4B, 0x62, +0x03, 0x69, 0x8B, 0x62, 0x43, 0x6A, 0xCB, 0x62, +0xD3, 0x6A, 0x0B, 0x63, 0x03, 0x68, 0x4B, 0x63, +0x43, 0x69, 0x8B, 0x63, 0x83, 0x6A, 0xCB, 0x63, +0x93, 0x6A, 0x1E, 0x49, 0x40, 0x31, 0x0B, 0x60, +0xD3, 0x6B, 0x4B, 0x60, 0x03, 0x69, 0x8B, 0x60, +0x43, 0x6A, 0xCB, 0x60, 0xD3, 0x6A, 0x0B, 0x61, +0x03, 0x68, 0x4B, 0x61, 0x43, 0x69, 0x8B, 0x61, +0x83, 0x6A, 0xCB, 0x61, 0x93, 0x6A, 0x0B, 0x62, +0xD3, 0x6B, 0x4B, 0x62, 0x03, 0x69, 0x8B, 0x62, +0x43, 0x6A, 0xCB, 0x62, 0xD2, 0x6A, 0x0A, 0x63, +0x02, 0x68, 0x4A, 0x63, 0x42, 0x69, 0x8A, 0x63, +0x80, 0x6A, 0xC8, 0x63, 0x0C, 0x49, 0xC0, 0x31, +0x88, 0x6A, 0x80, 0x05, 0x82, 0x09, 0x88, 0x6A, +0x80, 0x05, 0x80, 0x0D, 0x02, 0x43, 0x09, 0x48, +0x80, 0x30, 0x02, 0x60, 0x8A, 0x6A, 0x89, 0x6A, +0x92, 0x05, 0x92, 0x09, 0x89, 0x05, 0x89, 0x0D, +0x0A, 0x43, 0x42, 0x60, 0x04, 0x49, 0xC1, 0x60, +0x01, 0x61, 0xFF, 0x21, 0x81, 0x60, 0x70, 0x47, +0x00, 0x11, 0x00, 0x50, 0x00, 0x13, 0x00, 0x50, +0xBC, 0x00, 0xBC, 0x00, 0x70, 0xB5, 0x04, 0x46, +0x81, 0x00, 0x26, 0x48, 0x41, 0x58, 0x26, 0x48, +0x0A, 0x68, 0x42, 0x60, 0x4A, 0x68, 0x82, 0x60, +0x8A, 0x68, 0x02, 0x61, 0xCA, 0x68, 0x42, 0x61, +0x22, 0x4D, 0x08, 0x69, 0x28, 0x62, 0x20, 0x4A, +0x48, 0x69, 0xC0, 0x32, 0x90, 0x61, 0x88, 0x69, +0xD0, 0x61, 0x48, 0x6A, 0x10, 0x62, 0x88, 0x6A, +0x50, 0x62, 0x1B, 0x48, 0xCB, 0x69, 0x40, 0x38, +0xC3, 0x60, 0x0B, 0x6A, 0x03, 0x61, 0xCB, 0x6A, +0x93, 0x62, 0x0B, 0x6B, 0xD3, 0x63, 0x17, 0x4B, +0x4E, 0x6B, 0x80, 0x3B, 0x1E, 0x61, 0x8E, 0x6B, +0x5E, 0x62, 0xCE, 0x6B, 0xD6, 0x62, 0x0A, 0x6C, +0x1A, 0x60, 0x4A, 0x6C, 0x5A, 0x61, 0x8A, 0x6C, +0x9A, 0x62, 0xCA, 0x6C, 0xAA, 0x62, 0x0E, 0x4B, +0x0A, 0x6D, 0x40, 0x33, 0x5A, 0x63, 0x0C, 0x4B, +0x4A, 0x6D, 0x80, 0x33, 0x9A, 0x60, 0x0C, 0x4A, +0x8B, 0x6D, 0x13, 0x60, 0xC9, 0x6D, 0x51, 0x60, +0xC2, 0x68, 0x0A, 0x49, 0x0A, 0x60, 0x00, 0x69, +0x48, 0x60, 0xFF, 0xF7, 0x51, 0xFF, 0x20, 0x46, +0xFF, 0xF7, 0xDE, 0xFD, 0x20, 0x46, 0xFF, 0xF7, +0xD9, 0xFE, 0x70, 0xBD, 0x2C, 0x06, 0x00, 0x20, +0x40, 0x10, 0x00, 0x50, 0xC0, 0x11, 0x00, 0x50, +0x00, 0x19, 0x00, 0x50, 0x24, 0x00, 0x00, 0x20, +0x30, 0xB5, 0x05, 0x20, 0x40, 0x04, 0x00, 0x23, +0x0B, 0x4A, 0x0C, 0x49, 0x0C, 0x4C, 0x05, 0xE0, +0x0D, 0x78, 0xED, 0x07, 0x01, 0xD0, 0x13, 0x72, +0x09, 0xE0, 0x40, 0x1E, 0x15, 0x7A, 0x02, 0x2D, +0x02, 0xD0, 0x25, 0x68, 0xED, 0x07, 0x02, 0xD0, +0x00, 0x28, 0xF1, 0xD1, 0x01, 0xE0, 0x00, 0x28, +0x00, 0xD1, 0x13, 0x72, 0x30, 0xBD, 0x00, 0x00, +0x24, 0x00, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20, +0x00, 0x10, 0x00, 0x50, 0xF8, 0xB5, 0x1D, 0x48, +0x01, 0x25, 0x01, 0x68, 0x00, 0x24, 0x02, 0x22, +0x91, 0x43, 0x1B, 0x4F, 0x1B, 0x4E, 0x01, 0x60, +0x1B, 0x48, 0x00, 0x68, 0x00, 0x19, 0xC0, 0x7E, +0x41, 0x28, 0x14, 0xD0, 0x19, 0x48, 0x61, 0x00, +0x42, 0x5E, 0x04, 0x20, 0x38, 0x5E, 0x82, 0x42, +0x09, 0xDA, 0x21, 0x46, 0x16, 0xA0, 0xFD, 0xF7, +0xFD, 0xFD, 0x30, 0x5D, 0x02, 0x21, 0x08, 0x43, +0x30, 0x55, 0x00, 0x25, 0x03, 0xE0, 0x30, 0x5D, +0xFD, 0x21, 0x08, 0x40, 0x30, 0x55, 0x64, 0x1C, +0xE4, 0xB2, 0x30, 0x2C, 0xE0, 0xD3, 0x15, 0x49, +0x01, 0x2D, 0x79, 0x5E, 0x09, 0xD0, 0x14, 0xA0, +0xFD, 0xF7, 0xE8, 0xFD, 0x05, 0x49, 0x02, 0x22, +0x08, 0x68, 0x10, 0x43, 0x08, 0x60, 0x28, 0x46, +0xF8, 0xBD, 0x16, 0xA0, 0xFD, 0xF7, 0xDE, 0xFD, +0xF9, 0xE7, 0x00, 0x00, 0x0C, 0x05, 0x00, 0x20, +0x84, 0x06, 0x00, 0x20, 0xD8, 0x04, 0x00, 0x20, +0x80, 0x02, 0x00, 0x20, 0xE4, 0x02, 0x00, 0x20, +0x53, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x4E, 0x47, +0x20, 0x44, 0x61, 0x74, 0x61, 0x5B, 0x25, 0x64, +0x5D, 0x20, 0x3D, 0x20, 0x25, 0x64, 0x0D, 0x0A, +0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, +0x53, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x54, 0x65, +0x73, 0x74, 0x28, 0x54, 0x48, 0x44, 0x3A, 0x25, +0x64, 0x29, 0x20, 0x5B, 0x4E, 0x47, 0x5D, 0x0D, +0x0A, 0x00, 0x00, 0x00, 0x53, 0x68, 0x6F, 0x72, +0x74, 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x54, +0x48, 0x44, 0x3A, 0x25, 0x64, 0x29, 0x20, 0x5B, +0x50, 0x61, 0x73, 0x73, 0x5D, 0x0D, 0x0A, 0x00, +0x70, 0xB5, 0x0F, 0x48, 0x01, 0x25, 0xC4, 0x7F, +0x0E, 0x48, 0x80, 0x88, 0x00, 0x07, 0x01, 0xD4, +0x01, 0x20, 0x70, 0xBD, 0xFE, 0xF7, 0x32, 0xFB, +0xFE, 0xF7, 0xD8, 0xF9, 0xFF, 0xF7, 0x7E, 0xFF, +0x00, 0x28, 0x0A, 0xD1, 0x00, 0x25, 0x00, 0x2C, +0x07, 0xD0, 0x64, 0x1E, 0xE4, 0xB2, 0x21, 0x46, +0x05, 0xA0, 0xFD, 0xF7, 0x8B, 0xFD, 0x01, 0x25, +0xEE, 0xE7, 0x28, 0x46, 0x70, 0xBD, 0x00, 0x00, +0xA8, 0x06, 0x00, 0x20, 0x74, 0x06, 0x00, 0x20, +0x53, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x52, 0x65, +0x74, 0x72, 0x79, 0x20, 0x28, 0x25, 0x64, 0x29, +0x0D, 0x0A, 0x00, 0x00, 0xFE, 0xB5, 0x24, 0x49, +0x10, 0x26, 0x12, 0x27, 0x8E, 0x5F, 0xCF, 0x5F, +0x22, 0x49, 0x01, 0x91, 0x22, 0x49, 0x01, 0x20, +0x0A, 0x68, 0x03, 0x04, 0x9A, 0x43, 0x21, 0x4D, +0x00, 0x24, 0x0A, 0x60, 0x20, 0x49, 0x09, 0x68, +0x09, 0x19, 0xC9, 0x7E, 0x41, 0x29, 0x25, 0xD0, +0x01, 0x9A, 0x61, 0x00, 0x52, 0x5A, 0x52, 0x05, +0x52, 0x0D, 0x6A, 0x52, 0x53, 0x05, 0x03, 0xD5, +0x89, 0x23, 0x9B, 0x00, 0xD2, 0x1A, 0x6A, 0x52, +0x18, 0x4A, 0x6B, 0x5A, 0x52, 0x5A, 0xD1, 0x1A, +0x09, 0xB2, 0xB1, 0x42, 0x01, 0xDC, 0xB9, 0x42, +0x0B, 0xDA, 0x00, 0x91, 0x21, 0x46, 0x14, 0xA0, +0xFD, 0xF7, 0x48, 0xFD, 0x18, 0x48, 0x10, 0x22, +0x01, 0x5D, 0x11, 0x43, 0x01, 0x55, 0x00, 0x20, +0x04, 0xE0, 0x15, 0x49, 0xEF, 0x23, 0x0A, 0x5D, +0x1A, 0x40, 0x0A, 0x55, 0x64, 0x1C, 0xE4, 0xB2, +0x30, 0x2C, 0xCF, 0xD3, 0x01, 0x28, 0x05, 0xD0, +0x05, 0x4B, 0x01, 0x22, 0x19, 0x68, 0x12, 0x04, +0x11, 0x43, 0x19, 0x60, 0xFE, 0xBD, 0x00, 0x00, +0x84, 0x06, 0x00, 0x20, 0x00, 0x20, 0x00, 0x50, +0x0C, 0x05, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20, +0x80, 0x02, 0x00, 0x20, 0xEC, 0x06, 0x00, 0x20, +0x55, 0x43, 0x5B, 0x25, 0x64, 0x5D, 0x20, 0x4E, +0x47, 0x21, 0x20, 0x25, 0x64, 0x2D, 0x25, 0x64, +0x3D, 0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, 0x00, +0xD8, 0x04, 0x00, 0x20, 0x70, 0xB5, 0x0E, 0x48, +0x01, 0x25, 0x44, 0x79, 0x0D, 0x48, 0x80, 0x88, +0xC0, 0x06, 0x01, 0xD4, 0x01, 0x20, 0x70, 0xBD, +0xFF, 0xF7, 0x90, 0xFF, 0x00, 0x28, 0x0C, 0xD1, +0x00, 0x25, 0x00, 0x2C, 0x09, 0xD0, 0x64, 0x1E, +0xE4, 0xB2, 0xFE, 0xF7, 0xBF, 0xF9, 0x21, 0x46, +0x05, 0xA0, 0xFD, 0xF7, 0xFB, 0xFC, 0x01, 0x25, +0xEE, 0xE7, 0x28, 0x46, 0x70, 0xBD, 0x00, 0x00, +0xC8, 0x06, 0x00, 0x20, 0x74, 0x06, 0x00, 0x20, +0x55, 0x43, 0x20, 0x4E, 0x47, 0x20, 0x52, 0x65, +0x74, 0x72, 0x79, 0x20, 0x28, 0x25, 0x64, 0x29, +0x0D, 0x0A, 0x00, 0x00, 0x01, 0x21, 0x89, 0x07, +0x00, 0xB5, 0x8A, 0x14, 0x00, 0x28, 0x08, 0x68, +0x04, 0xD0, 0x10, 0x43, 0x08, 0x60, 0xFD, 0xF7, +0x07, 0xFD, 0x00, 0xBD, 0x90, 0x43, 0x08, 0x60, +0x00, 0xBD, 0x00, 0x00, 0xF8, 0xB5, 0x42, 0x4F, +0x01, 0x24, 0x38, 0x7B, 0x41, 0x49, 0x0A, 0x78, +0x41, 0x4B, 0x42, 0x4D, 0x42, 0x4E, 0x90, 0x42, +0x05, 0xD1, 0x28, 0x78, 0x81, 0x28, 0x02, 0xD0, +0x30, 0x78, 0xC0, 0x07, 0x54, 0xD0, 0x00, 0x20, +0x30, 0x70, 0x3A, 0x48, 0x00, 0x78, 0x07, 0x28, +0x0B, 0xD3, 0x18, 0x68, 0x40, 0x05, 0x40, 0x0F, +0x38, 0x73, 0x01, 0x20, 0xFD, 0xF7, 0x02, 0xFF, +0x00, 0x20, 0x30, 0x70, 0x38, 0x7B, 0x02, 0x28, +0x14, 0xD0, 0x38, 0x7B, 0x31, 0x4A, 0x10, 0x70, +0x81, 0x20, 0x28, 0x70, 0x33, 0x48, 0x01, 0x23, +0x01, 0x88, 0x1B, 0x03, 0x19, 0x43, 0x01, 0x80, +0x10, 0x78, 0x02, 0x27, 0x03, 0x00, 0xFD, 0xF7, +0x49, 0xFC, 0x07, 0x23, 0x23, 0x2C, 0x4A, 0x0B, +0x17, 0x0B, 0x4A, 0x00, 0x83, 0x20, 0x28, 0x70, +0x05, 0x20, 0xFF, 0xF7, 0xA7, 0xFC, 0xF8, 0xBD, +0x01, 0x20, 0xFE, 0xF7, 0x17, 0xFE, 0x00, 0x20, +0xFF, 0xF7, 0xA0, 0xFC, 0x00, 0x28, 0x01, 0xD0, +0x01, 0x20, 0x33, 0xE0, 0x00, 0x24, 0x32, 0xE0, +0x01, 0x20, 0xFE, 0xF7, 0x0B, 0xFE, 0x01, 0x20, +0xFF, 0xF7, 0x94, 0xFC, 0x00, 0x28, 0xF5, 0xD0, +0x04, 0x20, 0x27, 0xE0, 0x2F, 0x70, 0x26, 0xE0, +0x00, 0x20, 0xFE, 0xF7, 0xFF, 0xFD, 0x02, 0x20, +0xFF, 0xF7, 0x88, 0xFC, 0x00, 0x28, 0xF5, 0xD1, +0xE8, 0xE7, 0x83, 0x20, 0x28, 0x70, 0x05, 0x20, +0xFF, 0xF7, 0x80, 0xFC, 0x04, 0x46, 0x16, 0xE0, +0x15, 0x4A, 0x10, 0x78, 0x00, 0x28, 0x12, 0xD0, +0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, 0x10, 0x70, +0x0D, 0xD1, 0x18, 0x68, 0x40, 0x05, 0x40, 0x0F, +0x38, 0x73, 0x38, 0x7B, 0x09, 0x78, 0x88, 0x42, +0x05, 0xD0, 0x30, 0x78, 0x01, 0x21, 0x08, 0x43, +0x30, 0x70, 0x81, 0x20, 0x28, 0x70, 0x30, 0x78, +0xC0, 0x07, 0x02, 0xD0, 0x81, 0x20, 0x28, 0x70, +0x00, 0x24, 0x20, 0x46, 0xF8, 0xBD, 0x00, 0x00, +0x24, 0x00, 0x00, 0x20, 0x39, 0x01, 0x00, 0x20, +0x00, 0x11, 0x00, 0x50, 0xE0, 0x08, 0x00, 0x20, +0x34, 0x00, 0x00, 0x20, 0x4A, 0x01, 0x00, 0x20, +0x38, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x46, +0x01, 0x24, 0xFE, 0xF7, 0xE9, 0xFA, 0x00, 0x2D, +0x09, 0xD0, 0xF0, 0x20, 0xFD, 0xF7, 0xAC, 0xFB, +0xFE, 0xF7, 0x9E, 0xFB, 0xFE, 0xF7, 0x4E, 0xFB, +0x00, 0x20, 0xFE, 0xF7, 0xB3, 0xFD, 0xFE, 0xF7, +0xE3, 0xFD, 0xFF, 0xF7, 0x75, 0xFB, 0x20, 0x46, +0x70, 0xBD, 0x01, 0x20, 0x80, 0x07, 0x40, 0x69, +0x40, 0x05, 0x01, 0xD5, 0x01, 0x20, 0x70, 0x47, +0x00, 0x20, 0x70, 0x47, 0x70, 0xB5, 0x18, 0x4D, +0x1E, 0x20, 0x28, 0x70, 0x68, 0x78, 0x29, 0x46, +0x00, 0x24, 0x14, 0x31, 0x02, 0x28, 0x0C, 0xD8, +0x00, 0x28, 0x05, 0xD1, 0xA8, 0x78, 0x00, 0x28, +0x02, 0xD0, 0x08, 0x46, 0x1C, 0x30, 0x04, 0x70, +0x48, 0x88, 0x00, 0x28, 0x01, 0xD1, 0x03, 0x20, +0x68, 0x71, 0xAC, 0x70, 0x2C, 0x81, 0xEC, 0x80, +0xAC, 0x81, 0x6C, 0x81, 0x4C, 0x80, 0x8C, 0x80, +0x0C, 0x72, 0x0A, 0x48, 0x44, 0x70, 0x84, 0x70, +0x04, 0x70, 0x07, 0x48, 0xA0, 0x21, 0x30, 0x30, +0xFD, 0xF7, 0x3B, 0xFB, 0xEC, 0x70, 0x78, 0x20, +0xE8, 0x81, 0x01, 0x20, 0x28, 0x71, 0x2C, 0x61, +0x00, 0xF0, 0xC2, 0xF8, 0x70, 0xBD, 0x00, 0x00, +0xCC, 0x08, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, +0x06, 0x4A, 0x07, 0x4B, 0x00, 0x21, 0x00, 0x20, +0x40, 0x1C, 0x90, 0x42, 0xFC, 0xDB, 0x49, 0x1C, +0x99, 0x42, 0xF8, 0xDB, 0x03, 0x48, 0x01, 0x21, +0x01, 0x60, 0x70, 0x47, 0x10, 0x27, 0x00, 0x00, +0xB8, 0x0B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, +0x70, 0xB5, 0x7B, 0x24, 0x21, 0x48, 0x24, 0x02, +0x04, 0x60, 0xF7, 0x20, 0xC0, 0x01, 0x20, 0x4A, +0xC1, 0x7C, 0x11, 0x70, 0x1F, 0x4A, 0x01, 0x7D, +0x11, 0x70, 0x1F, 0x4A, 0x41, 0x7D, 0x11, 0x70, +0x1E, 0x49, 0x80, 0x7D, 0x08, 0x70, 0x60, 0x7C, +0x21, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, +0x1B, 0x49, 0x05, 0x46, 0x08, 0x80, 0xE0, 0x7C, +0xA1, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, +0x18, 0x49, 0x08, 0x80, 0x20, 0x7E, 0x61, 0x7E, +0x48, 0x43, 0x17, 0x49, 0x08, 0x80, 0x17, 0x48, +0xFD, 0xF7, 0xFB, 0xFA, 0x10, 0x21, 0xC8, 0x41, +0x15, 0x49, 0x08, 0x60, 0x04, 0x22, 0x21, 0x1D, +0x14, 0x48, 0xFD, 0xF7, 0xCD, 0xFA, 0x11, 0x49, +0xFF, 0x22, 0x1D, 0x32, 0x89, 0x1F, 0x12, 0x48, +0xFD, 0xF7, 0xC6, 0xFA, 0x60, 0x7D, 0x22, 0x7D, +0x01, 0x02, 0x11, 0x43, 0x28, 0x46, 0x0A, 0x22, +0x50, 0x43, 0xFD, 0xF7, 0xA9, 0xFA, 0x0D, 0x49, +0x08, 0x80, 0x70, 0xBD, 0x80, 0x02, 0x00, 0x20, +0x18, 0x00, 0x00, 0x20, 0x19, 0x00, 0x00, 0x20, +0x1A, 0x00, 0x00, 0x20, 0x1B, 0x00, 0x00, 0x20, +0x20, 0x00, 0x00, 0x20, 0x22, 0x00, 0x00, 0x20, +0x1C, 0x00, 0x00, 0x20, 0x10, 0x7B, 0x00, 0x00, +0xD8, 0x06, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20, +0x10, 0x05, 0x00, 0x20, 0x1E, 0x00, 0x00, 0x20, +0x00, 0x20, 0x0E, 0x49, 0x08, 0x70, 0x17, 0xE0, +0x0C, 0x48, 0x00, 0x78, 0x00, 0x28, 0x02, 0xD0, +0x07, 0x28, 0x0F, 0xD1, 0x0B, 0xE0, 0x00, 0xF0, +0x13, 0xF8, 0x01, 0x28, 0x03, 0xD1, 0x07, 0x20, +0x06, 0x49, 0x08, 0x70, 0x02, 0xE0, 0x03, 0x20, +0x04, 0x49, 0x08, 0x70, 0x03, 0xE0, 0xFD, 0xF7, +0x4F, 0xFB, 0x00, 0xE0, 0x00, 0xBF, 0x00, 0xBF, +0xE6, 0xE7, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x20, +0x10, 0xB5, 0x00, 0x24, 0xFD, 0xF7, 0x12, 0xFD, +0xFD, 0xF7, 0xAE, 0xFD, 0x01, 0x20, 0xFF, 0xF7, +0x0D, 0xFF, 0x04, 0x46, 0x00, 0x2C, 0x00, 0xD1, +0x10, 0xBD, 0x03, 0x49, 0x03, 0x48, 0x81, 0x70, +0x09, 0x0A, 0xC1, 0x70, 0x01, 0x20, 0xF7, 0xE7, +0xAA, 0x55, 0x00, 0x00, 0x88, 0x02, 0x00, 0x20, +0x05, 0x48, 0x01, 0x78, 0x82, 0x29, 0x05, 0xD1, +0xC0, 0x79, 0x01, 0x28, 0x02, 0xD1, 0x03, 0x49, +0xFF, 0x20, 0x08, 0x70, 0x70, 0x47, 0x00, 0x00, +0xE0, 0x08, 0x00, 0x20, 0x40, 0x01, 0x00, 0x20, +0x06, 0x48, 0x00, 0x21, 0x81, 0x80, 0xC1, 0x80, +0x41, 0x70, 0x81, 0x70, 0x01, 0x70, 0x04, 0x49, +0x01, 0x81, 0x41, 0x81, 0x03, 0x49, 0x03, 0x20, +0x08, 0x70, 0x70, 0x47, 0x0C, 0x00, 0x00, 0x20, +0xFF, 0x7F, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x20, +0x08, 0x49, 0x78, 0x20, 0x08, 0x70, 0x08, 0x49, +0x00, 0x20, 0x08, 0x70, 0x08, 0x48, 0x07, 0x49, +0x01, 0x81, 0x41, 0x81, 0x07, 0x49, 0x03, 0x20, +0x08, 0x70, 0x07, 0x49, 0x01, 0x20, 0x08, 0x70, +0x70, 0x47, 0x00, 0x00, 0x7F, 0x02, 0x00, 0x20, +0xCF, 0x08, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, +0x0C, 0x00, 0x00, 0x20, 0xD3, 0x00, 0x00, 0x20, +0x7C, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, +0x10, 0x03, 0x42, 0x88, 0x0C, 0x00, 0x00, 0x40, +0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x50, +0x1F, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x50, +0x64, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x50, +0x77, 0x00, 0x01, 0x48, 0x44, 0x09, 0x00, 0x50, +0x39, 0x5A, 0x5B, 0x00, 0x10, 0x06, 0x00, 0x50, +0x00, 0x00, 0x06, 0x07, 0x00, 0x06, 0x00, 0x50, +0x00, 0x00, 0x00, 0x78, 0x08, 0x06, 0x00, 0x50, +0x0C, 0x30, 0x00, 0x00, 0x28, 0x06, 0x00, 0x50, +0x06, 0x00, 0x00, 0x00, 0x2C, 0x06, 0x00, 0x50, +0x0A, 0x66, 0x00, 0x00, 0x30, 0x06, 0x00, 0x50, +0xCC, 0x02, 0x00, 0x20, 0x34, 0x06, 0x00, 0x50, +0x00, 0x20, 0x00, 0x00, 0x44, 0x00, 0x00, 0x50, +0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x50, +0x50, 0x71, 0x00, 0x00, 0x20, 0x00, 0x00, 0x50, +0x24, 0x29, 0x00, 0x00, 0x14, 0x00, 0x00, 0x40, +0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, +0x14, 0x33, 0x43, 0xC8, 0x0C, 0x00, 0x00, 0x40, +0x29, 0x0A, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, +0x10, 0x32, 0x00, 0x00, 0x1C, 0x0E, 0x00, 0x50, +0x03, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x50, +0x14, 0x07, 0x00, 0x00, 0x28, 0x00, 0x00, 0x40, +0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x50, +0x00, 0x00, 0x00, 0x00, 0x58, 0x10, 0x00, 0x50, +0x01, 0x00, 0x00, 0x00, 0x04, 0x11, 0x00, 0x50, +0x78, 0x11, 0x00, 0x00, 0x0C, 0x11, 0x00, 0x50, +0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x00, 0x50, +0x78, 0x01, 0x00, 0x00, 0x14, 0x11, 0x00, 0x50, +0xC8, 0x03, 0x60, 0x00, 0x18, 0x10, 0x00, 0x50, +0x00, 0x00, 0x00, 0x00, 0x1C, 0x10, 0x00, 0x50, +0x00, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x50, +0x31, 0x00, 0x00, 0x00, 0x3C, 0x10, 0x00, 0x50, +0x00, 0x00, 0x10, 0x00, 0xB4, 0x10, 0x00, 0x50, +0x00, 0x26, 0x31, 0x08, 0xC0, 0x10, 0x00, 0x50, +0x33, 0x03, 0x33, 0x03, 0xC4, 0x10, 0x00, 0x50, +0x33, 0x03, 0x33, 0x03, 0xC8, 0x10, 0x00, 0x50, +0x0C, 0x0A, 0x00, 0x00, 0xCC, 0x10, 0x00, 0x50, +0x1A, 0x00, 0x00, 0x00, 0xD0, 0x10, 0x00, 0x50, +0x03, 0x19, 0x19, 0x00, 0xF0, 0x11, 0x00, 0x50, +0x12, 0x00, 0x00, 0x00, 0xEC, 0x11, 0x00, 0x50, +0x5C, 0x00, 0x00, 0x00, 0xF4, 0x11, 0x00, 0x50, +0x01, 0x00, 0x01, 0x00, 0x2C, 0x10, 0x00, 0x50, +0x10, 0x00, 0x90, 0x00, 0x30, 0x10, 0x00, 0x50, +0x20, 0x0C, 0x90, 0x00, 0x34, 0x10, 0x00, 0x50, +0x30, 0x0C, 0x30, 0x0C, 0x38, 0x10, 0x00, 0x50, +0xFF, 0x0F, 0x00, 0x00, 0x7C, 0x10, 0x00, 0x50, +0x88, 0x88, 0xFE, 0x88, 0x80, 0x10, 0x00, 0x50, +0x88, 0xFF, 0x00, 0x00, 0x84, 0x10, 0x00, 0x50, +0x55, 0x55, 0x55, 0x55, 0x88, 0x10, 0x00, 0x50, +0x55, 0x55, 0x55, 0x55, 0x8C, 0x10, 0x00, 0x50, +0x55, 0x55, 0x55, 0x55, 0xE8, 0x10, 0x00, 0x50, +0x3F, 0x16, 0x3F, 0x15, 0x04, 0x00, 0x00, 0x40, +0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x60, 0x38, 0x00, 0x00, +0x04, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, +0x5C, 0x0A, 0x00, 0x00, 0x68, 0x38, 0x00, 0x00, +0x0C, 0x00, 0x00, 0x20, 0x94, 0x0F, 0x00, 0x00, +0x6A, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x36, 0x03, 0x35, 0x03, 0x2C, 0x03, 0x9C, 0x03, +0xE9, 0x02, 0x28, 0x03, 0x29, 0x03, 0x1C, 0x03, +0x9D, 0x03, 0x2F, 0x03, 0x4A, 0x03, 0x4D, 0x03, +0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, +0x3D, 0x03, 0x3C, 0x03, 0x2C, 0x03, 0x9C, 0x03, +0xD3, 0x02, 0x28, 0x03, 0x29, 0x03, 0xD6, 0x02, +0x9E, 0x03, 0x30, 0x03, 0x37, 0x03, 0x3A, 0x03, +0xFF, 0x03, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x78, 0x7F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, @@ -3973,1699 +6549,6 @@ const unsigned char u8_rad_fw_30[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0x6E, 0x53, 0x58, 0x91, -}; -const unsigned char u8_rad_para_30[] = { -0xA1, 0x00, 0x03, 0xF3, 0x02, 0x02, 0x00, 0x01, -0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, -0x40, 0x01, 0x68, 0x01, 0x44, 0x01, 0x44, 0x01, -0x05, 0x05, 0x00, 0x00, 0x01, 0x05, 0x41, 0x06, -0x0A, 0x0B, 0x41, 0x0F, 0x10, 0x14, 0x41, 0x15, -0x02, 0x07, 0x41, 0x16, 0x41, 0x41, 0x41, 0x41, -0x41, 0x41, 0x41, 0x04, 0x03, 0x09, 0x41, 0x08, -0x0E, 0x0D, 0x41, 0x13, 0x12, 0x18, 0x41, 0x17, -0x0C, 0x11, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, -0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, -0x0C, 0x3E, 0x0C, 0x3E, 0x08, 0x08, 0x02, 0x02, -0x14, 0x03, 0x08, 0x08, 0x01, 0x01, 0xC8, 0x00, -0x1A, 0x01, 0x08, 0x14, 0x82, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x01, -0xA0, 0x00, 0x64, 0x00, 0x4A, 0x00, 0x0A, 0x78, -0x0F, 0x08, 0x1A, 0x4A, 0x4A, 0x3C, 0x3C, 0x00, -0x00, 0x00, 0x6A, 0x00, 0x38, 0x00, 0x1C, 0x00, -0x06, 0x32, 0x69, 0xE3, 0x0D, 0x00, 0x00, 0x72, -0x45, 0x00, 0x00, 0x06, 0x00, 0xA0, 0x00, 0x78, -0x00, 0x64, 0xDB, 0x13, 0xB5, 0x0C, 0x2D, 0x03, -0xCB, 0x00, 0x14, 0x0A, 0xB1, 0x01, 0xF1, 0x05, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x55, 0xAA, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xD6, 0x9D, 0x10, 0x3D, 0xCB, 0x25, 0xBF, 0xD8, -}; -const unsigned char u8_rad_testfw_30[] = { -0x60, 0x0E, 0x00, 0x20, 0xB1, 0x08, 0x00, 0x00, -0xB9, 0x08, 0x00, 0x00, 0x95, 0x08, 0x00, 0x00, -0xBD, 0x08, 0x00, 0x00, 0xBF, 0x08, 0x00, 0x00, -0xC1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xC3, 0x08, 0x00, 0x00, -0xC5, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xC7, 0x08, 0x00, 0x00, 0x81, 0x0F, 0x00, 0x00, -0xE1, 0x0C, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00, -0xCB, 0x08, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x00, -0xA1, 0x0D, 0x00, 0x00, 0x41, 0x0A, 0x00, 0x00, -0xCB, 0x08, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00, -0xCB, 0x08, 0x00, 0x00, 0x41, 0x0B, 0x00, 0x00, -0xE1, 0x0A, 0x00, 0x00, 0xA1, 0x0A, 0x00, 0x00, -0xC1, 0x0A, 0x00, 0x00, 0x81, 0x0A, 0x00, 0x00, -0xCB, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, -0x03, 0x48, 0x85, 0x46, 0x00, 0xF0, 0x80, 0xF8, -0x00, 0x48, 0x00, 0x47, 0xD1, 0x35, 0x00, 0x00, -0x60, 0x0E, 0x00, 0x20, 0x04, 0x20, 0x71, 0x46, -0x08, 0x42, 0x02, 0xD0, 0xEF, 0xF3, 0x09, 0x80, -0x01, 0xE0, 0xEF, 0xF3, 0x08, 0x80, 0x71, 0x46, -0x00, 0x4A, 0x10, 0x47, 0xE9, 0x34, 0x00, 0x00, -0x06, 0x48, 0x80, 0x47, 0x06, 0x48, 0x00, 0x47, -0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, -0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, -0xFE, 0xE7, 0xFE, 0xE7, 0x15, 0x0A, 0x00, 0x00, -0x81, 0x08, 0x00, 0x00, 0x30, 0xB5, 0x0B, 0x46, -0x01, 0x46, 0x00, 0x20, 0x20, 0x22, 0x01, 0x24, -0x09, 0xE0, 0x0D, 0x46, 0xD5, 0x40, 0x9D, 0x42, -0x05, 0xD3, 0x1D, 0x46, 0x95, 0x40, 0x49, 0x1B, -0x25, 0x46, 0x95, 0x40, 0x40, 0x19, 0x15, 0x46, -0x52, 0x1E, 0x00, 0x2D, 0xF1, 0xDC, 0x30, 0xBD, -0x70, 0xB5, 0x00, 0x24, 0x25, 0x46, 0x00, 0x28, -0x01, 0xDA, 0x01, 0x24, 0x40, 0x42, 0x00, 0x29, -0x01, 0xDA, 0x01, 0x25, 0x49, 0x42, 0xFF, 0xF7, -0xDD, 0xFF, 0xAC, 0x42, 0x00, 0xD0, 0x40, 0x42, -0x00, 0x2C, 0x00, 0xD0, 0x49, 0x42, 0x70, 0xBD, -0x03, 0x46, 0x0B, 0x43, 0x9B, 0x07, 0x03, 0xD0, -0x09, 0xE0, 0x08, 0xC9, 0x12, 0x1F, 0x08, 0xC0, -0x04, 0x2A, 0xFA, 0xD2, 0x03, 0xE0, 0x0B, 0x78, -0x49, 0x1C, 0x03, 0x70, 0x40, 0x1C, 0x52, 0x1E, -0xF9, 0xD2, 0x70, 0x47, 0xD2, 0xB2, 0x01, 0xE0, -0x02, 0x70, 0x40, 0x1C, 0x49, 0x1E, 0xFB, 0xD2, -0x70, 0x47, 0x00, 0x22, 0xF6, 0xE7, 0x10, 0xB5, -0x04, 0x46, 0x08, 0x46, 0x11, 0x46, 0x02, 0x46, -0x20, 0x46, 0xFF, 0xF7, 0xEF, 0xFF, 0x20, 0x46, -0x10, 0xBD, 0x00, 0x1D, 0x03, 0x21, 0x40, 0x1E, -0x03, 0x78, 0x12, 0x02, 0x1A, 0x43, 0x49, 0x1E, -0xF9, 0xD5, 0x10, 0x46, 0x70, 0x47, 0x00, 0x00, -0x06, 0x4C, 0x01, 0x25, 0x06, 0x4E, 0x05, 0xE0, -0x20, 0x46, 0xE3, 0x68, 0x07, 0xC8, 0x2B, 0x43, -0x98, 0x47, 0x10, 0x34, 0xB4, 0x42, 0xF7, 0xD3, -0xFF, 0xF7, 0x72, 0xFF, 0x1C, 0x3B, 0x00, 0x00, -0x3C, 0x3B, 0x00, 0x00, 0xC1, 0x06, 0xC9, 0x0E, -0x01, 0x20, 0x88, 0x40, 0x01, 0x49, 0x08, 0x60, -0x70, 0x47, 0x00, 0x00, 0x00, 0xE1, 0x00, 0xE0, -0x0B, 0x49, 0x10, 0xB5, 0x88, 0x42, 0x01, 0xD9, -0x01, 0x20, 0x10, 0xBD, 0x01, 0x02, 0x09, 0x0A, -0x08, 0x48, 0x49, 0x1E, 0x41, 0x61, 0x08, 0x49, -0x07, 0x23, 0xCA, 0x69, 0x12, 0x02, 0x12, 0x0A, -0x04, 0x04, 0x22, 0x43, 0xCA, 0x61, 0x00, 0x21, -0x81, 0x61, 0x03, 0x61, 0x08, 0x46, 0x10, 0xBD, -0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xE0, 0x00, 0xE0, -0x04, 0xED, 0x00, 0xE0, 0x70, 0x47, 0x00, 0x00, -0x03, 0x49, 0x02, 0x20, 0x08, 0x60, 0x02, 0x49, -0x80, 0x39, 0x08, 0x60, 0x70, 0x47, 0x00, 0x00, -0x80, 0xE1, 0x00, 0xE0, 0x62, 0xB6, 0x02, 0x48, -0x00, 0x21, 0x01, 0x60, 0x70, 0x47, 0x00, 0x00, -0x0C, 0x00, 0x00, 0x40, 0x30, 0xB4, 0x74, 0x46, -0x64, 0x1E, 0x25, 0x78, 0x64, 0x1C, 0xAB, 0x42, -0x00, 0xD2, 0x1D, 0x46, 0x63, 0x5D, 0x5B, 0x00, -0xE3, 0x18, 0x30, 0xBC, 0x18, 0x47, 0x00, 0x00, -0x05, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, -0x01, 0x20, 0x80, 0x07, 0x01, 0x6A, 0x03, 0x22, -0xD2, 0x03, 0x11, 0x43, 0x01, 0x62, 0x70, 0x47, -0x34, 0x00, 0x00, 0x20, 0x02, 0xE0, 0x08, 0xC8, -0x12, 0x1F, 0x08, 0xC1, 0x00, 0x2A, 0xFA, 0xD1, -0x70, 0x47, 0x00, 0x20, 0x01, 0xE0, 0x01, 0xC1, -0x12, 0x1F, 0x00, 0x2A, 0xFB, 0xD1, 0x70, 0x47, -0x10, 0xB5, 0x00, 0xF0, 0x55, 0xF8, 0x10, 0xBD, -0x01, 0x20, 0x80, 0x07, 0x41, 0x6A, 0x82, 0x13, -0x11, 0x43, 0x41, 0x62, 0x41, 0x6A, 0xC2, 0x13, -0x11, 0x43, 0x41, 0x62, 0x70, 0x47, 0x10, 0xB5, -0x02, 0xF0, 0xDC, 0xF8, 0x10, 0xBD, 0x00, 0x00, -0x03, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, -0x02, 0x49, 0x01, 0x20, 0xC8, 0x60, 0x70, 0x47, -0x5C, 0x01, 0x00, 0x20, 0x00, 0x03, 0x00, 0x50, -0x10, 0xB5, 0x00, 0xF0, 0xE7, 0xFA, 0x10, 0xBD, -0x70, 0x47, 0x00, 0x00, 0x04, 0x49, 0x06, 0x22, -0x00, 0x28, 0x08, 0x68, 0x01, 0xD0, 0x10, 0x43, -0x00, 0xE0, 0x90, 0x43, 0x08, 0x60, 0x70, 0x47, -0x00, 0x09, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, -0x70, 0x47, 0x00, 0x00, 0x03, 0x48, 0x02, 0x49, -0x41, 0x60, 0x03, 0x49, 0x81, 0x60, 0x70, 0x47, -0x1F, 0x1F, 0x5F, 0x1F, 0x00, 0x10, 0x00, 0x50, -0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, 0x00, 0x00, -0x00, 0xB5, 0x07, 0x48, 0x01, 0x69, 0x02, 0x29, -0x06, 0xD1, 0x01, 0x61, 0x05, 0x49, 0x8A, 0x78, -0x00, 0x2A, 0x02, 0xD0, 0x02, 0xF0, 0x9A, 0xFA, -0x00, 0xBD, 0x01, 0x20, 0xC8, 0x70, 0x00, 0xBD, -0x00, 0x02, 0x00, 0x50, 0xE0, 0x05, 0x00, 0x20, -0x01, 0x21, 0x89, 0x07, 0x0A, 0x15, 0x00, 0x28, -0x48, 0x69, 0x02, 0xD0, 0x10, 0x43, 0x48, 0x61, -0x70, 0x47, 0x90, 0x43, 0xFB, 0xE7, 0x00, 0x00, -0x30, 0xB5, 0x1E, 0x4B, 0x58, 0x68, 0x99, 0x68, -0x00, 0x28, 0x01, 0xDA, 0xDA, 0x04, 0x5A, 0x60, -0x1B, 0x4A, 0x15, 0x68, 0x01, 0x24, 0x05, 0x40, -0x00, 0x2D, 0x02, 0xD1, 0x50, 0x68, 0x08, 0x42, -0x0F, 0xD0, 0x18, 0x48, 0x04, 0x70, 0xFF, 0xF7, -0xBD, 0xFF, 0x14, 0x72, 0x16, 0x48, 0x00, 0x78, -0x00, 0x28, 0x06, 0xD0, 0x18, 0x68, 0x01, 0x21, -0x00, 0x09, 0x00, 0x01, 0x89, 0x02, 0x08, 0x43, -0x18, 0x60, 0x58, 0x68, 0x80, 0x02, 0x02, 0xD5, -0x01, 0x20, 0x40, 0x05, 0x58, 0x60, 0x58, 0x68, -0x00, 0x04, 0x03, 0xD5, 0x01, 0x20, 0xC0, 0x03, -0x58, 0x60, 0x94, 0x72, 0x58, 0x68, 0x80, 0x00, -0x03, 0xD5, 0x01, 0x20, 0x40, 0x07, 0x58, 0x60, -0xD4, 0x72, 0x58, 0x68, 0x40, 0x00, 0x03, 0xD5, -0x01, 0x20, 0x80, 0x07, 0x58, 0x60, 0x94, 0x72, -0x30, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, -0x20, 0x00, 0x00, 0x20, 0x39, 0x00, 0x00, 0x20, -0x31, 0x00, 0x00, 0x20, 0x01, 0x28, 0x05, 0xD0, -0x02, 0x28, 0x05, 0xD0, 0x04, 0x28, 0x06, 0xD0, -0x00, 0x20, 0x70, 0x47, 0x03, 0x48, 0x70, 0x47, -0x02, 0x48, 0xC0, 0x30, 0x70, 0x47, 0x02, 0x48, -0x70, 0x47, 0x00, 0x00, 0x78, 0x7C, 0x00, 0x00, -0xF8, 0x7D, 0x00, 0x00, 0x10, 0xB5, 0x00, 0xF0, -0x67, 0xF8, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00, -0x70, 0x47, 0x00, 0x00, 0xF8, 0xB5, 0x06, 0x46, -0x0D, 0x46, 0x30, 0x23, 0xFF, 0xF7, 0xDE, 0xFF, -0x04, 0x00, 0x28, 0xD0, 0x19, 0x4F, 0x00, 0x2A, -0x21, 0xD0, 0x00, 0x20, 0xFF, 0xF7, 0x52, 0xFF, -0x02, 0xE0, 0x98, 0x00, 0x29, 0x58, 0x21, 0x50, -0x18, 0x46, 0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, -0xF7, 0xD1, 0x38, 0x68, 0x01, 0x88, 0x31, 0x43, -0x01, 0x80, 0x11, 0x49, 0x09, 0x68, 0xCA, 0x79, -0x8B, 0x79, 0x11, 0x02, 0x19, 0x43, 0x41, 0x80, -0xC1, 0x21, 0x89, 0x00, 0x0D, 0x48, 0x00, 0xF0, -0x1D, 0xF8, 0x0D, 0x49, 0xC8, 0x63, 0x01, 0x20, -0xFF, 0xF7, 0x34, 0xFF, 0x0C, 0xE0, 0x38, 0x68, -0x00, 0x88, 0x30, 0x40, 0x03, 0xD1, 0xF8, 0xBD, -0x98, 0x00, 0x21, 0x58, 0x29, 0x50, 0x18, 0x46, -0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, 0xF7, 0xD1, -0x01, 0x20, 0xF8, 0xBD, 0x08, 0x00, 0x00, 0x20, -0x98, 0x01, 0x00, 0x20, 0x78, 0x7C, 0x00, 0x00, -0x40, 0x7F, 0x00, 0x00, 0x70, 0xB5, 0x41, 0x18, -0x49, 0x1E, 0x64, 0x24, 0x09, 0x04, 0x0B, 0x4D, -0x01, 0x43, 0x69, 0x63, 0xE8, 0x68, 0x81, 0x21, -0x09, 0x06, 0x08, 0x43, 0xE8, 0x60, 0x02, 0xE0, -0x01, 0x20, 0xFF, 0xF7, 0xA3, 0xFF, 0xE8, 0x68, -0xC0, 0x01, 0x04, 0xD5, 0x20, 0x46, 0x64, 0x1E, -0xA4, 0xB2, 0x00, 0x28, 0xF4, 0xD1, 0xA8, 0x6B, -0x70, 0xBD, 0x00, 0x00, 0x40, 0x09, 0x00, 0x50, -0x03, 0x49, 0x0A, 0x68, 0x10, 0x18, 0x0A, 0x68, -0x90, 0x42, 0xFC, 0xD1, 0x70, 0x47, 0x00, 0x00, -0x5C, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, -0x70, 0xB5, 0x01, 0x25, 0xAD, 0x07, 0xE8, 0x68, -0x40, 0x08, 0x40, 0x00, 0xE8, 0x60, 0x19, 0x48, -0x41, 0x68, 0x01, 0x26, 0x49, 0x07, 0x00, 0x29, -0x25, 0xDA, 0x42, 0x68, 0x04, 0x21, 0x0A, 0x43, -0x42, 0x60, 0x15, 0x48, 0x00, 0x68, 0x15, 0x4C, -0x40, 0x05, 0x40, 0x0F, 0x20, 0x73, 0x20, 0x7B, -0x06, 0x28, 0x00, 0xD1, 0x21, 0x73, 0x20, 0x7B, -0x02, 0x28, 0x18, 0xD0, 0x20, 0x7B, 0x06, 0x28, -0x05, 0xD8, 0x20, 0x7B, 0x04, 0x28, 0x02, 0xD3, -0x01, 0x20, 0x01, 0xF0, 0x39, 0xF9, 0x20, 0x7B, -0x0B, 0x49, 0x09, 0x78, 0x88, 0x42, 0x06, 0xD0, -0x0A, 0x48, 0x01, 0x78, 0x31, 0x43, 0x01, 0x70, -0x09, 0x49, 0x81, 0x20, 0x08, 0x70, 0xE8, 0x68, -0x30, 0x43, 0xE8, 0x60, 0x70, 0xBD, 0x00, 0x20, -0xEB, 0xE7, 0x00, 0x00, 0x40, 0x00, 0x00, 0x50, -0x00, 0x11, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20, -0x39, 0x01, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20, -0x20, 0x08, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x46, -0x06, 0x4C, 0x01, 0xF0, 0x83, 0xFC, 0x00, 0x21, -0x8A, 0x00, 0x49, 0x1C, 0xA0, 0x58, 0xC9, 0xB2, -0xA8, 0x50, 0x18, 0x29, 0xF8, 0xD3, 0x01, 0x20, -0x70, 0xBD, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, -0x01, 0x20, 0x80, 0x07, 0x41, 0x68, 0x82, 0x14, -0x11, 0x43, 0x41, 0x60, 0x70, 0x47, 0x00, 0x00, -0x10, 0xB5, 0x14, 0x48, 0x80, 0x69, 0x40, 0x04, -0x23, 0xD5, 0x13, 0x4C, 0xE0, 0x68, 0xA2, 0x68, -0x02, 0x40, 0xE0, 0x68, 0xA1, 0x68, 0x88, 0x43, -0xE0, 0x60, 0x10, 0x07, 0x03, 0xD5, 0x08, 0x20, -0xE0, 0x60, 0x02, 0xF0, 0x85, 0xFD, 0x50, 0x07, -0x0E, 0xD5, 0x04, 0x20, 0xE0, 0x60, 0x02, 0xF0, -0x7F, 0xFD, 0x00, 0xF0, 0xCB, 0xF8, 0x00, 0x28, -0x06, 0xD1, 0x08, 0x48, 0x00, 0x78, 0x00, 0x28, -0x02, 0xD0, 0x01, 0x20, 0xFF, 0xF7, 0x48, 0xFE, -0xFF, 0x20, 0xF3, 0x30, 0xE0, 0x60, 0x01, 0x20, -0xE0, 0x60, 0x10, 0xBD, 0x00, 0x09, 0x00, 0x50, -0x00, 0x05, 0x00, 0x50, 0x90, 0x02, 0x00, 0x20, -0x70, 0xB5, 0x0C, 0x49, 0x00, 0x20, 0x0C, 0x4B, -0x89, 0x25, 0x0C, 0x68, 0xAD, 0x00, 0x21, 0x18, -0xC9, 0x7E, 0x41, 0x29, 0x08, 0xD0, 0x42, 0x00, -0x99, 0x5A, 0x49, 0x05, 0x49, 0x0D, 0x99, 0x52, -0x4E, 0x05, 0x01, 0xD5, 0x49, 0x1B, 0x99, 0x52, -0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, 0xEE, 0xD3, -0x01, 0x20, 0x70, 0xBD, 0x98, 0x01, 0x00, 0x20, -0xAC, 0x03, 0x00, 0x20, 0x01, 0x21, 0x89, 0x07, -0x00, 0xB5, 0x8A, 0x14, 0x00, 0x28, 0x08, 0x68, -0x04, 0xD0, 0x10, 0x43, 0x08, 0x60, 0xFF, 0xF7, -0x9F, 0xFF, 0x00, 0xBD, 0x90, 0x43, 0x08, 0x60, -0x00, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xF8, 0xB5, 0x33, 0x48, 0x80, 0x69, 0x40, 0x04, -0x61, 0xD5, 0x32, 0x4D, 0xA8, 0x6A, 0xE9, 0x68, -0xAC, 0x68, 0x0C, 0x40, 0xE9, 0x68, 0xAA, 0x68, -0x91, 0x43, 0xE9, 0x60, 0x2E, 0x4E, 0xC0, 0x07, -0x15, 0xD0, 0x68, 0x69, 0x89, 0x27, 0xC0, 0xB2, -0xEF, 0x60, 0xAA, 0x6A, 0x01, 0x21, 0x0A, 0x43, -0xAA, 0x62, 0x0B, 0x28, 0x03, 0xD2, 0x31, 0x70, -0x00, 0xF0, 0x92, 0xFD, 0x03, 0xE0, 0x00, 0x21, -0x31, 0x70, 0x00, 0xF0, 0x69, 0xFD, 0xBC, 0x43, -0x01, 0x20, 0xFF, 0xF7, 0xE5, 0xFD, 0x23, 0x48, -0xA1, 0x04, 0x04, 0xD5, 0x29, 0x6A, 0x81, 0x43, -0x29, 0x62, 0x41, 0x14, 0xE9, 0x60, 0xE1, 0x04, -0x05, 0xD5, 0x29, 0x6A, 0x81, 0x43, 0x29, 0x62, -0x01, 0x20, 0x00, 0x03, 0xE8, 0x60, 0xA0, 0x05, -0x02, 0xD5, 0x01, 0x20, 0x40, 0x02, 0xE8, 0x60, -0x20, 0x07, 0x09, 0xD5, 0x30, 0x78, 0x01, 0x28, -0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, 0x48, 0xFD, -0x64, 0x08, 0x64, 0x00, 0x09, 0x20, 0xE8, 0x60, -0x60, 0x07, 0x17, 0xD5, 0x30, 0x78, 0x01, 0x28, -0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, 0x3C, 0xFD, -0x05, 0x20, 0xE8, 0x60, 0xA8, 0x69, 0x0E, 0x49, -0x88, 0x42, 0x08, 0xD0, 0xA9, 0x69, 0x0C, 0x48, -0x40, 0x1C, 0x81, 0x42, 0x03, 0xD0, 0xA9, 0x69, -0xC0, 0x1C, 0x81, 0x42, 0x02, 0xD1, 0x01, 0x20, -0xFF, 0xF7, 0xAA, 0xFD, 0xFF, 0x20, 0xF3, 0x30, -0xE8, 0x60, 0x01, 0x20, 0xE8, 0x60, 0xF8, 0xBD, -0x00, 0x09, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50, -0xCC, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, -0x88, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x02, 0x46, -0x07, 0x49, 0x60, 0x32, 0x11, 0x80, 0x00, 0x21, -0x51, 0x80, 0x4C, 0x00, 0x53, 0x88, 0x04, 0x5B, -0x49, 0x1C, 0x1B, 0x19, 0xC9, 0xB2, 0x53, 0x80, -0x31, 0x29, 0xF6, 0xD3, 0x10, 0xBD, 0x00, 0x00, -0xAA, 0x55, 0x00, 0x00, 0x01, 0x20, 0x80, 0x07, -0x40, 0x69, 0x40, 0x05, 0x01, 0xD5, 0x01, 0x20, -0x70, 0x47, 0x00, 0x20, 0x70, 0x47, 0x00, 0x00, -0x02, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, -0x70, 0x47, 0x00, 0x00, 0x1C, 0x08, 0x00, 0x20, -0x01, 0x20, 0x01, 0x49, 0x08, 0x70, 0x70, 0x47, -0xD3, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, -0x05, 0x20, 0x00, 0x07, 0x82, 0x69, 0x0C, 0x49, -0x00, 0x2A, 0x07, 0xDA, 0x82, 0x69, 0xC3, 0x00, -0x92, 0x00, 0x92, 0x08, 0xD2, 0x18, 0x82, 0x61, -0x01, 0x22, 0x4A, 0x72, 0x82, 0x69, 0x52, 0x00, -0x08, 0xD5, 0x82, 0x69, 0x01, 0x23, 0x92, 0x00, -0x92, 0x08, 0x9B, 0x07, 0xD2, 0x18, 0x82, 0x61, -0x00, 0x20, 0x48, 0x72, 0x70, 0x47, 0x00, 0x00, -0x20, 0x08, 0x00, 0x20, 0x70, 0xB5, 0x1D, 0x4D, -0x68, 0x78, 0x00, 0x06, 0x2A, 0xD5, 0x05, 0x20, -0xFF, 0xF7, 0x04, 0xFE, 0x01, 0x20, 0xFF, 0xF7, -0x43, 0xFD, 0x68, 0x78, 0x40, 0x06, 0x40, 0x0E, -0x68, 0x70, 0x68, 0x78, 0x16, 0x4C, 0x10, 0x38, -0x03, 0x00, 0xFF, 0xF7, 0x0F, 0xFD, 0x05, 0x04, -0x06, 0x0C, 0x1C, 0x1E, 0x0E, 0x00, 0x06, 0x20, -0x06, 0xE0, 0x01, 0x20, 0x20, 0x70, 0x11, 0xA0, -0x02, 0xF0, 0xE7, 0xFB, 0x01, 0xE0, 0x02, 0x20, -0x20, 0x70, 0x68, 0x78, 0x80, 0x21, 0x08, 0x43, -0x28, 0x71, 0x00, 0x0A, 0x68, 0x71, 0x00, 0x20, -0xFF, 0xF7, 0x22, 0xFD, 0xA9, 0x88, 0x0C, 0xA0, -0x02, 0xF0, 0xD7, 0xFB, 0x70, 0xBD, 0x03, 0x20, -0xEE, 0xE7, 0x0D, 0x48, 0x69, 0x78, 0x82, 0x88, -0x0C, 0xA0, 0x02, 0xF0, 0xCE, 0xFB, 0x05, 0x20, -0xE6, 0xE7, 0x00, 0x00, 0x88, 0x02, 0x00, 0x20, -0xCE, 0x00, 0x00, 0x20, 0x43, 0x4D, 0x44, 0x5F, -0x49, 0x4E, 0x49, 0x54, 0x0D, 0x0A, 0x00, 0x00, -0x43, 0x4D, 0x44, 0x20, 0x45, 0x78, 0x69, 0x74, -0x5B, 0x25, 0x78, 0x5D, 0x3D, 0x0D, 0x0A, 0x00, -0x74, 0x06, 0x00, 0x20, 0x43, 0x4D, 0x44, 0x3D, -0x30, 0x78, 0x25, 0x78, 0x3A, 0x25, 0x78, 0x0D, -0x0A, 0x00, 0x00, 0x00, 0xF8, 0xB5, 0x1A, 0x4C, -0x01, 0x20, 0x20, 0x70, 0x19, 0x4E, 0x00, 0x20, -0x30, 0x71, 0x70, 0x71, 0x18, 0x48, 0xB0, 0x70, -0x00, 0x0A, 0xF0, 0x70, 0x00, 0x20, 0xFF, 0xF7, -0xC9, 0xFE, 0x16, 0x4F, 0x04, 0x25, 0x20, 0x78, -0x03, 0x00, 0xFF, 0xF7, 0xB7, 0xFC, 0x07, 0x14, -0x05, 0x12, 0x12, 0x16, 0x19, 0x1F, 0x14, 0x00, -0x37, 0x71, 0x38, 0x0A, 0x70, 0x71, 0x25, 0x70, -0x70, 0x20, 0x30, 0x70, 0x0E, 0x48, 0x30, 0x71, -0x00, 0x0A, 0x70, 0x71, 0x00, 0xF0, 0x92, 0xFB, -0xE9, 0xE7, 0x00, 0xF0, 0x8F, 0xFB, 0x25, 0x70, -0xE5, 0xE7, 0xFF, 0xF7, 0x7B, 0xFF, 0xE2, 0xE7, -0x08, 0xA0, 0x02, 0xF0, 0x7E, 0xFB, 0x00, 0xF0, -0x11, 0xF8, 0xF2, 0xE7, 0xF8, 0xBD, 0x00, 0x00, -0xCE, 0x00, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20, -0xCC, 0x55, 0x00, 0x00, 0x55, 0xAA, 0x00, 0x00, -0x11, 0x5A, 0x00, 0x00, 0x5B, 0x53, 0x54, 0x5D, -0x0D, 0x0A, 0x00, 0x00, 0x70, 0xB5, 0x1F, 0x48, -0x00, 0x25, 0x84, 0x88, 0x01, 0x21, 0x00, 0x2C, -0x1A, 0xD0, 0x0B, 0x00, 0xFF, 0xF7, 0x7E, 0xFC, -0x07, 0x16, 0x05, 0x13, 0x1A, 0x16, 0x0F, 0x1F, -0x16, 0x00, 0x00, 0xF0, 0x55, 0xF8, 0x00, 0xF0, -0xC1, 0xF9, 0x01, 0x28, 0x01, 0xD1, 0x05, 0x21, -0x08, 0xE0, 0x06, 0x21, 0x06, 0xE0, 0x02, 0xF0, -0xA1, 0xF8, 0x02, 0x21, 0x02, 0xE0, 0x00, 0xF0, -0xA7, 0xFE, 0x03, 0x21, 0x00, 0x2C, 0xE4, 0xD1, -0x28, 0x46, 0x70, 0xBD, 0x01, 0xF0, 0xFE, 0xFE, -0x00, 0xF0, 0x66, 0xFE, 0xED, 0xE7, 0x0C, 0xA0, -0x02, 0xF0, 0x3F, 0xFB, 0x0E, 0x48, 0xFF, 0xF7, -0xE9, 0xFE, 0x0E, 0x48, 0xFF, 0xF7, 0xE6, 0xFE, -0x0D, 0x48, 0xFF, 0xF7, 0xE3, 0xFE, 0x0D, 0x48, -0xFF, 0xF7, 0xE0, 0xFE, 0x00, 0xF0, 0x8A, 0xFA, -0x0B, 0x48, 0x01, 0x68, 0x0B, 0xA0, 0x02, 0xF0, -0x2C, 0xFB, 0xE1, 0xE7, 0x74, 0x06, 0x00, 0x20, -0x54, 0x45, 0x53, 0x54, 0x5F, 0x43, 0x4D, 0x44, -0x5F, 0x45, 0x58, 0x49, 0x54, 0x0D, 0x0A, 0x00, -0xE4, 0x02, 0x00, 0x20, 0x48, 0x03, 0x00, 0x20, -0xAC, 0x03, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20, -0x0C, 0x05, 0x00, 0x20, 0x67, 0x5F, 0x75, 0x33, -0x32, 0x5F, 0x77, 0x65, 0x61, 0x72, 0x61, 0x62, -0x6C, 0x65, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x5F, -0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x3A, 0x20, -0x30, 0x78, 0x25, 0x78, 0x0D, 0x0A, 0x00, 0x00, -0x10, 0xB5, 0xC8, 0x21, 0x0E, 0x48, 0xFF, 0xF7, -0xB8, 0xFB, 0xC8, 0x21, 0x0D, 0x48, 0xFF, 0xF7, -0xB4, 0xFB, 0xC8, 0x21, 0x0C, 0x48, 0xFF, 0xF7, -0xB0, 0xFB, 0xC8, 0x21, 0x0B, 0x48, 0xFF, 0xF7, -0xAC, 0xFB, 0x64, 0x21, 0x0A, 0x48, 0xFF, 0xF7, -0xA8, 0xFB, 0x32, 0x21, 0x09, 0x48, 0xFF, 0xF7, -0xA4, 0xFB, 0x09, 0x49, 0x00, 0x20, 0x08, 0x60, -0x08, 0xA0, 0x02, 0xF0, 0xE6, 0xFA, 0x10, 0xBD, -0xE4, 0x02, 0x00, 0x20, 0x48, 0x03, 0x00, 0x20, -0xAC, 0x03, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20, -0x74, 0x04, 0x00, 0x20, 0xD8, 0x04, 0x00, 0x20, -0x0C, 0x05, 0x00, 0x20, 0x46, 0x54, 0x20, 0x52, -0x65, 0x73, 0x75, 0x6C, 0x74, 0x20, 0x49, 0x6E, -0x69, 0x74, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0xF3, 0xB5, 0xC1, 0xB0, 0x42, 0x98, 0x04, 0x28, -0x01, 0xD2, 0x04, 0x20, 0x42, 0x90, 0xC0, 0x21, -0x68, 0x46, 0xFF, 0xF7, 0x7A, 0xFB, 0x01, 0xF0, -0xAB, 0xFB, 0x01, 0xF0, 0xDB, 0xFD, 0x01, 0xF0, -0x83, 0xF8, 0x00, 0x24, 0x6E, 0x46, 0x1A, 0xE0, -0x01, 0xF0, 0xEC, 0xFC, 0x01, 0xF0, 0xD2, 0xFD, -0x01, 0xF0, 0x7A, 0xF8, 0x18, 0x49, 0x00, 0x20, -0x0A, 0x68, 0x18, 0x49, 0x0F, 0x68, 0x39, 0x18, -0xC9, 0x7E, 0x41, 0x29, 0x05, 0xD0, 0x81, 0x00, -0x45, 0x00, 0x73, 0x58, 0x55, 0x5F, 0x5B, 0x19, -0x73, 0x50, 0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, -0xF1, 0xD3, 0x64, 0x1C, 0xE4, 0xB2, 0x42, 0x98, -0x84, 0x42, 0xE1, 0xD3, 0x01, 0xF0, 0xB6, 0xFD, -0x01, 0xF0, 0xE4, 0xFC, 0x0B, 0x4D, 0x00, 0x24, -0x28, 0x68, 0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, -0x07, 0xD0, 0xA0, 0x00, 0x30, 0x58, 0x42, 0x99, -0xFF, 0xF7, 0x16, 0xFB, 0x41, 0x99, 0x62, 0x00, -0x88, 0x52, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C, -0xEE, 0xD3, 0x01, 0x20, 0x43, 0xB0, 0xF0, 0xBD, -0x4C, 0x01, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, -0x70, 0xB5, 0x24, 0x48, 0xFF, 0x24, 0x41, 0x6B, -0x2D, 0x34, 0x03, 0x22, 0x12, 0x05, 0x91, 0x43, -0x01, 0x22, 0x12, 0x05, 0x89, 0x18, 0x41, 0x63, -0x1F, 0x4D, 0x20, 0x48, 0xA9, 0x7E, 0xFF, 0xF7, -0x9F, 0xFF, 0xE8, 0x7E, 0x29, 0x7E, 0x41, 0x43, -0x89, 0x04, 0x28, 0x7F, 0x09, 0x0E, 0x02, 0x04, -0x09, 0x02, 0x0A, 0x43, 0x17, 0x49, 0x0C, 0x32, -0x40, 0x39, 0x4A, 0x60, 0x0C, 0x38, 0xC0, 0xB2, -0xC9, 0x14, 0x40, 0x18, 0x13, 0x49, 0x40, 0x31, -0x88, 0x60, 0xA9, 0x7E, 0x14, 0x48, 0xFF, 0xF7, -0x87, 0xFF, 0x14, 0x49, 0x12, 0x4B, 0x0A, 0x68, -0x00, 0x20, 0x11, 0x18, 0xC9, 0x7E, 0x41, 0x29, -0x03, 0xD0, 0x41, 0x00, 0x5D, 0x5A, 0x2D, 0x1B, -0x5D, 0x52, 0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, -0xF3, 0xD3, 0x0D, 0xA0, 0x02, 0xF0, 0x41, 0xFA, -0x01, 0x21, 0x08, 0x48, 0x01, 0xF0, 0x24, 0xFE, -0x0C, 0xA0, 0x02, 0xF0, 0x3A, 0xFA, 0x01, 0x21, -0x05, 0x48, 0x01, 0xF0, 0x1D, 0xFE, 0x01, 0x20, -0x70, 0xBD, 0x00, 0x00, 0x80, 0x10, 0x00, 0x50, -0xA8, 0x06, 0x00, 0x20, 0x74, 0x04, 0x00, 0x20, -0x48, 0x03, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, -0x42, 0x61, 0x73, 0x65, 0x6C, 0x69, 0x6E, 0x65, -0x0D, 0x0A, 0x00, 0x00, 0x51, 0x75, 0x69, 0x63, -0x6B, 0x28, 0x30, 0x78, 0x31, 0x32, 0x43, 0x29, -0x0D, 0x0A, 0x00, 0x00, 0xF8, 0xB5, 0x3C, 0x48, -0x00, 0x24, 0x60, 0x21, 0x00, 0x68, 0xFF, 0xF7, -0xCC, 0xFA, 0x3A, 0x4E, 0x3A, 0x48, 0x00, 0x68, -0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x5A, 0xD0, -0xA5, 0x07, 0xAD, 0x0F, 0xA7, 0x08, 0x0C, 0x21, -0x20, 0x46, 0xFF, 0xF7, 0x7B, 0xFA, 0x88, 0x08, -0x00, 0x21, 0xC9, 0x43, 0x71, 0x62, 0x09, 0x0C, -0xB1, 0x62, 0x20, 0x2C, 0x05, 0xD2, 0x72, 0x6A, -0x01, 0x21, 0xA1, 0x40, 0x8A, 0x43, 0x72, 0x62, -0x06, 0xE0, 0xB3, 0x6A, 0x21, 0x46, 0x20, 0x39, -0x01, 0x22, 0x8A, 0x40, 0x93, 0x43, 0xB3, 0x62, -0x03, 0x2D, 0x02, 0xD2, 0x69, 0x1C, 0xC9, 0xB2, -0x00, 0xE0, 0x08, 0x21, 0xF2, 0x6A, 0x27, 0x4B, -0x1A, 0x40, 0x01, 0x23, 0xBB, 0x40, 0x1B, 0x05, -0x1B, 0x09, 0x1A, 0x43, 0xF2, 0x62, 0x72, 0x6B, -0x0E, 0x23, 0x9A, 0x43, 0x01, 0x23, 0x83, 0x40, -0x5B, 0x07, 0x1B, 0x0F, 0x1A, 0x43, 0x72, 0x63, -0x40, 0x1C, 0x82, 0x07, 0x92, 0x0F, 0x90, 0x05, -0x13, 0x05, 0x18, 0x43, 0x93, 0x04, 0x18, 0x43, -0x12, 0x04, 0x18, 0x4F, 0x10, 0x43, 0x40, 0x37, -0xFA, 0x6A, 0xFF, 0x23, 0x1B, 0x04, 0x9A, 0x43, -0x10, 0x43, 0xF8, 0x62, 0xF0, 0x6B, 0x09, 0x07, -0xF4, 0x22, 0x09, 0x0E, 0x90, 0x43, 0x09, 0x1D, -0x08, 0x43, 0xF0, 0x63, 0xB0, 0x6B, 0x74, 0x21, -0x88, 0x43, 0x6D, 0x1C, 0x69, 0x07, 0x49, 0x0E, -0x09, 0x1D, 0x08, 0x43, 0xB0, 0x63, 0x01, 0xF0, -0xE9, 0xFB, 0x01, 0xF0, 0xCF, 0xFC, 0x64, 0x1C, -0xE4, 0xB2, 0x30, 0x2C, 0x9A, 0xD3, 0x06, 0x48, -0x60, 0x22, 0x01, 0x68, 0x08, 0x48, 0xFF, 0xF7, -0x47, 0xFA, 0x01, 0x21, 0x06, 0x48, 0x01, 0xF0, -0x8B, 0xFD, 0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, -0x4C, 0x01, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, -0x98, 0x01, 0x00, 0x20, 0xFF, 0xFF, 0x00, 0xF0, -0xE4, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x00, 0xF0, -0xC1, 0xF8, 0x0E, 0x48, 0x00, 0x68, 0x0E, 0x4C, -0x40, 0x05, 0x40, 0x0F, 0x20, 0x70, 0x00, 0x20, -0x01, 0xF0, 0xAA, 0xFF, 0x0B, 0x49, 0x01, 0x20, -0x08, 0x70, 0x08, 0x20, 0x20, 0x70, 0x0A, 0x49, -0x06, 0x20, 0x08, 0x70, 0x01, 0x20, 0x09, 0x49, -0x80, 0x02, 0x08, 0x80, 0x01, 0xF0, 0xFA, 0xFE, -0x01, 0xF0, 0x98, 0xFC, 0x01, 0xF0, 0xC6, 0xFB, -0x01, 0x20, 0x10, 0xBD, 0x00, 0x11, 0x00, 0x50, -0x39, 0x01, 0x00, 0x20, 0x3B, 0x01, 0x00, 0x20, -0x0D, 0x08, 0x00, 0x20, 0x46, 0x01, 0x00, 0x20, -0xF8, 0xB5, 0x36, 0x48, 0x00, 0x24, 0x36, 0x4D, -0x44, 0x80, 0x2C, 0x60, 0x6C, 0x60, 0x35, 0xA0, -0x02, 0xF0, 0x63, 0xF9, 0x32, 0x48, 0x61, 0x1E, -0x80, 0x38, 0xC1, 0x61, 0x09, 0x0C, 0x01, 0x62, -0x35, 0x49, 0x41, 0x62, 0x81, 0x62, 0xC1, 0x62, -0x01, 0x06, 0x0A, 0x68, 0x0B, 0x13, 0x9A, 0x43, -0x0A, 0x60, 0x32, 0x49, 0x01, 0x26, 0x8E, 0x61, -0xCE, 0x61, 0x30, 0x4A, 0x40, 0x3A, 0xD4, 0x63, -0x0C, 0x60, 0x8C, 0x60, 0x2E, 0x4B, 0x1F, 0x7F, -0xDB, 0x7E, 0x3A, 0x04, 0x1B, 0x02, 0x1A, 0x43, -0x0C, 0x32, 0xBC, 0x46, 0x42, 0x60, 0x82, 0x60, -0x1F, 0x22, 0x02, 0x61, 0x44, 0x61, 0x0C, 0x62, -0x1F, 0x48, 0x28, 0x4A, 0x40, 0x30, 0x82, 0x61, -0x03, 0x22, 0xC2, 0x61, 0x1C, 0x4A, 0xC0, 0x3A, -0xD4, 0x60, 0x03, 0x23, 0x1B, 0x02, 0x13, 0x61, -0x14, 0x23, 0x83, 0x62, 0x34, 0x23, 0xC3, 0x63, -0x17, 0x4B, 0x54, 0x27, 0x80, 0x33, 0x1F, 0x61, -0x5C, 0x62, 0xD0, 0x27, 0xC7, 0x62, 0xF0, 0x27, -0x1F, 0x60, 0xFF, 0x27, 0x11, 0x37, 0x5F, 0x61, -0x9C, 0x62, 0x04, 0x62, 0x1A, 0x4B, 0x43, 0x62, -0x1A, 0x48, 0x88, 0x62, 0x0E, 0x49, 0x1A, 0x48, -0x40, 0x39, 0x48, 0x63, 0x67, 0x46, 0x0C, 0x3F, -0xF8, 0xB2, 0xC9, 0x14, 0x40, 0x18, 0xA8, 0x60, -0x17, 0x48, 0x16, 0x49, 0x01, 0x60, 0x44, 0x60, -0xD1, 0x68, 0x16, 0x48, 0x01, 0x60, 0x11, 0x69, -0x41, 0x60, 0x15, 0x48, 0x06, 0x70, 0x10, 0x15, -0x10, 0x60, 0x14, 0xA0, 0x02, 0xF0, 0x01, 0xF9, -0xF8, 0xBD, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x20, -0xC0, 0x10, 0x00, 0x50, 0x4F, 0x70, 0x65, 0x6E, -0x20, 0x49, 0x6E, 0x69, 0x74, 0x20, 0x53, 0x74, -0x61, 0x72, 0x74, 0x0D, 0x0A, 0x00, 0x00, 0x00, -0x55, 0x55, 0x55, 0x55, 0xC0, 0x11, 0x00, 0x50, -0xA8, 0x06, 0x00, 0x20, 0x03, 0x00, 0x03, 0x00, -0x00, 0x3F, 0x3F, 0x3F, 0x03, 0x30, 0xBC, 0x00, -0x00, 0x26, 0x32, 0x00, 0x21, 0x20, 0x00, 0x00, -0x00, 0x19, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20, -0x31, 0x00, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, -0x20, 0x49, 0x6E, 0x69, 0x74, 0x20, 0x45, 0x6E, -0x64, 0x0D, 0x0A, 0x00, 0x10, 0xB5, 0x90, 0xB0, -0x40, 0x21, 0x68, 0x46, 0xFF, 0xF7, 0x85, 0xF9, -0x41, 0x22, 0x40, 0x21, 0x68, 0x46, 0xFF, 0xF7, -0x79, 0xF9, 0x0E, 0x49, 0x00, 0x20, 0x0B, 0x68, -0x6A, 0x46, 0x19, 0x18, 0xC9, 0x7E, 0x41, 0x29, -0x00, 0xD0, 0x50, 0x54, 0x40, 0x1C, 0xC0, 0xB2, -0x30, 0x28, 0xF6, 0xD3, 0x08, 0x49, 0x00, 0x20, -0x13, 0x5C, 0x44, 0x00, 0x40, 0x1C, 0xC0, 0xB2, -0x0B, 0x53, 0x40, 0x28, 0xF8, 0xD3, 0x00, 0x21, -0x03, 0x48, 0x01, 0xF0, 0x95, 0xFC, 0x01, 0x20, -0x10, 0xB0, 0x10, 0xBD, 0x98, 0x01, 0x00, 0x20, -0x9C, 0x01, 0x00, 0x20, 0x0E, 0x49, 0x10, 0xB5, -0x5A, 0x20, 0x08, 0x74, 0x00, 0x20, 0x0A, 0x46, -0x48, 0x74, 0x20, 0x3A, 0x14, 0x5C, 0x4B, 0x7C, -0x40, 0x1C, 0x1B, 0x19, 0xC0, 0xB2, 0x4B, 0x74, -0x31, 0x28, 0xF7, 0xD3, 0x07, 0x49, 0x00, 0x20, -0x13, 0x5C, 0x44, 0x00, 0x40, 0x1C, 0xC0, 0xB2, -0x0B, 0x53, 0x40, 0x28, 0xF8, 0xD3, 0x01, 0x21, -0x02, 0x48, 0x01, 0xF0, 0x71, 0xFC, 0x10, 0xBD, -0xF8, 0x04, 0x00, 0x20, 0x9C, 0x01, 0x00, 0x20, -0x70, 0xB5, 0x2E, 0xA0, 0x02, 0xF0, 0x81, 0xF8, -0x32, 0x49, 0x31, 0x48, 0x08, 0x60, 0x33, 0x4B, -0x31, 0x48, 0x58, 0x60, 0x31, 0x4C, 0x32, 0x48, -0x80, 0x34, 0xA0, 0x60, 0x31, 0x48, 0x40, 0x78, -0x41, 0x1E, 0xC9, 0x05, 0xC9, 0x0D, 0x19, 0x61, -0x2F, 0x4A, 0x00, 0x21, 0x11, 0x62, 0x2B, 0x4A, -0x2E, 0x4D, 0x40, 0x32, 0x55, 0x63, 0x05, 0x02, -0x2D, 0x48, 0x6D, 0x1C, 0x05, 0x60, 0x41, 0x60, -0x09, 0x25, 0x26, 0x48, 0x2D, 0x05, 0x40, 0x38, -0x05, 0x63, 0x41, 0x63, 0x81, 0x63, 0xD9, 0x63, -0x11, 0x60, 0xA1, 0x62, 0x27, 0x4B, 0x19, 0x70, -0x04, 0x23, 0x03, 0x60, 0x83, 0x02, 0xC3, 0x60, -0x01, 0x61, 0xC3, 0x68, 0x24, 0x49, 0x0B, 0x60, -0x03, 0x69, 0x4B, 0x60, 0x03, 0x21, 0x23, 0x4B, -0x09, 0x06, 0x19, 0x60, 0x01, 0x21, 0x49, 0x02, -0x11, 0x62, 0x81, 0x04, 0xD1, 0x62, 0x81, 0x69, -0x03, 0x23, 0x9B, 0x03, 0x19, 0x43, 0x81, 0x61, -0xC1, 0x69, 0x1D, 0x4B, 0x19, 0x43, 0xC1, 0x61, -0xD0, 0x21, 0x11, 0x63, 0xC1, 0x6A, 0x10, 0x22, -0x11, 0x43, 0xC1, 0x62, 0x55, 0x20, 0xE0, 0x62, -0x60, 0x21, 0x18, 0x48, 0xFF, 0xF7, 0xE9, 0xF8, -0x16, 0x48, 0x60, 0x21, 0x60, 0x30, 0xFF, 0xF7, -0xE4, 0xF8, 0x15, 0xA0, 0x02, 0xF0, 0x29, 0xF8, -0x70, 0xBD, 0x00, 0x00, 0x53, 0x68, 0x6F, 0x72, -0x74, 0x20, 0x49, 0x6E, 0x69, 0x74, 0x20, 0x53, -0x74, 0x61, 0x72, 0x74, 0x0D, 0x0A, 0x00, 0x00, -0x00, 0x00, 0x04, 0x20, 0x4C, 0x01, 0x00, 0x20, -0x0C, 0x0D, 0xF2, 0x00, 0x40, 0x10, 0x00, 0x50, -0xE6, 0x0A, 0x00, 0x00, 0xC8, 0x06, 0x00, 0x20, -0xC0, 0x11, 0x00, 0x50, 0x00, 0x36, 0x30, 0x04, -0x00, 0x19, 0x00, 0x50, 0x31, 0x00, 0x00, 0x20, -0x20, 0x00, 0x00, 0x20, 0x00, 0x07, 0x00, 0x50, -0x00, 0xC0, 0x00, 0xC0, 0x00, 0x20, 0x00, 0x50, -0x53, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x49, 0x6E, -0x69, 0x74, 0x20, 0x45, 0x6E, 0x64, 0x0D, 0x0A, -0x00, 0x00, 0x00, 0x00, 0x10, 0xB5, 0x1C, 0x48, -0x64, 0x24, 0x41, 0x79, 0x02, 0x79, 0x09, 0x02, -0x11, 0x43, 0x80, 0x22, 0x91, 0x43, 0x01, 0x71, -0x09, 0x0A, 0x41, 0x71, 0x41, 0x79, 0x02, 0x79, -0x09, 0x02, 0x11, 0x43, 0x2D, 0x22, 0x52, 0x02, -0x11, 0x43, 0x01, 0x71, 0x09, 0x0A, 0x41, 0x71, -0x81, 0x88, 0x12, 0xA0, 0x01, 0xF0, 0xDD, 0xFF, -0x06, 0xE0, 0x21, 0x46, 0x16, 0xA0, 0x01, 0xF0, -0xD8, 0xFF, 0x01, 0x20, 0xFF, 0xF7, 0xDA, 0xF9, -0xFF, 0xF7, 0x94, 0xFB, 0x00, 0x28, 0x04, 0xD1, -0x20, 0x46, 0x64, 0x1E, 0x24, 0xB2, 0x00, 0x28, -0xEF, 0xD1, 0x64, 0x1C, 0x08, 0xD1, 0x01, 0x20, -0xFF, 0xF7, 0x0E, 0xF9, 0x10, 0xA0, 0x01, 0xF0, -0xC4, 0xFF, 0x0A, 0x20, 0xFF, 0xF7, 0xC6, 0xF9, -0x00, 0x20, 0xFF, 0xF7, 0x05, 0xF9, 0x10, 0xBD, -0x88, 0x02, 0x00, 0x20, 0x46, 0x54, 0x20, 0x55, -0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x53, 0x74, -0x61, 0x74, 0x75, 0x73, 0x20, 0x28, 0x30, 0x78, -0x25, 0x34, 0x78, 0x29, 0x0D, 0x0A, 0x00, 0x00, -0x49, 0x4E, 0x54, 0x20, 0x4C, 0x6F, 0x77, 0x21, -0x21, 0x28, 0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, -0x49, 0x4E, 0x54, 0x20, 0x70, 0x61, 0x64, 0x20, -0x69, 0x73, 0x20, 0x6C, 0x6F, 0x77, 0x20, 0x6C, -0x65, 0x76, 0x65, 0x6C, 0x20, 0x21, 0x21, 0x00, -0x70, 0xB5, 0x17, 0x48, 0x5A, 0x25, 0x01, 0x78, -0x16, 0x48, 0x00, 0x29, 0x25, 0xD0, 0x09, 0x21, -0x01, 0x70, 0x15, 0x4C, 0x01, 0x21, 0xE0, 0x89, -0x89, 0x02, 0x88, 0x42, 0x01, 0xD0, 0x03, 0x20, -0x60, 0x71, 0x11, 0x48, 0x30, 0x21, 0x28, 0x30, -0xFF, 0xF7, 0x3B, 0xF8, 0x00, 0x20, 0x60, 0x72, -0xA0, 0x71, 0x60, 0x62, 0x16, 0x21, 0x21, 0x72, -0x0C, 0x49, 0x20, 0x71, 0x08, 0x70, 0xA5, 0x81, -0x60, 0x81, 0x60, 0x8A, 0x09, 0x21, 0x09, 0x03, -0x08, 0x43, 0x60, 0x82, 0x03, 0x20, 0x40, 0x02, -0xE0, 0x61, 0x0D, 0x20, 0xC0, 0x01, 0x20, 0x62, -0x70, 0xBD, 0x05, 0x70, 0xD9, 0xE7, 0x00, 0x00, -0x92, 0x01, 0x00, 0x20, 0x94, 0x01, 0x00, 0x20, -0x38, 0x01, 0x00, 0x20, 0x93, 0x01, 0x00, 0x20, -0x0E, 0x48, 0x03, 0x21, 0x41, 0x71, 0x0E, 0x49, -0x41, 0x61, 0x0D, 0x49, 0x60, 0x31, 0x81, 0x61, -0x01, 0x21, 0x01, 0x70, 0x07, 0x22, 0x42, 0x70, -0x0A, 0x4B, 0x05, 0x22, 0x1A, 0x70, 0x0A, 0x4B, -0x1A, 0x70, 0x0A, 0x4B, 0x55, 0x22, 0xDA, 0x70, -0x04, 0x22, 0x02, 0x82, 0x00, 0x22, 0xC2, 0x70, -0x09, 0x22, 0x12, 0x03, 0x42, 0x82, 0x81, 0x70, -0x70, 0x47, 0x00, 0x00, 0x38, 0x01, 0x00, 0x20, -0x00, 0x00, 0x04, 0x20, 0x92, 0x01, 0x00, 0x20, -0x0D, 0x08, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, -0x04, 0x22, 0x0F, 0x49, 0x0C, 0x28, 0x10, 0xD0, -0x8B, 0x05, 0x0D, 0x28, 0x08, 0x6A, 0x10, 0xD0, -0x18, 0x43, 0x08, 0x62, 0x88, 0x6A, 0x10, 0x43, -0x88, 0x62, 0x0A, 0x4A, 0x01, 0x20, 0x10, 0x70, -0xC8, 0x68, 0xC8, 0x60, 0x88, 0x6A, 0x88, 0x62, -0x70, 0x47, 0x08, 0x6A, 0x40, 0x00, 0x40, 0x08, -0x00, 0xE0, 0x18, 0x43, 0x08, 0x62, 0x88, 0x6A, -0x90, 0x43, 0x88, 0x62, 0xF0, 0xE7, 0x00, 0x00, -0x00, 0x06, 0x00, 0x50, 0xCC, 0x00, 0x00, 0x20, -0x00, 0xB5, 0x08, 0x49, 0x0A, 0x28, 0x05, 0xD0, -0x07, 0x48, 0x00, 0x0C, 0x48, 0x63, 0x07, 0x48, -0x08, 0x63, 0x00, 0xBD, 0x06, 0x48, 0x00, 0x68, -0x08, 0x62, 0x0D, 0x20, 0xFF, 0xF7, 0xCC, 0xFF, -0x00, 0xBD, 0x00, 0x00, 0x00, 0x06, 0x00, 0x50, -0xBC, 0x02, 0x00, 0x20, 0xCC, 0x02, 0x00, 0x20, -0xC0, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x1B, 0x49, -0x00, 0x20, 0x03, 0x00, 0xFF, 0xF7, 0x12, 0xF8, -0x0C, 0x07, 0x0A, 0x0E, 0x26, 0x26, 0x11, 0x14, -0x17, 0x1A, 0x1D, 0x20, 0x23, 0x26, 0x16, 0x4A, -0x0A, 0x80, 0x1E, 0xE0, 0x14, 0x4A, 0x12, 0x1D, -0x4A, 0x80, 0x1A, 0xE0, 0x13, 0x4A, 0x8A, 0x80, -0x17, 0xE0, 0x13, 0x4A, 0x4A, 0x81, 0x14, 0xE0, -0x12, 0x4A, 0x8A, 0x81, 0x11, 0xE0, 0x12, 0x4A, -0xCA, 0x81, 0x0E, 0xE0, 0x11, 0x4A, 0x0A, 0x82, -0x0B, 0xE0, 0x11, 0x4A, 0x4A, 0x82, 0x08, 0xE0, -0x10, 0x4A, 0x8A, 0x82, 0x05, 0xE0, 0x10, 0x4A, -0xCA, 0x82, 0x02, 0xE0, 0x0F, 0x4A, 0x43, 0x00, -0xCA, 0x52, 0x40, 0x1C, 0x80, 0xB2, 0x0C, 0x28, -0xCF, 0xD3, 0x0D, 0xA0, 0x01, 0xF0, 0xC5, 0xFE, -0x10, 0xBD, 0x00, 0x00, 0xCC, 0x02, 0x00, 0x20, -0x90, 0x02, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20, -0x34, 0x01, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20, -0xE4, 0x06, 0x00, 0x20, 0xD8, 0x06, 0x00, 0x20, -0xC0, 0x02, 0x00, 0x20, 0xBC, 0x02, 0x00, 0x20, -0x9C, 0x01, 0x00, 0x20, 0xC4, 0x02, 0x00, 0x20, -0x49, 0x32, 0x43, 0x20, 0x4F, 0x4B, 0x0D, 0x0A, -0x00, 0x00, 0x00, 0x00, 0x70, 0xB5, 0x01, 0x25, -0xCA, 0x07, 0x0A, 0xD0, 0x00, 0x20, 0x70, 0xBD, -0x93, 0x00, 0xC4, 0x58, 0x66, 0x1C, 0x02, 0xD0, -0x1B, 0x18, 0x5B, 0x68, 0x23, 0x60, 0x92, 0x1C, -0x92, 0xB2, 0x8A, 0x42, 0xF4, 0xD3, 0x28, 0x46, -0x70, 0xBD, 0x00, 0x00, 0x10, 0xB5, 0x1D, 0x49, -0x0A, 0x68, 0x1C, 0x48, 0x40, 0x30, 0x02, 0x61, -0x4A, 0x68, 0x42, 0x61, 0x8A, 0x68, 0x82, 0x61, -0xC9, 0x68, 0xC1, 0x61, 0x82, 0x69, 0xF0, 0x21, -0x8A, 0x43, 0x82, 0x61, 0x82, 0x69, 0x0A, 0x43, -0x82, 0x61, 0x41, 0x69, 0x49, 0x04, 0x04, 0xD5, -0x41, 0x69, 0x01, 0x22, 0x52, 0x03, 0x89, 0x1A, -0x41, 0x61, 0x10, 0x48, 0x40, 0x38, 0x01, 0x68, -0x01, 0x22, 0x12, 0x06, 0x11, 0x43, 0x01, 0x60, -0x30, 0x21, 0x0D, 0x48, 0xFF, 0xF7, 0xC6, 0xFF, -0x0B, 0x48, 0x40, 0x21, 0xC0, 0x30, 0xFF, 0xF7, -0xC1, 0xFF, 0x0B, 0x20, 0xFE, 0xF7, 0x46, 0xFF, -0x03, 0x20, 0xFE, 0xF7, 0x43, 0xFF, 0x00, 0x20, -0xFE, 0xF7, 0x40, 0xFF, 0x05, 0x20, 0xFE, 0xF7, -0x3D, 0xFF, 0x09, 0x20, 0xFE, 0xF7, 0x3A, 0xFF, -0x01, 0x20, 0x10, 0xBD, 0x40, 0x14, 0x00, 0x50, -0x5C, 0x39, 0x00, 0x00, 0xFE, 0xB5, 0x27, 0x48, -0x0A, 0x25, 0x45, 0x5F, 0x01, 0x26, 0x29, 0x46, -0x25, 0xA0, 0x01, 0xF0, 0x4A, 0xFE, 0x2A, 0x48, -0x72, 0x04, 0x01, 0x68, 0x29, 0x4F, 0x91, 0x43, -0x00, 0x24, 0x01, 0x60, 0x28, 0x48, 0x00, 0x68, -0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x18, 0xD0, -0x26, 0x49, 0x60, 0x00, 0x0A, 0x5E, 0x26, 0x49, -0x0B, 0x5A, 0xD0, 0x1A, 0x00, 0xB2, 0xA8, 0x42, -0x0B, 0xDA, 0x00, 0x90, 0x21, 0x46, 0x01, 0x95, -0x22, 0xA0, 0x01, 0xF0, 0x2E, 0xFE, 0x38, 0x5D, -0x20, 0x21, 0x08, 0x43, 0x38, 0x55, 0x00, 0x26, -0x03, 0xE0, 0x38, 0x5D, 0xDF, 0x21, 0x08, 0x40, -0x38, 0x55, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C, -0xDC, 0xD3, 0x01, 0x2E, 0x18, 0xD0, 0x14, 0x4A, -0x01, 0x21, 0x10, 0x68, 0x49, 0x04, 0x08, 0x43, -0x10, 0x60, 0x23, 0xA0, 0x01, 0xF0, 0x15, 0xFE, -0x27, 0xA0, 0x01, 0xF0, 0x12, 0xFE, 0x01, 0x21, -0x10, 0x48, 0x01, 0xF0, 0xF5, 0xF9, 0x27, 0xA0, -0x01, 0xF0, 0x0B, 0xFE, 0x01, 0x21, 0x0E, 0x48, -0x01, 0xF0, 0xEE, 0xF9, 0x30, 0x46, 0xFE, 0xBD, -0x27, 0xA0, 0xEB, 0xE7, 0x84, 0x06, 0x00, 0x20, -0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x43, 0x20, -0x43, 0x68, 0x65, 0x63, 0x6B, 0x20, 0x54, 0x48, -0x44, 0x3D, 0x25, 0x64, 0x20, 0x0D, 0x0A, 0x00, -0x0C, 0x05, 0x00, 0x20, 0xD8, 0x04, 0x00, 0x20, -0x98, 0x01, 0x00, 0x20, 0xAC, 0x03, 0x00, 0x20, -0x4C, 0x07, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, -0x20, 0x43, 0x43, 0x20, 0x4E, 0x47, 0x20, 0x50, -0x69, 0x6E, 0x5B, 0x25, 0x64, 0x5D, 0x2C, 0x20, -0x44, 0x69, 0x66, 0x66, 0x3D, 0x25, 0x64, 0x2D, -0x25, 0x64, 0x3D, 0x25, 0x64, 0x2C, 0x20, 0x4C, -0x6F, 0x77, 0x54, 0x48, 0x44, 0x20, 0x3D, 0x20, -0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x43, -0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x5B, -0x4E, 0x47, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, -0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x43, 0x0D, -0x0A, 0x00, 0x00, 0x00, 0x47, 0x6F, 0x6C, 0x64, -0x65, 0x6E, 0x20, 0x4F, 0x70, 0x65, 0x6E, 0x20, -0x43, 0x43, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x43, -0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x5B, -0x50, 0x61, 0x73, 0x73, 0x5D, 0x0D, 0x0A, 0x00, -0xFE, 0xB5, 0x36, 0x49, 0x02, 0x20, 0x08, 0x5E, -0x00, 0x90, 0x0A, 0x20, 0x08, 0x5E, 0x03, 0x21, -0x89, 0x03, 0x48, 0x43, 0x00, 0x14, 0x01, 0x90, -0x31, 0x48, 0x01, 0x27, 0x01, 0x68, 0xB9, 0x43, -0x00, 0x24, 0x01, 0x60, 0x2F, 0x48, 0x00, 0x68, -0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x38, 0xD0, -0x65, 0x00, 0x2D, 0x4B, 0x2D, 0x48, 0x5E, 0x5F, -0x41, 0x5B, 0x00, 0x20, 0x71, 0x1A, 0x09, 0xB2, -0xCA, 0x00, 0x51, 0x1A, 0xCA, 0x17, 0x52, 0x0F, -0x51, 0x18, 0xC9, 0x10, 0x09, 0xB2, 0x59, 0x53, -0x00, 0x9A, 0x91, 0x42, 0x06, 0xDA, 0x0B, 0x46, -0x32, 0x46, 0x21, 0x46, 0x24, 0xA0, 0x01, 0xF0, -0x78, 0xFD, 0x01, 0x20, 0x00, 0x2E, 0x0E, 0xD1, -0x2B, 0x49, 0x2C, 0x4A, 0x49, 0x5B, 0x52, 0x5B, -0x89, 0x1A, 0x0A, 0xB2, 0x01, 0x99, 0x8A, 0x42, -0x05, 0xDA, 0x0B, 0x46, 0x21, 0x46, 0x28, 0xA0, -0x01, 0xF0, 0x67, 0xFD, 0x01, 0xE0, 0x00, 0x28, -0x06, 0xD0, 0x2F, 0x48, 0x01, 0x22, 0x01, 0x5D, -0x11, 0x43, 0x01, 0x55, 0x00, 0x27, 0x04, 0xE0, -0x2B, 0x48, 0x01, 0x5D, 0x49, 0x08, 0x49, 0x00, -0x01, 0x55, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C, -0xBC, 0xD3, 0x00, 0x99, 0x01, 0x2F, 0x10, 0xD0, -0x26, 0xA0, 0x01, 0xF0, 0x4E, 0xFD, 0x0A, 0x49, -0x01, 0x22, 0x08, 0x68, 0x10, 0x43, 0x08, 0x60, -0x29, 0xA0, 0x01, 0xF0, 0x46, 0xFD, 0x01, 0x21, -0x07, 0x48, 0x01, 0xF0, 0x29, 0xF9, 0x38, 0x46, -0xFE, 0xBD, 0x2A, 0xA0, 0x01, 0xF0, 0x3D, 0xFD, -0xF2, 0xE7, 0x00, 0x00, 0x84, 0x06, 0x00, 0x20, -0x0C, 0x05, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, -0x48, 0x03, 0x00, 0x20, 0x74, 0x04, 0x00, 0x20, -0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x20, 0x54, -0x48, 0x44, 0x20, 0x4F, 0x70, 0x65, 0x6E, 0x20, -0x4E, 0x47, 0x20, 0x44, 0x61, 0x74, 0x61, 0x5B, -0x25, 0x64, 0x5D, 0x5B, 0x25, 0x64, 0x3A, 0x25, -0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0xAC, 0x03, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, -0x4F, 0x70, 0x65, 0x6E, 0x20, 0x4E, 0x47, 0x20, -0x50, 0x69, 0x6E, 0x5B, 0x25, 0x64, 0x5D, 0x2C, -0x20, 0x43, 0x43, 0x5F, 0x44, 0x69, 0x66, 0x66, -0x3D, 0x25, 0x64, 0x2C, 0x20, 0x54, 0x48, 0x44, -0x3D, 0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, 0x00, -0xD8, 0x04, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, -0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x54, 0x48, -0x44, 0x3A, 0x25, 0x64, 0x29, 0x20, 0x5B, 0x4E, -0x47, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x51, 0x75, 0x69, 0x63, 0x6B, 0x20, 0x2D, 0x20, -0x42, 0x61, 0x73, 0x65, 0x6C, 0x69, 0x6E, 0x65, -0x0D, 0x0A, 0x00, 0x00, 0x4F, 0x70, 0x65, 0x6E, -0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x54, 0x48, -0x44, 0x3A, 0x25, 0x64, 0x29, 0x20, 0x5B, 0x50, -0x61, 0x73, 0x73, 0x5D, 0x0D, 0x0A, 0x00, 0x00, -0xF0, 0xB5, 0x17, 0x49, 0x00, 0x20, 0x7D, 0x27, -0x09, 0x68, 0xFF, 0x00, 0x02, 0x46, 0x89, 0x07, -0x24, 0xD5, 0x14, 0x49, 0x14, 0x4D, 0x09, 0x68, -0xCE, 0x26, 0x0B, 0x18, 0xDB, 0x7E, 0x41, 0x2B, -0x0A, 0xD0, 0x12, 0x4B, 0x44, 0x00, 0x1B, 0x5F, -0xBB, 0x42, 0x00, 0xDA, 0x01, 0x22, 0x2B, 0x5C, -0x9C, 0x07, 0x01, 0xD5, 0x33, 0x40, 0x2B, 0x54, -0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, 0xEC, 0xD3, -0x00, 0x2A, 0x0B, 0xD0, 0x00, 0x20, 0x0A, 0x18, -0xD2, 0x7E, 0x41, 0x2A, 0x02, 0xD0, 0x2A, 0x5C, -0x32, 0x40, 0x2A, 0x54, 0x40, 0x1C, 0xC0, 0xB2, -0x30, 0x28, 0xF4, 0xD3, 0x01, 0x20, 0xF0, 0xBD, -0x0C, 0x05, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, -0xD8, 0x04, 0x00, 0x20, 0xE4, 0x02, 0x00, 0x20, -0x70, 0xB5, 0x20, 0x48, 0x01, 0x25, 0xC4, 0x7D, -0x1F, 0x48, 0x80, 0x88, 0x40, 0x07, 0x01, 0xD4, -0x01, 0x20, 0x70, 0xBD, 0xFF, 0xF7, 0x2C, 0xFB, -0x1C, 0x48, 0xFE, 0xF7, 0x57, 0xFF, 0x00, 0x28, -0x0F, 0xD1, 0x1B, 0xA0, 0x01, 0xF0, 0x91, 0xFC, -0x01, 0x20, 0x20, 0x49, 0x00, 0x25, 0x08, 0x70, -0x00, 0x2C, 0x06, 0xD0, 0x64, 0x1E, 0xE4, 0xB2, -0x21, 0x46, 0x1D, 0xA0, 0x01, 0xF0, 0x85, 0xFC, -0xE8, 0xE7, 0xFE, 0xF7, 0x8D, 0xFF, 0xFF, 0xF7, -0x03, 0xFA, 0xFF, 0xF7, 0xD5, 0xFE, 0x00, 0x28, -0x07, 0xD1, 0x00, 0x25, 0x00, 0x2C, 0x04, 0xD0, -0x64, 0x1E, 0xE4, 0xB2, 0x21, 0x46, 0x1C, 0xA0, -0x0A, 0xE0, 0xFF, 0xF7, 0x1F, 0xFE, 0x00, 0x28, -0x0A, 0xD1, 0x00, 0x25, 0x00, 0x2C, 0x07, 0xD0, -0x64, 0x1E, 0xE4, 0xB2, 0x21, 0x46, 0x1C, 0xA0, -0x01, 0xF0, 0x67, 0xFC, 0x01, 0x25, 0xC9, 0xE7, -0x28, 0x46, 0x70, 0xBD, 0xA8, 0x06, 0x00, 0x20, -0x74, 0x06, 0x00, 0x20, 0xAC, 0x03, 0x00, 0x20, -0x4F, 0x70, 0x65, 0x6E, 0x20, 0x54, 0x65, 0x73, -0x74, 0x20, 0x43, 0x61, 0x6C, 0x69, 0x62, 0x72, -0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x4E, 0x47, -0x21, 0x0D, 0x0A, 0x00, 0xD8, 0x04, 0x00, 0x20, -0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x61, 0x6C, -0x69, 0x62, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, -0x20, 0x52, 0x65, 0x74, 0x72, 0x79, 0x20, 0x28, -0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, 0x00, -0x4F, 0x70, 0x65, 0x6E, 0x20, 0x54, 0x65, 0x73, -0x74, 0x20, 0x52, 0x65, 0x74, 0x72, 0x79, 0x20, -0x28, 0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, -0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x43, 0x20, -0x52, 0x65, 0x74, 0x72, 0x79, 0x20, 0x28, 0x25, -0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x10, 0xB5, 0x17, 0x4A, 0x11, 0x78, 0x81, 0x42, -0x29, 0xD0, 0x16, 0x49, 0x0B, 0x68, 0x01, 0x24, -0xA4, 0x02, 0x23, 0x43, 0x0B, 0x60, 0x10, 0x70, -0x05, 0x22, 0x40, 0x24, 0x12, 0x49, 0x12, 0x07, -0x80, 0x23, 0x00, 0x28, 0x09, 0xD0, 0x08, 0x68, -0x40, 0x06, 0x0C, 0xD4, 0x08, 0x68, 0x20, 0x43, -0x08, 0x60, 0xD0, 0x68, 0x98, 0x43, 0xD0, 0x60, -0x05, 0xE0, 0xD0, 0x68, 0x18, 0x43, 0xD0, 0x60, -0x08, 0x68, 0xA0, 0x43, 0x08, 0x60, 0x08, 0x68, -0x18, 0x43, 0x08, 0x60, 0xD0, 0x68, 0x01, 0x21, -0xC9, 0x03, 0x08, 0x43, 0xD0, 0x60, 0xE1, 0x20, -0x00, 0x02, 0x01, 0xF0, 0x3B, 0xF8, 0x10, 0xBD, -0x04, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, -0x00, 0x11, 0x00, 0x50, 0x06, 0x49, 0x83, 0x20, -0x08, 0x70, 0x06, 0x49, 0x00, 0x20, 0x08, 0x70, -0x05, 0x48, 0x00, 0x68, 0x05, 0x49, 0x40, 0x05, -0x40, 0x0F, 0x08, 0x73, 0x70, 0x47, 0x00, 0x00, -0x20, 0x08, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20, -0x00, 0x11, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20, -0xF0, 0xB5, 0x04, 0x46, 0x8F, 0xB0, 0xA6, 0x48, -0x0A, 0x90, 0xA5, 0x48, 0xA6, 0x4D, 0x60, 0x30, -0x0B, 0x90, 0xA4, 0x48, 0x03, 0x90, 0xA5, 0x48, -0x05, 0x95, 0x01, 0x68, 0x22, 0x46, 0xA4, 0xA0, -0x01, 0xF0, 0xC7, 0xFB, 0x03, 0x98, 0x08, 0x90, -0xA5, 0x48, 0xA6, 0x4A, 0x00, 0x21, 0x09, 0x95, -0x8B, 0x00, 0x08, 0x9D, 0x49, 0x1C, 0xE8, 0x50, -0x09, 0x9D, 0x89, 0xB2, 0xEA, 0x50, 0x18, 0x29, -0xF6, 0xD3, 0x01, 0x20, 0x80, 0x02, 0x06, 0x90, -0x20, 0x46, 0x07, 0x94, 0x50, 0x30, 0x00, 0xB2, -0x50, 0x3C, 0x0C, 0x90, 0x20, 0xB2, 0x0D, 0x90, -0x00, 0xF0, 0x96, 0xFC, 0x00, 0xF0, 0xC6, 0xFE, -0x00, 0xF0, 0x6E, 0xF9, 0x98, 0x48, 0x00, 0x78, -0xC0, 0x07, 0x18, 0xD1, 0x01, 0x90, 0x00, 0x20, -0x08, 0x9A, 0x81, 0x00, 0x53, 0x58, 0x0A, 0x9A, -0x40, 0x1C, 0x53, 0x50, 0x09, 0x9A, 0x80, 0xB2, -0x53, 0x58, 0x0B, 0x9A, 0x18, 0x28, 0x53, 0x50, -0xF2, 0xD3, 0x00, 0xF0, 0xC7, 0xFD, 0x00, 0xF0, -0xAD, 0xFE, 0x00, 0xF0, 0x55, 0xF9, 0x8C, 0x48, -0x00, 0x78, 0xC0, 0x07, 0x02, 0xD0, 0x00, 0x20, -0x0F, 0xB0, 0xF0, 0xBD, 0x3B, 0x46, 0x89, 0xA0, -0x00, 0x9A, 0x01, 0x99, 0x01, 0xF0, 0x81, 0xFB, -0x00, 0x25, 0x2D, 0xE0, 0x00, 0x2D, 0x2B, 0xD0, -0x01, 0x98, 0x05, 0x28, 0x73, 0xD9, 0x30, 0x2F, -0x71, 0xD2, 0x7A, 0x4C, 0x05, 0x98, 0x40, 0x3C, -0x04, 0x90, 0x60, 0x6B, 0x03, 0x26, 0x36, 0x05, -0x01, 0x21, 0xB0, 0x43, 0x49, 0x05, 0x40, 0x18, -0x60, 0x63, 0x00, 0xF0, 0x9F, 0xFD, 0x00, 0xF0, -0x85, 0xFE, 0x00, 0xF0, 0x2D, 0xF9, 0x60, 0x6B, -0x01, 0x21, 0xB0, 0x43, 0x09, 0x05, 0x40, 0x18, -0x60, 0x63, 0x00, 0x27, 0x00, 0x26, 0x79, 0x48, -0x00, 0x68, 0x80, 0x19, 0xC0, 0x7E, 0x41, 0x28, -0x0E, 0xD0, 0x70, 0x00, 0x04, 0x99, 0x02, 0x90, -0x0C, 0x5A, 0x16, 0x2E, 0x12, 0xD0, 0x1A, 0xE0, -0x00, 0x98, 0x30, 0x28, 0x77, 0xD0, 0x03, 0x98, -0x04, 0x90, 0x00, 0x20, 0x00, 0x90, 0xE9, 0xE7, -0x00, 0x2D, 0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, -0x68, 0xE0, 0x00, 0x98, 0x40, 0x1C, 0x80, 0xB2, -0x00, 0x90, 0x63, 0xE0, 0x6A, 0x48, 0x2C, 0x23, -0x00, 0x68, 0x22, 0x46, 0xC3, 0x5E, 0x31, 0x46, -0x68, 0xA0, 0x01, 0xF0, 0x36, 0xFB, 0x01, 0x98, -0x0A, 0x28, 0x0C, 0xD2, 0x64, 0x49, 0x02, 0x98, -0x09, 0x68, 0x09, 0x5E, 0x07, 0x98, 0x81, 0x42, -0x01, 0xDA, 0x06, 0x98, 0x84, 0x43, 0x06, 0x98, -0x40, 0x08, 0x04, 0x43, 0x32, 0xE0, 0x5E, 0x48, -0x02, 0x99, 0x00, 0x68, 0x41, 0x5E, 0x00, 0x29, -0x01, 0xDB, 0x08, 0x46, 0x00, 0xE0, 0x48, 0x42, -0x00, 0xB2, 0x40, 0x30, 0xC2, 0x17, 0x52, 0x0E, -0x10, 0x18, 0x0D, 0x9A, 0xC0, 0x11, 0x91, 0x42, -0x10, 0xDA, 0x21, 0x05, 0x06, 0xD5, 0x20, 0x1A, -0x84, 0xB2, 0x01, 0x20, 0xC0, 0x02, 0x84, 0x42, -0x11, 0xD3, 0x17, 0xE0, 0x84, 0x42, 0x03, 0xDD, -0x20, 0x1A, 0x84, 0xB2, 0x12, 0xE0, 0x2E, 0xE0, -0x00, 0x24, 0x0F, 0xE0, 0x0C, 0x9A, 0x91, 0x42, -0x15, 0xDD, 0x21, 0x05, 0x05, 0xD5, 0x20, 0x18, -0x84, 0xB2, 0x4D, 0x48, 0x04, 0xE0, 0x04, 0x46, -0x04, 0xE0, 0x20, 0x18, 0x84, 0xB2, 0x4B, 0x48, -0x84, 0x42, 0xF8, 0xD8, 0x00, 0x2D, 0x0F, 0xD0, -0x01, 0x20, 0xC0, 0x02, 0x04, 0x43, 0x05, 0x99, -0x02, 0x98, 0x0C, 0x52, 0x0E, 0xE0, 0x00, 0x2D, -0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, 0xF3, 0xE7, -0x00, 0x98, 0x40, 0x1C, 0x80, 0xB2, 0x00, 0x90, -0x03, 0x99, 0x02, 0x98, 0x0C, 0x52, 0x01, 0x98, -0x05, 0x28, 0xE9, 0xD9, 0x76, 0x1C, 0xB6, 0xB2, -0x30, 0x2E, 0x00, 0xD2, 0x77, 0xE7, 0x6D, 0x1C, -0xED, 0xB2, 0x02, 0x2D, 0x00, 0xD2, 0x51, 0xE7, -0x06, 0x98, 0x40, 0x08, 0x06, 0x90, 0x38, 0xA0, -0x01, 0xF0, 0xCB, 0xFA, 0x00, 0x98, 0x30, 0x28, -0x03, 0xD1, 0x30, 0x2F, 0x01, 0xD1, 0x40, 0x20, -0x01, 0x90, 0x01, 0x98, 0x40, 0x1C, 0x80, 0xB2, -0x01, 0x90, 0x40, 0x28, 0x00, 0xD8, 0x1A, 0xE7, -0x1C, 0x4C, 0x40, 0x3C, 0x60, 0x6B, 0x03, 0x25, -0x2D, 0x05, 0x01, 0x21, 0xA8, 0x43, 0x09, 0x05, -0x40, 0x18, 0x60, 0x63, 0x00, 0xF0, 0xE6, 0xFC, -0x00, 0xF0, 0xCC, 0xFD, 0x00, 0xF0, 0x74, 0xF8, -0x21, 0x4E, 0x01, 0x21, 0x30, 0x68, 0x00, 0xF0, -0x8F, 0xFE, 0x60, 0x6B, 0x01, 0x21, 0xA8, 0x43, -0x49, 0x05, 0x40, 0x18, 0x60, 0x63, 0x00, 0xF0, -0xD5, 0xFC, 0x00, 0xF0, 0xBB, 0xFD, 0x00, 0xF0, -0x63, 0xF8, 0x01, 0x21, 0x30, 0x68, 0x00, 0xF0, -0x7F, 0xFE, 0x60, 0x6B, 0x28, 0x43, 0x60, 0x63, -0x00, 0xF0, 0xC8, 0xFC, 0x00, 0xF0, 0xAE, 0xFD, -0x00, 0xF0, 0x56, 0xF8, 0x01, 0x21, 0x30, 0x68, -0x00, 0xF0, 0x72, 0xFE, 0x01, 0x20, 0xFF, 0xE6, -0x00, 0x20, 0x00, 0x50, 0xF4, 0x05, 0x00, 0x20, -0x00, 0x00, 0x01, 0x20, 0xC0, 0x10, 0x00, 0x50, -0x5B, 0x43, 0x46, 0x42, 0x3A, 0x25, 0x78, 0x3A, -0x25, 0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, -0x00, 0x04, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x0C, -0x30, 0x00, 0x00, 0x20, 0x5B, 0x25, 0x64, 0x3A, -0x25, 0x64, 0x3A, 0x25, 0x64, 0x5D, 0x20, 0x0D, -0x0A, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20, -0x4C, 0x01, 0x00, 0x20, 0x25, 0x64, 0x2C, 0x30, -0x78, 0x25, 0x78, 0x2C, 0x25, 0x64, 0x2C, 0x00, -0xFF, 0x0F, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, -0x0D, 0x0A, 0x00, 0x00, 0x10, 0xB5, 0x0B, 0x49, -0x30, 0x24, 0x00, 0x28, 0x0A, 0x4A, 0x0B, 0x4B, -0x08, 0x68, 0x06, 0xD0, 0x20, 0x43, 0x08, 0x60, -0x09, 0x48, 0x10, 0x60, 0x08, 0x48, 0x60, 0x30, -0x05, 0xE0, 0xA0, 0x43, 0x08, 0x60, 0x07, 0x48, -0x10, 0x60, 0x06, 0x48, 0x60, 0x30, 0x18, 0x60, -0x10, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, -0x4C, 0x01, 0x00, 0x20, 0x50, 0x01, 0x00, 0x20, -0x00, 0x00, 0x04, 0x20, 0x00, 0x10, 0x04, 0x20, -0xF0, 0xB5, 0x11, 0x48, 0x01, 0x68, 0x11, 0x4D, -0x89, 0x06, 0x30, 0x22, 0x60, 0x35, 0x00, 0x29, -0x0E, 0x4B, 0x0F, 0x4C, 0x0F, 0x4E, 0x10, 0x4F, -0x01, 0x68, 0x0A, 0xDA, 0x91, 0x43, 0x01, 0x60, -0x23, 0x60, 0x0E, 0x48, 0x35, 0x60, 0x07, 0x60, -0x0B, 0x48, 0x0D, 0x49, 0x60, 0x30, 0x08, 0x60, -0xF0, 0xBD, 0x11, 0x43, 0x01, 0x60, 0x08, 0x48, -0x27, 0x60, 0x60, 0x30, 0x30, 0x60, 0x07, 0x48, -0x03, 0x60, 0x07, 0x48, 0x05, 0x60, 0xF0, 0xBD, -0x00, 0x10, 0x00, 0x50, 0x00, 0x00, 0x04, 0x20, -0x40, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, -0x00, 0x10, 0x04, 0x20, 0x4C, 0x01, 0x00, 0x20, -0x50, 0x01, 0x00, 0x20, 0x00, 0xB5, 0x09, 0x48, -0x00, 0x78, 0x00, 0x28, 0x08, 0xD0, 0x01, 0x28, -0x09, 0xD0, 0x02, 0x28, 0x03, 0xD1, 0x06, 0x49, -0x04, 0x20, 0x00, 0xF0, 0x65, 0xF8, 0x00, 0xBD, -0x04, 0x49, 0x01, 0x20, 0xF9, 0xE7, 0x04, 0x49, -0x02, 0x20, 0xF6, 0xE7, 0x0D, 0x08, 0x00, 0x20, -0x70, 0x05, 0x00, 0x20, 0x1C, 0x02, 0x00, 0x20, -0x10, 0x05, 0x00, 0x20, 0xF8, 0xB5, 0x07, 0x46, -0x00, 0xF0, 0xD2, 0xFA, 0x21, 0x4D, 0x60, 0x21, -0x28, 0x46, 0xFE, 0xF7, 0x9A, 0xFA, 0x00, 0x24, -0x00, 0xF0, 0xFC, 0xFC, 0xFF, 0xF7, 0xA4, 0xFF, -0xFE, 0xF7, 0x31, 0xFB, 0x00, 0x2C, 0x0C, 0xD0, -0x1B, 0x49, 0x00, 0x20, 0x0E, 0x68, 0x42, 0x00, -0x51, 0x19, 0x0B, 0x88, 0xB2, 0x5A, 0x40, 0x1C, -0x9A, 0x18, 0xC0, 0xB2, 0x0A, 0x80, 0x30, 0x28, -0xF5, 0xD3, 0x64, 0x1C, 0xE4, 0xB2, 0x05, 0x2C, -0xE6, 0xD3, 0x00, 0x20, 0x41, 0x00, 0x49, 0x19, -0x00, 0x22, 0x8A, 0x5E, 0x40, 0x1C, 0x92, 0x10, -0xC0, 0xB2, 0x0A, 0x80, 0x30, 0x28, 0xF5, 0xD3, -0x39, 0x46, 0x0E, 0xA0, 0x01, 0xF0, 0xB9, 0xF9, -0x01, 0x21, 0x28, 0x46, 0x00, 0xF0, 0x9C, 0xFD, -0x01, 0x20, 0xFE, 0xF7, 0xB7, 0xFB, 0x0C, 0x4C, -0x20, 0x78, 0x38, 0x42, 0x08, 0xD1, 0x0B, 0x48, -0x60, 0x22, 0x29, 0x46, 0x00, 0x68, 0xFE, 0xF7, -0x47, 0xFA, 0x20, 0x78, 0x38, 0x43, 0x20, 0x70, -0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, 0x01, 0x20, -0x4C, 0x01, 0x00, 0x20, 0x42, 0x43, 0x5F, 0x4F, -0x4B, 0x5F, 0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, -0x38, 0x00, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, -0xF0, 0xB5, 0x21, 0x48, 0x00, 0x22, 0x00, 0x68, -0x20, 0x4C, 0xA0, 0x42, 0x28, 0xD8, 0x20, 0x48, -0x00, 0x78, 0x04, 0x28, 0x27, 0xD1, 0x1F, 0x4D, -0x1F, 0x4C, 0x2D, 0x68, 0x00, 0x20, 0x2E, 0x18, -0xF6, 0x7E, 0x41, 0x2E, 0x08, 0xD0, 0x43, 0x00, -0x1F, 0x19, 0x00, 0x26, 0xBE, 0x5F, 0xCB, 0x5E, -0xF3, 0x1A, 0x00, 0xD5, 0x5B, 0x42, 0x1B, 0xB2, -0x9A, 0x42, 0x00, 0xDA, 0x1A, 0x46, 0x40, 0x1C, -0xC0, 0xB2, 0x30, 0x28, 0xEB, 0xD3, 0x60, 0x20, -0x40, 0x5D, 0x90, 0x35, 0x40, 0x1C, 0x50, 0x43, -0x6A, 0x7B, 0x2B, 0x7B, 0x00, 0x11, 0x12, 0x02, -0x00, 0xB2, 0x1A, 0x43, 0x82, 0x42, 0x03, 0xDA, -0x0E, 0x49, 0x00, 0x20, 0x08, 0x70, 0xF0, 0xBD, -0x00, 0x20, 0x42, 0x00, 0x8B, 0x5E, 0x00, 0x26, -0xDD, 0x00, 0xEB, 0x1A, 0x15, 0x19, 0xAE, 0x5F, -0x40, 0x1C, 0x9B, 0x19, 0xDB, 0x10, 0xC0, 0xB2, -0x8B, 0x52, 0x30, 0x28, 0xF1, 0xD3, 0xF0, 0xBD, -0x5C, 0x01, 0x00, 0x20, 0x40, 0x77, 0x1B, 0x00, -0x93, 0x01, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, -0x00, 0x00, 0x01, 0x20, 0x38, 0x00, 0x00, 0x20, -0x70, 0xB5, 0x21, 0x48, 0x21, 0x49, 0x00, 0x78, -0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x08, 0xD0, -0x02, 0x28, 0x16, 0xD0, 0x05, 0x28, 0x2F, 0xD1, -0x34, 0xE0, 0x01, 0x25, 0x2C, 0x46, 0x1C, 0x48, -0x02, 0xE0, 0x02, 0x25, 0x1B, 0x48, 0x2C, 0x46, -0x08, 0x60, 0x00, 0x22, 0x1A, 0x49, 0x28, 0x46, -0xFE, 0xF7, 0x38, 0xFB, 0x00, 0x28, 0x18, 0xD0, -0x18, 0x48, 0x00, 0x78, 0x00, 0x28, 0x04, 0xD0, -0x13, 0xE0, 0x04, 0x25, 0x2C, 0x46, 0x16, 0x48, -0xEE, 0xE7, 0x16, 0x48, 0x41, 0x6B, 0x03, 0x22, -0x12, 0x05, 0x11, 0x43, 0x41, 0x63, 0x14, 0x48, -0x00, 0xF0, 0x4C, 0xFA, 0x00, 0x28, 0x0B, 0xD1, -0x20, 0x46, 0xFF, 0xF7, 0x27, 0xFF, 0x00, 0x28, -0x06, 0xD1, 0x01, 0x22, 0x21, 0x46, 0x28, 0x46, -0x00, 0xF0, 0x1E, 0xF8, 0x00, 0x28, 0x04, 0xD0, -0x0C, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, -0x00, 0x20, 0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD, -0x92, 0x01, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, -0x1C, 0x02, 0x00, 0x20, 0x10, 0x05, 0x00, 0x20, -0x00, 0x20, 0x00, 0x50, 0x3B, 0x01, 0x00, 0x20, -0x70, 0x05, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, -0x00, 0x00, 0x01, 0x20, 0x30, 0x00, 0x00, 0x20, -0xF7, 0xB5, 0x07, 0x46, 0x14, 0x48, 0x00, 0x25, -0x05, 0x70, 0x78, 0x07, 0x1D, 0xD0, 0x01, 0x21, -0x02, 0x20, 0x00, 0xF0, 0x45, 0xFB, 0x11, 0x4C, -0x60, 0x6B, 0x03, 0x26, 0x36, 0x05, 0x01, 0x21, -0xB0, 0x43, 0x09, 0x05, 0x40, 0x18, 0x60, 0x63, -0x00, 0x20, 0xFF, 0xF7, 0xFD, 0xFC, 0x00, 0x28, -0x0D, 0xD0, 0x01, 0x98, 0xFF, 0xF7, 0xE6, 0xFE, -0x01, 0x22, 0x09, 0x49, 0x38, 0x46, 0xFE, 0xF7, -0xD9, 0xFA, 0x08, 0x48, 0x05, 0x70, 0x08, 0x48, -0x05, 0x70, 0x01, 0x20, 0xFE, 0xBD, 0x60, 0x6B, -0x30, 0x43, 0x60, 0x63, 0x00, 0x20, 0xFE, 0xBD, -0x90, 0x01, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, -0x00, 0x20, 0x00, 0x50, 0x3B, 0x01, 0x00, 0x20, -0x3C, 0x01, 0x00, 0x20, 0x70, 0xB5, 0x10, 0x4C, -0x60, 0x6B, 0x03, 0x25, 0x2D, 0x05, 0x01, 0x26, -0xA8, 0x43, 0x36, 0x05, 0x80, 0x19, 0x60, 0x63, -0x0C, 0x49, 0x02, 0x20, 0x08, 0x5E, 0xFF, 0xF7, -0xCF, 0xFC, 0x00, 0x28, 0x60, 0x6B, 0x09, 0xD0, -0xA8, 0x43, 0x80, 0x19, 0x60, 0x63, 0x08, 0x49, -0x00, 0x20, 0x08, 0x70, 0x07, 0x49, 0x08, 0x70, -0x01, 0x20, 0x70, 0xBD, 0xA8, 0x43, 0x80, 0x19, -0x60, 0x63, 0x00, 0x20, 0x70, 0xBD, 0x00, 0x00, -0x80, 0x10, 0x00, 0x50, 0xCE, 0x00, 0x00, 0x20, -0x3B, 0x01, 0x00, 0x20, 0x3C, 0x01, 0x00, 0x20, -0xF0, 0xB5, 0xB4, 0x48, 0x01, 0x22, 0x23, 0x23, -0x13, 0x24, 0x92, 0x02, 0x5B, 0x01, 0xA4, 0x01, -0x05, 0x46, 0x1C, 0xC5, 0x29, 0x21, 0x49, 0x01, -0xC1, 0x60, 0xAF, 0x48, 0x07, 0x68, 0x38, 0x46, -0x40, 0x30, 0x45, 0x7C, 0x06, 0x7C, 0x2D, 0x04, -0x36, 0x02, 0x35, 0x43, 0x0C, 0x35, 0x15, 0x60, -0x45, 0x7C, 0x06, 0x7C, 0x2D, 0x04, 0x36, 0x02, -0x35, 0x43, 0x0C, 0x35, 0x55, 0x60, 0x01, 0x25, -0x95, 0x60, 0xD5, 0x60, 0x05, 0x7D, 0xBC, 0x46, -0x6E, 0x1E, 0x6D, 0x08, 0x6D, 0x1E, 0xF6, 0x05, -0xED, 0x05, 0xF6, 0x09, 0xED, 0x0D, 0x2E, 0x43, -0x16, 0x61, 0xA0, 0x4D, 0x55, 0x61, 0x03, 0x25, -0x95, 0x61, 0x03, 0x26, 0x36, 0x02, 0x00, 0x25, -0x16, 0x62, 0xD5, 0x61, 0x06, 0x7E, 0xD6, 0x62, -0x06, 0x7E, 0x87, 0x7E, 0xF6, 0x19, 0x16, 0x63, -0x87, 0x7E, 0x06, 0x7E, 0x7F, 0x00, 0xF6, 0x19, -0x56, 0x63, 0x95, 0x63, 0x06, 0x7E, 0xBC, 0x36, -0xD6, 0x63, 0x06, 0x7E, 0x87, 0x7E, 0xF6, 0x19, -0xBC, 0x36, 0x16, 0x64, 0x07, 0x7E, 0x86, 0x7E, -0x95, 0x64, 0x76, 0x00, 0xBC, 0x36, 0xBE, 0x19, -0x56, 0x64, 0x67, 0x46, 0x55, 0x62, 0x8E, 0x4E, -0x50, 0x37, 0x96, 0x62, 0xBC, 0x46, 0xFE, 0x7B, -0xBF, 0x7B, 0x36, 0x02, 0x3E, 0x43, 0xF6, 0x1C, -0xB7, 0x05, 0x8A, 0x4E, 0xBF, 0x0D, 0xBE, 0x19, -0xD6, 0x64, 0x89, 0x4E, 0x16, 0x65, 0x46, 0x7C, -0x0C, 0x3E, 0xF7, 0xB2, 0x05, 0x26, 0x76, 0x02, -0xBE, 0x19, 0x56, 0x65, 0x06, 0x7D, 0x36, 0x02, -0x21, 0x36, 0x96, 0x65, 0xD5, 0x65, 0x42, 0x7C, -0x06, 0x7C, 0x12, 0x04, 0x36, 0x02, 0x32, 0x43, -0x0C, 0x32, 0x1A, 0x60, 0x42, 0x7C, 0x06, 0x7C, -0x12, 0x04, 0x36, 0x02, 0x32, 0x43, 0x0C, 0x32, -0x5A, 0x60, 0x01, 0x22, 0x9A, 0x60, 0xDA, 0x60, -0x42, 0x7D, 0x56, 0x1E, 0x52, 0x08, 0x52, 0x1E, -0xF6, 0x05, 0xD2, 0x05, 0xF7, 0x09, 0xD2, 0x0D, -0x17, 0x43, 0x72, 0x4A, 0x1F, 0x61, 0x5A, 0x61, -0x03, 0x22, 0xDD, 0x61, 0x9A, 0x61, 0x12, 0x02, -0x1A, 0x62, 0x02, 0x7E, 0xDA, 0x62, 0x02, 0x7E, -0xC6, 0x7E, 0x92, 0x19, 0x1A, 0x63, 0xC6, 0x7E, -0x02, 0x7E, 0x76, 0x00, 0x92, 0x19, 0x9D, 0x63, -0x5A, 0x63, 0x02, 0x7E, 0x67, 0x46, 0xBC, 0x32, -0xDA, 0x63, 0x02, 0x7E, 0xC6, 0x7E, 0x92, 0x19, -0xBC, 0x32, 0x1A, 0x64, 0xC6, 0x7E, 0x02, 0x7E, -0x76, 0x00, 0xBC, 0x36, 0x9D, 0x64, 0x92, 0x19, -0x5D, 0x62, 0x5A, 0x64, 0x60, 0x4A, 0x9A, 0x62, -0xFA, 0x7B, 0xBE, 0x7B, 0x12, 0x02, 0x32, 0x43, -0xD2, 0x1C, 0x96, 0x05, 0x5D, 0x4A, 0xB6, 0x0D, -0xB2, 0x18, 0xDA, 0x64, 0x5C, 0x4A, 0x1A, 0x65, -0x42, 0x7C, 0x0C, 0x3A, 0xD6, 0xB2, 0x05, 0x22, -0x52, 0x02, 0xB2, 0x18, 0x5A, 0x65, 0x42, 0x7D, -0xDD, 0x65, 0x12, 0x02, 0x21, 0x32, 0x9A, 0x65, -0xC2, 0x7C, 0x83, 0x7C, 0x12, 0x04, 0x1B, 0x02, -0x1A, 0x43, 0x0C, 0x32, 0x23, 0x46, 0x1A, 0x60, -0xC2, 0x7C, 0x83, 0x7C, 0x12, 0x04, 0x1B, 0x02, -0x1A, 0x43, 0x0C, 0x32, 0x23, 0x46, 0x5A, 0x60, -0x82, 0x7D, 0x52, 0x1E, 0xD3, 0x05, 0xDB, 0x0D, -0x22, 0x46, 0x93, 0x60, 0x82, 0x7D, 0x52, 0x08, -0x52, 0x1E, 0xD2, 0x05, 0x23, 0x46, 0xD2, 0x0D, -0x1D, 0x61, 0xDA, 0x60, 0x47, 0x4A, 0x5A, 0x61, -0x26, 0x46, 0x01, 0x23, 0xF5, 0x61, 0xB3, 0x61, -0x13, 0x12, 0x33, 0x62, 0x43, 0x7E, 0xF3, 0x62, -0x43, 0x7E, 0x06, 0x7F, 0x9B, 0x19, 0x26, 0x46, -0x33, 0x63, 0x06, 0x7F, 0x43, 0x7E, 0x76, 0x00, -0x9B, 0x19, 0x26, 0x46, 0xB5, 0x63, 0xF5, 0x63, -0x35, 0x64, 0x73, 0x63, 0x65, 0x64, 0x38, 0x4B, -0xA5, 0x64, 0x3F, 0x33, 0x65, 0x62, 0xA3, 0x62, -0x36, 0x4B, 0xDB, 0x1C, 0xE3, 0x64, 0x36, 0x4B, -0x23, 0x65, 0xC6, 0x7C, 0x0C, 0x3E, 0xF7, 0xB2, -0x05, 0x26, 0x76, 0x02, 0xBF, 0x19, 0x67, 0x65, -0x87, 0x7D, 0x7F, 0x08, 0x3F, 0x02, 0x21, 0x37, -0xA7, 0x65, 0xE5, 0x65, 0xC4, 0x7C, 0x80, 0x7C, -0x24, 0x04, 0x00, 0x02, 0x04, 0x43, 0x0C, 0x34, -0x27, 0x48, 0x0C, 0x60, 0x00, 0x68, 0x40, 0x30, -0xC4, 0x7C, 0x87, 0x7C, 0x24, 0x04, 0x3F, 0x02, -0x3C, 0x43, 0x0C, 0x34, 0x4C, 0x60, 0xC4, 0x7D, -0x64, 0x1E, 0xE4, 0x05, 0xE4, 0x0D, 0x8C, 0x60, -0xC4, 0x7D, 0x0D, 0x61, 0x64, 0x08, 0x64, 0x1E, -0xE4, 0x05, 0xE4, 0x0D, 0xCC, 0x60, 0x4A, 0x61, -0x01, 0x22, 0xCD, 0x61, 0x8A, 0x61, 0x12, 0x02, -0x0A, 0x62, 0x42, 0x7E, 0xCA, 0x62, 0x42, 0x7E, -0x44, 0x7F, 0x12, 0x19, 0x0A, 0x63, 0x44, 0x7F, -0x42, 0x7E, 0x64, 0x00, 0x12, 0x19, 0x8D, 0x63, -0x4A, 0x63, 0x42, 0x7E, 0xBC, 0x32, 0xCA, 0x63, -0x42, 0x7E, 0x44, 0x7F, 0x12, 0x19, 0xBC, 0x32, -0x0A, 0x64, 0x42, 0x7F, 0x44, 0x7E, 0x52, 0x00, -0xBC, 0x32, 0xA2, 0x18, 0x8D, 0x64, 0x4A, 0x64, -0x01, 0x22, 0x92, 0x04, 0x4A, 0x62, 0x0C, 0x4A, -0x3F, 0x32, 0x8A, 0x62, 0x0B, 0x4A, 0x0B, 0x65, -0xD2, 0x1C, 0xCA, 0x64, 0xC2, 0x7C, 0x0C, 0x3A, -0xD2, 0xB2, 0x92, 0x19, 0x4A, 0x65, 0xC0, 0x7D, -0xCD, 0x65, 0x40, 0x08, 0x00, 0x02, 0x21, 0x30, -0x88, 0x65, 0xF0, 0xBD, 0xD0, 0x05, 0x00, 0x20, -0x98, 0x01, 0x00, 0x20, 0x03, 0x00, 0x03, 0x00, -0x00, 0x3F, 0x3F, 0x3F, 0x00, 0x30, 0xBC, 0x00, -0x00, 0x26, 0x31, 0x00, 0x01, 0x00, 0x01, 0x00, -0x10, 0xB5, 0x00, 0xF0, 0x2F, 0xFA, 0x00, 0xF0, -0x5D, 0xF9, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, -0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x00, 0x20, -0xFF, 0xF7, 0xAC, 0xFC, 0x09, 0x48, 0x01, 0x78, -0x09, 0x48, 0x00, 0x29, 0x04, 0xD0, 0x01, 0x68, -0xFF, 0x22, 0x01, 0x32, 0x11, 0x43, 0x01, 0x60, -0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x91, 0x43, -0x01, 0x60, 0x04, 0x49, 0x02, 0x20, 0x08, 0x72, -0x10, 0xBD, 0x00, 0x00, 0x31, 0x00, 0x00, 0x20, -0x00, 0x10, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20, -0x10, 0xB5, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, -0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x09, 0x48, -0x07, 0x49, 0x41, 0x60, 0x08, 0x49, 0x81, 0x60, -0xFF, 0xF7, 0x52, 0xFE, 0x80, 0x21, 0x07, 0x48, -0xFD, 0xF7, 0x93, 0xFF, 0x80, 0x21, 0x06, 0x48, -0xFD, 0xF7, 0x8F, 0xFF, 0x10, 0xBD, 0x00, 0x00, -0x1F, 0x1F, 0x5F, 0x1F, 0x00, 0x10, 0x00, 0x50, -0x1F, 0x1F, 0x1F, 0x1F, 0xDC, 0x08, 0x00, 0x20, -0x9C, 0x01, 0x00, 0x20, 0x10, 0xB5, 0x0F, 0x49, -0x0A, 0x78, 0x0F, 0x49, 0x00, 0x2A, 0x09, 0x78, -0x04, 0xD0, 0x01, 0x2A, 0x07, 0xD0, 0x02, 0x2A, -0x12, 0xD1, 0x09, 0xE0, 0xC9, 0x07, 0x0F, 0xD0, -0x60, 0x22, 0x0A, 0x49, 0x08, 0xE0, 0x89, 0x07, -0x0A, 0xD5, 0x60, 0x22, 0x08, 0x49, 0x03, 0xE0, -0x49, 0x07, 0x05, 0xD5, 0x60, 0x22, 0x07, 0x49, -0xFD, 0xF7, 0x4E, 0xFF, 0x01, 0x20, 0x10, 0xBD, -0x00, 0x20, 0x10, 0xBD, 0x92, 0x01, 0x00, 0x20, -0x38, 0x00, 0x00, 0x20, 0x1C, 0x02, 0x00, 0x20, -0x10, 0x05, 0x00, 0x20, 0x70, 0x05, 0x00, 0x20, -0x10, 0xB5, 0x13, 0x49, 0x13, 0x4B, 0x09, 0x68, -0x13, 0x4A, 0x40, 0x31, 0x00, 0x28, 0x0F, 0xD0, -0x01, 0x28, 0x12, 0xD0, 0x02, 0x28, 0x15, 0xD0, -0x03, 0x28, 0x08, 0xD1, 0x48, 0x7E, 0x84, 0x1E, -0x1C, 0x80, 0x49, 0x7F, 0x4B, 0x00, 0xC9, 0x18, -0x89, 0x1C, 0x40, 0x18, 0x10, 0x80, 0x10, 0xBD, -0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, 0x89, 0x7E, -0xF4, 0xE7, 0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, -0xC9, 0x7E, 0xEF, 0xE7, 0x48, 0x7E, 0x84, 0x1E, -0x1C, 0x80, 0x09, 0x7F, 0xEA, 0xE7, 0x00, 0x00, -0x98, 0x01, 0x00, 0x20, 0x3A, 0x00, 0x00, 0x20, -0x3C, 0x00, 0x00, 0x20, 0xF8, 0xB5, 0x05, 0x46, -0x20, 0x48, 0x00, 0x21, 0x01, 0x60, 0x41, 0x60, -0x1F, 0x49, 0x01, 0x20, 0x08, 0x70, 0x1F, 0x48, -0x1F, 0x4C, 0x00, 0x78, 0x00, 0x28, 0x06, 0xD0, -0x2D, 0x26, 0x09, 0x27, 0x01, 0x28, 0x0D, 0xD0, -0x02, 0x28, 0x29, 0xD1, 0x17, 0xE0, 0x1B, 0xA0, -0x00, 0xF0, 0x5B, 0xFE, 0x5A, 0x20, 0x00, 0x2D, -0x20, 0x70, 0x01, 0xD0, 0x00, 0x20, 0x0B, 0xE0, -0x01, 0x20, 0x09, 0xE0, 0x18, 0xA0, 0x00, 0xF0, -0x50, 0xFE, 0x00, 0x2D, 0x02, 0xD0, 0x26, 0x70, -0x02, 0x20, 0x01, 0xE0, 0x27, 0x70, 0x03, 0x20, -0x00, 0xF0, 0x0C, 0xF9, 0x10, 0xE0, 0x14, 0xA0, -0x00, 0xF0, 0x43, 0xFE, 0x00, 0x2D, 0x02, 0xD0, -0x26, 0x70, 0x02, 0x20, 0x01, 0xE0, 0x27, 0x70, -0x03, 0x20, 0x00, 0xF0, 0xFF, 0xF8, 0x11, 0x48, -0x81, 0x6A, 0x11, 0x4A, 0x11, 0x40, 0x81, 0x62, -0x05, 0x20, 0x10, 0x49, 0x00, 0x02, 0x08, 0x60, -0xF8, 0xBD, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, -0x31, 0x00, 0x00, 0x20, 0x92, 0x01, 0x00, 0x20, -0x94, 0x01, 0x00, 0x20, 0x41, 0x63, 0x74, 0x69, -0x76, 0x65, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x49, 0x64, 0x6C, 0x65, 0x0D, 0x0A, 0x00, 0x00, -0x47, 0x65, 0x73, 0x74, 0x75, 0x72, 0x65, 0x0D, -0x0A, 0x00, 0x00, 0x00, 0xC0, 0x11, 0x00, 0x50, -0xFF, 0xFF, 0x00, 0xF8, 0x00, 0x10, 0x00, 0x50, -0x70, 0xB5, 0x19, 0x4D, 0x19, 0x4C, 0x28, 0x70, -0x02, 0x46, 0x21, 0x78, 0x18, 0xA0, 0x00, 0xF0, -0x08, 0xFE, 0x1B, 0x49, 0x01, 0x20, 0x08, 0x70, -0x00, 0xF0, 0x20, 0xF9, 0x00, 0xF0, 0x4E, 0xF8, -0x01, 0x20, 0xFD, 0xF7, 0x45, 0xFF, 0xFF, 0xF7, -0xF5, 0xFB, 0x01, 0x20, 0xFF, 0xF7, 0x86, 0xFF, -0x00, 0xF0, 0x42, 0xFC, 0xFE, 0xF7, 0x5C, 0xFE, -0x28, 0x78, 0x20, 0x70, 0x05, 0x28, 0x0F, 0xD0, -0xFF, 0xF7, 0xAA, 0xFC, 0x00, 0x28, 0x0C, 0xD0, -0x0E, 0x48, 0x00, 0x7A, 0x00, 0x28, 0x03, 0xD1, -0x00, 0x21, 0x02, 0x20, 0x00, 0xF0, 0x48, 0xF8, -0x0B, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, -0x00, 0x20, 0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD, -0x92, 0x01, 0x00, 0x20, 0x0D, 0x08, 0x00, 0x20, -0x53, 0x4D, 0x3D, 0x5B, 0x25, 0x64, 0x3A, 0x25, -0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x91, 0x01, 0x00, 0x20, 0x20, 0x00, 0x00, 0x20, -0x30, 0x00, 0x00, 0x20, 0x08, 0x49, 0x02, 0x20, -0x08, 0x72, 0x08, 0x48, 0x01, 0x78, 0x08, 0x48, -0x00, 0x29, 0x01, 0x68, 0x04, 0xD0, 0x01, 0x22, -0x92, 0x02, 0x91, 0x43, 0x01, 0x60, 0x70, 0x47, -0x01, 0x22, 0x11, 0x43, 0xFA, 0xE7, 0x00, 0x00, -0x20, 0x00, 0x00, 0x20, 0x31, 0x00, 0x00, 0x20, -0x00, 0x10, 0x00, 0x50, 0x10, 0xB5, 0x08, 0x48, -0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x11, 0x43, -0x01, 0x60, 0x00, 0x68, 0x05, 0x4C, 0xC0, 0x07, -0x03, 0xD0, 0x02, 0x20, 0x20, 0x72, 0x00, 0xF0, -0xC1, 0xF8, 0x00, 0x20, 0x20, 0x72, 0x10, 0xBD, -0x00, 0x10, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20, -0x70, 0xB5, 0x05, 0x00, 0x0E, 0x46, 0x16, 0xD0, -0xFF, 0xF7, 0x82, 0xFE, 0x00, 0x24, 0x6D, 0x1E, -0x07, 0xE0, 0x00, 0xF0, 0xAF, 0xF8, 0xFF, 0xF7, -0x57, 0xFB, 0xFF, 0xF7, 0xC3, 0xFF, 0x64, 0x1C, -0xE4, 0xB2, 0xAC, 0x42, 0xF5, 0xDB, 0x00, 0xF0, -0xA5, 0xF8, 0xFF, 0xF7, 0x4D, 0xFB, 0x00, 0x2E, -0x02, 0xD0, 0xFF, 0xF7, 0xCF, 0xFF, 0x70, 0xBD, -0xFF, 0xF7, 0xB4, 0xFF, 0x70, 0xBD, 0x00, 0x00, -0x30, 0xB5, 0x01, 0x24, 0x1C, 0x4A, 0xA4, 0x07, -0x23, 0x13, 0x1B, 0x49, 0x40, 0x32, 0x00, 0x28, -0x21, 0xD0, 0x01, 0x28, 0x1F, 0xD0, 0x02, 0x28, -0x01, 0xD0, 0x03, 0x28, 0x1A, 0xD1, 0x25, 0x68, -0x1D, 0x43, 0x25, 0x60, 0x62, 0x23, 0x93, 0x61, -0xD3, 0x61, 0x14, 0x4B, 0xCB, 0x63, 0x14, 0x49, -0x09, 0x68, 0x40, 0x31, 0x02, 0x28, 0x48, 0x7E, -0x17, 0xD0, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D, -0x10, 0x60, 0x4B, 0x7E, 0x48, 0x7F, 0x41, 0x00, -0x40, 0x18, 0x80, 0x1C, 0x18, 0x18, 0x40, 0x05, -0x40, 0x0D, 0x90, 0x60, 0x30, 0xBD, 0x20, 0x68, -0x98, 0x43, 0x20, 0x60, 0x01, 0x20, 0x90, 0x61, -0xD0, 0x61, 0x00, 0x20, 0xC8, 0x63, 0x10, 0x60, -0xF3, 0xE7, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D, -0x10, 0x60, 0x4B, 0x7E, 0x08, 0x7F, 0xE6, 0xE7, -0x80, 0x11, 0x00, 0x50, 0x01, 0x00, 0x01, 0x00, -0x98, 0x01, 0x00, 0x20, 0x70, 0xB5, 0x04, 0x46, -0x81, 0x00, 0x25, 0x48, 0x41, 0x58, 0x25, 0x48, -0x0A, 0x68, 0x42, 0x60, 0x4A, 0x68, 0x82, 0x60, -0x8A, 0x68, 0x02, 0x61, 0xCA, 0x68, 0x42, 0x61, -0x21, 0x4D, 0x08, 0x69, 0x28, 0x62, 0x1F, 0x4A, -0x48, 0x69, 0xC0, 0x32, 0x90, 0x61, 0x88, 0x69, -0xD0, 0x61, 0x48, 0x6A, 0x10, 0x62, 0x88, 0x6A, -0x50, 0x62, 0x1A, 0x48, 0xCB, 0x69, 0x40, 0x38, -0xC3, 0x60, 0x0B, 0x6A, 0x03, 0x61, 0xCB, 0x6A, -0x93, 0x62, 0x0B, 0x6B, 0xD3, 0x63, 0x16, 0x4B, -0x4E, 0x6B, 0x80, 0x3B, 0x1E, 0x61, 0x8E, 0x6B, -0x5E, 0x62, 0xCE, 0x6B, 0xD6, 0x62, 0x0A, 0x6C, -0x1A, 0x60, 0x4A, 0x6C, 0x5A, 0x61, 0x8A, 0x6C, -0x9A, 0x62, 0xCA, 0x6C, 0xAA, 0x62, 0x0D, 0x4B, -0x0A, 0x6D, 0x40, 0x33, 0x5A, 0x63, 0x0B, 0x4B, -0x4A, 0x6D, 0x80, 0x33, 0x9A, 0x60, 0x0B, 0x4A, -0x8B, 0x6D, 0x13, 0x60, 0xC9, 0x6D, 0x51, 0x60, -0xC2, 0x68, 0x09, 0x49, 0x0A, 0x60, 0x00, 0x69, -0x48, 0x60, 0x20, 0x46, 0xFF, 0xF7, 0x50, 0xFE, -0x20, 0x46, 0xFF, 0xF7, 0x71, 0xFF, 0x70, 0xBD, -0xD0, 0x05, 0x00, 0x20, 0x40, 0x10, 0x00, 0x50, -0xC0, 0x11, 0x00, 0x50, 0x00, 0x19, 0x00, 0x50, -0x20, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x20, -0x40, 0x04, 0x00, 0x23, 0x0C, 0x4A, 0x0D, 0x49, -0x0D, 0x4C, 0x05, 0xE0, 0x0D, 0x78, 0xED, 0x07, -0x01, 0xD0, 0x13, 0x72, 0x09, 0xE0, 0x40, 0x1E, -0x15, 0x7A, 0x02, 0x2D, 0x02, 0xD0, 0x25, 0x68, -0xED, 0x07, 0x02, 0xD0, 0x00, 0x28, 0xF1, 0xD1, -0x01, 0xE0, 0x00, 0x28, 0x03, 0xD1, 0x13, 0x72, -0x04, 0xA0, 0x00, 0xF0, 0xC6, 0xFC, 0x70, 0xBD, -0x20, 0x00, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20, -0x00, 0x10, 0x00, 0x50, 0x57, 0x53, 0x46, 0x20, -0x54, 0x4F, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0xF8, 0xB5, 0x1D, 0x48, 0x01, 0x25, 0x01, 0x68, -0x00, 0x24, 0x02, 0x22, 0x91, 0x43, 0x1B, 0x4F, -0x1B, 0x4E, 0x01, 0x60, 0x1B, 0x48, 0x00, 0x68, -0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x14, 0xD0, -0x19, 0x48, 0x61, 0x00, 0x42, 0x5E, 0x04, 0x20, -0x38, 0x5E, 0x82, 0x42, 0x09, 0xDA, 0x21, 0x46, -0x16, 0xA0, 0x00, 0xF0, 0x9E, 0xFC, 0x30, 0x5D, -0x02, 0x21, 0x08, 0x43, 0x30, 0x55, 0x00, 0x25, -0x03, 0xE0, 0x30, 0x5D, 0xFD, 0x21, 0x08, 0x40, -0x30, 0x55, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C, -0xE0, 0xD3, 0x15, 0x49, 0x01, 0x2D, 0x79, 0x5E, -0x09, 0xD0, 0x14, 0xA0, 0x00, 0xF0, 0x89, 0xFC, -0x05, 0x49, 0x02, 0x22, 0x08, 0x68, 0x10, 0x43, -0x08, 0x60, 0x28, 0x46, 0xF8, 0xBD, 0x16, 0xA0, -0x00, 0xF0, 0x7F, 0xFC, 0xF9, 0xE7, 0x00, 0x00, -0x0C, 0x05, 0x00, 0x20, 0x84, 0x06, 0x00, 0x20, -0xD8, 0x04, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, -0xE4, 0x02, 0x00, 0x20, 0x53, 0x68, 0x6F, 0x72, -0x74, 0x20, 0x4E, 0x47, 0x20, 0x44, 0x61, 0x74, -0x61, 0x5B, 0x25, 0x64, 0x5D, 0x20, 0x3D, 0x20, -0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x04, 0x00, 0x00, 0x00, 0x53, 0x68, 0x6F, 0x72, -0x74, 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x54, -0x48, 0x44, 0x3A, 0x25, 0x64, 0x29, 0x20, 0x5B, -0x4E, 0x47, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, -0x53, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x54, 0x65, -0x73, 0x74, 0x28, 0x54, 0x48, 0x44, 0x3A, 0x25, -0x64, 0x29, 0x20, 0x5B, 0x50, 0x61, 0x73, 0x73, -0x5D, 0x0D, 0x0A, 0x00, 0x70, 0xB5, 0x0F, 0x48, -0x01, 0x25, 0xC4, 0x7F, 0x0E, 0x48, 0x80, 0x88, -0x00, 0x07, 0x01, 0xD4, 0x01, 0x20, 0x70, 0xBD, -0xFE, 0xF7, 0xB6, 0xFB, 0xFE, 0xF7, 0x1E, 0xFA, -0xFF, 0xF7, 0x7E, 0xFF, 0x00, 0x28, 0x0A, 0xD1, -0x00, 0x25, 0x00, 0x2C, 0x07, 0xD0, 0x64, 0x1E, -0xE4, 0xB2, 0x21, 0x46, 0x05, 0xA0, 0x00, 0xF0, -0x2C, 0xFC, 0x01, 0x25, 0xEE, 0xE7, 0x28, 0x46, -0x70, 0xBD, 0x00, 0x00, 0xA8, 0x06, 0x00, 0x20, -0x74, 0x06, 0x00, 0x20, 0x53, 0x68, 0x6F, 0x72, -0x74, 0x20, 0x52, 0x65, 0x74, 0x72, 0x79, 0x20, -0x28, 0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, -0xF8, 0xB5, 0x1B, 0x4E, 0x05, 0x46, 0x0C, 0x46, -0x80, 0x21, 0x30, 0x46, 0xFD, 0xF7, 0xC9, 0xFC, -0x00, 0x2C, 0x04, 0xD0, 0x31, 0x46, 0x28, 0x46, -0x00, 0xF0, 0x42, 0xFA, 0x04, 0xE0, 0x80, 0x22, -0x29, 0x46, 0x30, 0x46, 0xFD, 0xF7, 0xA4, 0xFC, -0x29, 0x46, 0x12, 0xA0, 0x00, 0xF0, 0x01, 0xFC, -0x00, 0x25, 0x14, 0x4F, 0x13, 0xE0, 0x00, 0x24, -0x08, 0xE0, 0x68, 0x43, 0x00, 0x19, 0x40, 0x00, -0x31, 0x5E, 0x11, 0xA0, 0x00, 0xF0, 0xF5, 0xFB, -0x64, 0x1C, 0xE4, 0xB2, 0x38, 0x68, 0x00, 0x7E, -0xA0, 0x42, 0xF2, 0xD8, 0x0E, 0xA0, 0x00, 0xF0, -0xEC, 0xFB, 0x6D, 0x1C, 0xED, 0xB2, 0x38, 0x68, -0x40, 0x7E, 0xA8, 0x42, 0xE7, 0xD8, 0x0A, 0xA0, -0x00, 0xF0, 0xE3, 0xFB, 0xF8, 0xBD, 0x00, 0x00, -0x4C, 0x00, 0x00, 0x20, 0x49, 0x6D, 0x61, 0x67, -0x65, 0x3A, 0x30, 0x78, 0x25, 0x78, 0x0D, 0x0A, -0x00, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20, -0x25, 0x36, 0x64, 0x2C, 0x00, 0x00, 0x00, 0x00, -0x0D, 0x0A, 0x00, 0x00, 0x08, 0x49, 0x8A, 0x78, -0x52, 0x1E, 0x8A, 0x70, 0x4B, 0x78, 0x0A, 0x1D, -0xD2, 0x5C, 0x02, 0x70, 0x48, 0x78, 0x40, 0x1C, -0x48, 0x70, 0x48, 0x78, 0x10, 0x28, 0x01, 0xD1, -0x00, 0x20, 0x48, 0x70, 0x70, 0x47, 0x00, 0x00, -0xE0, 0x05, 0x00, 0x20, 0xF8, 0xB5, 0x01, 0x27, -0x05, 0x46, 0xBF, 0x07, 0x38, 0x68, 0x08, 0x21, -0x08, 0x43, 0x38, 0x60, 0x01, 0x23, 0x15, 0x48, -0x80, 0x22, 0x02, 0x60, 0x14, 0x48, 0x00, 0x21, -0x81, 0x70, 0x01, 0x70, 0x41, 0x70, 0xC3, 0x70, -0x12, 0x4B, 0x98, 0x68, 0x02, 0x21, 0x88, 0x43, -0x98, 0x60, 0xF8, 0x68, 0x10, 0x43, 0xF8, 0x60, -0x0F, 0x4C, 0x61, 0x61, 0x0F, 0x48, 0x00, 0x68, -0xE9, 0x00, 0x46, 0x06, 0x0E, 0x48, 0xFD, 0xF7, -0x0D, 0xFC, 0xE0, 0x60, 0x30, 0x20, 0xA0, 0x60, -0x06, 0x49, 0x80, 0x20, 0x80, 0x39, 0x08, 0x60, -0x08, 0x20, 0x78, 0x60, 0xE0, 0x68, 0x68, 0x43, -0xC1, 0x00, 0x08, 0xA0, 0x00, 0xF0, 0x89, 0xFB, -0xF8, 0xBD, 0x00, 0x00, 0x80, 0xE1, 0x00, 0xE0, -0xE0, 0x05, 0x00, 0x20, 0x40, 0x09, 0x00, 0x50, -0x00, 0x02, 0x00, 0x50, 0x00, 0x11, 0x00, 0x50, -0x00, 0x36, 0x6E, 0x01, 0x55, 0x41, 0x52, 0x54, -0x28, 0x25, 0x64, 0x29, 0x21, 0x0D, 0x0A, 0x00, -0x70, 0xB5, 0x14, 0x4A, 0x91, 0x78, 0x14, 0x4C, -0x0E, 0x29, 0x02, 0xD3, 0x61, 0x68, 0x49, 0x07, -0xFC, 0xD5, 0x61, 0x69, 0x02, 0x25, 0xA9, 0x43, -0x61, 0x61, 0x91, 0x78, 0x00, 0x26, 0x10, 0x29, -0x0D, 0xD2, 0x0C, 0x49, 0x13, 0x78, 0x09, 0x1D, -0xC8, 0x54, 0x10, 0x78, 0x40, 0x1C, 0x10, 0x70, -0x10, 0x78, 0x10, 0x28, 0x00, 0xD1, 0x16, 0x70, -0x90, 0x78, 0x40, 0x1C, 0x90, 0x70, 0xD0, 0x78, -0x00, 0x28, 0x03, 0xD0, 0xD6, 0x70, 0x20, 0x46, -0xFF, 0xF7, 0x80, 0xFF, 0x60, 0x69, 0x28, 0x43, -0x60, 0x61, 0x70, 0xBD, 0xE0, 0x05, 0x00, 0x20, -0x00, 0x02, 0x00, 0x50, 0xFE, 0xB5, 0x2F, 0x48, -0x10, 0x26, 0x12, 0x27, 0x86, 0x5F, 0xC7, 0x5F, -0x2D, 0x48, 0x01, 0x90, 0x2D, 0x48, 0x01, 0x25, -0x01, 0x68, 0x2A, 0x04, 0x91, 0x43, 0x00, 0x24, -0x01, 0x60, 0x2B, 0x48, 0x00, 0x68, 0x00, 0x19, -0xC0, 0x7E, 0x41, 0x28, 0x26, 0xD0, 0x01, 0x99, -0x60, 0x00, 0x09, 0x5A, 0x4A, 0x05, 0x52, 0x0D, -0x26, 0x49, 0x53, 0x05, 0x0A, 0x52, 0x03, 0xD5, -0x89, 0x23, 0x9B, 0x00, 0xD2, 0x1A, 0x0A, 0x52, -0x23, 0x4A, 0x0B, 0x5A, 0x12, 0x5A, 0xD0, 0x1A, -0x00, 0xB2, 0xB0, 0x42, 0x01, 0xDC, 0xB8, 0x42, -0x0B, 0xDA, 0x00, 0x90, 0x21, 0x46, 0x1F, 0xA0, -0x00, 0xF0, 0x17, 0xFB, 0x23, 0x48, 0x10, 0x22, -0x01, 0x5D, 0x11, 0x43, 0x01, 0x55, 0x00, 0x25, -0x04, 0xE0, 0x20, 0x48, 0xEF, 0x22, 0x01, 0x5D, -0x11, 0x40, 0x01, 0x55, 0x64, 0x1C, 0xE4, 0xB2, -0x30, 0x2C, 0xCE, 0xD3, 0x01, 0x2D, 0x18, 0xD0, -0x10, 0x4A, 0x01, 0x20, 0x11, 0x68, 0x00, 0x04, -0x01, 0x43, 0x19, 0xA0, 0x11, 0x60, 0x00, 0xF0, -0xFC, 0xFA, 0x1C, 0xA0, 0x00, 0xF0, 0xF9, 0xFA, -0x01, 0x21, 0x0C, 0x48, 0xFF, 0xF7, 0xDC, 0xFE, -0x1A, 0xA0, 0x00, 0xF0, 0xF2, 0xFA, 0x01, 0x21, -0x09, 0x48, 0xFF, 0xF7, 0xD5, 0xFE, 0x28, 0x46, -0xFE, 0xBD, 0x19, 0xA0, 0x00, 0xF0, 0xE9, 0xFA, -0xEB, 0xE7, 0x00, 0x00, 0x84, 0x06, 0x00, 0x20, -0x00, 0x20, 0x00, 0x50, 0x0C, 0x05, 0x00, 0x20, -0x98, 0x01, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20, -0xEC, 0x06, 0x00, 0x20, 0x55, 0x43, 0x5B, 0x25, -0x64, 0x5D, 0x20, 0x4E, 0x47, 0x21, 0x20, 0x25, -0x64, 0x2D, 0x25, 0x64, 0x3D, 0x25, 0x64, 0x0D, -0x0A, 0x00, 0x00, 0x00, 0xD8, 0x04, 0x00, 0x20, -0x55, 0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, -0x5B, 0x4E, 0x47, 0x5D, 0x3A, 0x30, 0x78, 0x25, -0x78, 0x0D, 0x0A, 0x00, 0x55, 0x43, 0x0D, 0x0A, -0x00, 0x00, 0x00, 0x00, 0x47, 0x6F, 0x6C, 0x64, -0x65, 0x6E, 0x20, 0x55, 0x43, 0x0D, 0x0A, 0x00, -0x55, 0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, -0x5B, 0x50, 0x61, 0x73, 0x73, 0x5D, 0x0D, 0x0A, -0x00, 0x00, 0x00, 0x00, 0x70, 0xB5, 0x0E, 0x48, -0x01, 0x25, 0x44, 0x79, 0x0D, 0x48, 0x80, 0x88, -0xC0, 0x06, 0x01, 0xD4, 0x01, 0x20, 0x70, 0xBD, -0xFF, 0xF7, 0x5C, 0xFF, 0x00, 0x28, 0x0C, 0xD1, -0x00, 0x25, 0x00, 0x2C, 0x09, 0xD0, 0x64, 0x1E, -0xE4, 0xB2, 0xFE, 0xF7, 0x03, 0xF9, 0x21, 0x46, -0x05, 0xA0, 0x00, 0xF0, 0x96, 0xFA, 0x01, 0x25, -0xEE, 0xE7, 0x28, 0x46, 0x70, 0xBD, 0x00, 0x00, -0xC8, 0x06, 0x00, 0x20, 0x74, 0x06, 0x00, 0x20, -0x55, 0x43, 0x20, 0x4E, 0x47, 0x20, 0x52, 0x65, -0x74, 0x72, 0x79, 0x20, 0x28, 0x25, 0x64, 0x29, -0x0D, 0x0A, 0x00, 0x00, 0xF8, 0xB5, 0x3D, 0x4F, -0x01, 0x24, 0x38, 0x7B, 0x3C, 0x49, 0x0A, 0x78, -0x3C, 0x4E, 0x3D, 0x4D, 0x90, 0x42, 0x05, 0xD1, -0x28, 0x78, 0x81, 0x28, 0x02, 0xD0, 0x30, 0x78, -0xC0, 0x07, 0x63, 0xD0, 0x00, 0x20, 0x30, 0x70, -0x35, 0x48, 0x00, 0x78, 0x07, 0x28, 0x0C, 0xD3, -0x36, 0x48, 0x00, 0x68, 0x40, 0x05, 0x40, 0x0F, -0x38, 0x73, 0x01, 0x20, 0xFD, 0xF7, 0xD4, 0xFC, -0x00, 0x20, 0x30, 0x70, 0x38, 0x7B, 0x02, 0x28, -0x13, 0xD0, 0x38, 0x7B, 0x2C, 0x4F, 0x38, 0x70, -0x81, 0x20, 0x28, 0x70, 0x2E, 0x4A, 0x01, 0x21, -0x10, 0x88, 0x09, 0x03, 0x08, 0x43, 0x10, 0x80, -0x38, 0x78, 0x03, 0x00, 0xFD, 0xF7, 0x6E, 0xFB, -0x07, 0x25, 0x27, 0x37, 0x3D, 0x0B, 0x18, 0x0B, -0x3D, 0x00, 0x83, 0x20, 0x28, 0x70, 0x05, 0x20, -0xFF, 0xF7, 0x36, 0xFC, 0xF8, 0xBD, 0x25, 0xA0, -0x00, 0xF0, 0x43, 0xFA, 0x01, 0x20, 0xFE, 0xF7, -0x1B, 0xFE, 0x00, 0x20, 0xFF, 0xF7, 0x2C, 0xFC, -0x00, 0x28, 0x1E, 0xD0, 0x01, 0x20, 0x1A, 0xE0, -0x20, 0xA0, 0x00, 0xF0, 0x36, 0xFA, 0x01, 0x20, -0xFE, 0xF7, 0x0E, 0xFE, 0x01, 0x20, 0xFF, 0xF7, -0x1F, 0xFC, 0x00, 0x28, 0x11, 0xD0, 0x04, 0x20, -0x0D, 0xE0, 0x1C, 0xA0, 0x00, 0xE0, 0x1D, 0xA0, -0x00, 0xF0, 0x27, 0xFA, 0x00, 0x20, 0xFE, 0xF7, -0xFF, 0xFD, 0x02, 0x20, 0xFF, 0xF7, 0x10, 0xFC, -0x00, 0x28, 0x02, 0xD0, 0x02, 0x20, 0x28, 0x70, -0x07, 0xE0, 0x00, 0x24, 0x05, 0xE0, 0x83, 0x20, -0x28, 0x70, 0x05, 0x20, 0xFF, 0xF7, 0x04, 0xFC, -0x04, 0x46, 0x2A, 0x78, 0x39, 0x78, 0x13, 0xA0, -0x00, 0xF0, 0x0F, 0xFA, 0x30, 0x78, 0xC0, 0x07, -0x02, 0xD0, 0x81, 0x20, 0x28, 0x70, 0x00, 0x24, -0x20, 0x46, 0xF8, 0xBD, 0x20, 0x00, 0x00, 0x20, -0x39, 0x01, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20, -0x20, 0x08, 0x00, 0x20, 0x00, 0x11, 0x00, 0x50, -0x4A, 0x01, 0x00, 0x20, 0x44, 0x41, 0x0D, 0x0A, -0x00, 0x00, 0x00, 0x00, 0x44, 0x49, 0x0D, 0x0A, -0x00, 0x00, 0x00, 0x00, 0x44, 0x53, 0x54, 0x42, -0x0D, 0x0A, 0x00, 0x00, 0x44, 0x47, 0x0D, 0x0A, -0x00, 0x00, 0x00, 0x00, 0x44, 0x53, 0x50, 0x3D, -0x25, 0x64, 0x2C, 0x50, 0x57, 0x52, 0x3D, 0x25, -0x64, 0x20, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x70, 0xB5, 0x05, 0x46, 0x01, 0x24, 0xFE, 0xF7, -0x7B, 0xFA, 0x00, 0x2D, 0x09, 0xD0, 0xF0, 0x20, -0xFD, 0xF7, 0xC2, 0xFA, 0xFE, 0xF7, 0x42, 0xFB, -0xFE, 0xF7, 0xD8, 0xFA, 0x00, 0x20, 0xFE, 0xF7, -0xAB, 0xFD, 0xFE, 0xF7, 0xDF, 0xFD, 0xFF, 0xF7, -0xDF, 0xFA, 0x20, 0x46, 0x70, 0xBD, 0x00, 0x00, -0x30, 0xB5, 0x08, 0x4A, 0x14, 0x68, 0x1B, 0x34, -0x00, 0x22, 0xA3, 0x5C, 0x41, 0x2B, 0x03, 0xD0, -0x55, 0x00, 0x45, 0x5B, 0x5B, 0x00, 0xCD, 0x52, -0x52, 0x1C, 0xD2, 0xB2, 0x30, 0x2A, 0xF4, 0xD3, -0x30, 0xBD, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20, -0x70, 0xB5, 0x17, 0x4D, 0x16, 0x20, 0x28, 0x70, -0x68, 0x78, 0x29, 0x46, 0x00, 0x24, 0x14, 0x31, -0x02, 0x28, 0x0C, 0xD8, 0x00, 0x28, 0x05, 0xD1, -0xA8, 0x78, 0x00, 0x28, 0x02, 0xD0, 0x08, 0x46, -0x1C, 0x30, 0x04, 0x70, 0x48, 0x88, 0x00, 0x28, -0x01, 0xD1, 0x03, 0x20, 0x68, 0x71, 0xAC, 0x70, -0x2C, 0x81, 0xEC, 0x80, 0xAC, 0x81, 0x6C, 0x81, -0x4C, 0x80, 0x8C, 0x80, 0x0C, 0x72, 0x09, 0x48, -0x44, 0x70, 0x84, 0x70, 0x04, 0x70, 0x06, 0x48, -0xA0, 0x21, 0x30, 0x30, 0xFD, 0xF7, 0x45, 0xFA, -0xEC, 0x70, 0x5A, 0x20, 0xE8, 0x81, 0x2C, 0x71, -0x2C, 0x61, 0x00, 0xF0, 0x09, 0xFA, 0x70, 0xBD, -0x0C, 0x08, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, -0x06, 0x4A, 0x07, 0x4B, 0x00, 0x21, 0x00, 0x20, -0x40, 0x1C, 0x90, 0x42, 0xFC, 0xDB, 0x49, 0x1C, -0x99, 0x42, 0xF8, 0xDB, 0x03, 0x48, 0x01, 0x21, -0x01, 0x60, 0x70, 0x47, 0x10, 0x27, 0x00, 0x00, -0xB8, 0x0B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, -0x70, 0xB5, 0x7B, 0x24, 0x21, 0x48, 0x24, 0x02, -0x04, 0x60, 0xF7, 0x20, 0xC0, 0x01, 0x20, 0x4A, -0xC1, 0x7C, 0x11, 0x70, 0x1F, 0x4A, 0x01, 0x7D, -0x11, 0x70, 0x1F, 0x4A, 0x41, 0x7D, 0x11, 0x70, -0x1E, 0x49, 0x80, 0x7D, 0x08, 0x70, 0x60, 0x7C, -0x21, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, -0x1B, 0x49, 0x05, 0x46, 0x08, 0x80, 0xE0, 0x7C, -0xA1, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, -0x18, 0x49, 0x08, 0x80, 0x20, 0x7E, 0x61, 0x7E, -0x48, 0x43, 0x17, 0x49, 0x08, 0x80, 0x17, 0x48, -0xFD, 0xF7, 0x07, 0xFA, 0x10, 0x21, 0xC8, 0x41, -0x15, 0x49, 0x08, 0x60, 0x04, 0x22, 0x21, 0x1D, -0x14, 0x48, 0xFD, 0xF7, 0xD9, 0xF9, 0x11, 0x49, -0x06, 0x22, 0x89, 0x1F, 0x12, 0x48, 0xFD, 0xF7, -0xD3, 0xF9, 0x60, 0x7D, 0x22, 0x7D, 0x01, 0x02, -0x11, 0x43, 0x28, 0x46, 0x0A, 0x22, 0x50, 0x43, -0xFD, 0xF7, 0xB6, 0xF9, 0x0D, 0x49, 0x08, 0x80, -0x70, 0xBD, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20, -0x0C, 0x00, 0x00, 0x20, 0x0D, 0x00, 0x00, 0x20, -0x0E, 0x00, 0x00, 0x20, 0x0F, 0x00, 0x00, 0x20, -0x1C, 0x00, 0x00, 0x20, 0x1E, 0x00, 0x00, 0x20, -0x18, 0x00, 0x00, 0x20, 0x10, 0x7B, 0x00, 0x00, -0xD8, 0x06, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20, -0xE4, 0x06, 0x00, 0x20, 0x1A, 0x00, 0x00, 0x20, -0x00, 0x20, 0x0E, 0x49, 0x08, 0x70, 0x17, 0xE0, -0x0C, 0x48, 0x00, 0x78, 0x00, 0x28, 0x02, 0xD0, -0x07, 0x28, 0x0F, 0xD1, 0x0B, 0xE0, 0x00, 0xF0, -0x13, 0xF8, 0x01, 0x28, 0x03, 0xD1, 0x07, 0x20, -0x06, 0x49, 0x08, 0x70, 0x02, 0xE0, 0x03, 0x20, -0x04, 0x49, 0x08, 0x70, 0x03, 0xE0, 0xFD, 0xF7, -0x5B, 0xFA, 0x00, 0xE0, 0x00, 0xBF, 0x00, 0xBF, -0xE6, 0xE7, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x20, -0x10, 0xB5, 0x00, 0x24, 0xFD, 0xF7, 0xBC, 0xFC, -0x00, 0xF0, 0x9A, 0xF9, 0x01, 0x20, 0xFF, 0xF7, -0x03, 0xFF, 0x04, 0x46, 0x00, 0x2C, 0x04, 0xD1, -0x05, 0xA0, 0x00, 0xF0, 0xDE, 0xF8, 0x00, 0x20, -0x10, 0xBD, 0x06, 0x49, 0x06, 0x48, 0x81, 0x70, -0x09, 0x0A, 0xC1, 0x70, 0x01, 0x20, 0xF7, 0xE7, -0x49, 0x4E, 0x49, 0x54, 0x20, 0x4E, 0x47, 0x0D, -0x0A, 0x00, 0x00, 0x00, 0xAA, 0x55, 0x00, 0x00, -0x88, 0x02, 0x00, 0x20, 0xF7, 0xB5, 0x00, 0x25, -0x84, 0xB0, 0x0C, 0x46, 0x16, 0x46, 0x61, 0x27, -0x5C, 0xE0, 0x25, 0x28, 0x54, 0xD1, 0x64, 0x1C, -0x00, 0x22, 0x20, 0x78, 0x13, 0x46, 0x00, 0x28, -0x57, 0xD0, 0x25, 0x28, 0x4C, 0xD0, 0x2D, 0x28, -0x01, 0xD1, 0x64, 0x1C, 0x01, 0x23, 0x02, 0x20, -0x21, 0x78, 0x30, 0x29, 0x07, 0xD1, 0x64, 0x1C, -0x03, 0x43, 0xF9, 0xE7, 0x0A, 0x21, 0x4A, 0x43, -0x30, 0x3A, 0x82, 0x18, 0x64, 0x1C, 0x20, 0x78, -0x01, 0x46, 0x30, 0x39, 0x09, 0x29, 0xF5, 0xD9, -0xC1, 0xB2, 0x73, 0x29, 0x0A, 0xD0, 0x64, 0x28, -0x10, 0xD0, 0x78, 0x28, 0x13, 0xD0, 0x58, 0x28, -0x19, 0xD0, 0x75, 0x28, 0x1F, 0xD0, 0x63, 0x28, -0x23, 0xD0, 0x2E, 0xE0, 0x02, 0xCE, 0x00, 0x29, -0x00, 0xD1, 0x1D, 0xA1, 0x04, 0x98, 0x00, 0xF0, -0xCB, 0xF8, 0x0A, 0xE0, 0x68, 0x46, 0x8C, 0xC0, -0x02, 0xCE, 0x01, 0x23, 0x13, 0xE0, 0x68, 0x46, -0x8C, 0xC0, 0x08, 0xE0, 0x04, 0x98, 0x00, 0xF0, -0x3C, 0xF8, 0x45, 0x19, 0x19, 0xE0, 0x41, 0x20, -0x01, 0x93, 0x00, 0x92, 0x02, 0x90, 0x02, 0xCE, -0x00, 0x23, 0x10, 0x22, 0xF2, 0xE7, 0x68, 0x46, -0x8C, 0xC0, 0x02, 0xCE, 0x00, 0x23, 0x0A, 0x22, -0xEC, 0xE7, 0x02, 0xCE, 0x68, 0x46, 0x01, 0x73, -0x00, 0x21, 0x41, 0x73, 0x03, 0xA9, 0xD9, 0xE7, -0xC1, 0xB2, 0x04, 0x98, 0x00, 0xF0, 0x14, 0xF8, -0x6D, 0x1C, 0x64, 0x1C, 0x20, 0x78, 0x00, 0x28, -0x9F, 0xD1, 0x04, 0x98, 0x00, 0x28, 0x03, 0xD0, -0x04, 0x99, 0x00, 0x20, 0x09, 0x68, 0x08, 0x70, -0x28, 0x46, 0x07, 0xB0, 0xF0, 0xBD, 0x00, 0x00, -0x28, 0x6E, 0x75, 0x6C, 0x6C, 0x29, 0x00, 0x00, -0x10, 0xB5, 0x00, 0x28, 0x05, 0xD0, 0x02, 0x68, -0x11, 0x70, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, -0x10, 0xBD, 0xC8, 0xB2, 0xFF, 0xF7, 0xD4, 0xFC, -0x10, 0xBD, 0xFF, 0xB5, 0x00, 0x27, 0x83, 0xB0, -0x0C, 0x9D, 0x3E, 0x46, 0x08, 0x00, 0x3A, 0x46, -0x05, 0xD0, 0x00, 0x2B, 0x12, 0xD0, 0x05, 0x9B, -0x0A, 0x2B, 0x0B, 0xD0, 0x0E, 0xE0, 0x30, 0x20, -0x69, 0x46, 0x08, 0x70, 0x4A, 0x70, 0x2A, 0x46, -0x0D, 0x9B, 0x03, 0x98, 0x00, 0xF0, 0x6C, 0xF8, -0x07, 0xB0, 0xF0, 0xBD, 0x00, 0x29, 0x01, 0xDA, -0x01, 0x27, 0x40, 0x42, 0x02, 0xAC, 0x69, 0x46, -0x03, 0x34, 0xCA, 0x72, 0x0A, 0xE0, 0x05, 0x99, -0xFD, 0xF7, 0x98, 0xF8, 0x0A, 0x29, 0x02, 0xDB, -0x0E, 0x9A, 0x89, 0x18, 0x3A, 0x39, 0x30, 0x31, -0x64, 0x1E, 0x21, 0x70, 0x00, 0x28, 0xF2, 0xD1, -0x00, 0x2F, 0x0E, 0xD0, 0x00, 0x2D, 0x09, 0xD0, -0x0D, 0x98, 0x80, 0x07, 0x06, 0xD5, 0x2D, 0x21, -0x03, 0x98, 0xFF, 0xF7, 0xB9, 0xFF, 0x76, 0x1C, -0x6D, 0x1E, 0x02, 0xE0, 0x2D, 0x20, 0x64, 0x1E, -0x20, 0x70, 0x2A, 0x46, 0x21, 0x46, 0x0D, 0x9B, -0x03, 0x98, 0x00, 0xF0, 0x3D, 0xF8, 0x80, 0x19, -0xCE, 0xE7, 0x0F, 0xB4, 0x10, 0xB5, 0x03, 0xAA, -0x00, 0x20, 0x02, 0x99, 0xFF, 0xF7, 0x2E, 0xFF, -0x10, 0xBC, 0x08, 0xBC, 0x04, 0xB0, 0x18, 0x47, -0x36, 0x03, 0x35, 0x03, 0x2C, 0x03, 0x9C, 0x03, -0xE9, 0x02, 0x28, 0x03, 0x29, 0x03, 0x1C, 0x03, -0x9D, 0x03, 0x2F, 0x03, 0x4A, 0x03, 0x4D, 0x03, -0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, -0x3D, 0x03, 0x3C, 0x03, 0x2C, 0x03, 0x9C, 0x03, -0xD3, 0x02, 0x28, 0x03, 0x29, 0x03, 0xD6, 0x02, -0x9E, 0x03, 0x30, 0x03, 0x37, 0x03, 0x3A, 0x03, -0xFF, 0x03, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xB5, 0x00, 0x25, 0x20, 0x27, 0x81, 0xB0, -0x0E, 0x46, 0x14, 0x46, 0x00, 0x2A, 0x0E, 0xDD, -0x00, 0x20, 0x01, 0xE0, 0x40, 0x1C, 0x49, 0x1C, -0x0A, 0x78, 0x00, 0x2A, 0xFA, 0xD1, 0xA0, 0x42, -0x01, 0xDB, 0x00, 0x24, 0x00, 0xE0, 0x24, 0x1A, -0x98, 0x07, 0x00, 0xD5, 0x30, 0x27, 0xD8, 0x07, -0x06, 0xD0, 0x0D, 0xE0, 0x39, 0x46, 0x01, 0x98, -0xFF, 0xF7, 0x52, 0xFF, 0x6D, 0x1C, 0x64, 0x1E, -0x00, 0x2C, 0xF7, 0xDC, 0x04, 0xE0, 0x01, 0x98, -0xFF, 0xF7, 0x4A, 0xFF, 0x6D, 0x1C, 0x76, 0x1C, -0x31, 0x78, 0x00, 0x29, 0xF7, 0xD1, 0x05, 0xE0, -0x39, 0x46, 0x01, 0x98, 0xFF, 0xF7, 0x40, 0xFF, -0x6D, 0x1C, 0x64, 0x1E, 0x00, 0x2C, 0xF7, 0xDC, -0x28, 0x46, 0x05, 0xB0, 0xF0, 0xBD, 0x00, 0x00, -0x05, 0x48, 0x01, 0x78, 0x82, 0x29, 0x05, 0xD1, -0xC0, 0x79, 0x01, 0x28, 0x02, 0xD1, 0x03, 0x49, -0xFF, 0x20, 0x08, 0x70, 0x70, 0x47, 0x00, 0x00, -0x20, 0x08, 0x00, 0x20, 0x40, 0x01, 0x00, 0x20, -0x05, 0x48, 0x00, 0x21, 0x01, 0x80, 0x41, 0x80, -0x04, 0x49, 0x81, 0x80, 0xC1, 0x80, 0x04, 0x49, -0x03, 0x20, 0x08, 0x70, 0x70, 0x47, 0x00, 0x00, -0x10, 0x00, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, -0x90, 0x01, 0x00, 0x20, 0x08, 0x49, 0x5A, 0x20, -0x08, 0x70, 0x08, 0x49, 0x00, 0x20, 0x08, 0x70, -0x08, 0x48, 0x07, 0x49, 0x81, 0x80, 0xC1, 0x80, -0x07, 0x49, 0x03, 0x20, 0x08, 0x70, 0x07, 0x49, -0x01, 0x20, 0x08, 0x70, 0x70, 0x47, 0x00, 0x00, -0x94, 0x01, 0x00, 0x20, 0x0F, 0x08, 0x00, 0x20, -0xFF, 0x7F, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, -0x90, 0x01, 0x00, 0x20, 0x91, 0x01, 0x00, 0x20, -0x10, 0xB5, 0xFF, 0xF7, 0xDD, 0xFD, 0xFF, 0xF7, -0xDD, 0xFF, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x40, -0x10, 0x03, 0x42, 0x88, 0x0C, 0x00, 0x00, 0x40, -0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x50, -0x1F, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x50, -0x64, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x50, -0x77, 0x00, 0x01, 0x48, 0x44, 0x09, 0x00, 0x50, -0x39, 0x5A, 0x5B, 0x00, 0x10, 0x06, 0x00, 0x50, -0x00, 0x00, 0x06, 0x07, 0x00, 0x06, 0x00, 0x50, -0x00, 0x00, 0x00, 0x78, 0x08, 0x06, 0x00, 0x50, -0x0C, 0x30, 0x00, 0x00, 0x28, 0x06, 0x00, 0x50, -0x06, 0x00, 0x00, 0x00, 0x2C, 0x06, 0x00, 0x50, -0x0A, 0x66, 0x00, 0x00, 0x30, 0x06, 0x00, 0x50, -0xCC, 0x02, 0x00, 0x20, 0x34, 0x06, 0x00, 0x50, -0x00, 0x20, 0x00, 0x00, 0x44, 0x00, 0x00, 0x50, -0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x50, -0x50, 0x71, 0x00, 0x00, 0x20, 0x00, 0x00, 0x50, -0x24, 0x29, 0x00, 0x00, 0x14, 0x00, 0x00, 0x40, -0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -0x14, 0x33, 0x43, 0xC8, 0x0C, 0x00, 0x00, 0x40, -0x29, 0x0A, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, -0x10, 0x32, 0x00, 0x00, 0x1C, 0x0E, 0x00, 0x50, -0x03, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x50, -0x14, 0x07, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x50, -0x00, 0x00, 0x00, 0x00, 0x58, 0x10, 0x00, 0x50, -0x01, 0x00, 0x00, 0x00, 0x04, 0x11, 0x00, 0x50, -0x78, 0x11, 0x00, 0x00, 0x0C, 0x11, 0x00, 0x50, -0x01, 0x00, 0x00, 0x00, 0x10, 0x11, 0x00, 0x50, -0x78, 0x01, 0x00, 0x00, 0x14, 0x11, 0x00, 0x50, -0xC8, 0x03, 0x60, 0x00, 0x4C, 0x00, 0x00, 0x50, -0x31, 0x00, 0x00, 0x00, 0x3C, 0x10, 0x00, 0x50, -0x00, 0x00, 0x10, 0x00, 0xB4, 0x10, 0x00, 0x50, -0x00, 0x26, 0x31, 0x00, 0xC0, 0x10, 0x00, 0x50, -0x33, 0x03, 0x33, 0x03, 0xC4, 0x10, 0x00, 0x50, -0x33, 0x03, 0x33, 0x03, 0xC8, 0x10, 0x00, 0x50, -0x0C, 0x0A, 0x00, 0x00, 0xCC, 0x10, 0x00, 0x50, -0x1A, 0x00, 0x00, 0x00, 0xD0, 0x10, 0x00, 0x50, -0x03, 0x19, 0x19, 0x00, 0xF0, 0x11, 0x00, 0x50, -0x12, 0x00, 0x00, 0x00, 0xEC, 0x11, 0x00, 0x50, -0x5C, 0x00, 0x00, 0x00, 0xF4, 0x11, 0x00, 0x50, -0x01, 0x00, 0x01, 0x00, 0x2C, 0x10, 0x00, 0x50, -0x10, 0x00, 0x90, 0x00, 0x30, 0x10, 0x00, 0x50, -0x20, 0x0C, 0x90, 0x00, 0x34, 0x10, 0x00, 0x50, -0x30, 0x0C, 0x30, 0x0C, 0x38, 0x10, 0x00, 0x50, -0xFF, 0x0F, 0x00, 0x00, 0x7C, 0x10, 0x00, 0x50, -0x88, 0x88, 0xFE, 0x88, 0x80, 0x10, 0x00, 0x50, -0x88, 0xFF, 0x00, 0x00, 0x84, 0x10, 0x00, 0x50, -0x55, 0x55, 0x55, 0x55, 0x88, 0x10, 0x00, 0x50, -0x55, 0x55, 0x55, 0x55, 0x8C, 0x10, 0x00, 0x50, -0x55, 0x55, 0x55, 0x55, 0xE8, 0x10, 0x00, 0x50, -0x3F, 0x16, 0x3F, 0x15, 0x04, 0x00, 0x00, 0x40, -0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0x00, 0x00, 0x00, 0x00, 0x3C, 0x3B, 0x00, 0x00, -0x04, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, -0x5C, 0x0A, 0x00, 0x00, 0x44, 0x3B, 0x00, 0x00, -0x0C, 0x00, 0x00, 0x20, 0x54, 0x0E, 0x00, 0x00, -0x6A, 0x0A, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, -0x78, 0x7F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, @@ -6821,895 +7704,12 @@ const unsigned char u8_rad_testfw_30[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x94, 0x41, 0x4C, +0xFF, 0xFF, 0xFF, 0xFF, 0x42, 0x53, 0x71, 0x97, }; const unsigned char u8_rad_testpara_30[] = { -0xA1, 0x00, 0x03, 0xF3, 0x02, 0x02, 0x40, 0x01, +0xA2, 0x00, 0x03, 0xF3, 0x02, 0x02, 0x40, 0x04, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, -0x40, 0x01, 0x68, 0x01, 0x44, 0x01, 0x44, 0x01, +0x40, 0x01, 0x68, 0x01, 0xFA, 0x00, 0x18, 0x01, 0x05, 0x05, 0x00, 0x00, 0x01, 0x05, 0x41, 0x06, 0x0A, 0x0B, 0x41, 0x0F, 0x10, 0x14, 0x41, 0x15, 0x02, 0x07, 0x41, 0x16, 0x41, 0x41, 0x41, 0x41, @@ -7717,20 +7717,20 @@ const unsigned char u8_rad_testpara_30[] = { 0x0E, 0x0D, 0x41, 0x13, 0x12, 0x18, 0x41, 0x17, 0x0C, 0x11, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, -0x0C, 0x3E, 0x0C, 0x3E, 0x08, 0x08, 0x02, 0x02, -0x14, 0x03, 0x08, 0x08, 0x01, 0x01, 0xC8, 0x00, -0x1A, 0x01, 0x08, 0x14, 0x82, 0x00, 0x00, 0x00, +0x0C, 0x3E, 0x0C, 0x3E, 0x06, 0x06, 0x02, 0x02, +0x14, 0x03, 0x06, 0x06, 0x01, 0x01, 0xE6, 0x00, +0x1F, 0x01, 0x08, 0x14, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x01, -0xA0, 0x00, 0x64, 0x00, 0x4A, 0x00, 0x0A, 0x78, -0x0F, 0x08, 0x1A, 0x4A, 0x4A, 0x3C, 0x3C, 0x00, -0x00, 0x00, 0x6A, 0x00, 0x38, 0x00, 0x1C, 0x00, -0x06, 0x32, 0x69, 0xE3, 0x0D, 0x00, 0x00, 0x72, -0x45, 0x00, 0x00, 0x06, 0x00, 0xA0, 0x00, 0x78, -0x00, 0x64, 0xDB, 0x13, 0xB5, 0x0C, 0x2D, 0x03, -0xCB, 0x00, 0x14, 0x0A, 0xB1, 0x01, 0xF1, 0x05, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, 0x01, +0xB2, 0x00, 0x73, 0x00, 0x3C, 0x00, 0x09, 0x78, +0x0F, 0x08, 0x1D, 0x40, 0x48, 0x40, 0x48, 0x00, +0x00, 0x00, 0x88, 0x00, 0x62, 0x00, 0x24, 0x00, +0x06, 0x32, 0x6C, 0x41, 0x0F, 0x00, 0x00, 0x45, +0x4C, 0x00, 0x00, 0x06, 0x00, 0xA0, 0x00, 0x78, +0x00, 0x64, 0xCE, 0x15, 0xF4, 0x0D, 0x7D, 0x03, +0xDF, 0x00, 0x16, 0x0B, 0xD8, 0x01, 0x7C, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -7753,5 +7753,5 @@ const unsigned char u8_rad_testpara_30[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0x09, 0x5D, 0xA5, 0xB2, 0x35, 0x48, 0x5B, 0x66, +0x01, 0xA8, 0x56, 0x2E, 0xE0, 0x85, 0x46, 0x49, }; diff --git a/raydium/raydium_driver.h b/raydium/raydium_driver.h index 65248c96f8..9d8f09f82d 100644 --- a/raydium/raydium_driver.h +++ b/raydium/raydium_driver.h @@ -232,7 +232,7 @@ #define GESTURE_EN /* Enable FW update */ -/* #define FW_UPDATE_EN */ +#define FW_UPDATE_EN /* #define FW_MAPPING_EN */ #define MSM_NEW_VER From e54f813e0a92a83598a94c3217ff8bfa088344c0 Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Fri, 16 Sep 2022 22:47:42 +0530 Subject: [PATCH 050/170] touch: Remove unnecessary log Remove unnecessary print log and adjust poweron sequence. Change-Id: I99d34b40f06434146bcc1f438fbfe72bf6e68c2d Signed-off-by: Srikanth Katteboina --- raydium/raydium_driver.c | 30 +++++++++++++++++++----------- raydium/raydium_driver.h | 1 + raydium/raydium_fw_update.c | 2 +- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index acdcadae18..c4c3d1ccd9 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -152,6 +152,14 @@ static int raydium_gpio_configure(bool on) goto err_irq_gpio_req; } + i32_err = gpio_direction_output(g_raydium_ts->rst_gpio, 0); + msleep(RAYDIUM_RESET_INTERVAL_10MSEC); + if (i32_err) { + LOGD(LOG_ERR, + "[touch]set_direction for irq gpio failed\n"); + goto err_rst_gpio_dir; + } + i32_err = gpio_direction_output(g_raydium_ts->rst_gpio, 1); if (i32_err) { LOGD(LOG_ERR, @@ -862,7 +870,7 @@ static int raydium_create_sysfs(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &raydium_attr_group); ret = -EIO; } else { - LOGD(LOG_INFO, "[touch]create raydium sysfs attr_group successful\n"); + LOGD(LOG_DEBUG, "[touch]create raydium sysfs attr_group successful\n"); } return ret; } @@ -962,7 +970,7 @@ static int raydium_touch_report(unsigned char *p_u8_buf, gst_slot[u8_j].pt_id = u8_pt_id; gst_slot[u8_j].need_update = 1; gst_slot[u8_j].pt_report_offset = u8_i; - LOGD(LOG_INFO, "[touch]x:%d,y:%d\n", + LOGD(LOG_DEBUG, "[touch]x:%d,y:%d\n", p_u8_buf[POS_X_L + u8_offset] | p_u8_buf[POS_X_H + u8_offset] << 8, p_u8_buf[POS_Y_L + u8_offset] | @@ -1124,7 +1132,7 @@ int raydium_read_touchdata(unsigned char *p_u8_tp_status, unsigned char *p_u8_b LOGD(LOG_ERR, "[touch]%s: write data failed: %d\n", __func__, i32_ret); goto exit_error; } - LOGD(LOG_WARNING, "[touch]%s -> report not updated.\n", __func__); + LOGD(LOG_DEBUG, "[touch]%s -> report not updated.\n", __func__); goto exit_error; } u8_seq_no = p_u8_tp_status[POS_SEQ]; @@ -1285,7 +1293,7 @@ static irqreturn_t raydium_ts_interrupt(int irq, void *dev_id) } mutex_unlock(&g_raydium_ts->lock); - LOGD(LOG_WARNING, "[touch]work_pending\n"); + LOGD(LOG_DEBUG, "[touch]work_pending\n"); } } return IRQ_HANDLED; @@ -1322,7 +1330,7 @@ static int raydium_check_i2c_ready(unsigned short *u16_i2c_data) *u16_i2c_data = u8_buf[3] << 8 | u8_buf[2]; - LOGD(LOG_INFO, "[touch]RAD check I2C : 0x%02X%02X\n", u8_buf[3], u8_buf[2]); + LOGD(LOG_DEBUG, "[touch]RAD check I2C : 0x%02X%02X\n", u8_buf[3], u8_buf[2]); exit_error: mutex_unlock(&g_raydium_ts->lock); @@ -1476,9 +1484,9 @@ static int raydium_ts_open(struct input_dev *input_dev) { //int i32_ret = 0; - LOGD(LOG_INFO, "[touch]%s()+\n", __func__); + LOGD(LOG_DEBUG, "[touch]%s()+\n", __func__); - LOGD(LOG_INFO, "[touch]ts->blank:%x\n", g_raydium_ts->blank); + LOGD(LOG_DEBUG, "[touch]ts->blank:%x\n", g_raydium_ts->blank); if (g_raydium_ts->is_sleep == 1) { mutex_lock(&g_raydium_ts->lock); @@ -1824,7 +1832,7 @@ static int raydium_set_resolution(void) u32_x = u8_buf[3] << 8 | u8_buf[2]; u32_y = u8_buf[1] << 8 | u8_buf[0]; - LOGD(LOG_INFO, "[touch]RAD display info x:%d, y:%d\n", u32_x, u32_y); + LOGD(LOG_DEBUG, "[touch]RAD display info x:%d, y:%d\n", u32_x, u32_y); if (u32_x > 100 && u32_y > 100 && u32_x < 600 && u32_y < 600) { @@ -2115,8 +2123,8 @@ static int raydium_ts_probe(struct i2c_client *client, g_raydium_ts->workqueue = create_singlethread_workqueue("raydium_ts"); /*irq_gpio = 13 irqflags = 108*/ - LOGD(LOG_INFO, "[touch]pdata irq : %d\n", g_raydium_ts->irq_gpio); - LOGD(LOG_INFO, "[touch]client irq : %d, pdata flags : %d\n", + LOGD(LOG_DEBUG, "[touch]pdata irq : %d\n", g_raydium_ts->irq_gpio); + LOGD(LOG_DEBUG, "[touch]client irq : %d, pdata flags : %d\n", client->irq, pdata->irqflags); g_raydium_ts->irq = gpio_to_irq(pdata->irq_gpio); @@ -2136,7 +2144,7 @@ static int raydium_ts_probe(struct i2c_client *client, /*raydium_irq_control(ts, ENABLE);*/ - LOGD(LOG_INFO, "[touch]RAD Touch driver ver :0x%X\n", g_u32_driver_version); + LOGD(LOG_DEBUG, "[touch]RAD Touch driver ver :0x%X\n", g_u32_driver_version); /*fw update check*/ ret = raydium_fw_update_check(u16_i2c_data); diff --git a/raydium/raydium_driver.h b/raydium/raydium_driver.h index 9d8f09f82d..17d94e1eda 100644 --- a/raydium/raydium_driver.h +++ b/raydium/raydium_driver.h @@ -37,6 +37,7 @@ #define RAYDIUM_RESET_INTERVAL_MSEC 5 #define RAYDIUM_RESET_RESTORE_USEC 200 #define RAYDIUM_RESET_DELAY_MSEC 100 +#define RAYDIUM_RESET_INTERVAL_10MSEC 10 /* I2C bus slave address(ID) */ #define RAYDIUM_I2C_EID (0x5A) diff --git a/raydium/raydium_fw_update.c b/raydium/raydium_fw_update.c index 7b18038765..e8cf157825 100644 --- a/raydium/raydium_fw_update.c +++ b/raydium/raydium_fw_update.c @@ -81,7 +81,7 @@ int raydium_mem_table_setting(void) break; #endif default: - LOGD(LOG_WARNING, "[touch]mapping ic setting use default fw\n"); + LOGD(LOG_DEBUG, "[touch]mapping ic setting use default fw\n"); memcpy(g_rad_boot_image, u8_rad_boot_30, RAD_BOOT_3X_SIZE); memcpy(g_rad_init_image, u8_rad_init_30, RAD_INIT_3X_SIZE); memcpy(g_rad_fw_image, u8_rad_fw_30, RAD_FW_3X_SIZE); From 447a6d32fc0fc52764ffb20cbb8779833416539b Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Fri, 16 Sep 2022 23:05:38 +0530 Subject: [PATCH 051/170] touch: Request firmware from userspace Add upgrade mode with request firmware file from userspace. Change-Id: Ifb4329d39eb8e80c11e6738d173870be94a63ea3 Signed-off-by: Srikanth Katteboina --- raydium/raydium_driver.c | 4 +- raydium/raydium_driver.h | 7 +- raydium/raydium_fw_update.c | 174 ++++++++++++++++++++++++++++-------- raydium/raydium_sysfs.c | 3 +- 4 files changed, 149 insertions(+), 39 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index c4c3d1ccd9..3b9a82fc79 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -2147,9 +2147,9 @@ static int raydium_ts_probe(struct i2c_client *client, LOGD(LOG_DEBUG, "[touch]RAD Touch driver ver :0x%X\n", g_u32_driver_version); /*fw update check*/ - ret = raydium_fw_update_check(u16_i2c_data); + ret = raydium_fw_update_init(u16_i2c_data); if (ret < 0) { - LOGD(LOG_ERR, "[touch]FW update check failed\n"); + LOGD(LOG_ERR, "[touch]FW update init failed\n"); ret = -ENODEV; goto exit_irq_request_failed; } diff --git a/raydium/raydium_driver.h b/raydium/raydium_driver.h index 17d94e1eda..ed6a4f0ff5 100644 --- a/raydium/raydium_driver.h +++ b/raydium/raydium_driver.h @@ -204,6 +204,7 @@ #define RAD_FW_3X_SIZE 0x7300 #define RAD_PARA_3X_SIZE 0x174 #define RAD_TESTFW_3X_SIZE (RAD_FW_3X_SIZE + RAD_PARA_3X_SIZE + 4) +#define RAD_ALLFW_3X_SIZE 0xF170 #define RAD_CMD_UPDATE_BIN 0x80 #define RAD_CMD_UPDATE_END 0x81 @@ -243,6 +244,9 @@ #define RAD_SELFTEST #define PARA_FW_VERSION_OFFSET 4 +#define ENABLE_FW_LOADER 1 +#define FW_NAME "RM6D030.bin" + #define PINCTRL_STATE_ACTIVE "pmx_ts_active" #define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" #define PINCTRL_STATE_RELEASE "pmx_ts_release" @@ -399,7 +403,8 @@ extern int raydium_burn_comp(struct i2c_client *client); extern int raydium_burn_fw(struct i2c_client *client); extern int raydium_load_test_fw(struct i2c_client *client); -extern int raydium_fw_update_check(unsigned short u16_i2c_data); +extern int raydium_fw_update_init(unsigned short u16_i2c_data); +extern int raydium_fw_update_check(unsigned int u32_check_version); extern int raydium_i2c_pda_set_address(unsigned int u32_address, unsigned char u8_mode); extern void raydium_mem_table_init(unsigned short u16_id); diff --git a/raydium/raydium_fw_update.c b/raydium/raydium_fw_update.c index e8cf157825..f5e68094bd 100644 --- a/raydium/raydium_fw_update.c +++ b/raydium/raydium_fw_update.c @@ -31,7 +31,9 @@ #include #include #include "raydium_driver.h" +#if !ENABLE_FW_LOADER #include "rad_fw_image_30.h" +#endif #if defined(FW_MAPPING_EN) #include "rad_fw_image_31.h" #endif @@ -49,7 +51,79 @@ void raydium_mem_table_init(unsigned short u16_id) GFP_KERNEL); g_u8_table_init = SUCCESS; } +#if ENABLE_FW_LOADER +static void raydium_cb(const struct firmware *fw, void *ctx) +{ + unsigned int u32_offset = 0; + unsigned int u32_image_version; +#ifdef FW_UPDATE_EN + int i32_ret = ERROR; +#endif + if (fw && (fw->size == RAD_ALLFW_3X_SIZE)) { + LOGD(LOG_DEBUG, "[touch]get firmware success size:%x\n", fw->size); + memcpy(g_rad_boot_image, fw->data, RAD_BOOT_3X_SIZE); + u32_offset += RAD_BOOT_3X_SIZE; + memcpy(g_rad_init_image, fw->data + u32_offset, RAD_INIT_3X_SIZE); + u32_offset += RAD_INIT_3X_SIZE; + memcpy(g_rad_fw_image, fw->data + u32_offset, RAD_FW_3X_SIZE); + u32_offset += RAD_FW_3X_SIZE; + memcpy(g_rad_para_image, fw->data + u32_offset, RAD_PARA_3X_SIZE + 4); + u32_offset += RAD_PARA_3X_SIZE + 4; + memcpy(g_rad_testfw_image, fw->data + u32_offset, RAD_FW_3X_SIZE); + u32_offset += RAD_FW_3X_SIZE; + memcpy(g_rad_testpara_image, fw->data + u32_offset, RAD_PARA_3X_SIZE + 4); + + memcpy(g_rad_testfw_image + RAD_FW_3X_SIZE, g_rad_testpara_image + , RAD_PARA_3X_SIZE + 4); + u32_image_version = (g_rad_para_image[PARA_FW_VERSION_OFFSET] << 24) | + (g_rad_para_image[PARA_FW_VERSION_OFFSET + 1] << 16) | + (g_rad_para_image[PARA_FW_VERSION_OFFSET + 2] << 8) | + g_rad_para_image[PARA_FW_VERSION_OFFSET + 3]; + + LOGD(LOG_INFO, "[touch]RAD Image FW ver : 0x%x\n", u32_image_version); +#ifdef FW_UPDATE_EN + i32_ret = raydium_fw_update_check(u32_image_version); + if (i32_ret < 0) + LOGD(LOG_ERR, "[touch]fw update fail!\n"); +#endif + } else { + LOGD(LOG_ERR, "[touch]get firmware file fail!\n"); + } +} +int raydium_load_image(unsigned char *u8_buf, char *name) +{ + int i32_ret = SUCCESS; + struct i2c_client *client = g_raydium_ts->client; + + i32_ret = request_firmware_nowait(THIS_MODULE, true, FW_NAME, &client->dev, + GFP_KERNEL, g_raydium_ts, raydium_cb); + + if (i32_ret) { + LOGD(LOG_ERR, "[touch]failed to get firmware %s %d\n", + name, i32_ret); + return i32_ret; + } + + return i32_ret; +} +int raydium_mem_table_setting(void) +{ + int i32_ret = SUCCESS; + char name[RAYDIUM_FW_BIN_PATH_LENGTH]; + + snprintf((char *)name, RAYDIUM_FW_BIN_PATH_LENGTH, "%s", FW_NAME); + LOGD(LOG_DEBUG, "[touch]firmware path %s\n", name); + i32_ret = raydium_load_image(g_rad_fw_image, name); + + if (i32_ret < 0) + return ERROR; + + i32_ret = SUCCESS; + return i32_ret; +} + +#else int raydium_mem_table_setting(void) { int i32_ret = SUCCESS; @@ -99,7 +173,9 @@ int raydium_mem_table_setting(void) g_u8_table_setting = 0; return i32_ret; } +#endif +#if !ENABLE_FW_LOADER int raydium_id_init(unsigned char u8_type) { int i32_ret = ERROR; @@ -119,7 +195,7 @@ int raydium_id_init(unsigned char u8_type) } return i32_ret; } - +#endif unsigned int bits_reverse(unsigned int u32_num, unsigned int bit_num) { unsigned int reverse = 0, u32_i; @@ -816,6 +892,53 @@ exit_upgrade: return i32_ret; } +int raydium_fw_update_check(unsigned int u32_check_version) +{ + int i32_ret = ERROR; + unsigned int u32_fw_version; + unsigned char u8_rbuffer[4]; + + if (g_raydium_ts->fw_version != u32_check_version) { + + LOGD(LOG_INFO, "[touch]FW need update.\n"); + g_u8_raydium_flag |= ENG_MODE; + + i32_ret = raydium_burn_fw(g_raydium_ts->client); + if (i32_ret < 0) { + LOGD(LOG_ERR, "[touch]FW update fail:%d\n", i32_ret); + goto exit_error; + } + g_u8_raydium_flag &= ~ENG_MODE; + + i32_ret = raydium_i2c_pda2_set_page(g_raydium_ts->client, + g_raydium_ts->is_suspend, + RAYDIUM_PDA2_PAGE_0); + if (i32_ret < 0) + goto exit_error; + + i32_ret = raydium_i2c_pda2_read(g_raydium_ts->client, + RAYDIUM_PDA2_FW_VERSION_ADDR, + u8_rbuffer, + 4); + if (i32_ret < 0) + goto exit_error; + + u32_fw_version = (u8_rbuffer[0] << 24) | + (u8_rbuffer[1] << 16) | + (u8_rbuffer[2] << 8) | + u8_rbuffer[3]; + LOGD(LOG_INFO, "[touch]RAD FW ver is 0x%x\n", + u32_fw_version); + g_raydium_ts->fw_version = u32_fw_version; + } else + LOGD(LOG_INFO, "[touch]FW is the latest version.\n"); + + i32_ret = SUCCESS; + return i32_ret; + +exit_error: + return i32_ret; +} int raydium_burn_fw(struct i2c_client *client) { @@ -843,12 +966,15 @@ exit_upgrade: return i32_ret; } -int raydium_fw_update_check(unsigned short u16_i2c_data) +int raydium_fw_update_init(unsigned short u16_i2c_data) { unsigned char u8_rbuffer[4]; - unsigned int u32_fw_version, u32_image_version; + unsigned int u32_fw_version; +#if !ENABLE_FW_LOADER + unsigned int u32_image_version; +#endif int i32_ret = ERROR; mutex_lock(&g_raydium_ts->lock); @@ -878,6 +1004,7 @@ int raydium_fw_update_check(unsigned short u16_i2c_data) raydium_mem_table_init(g_raydium_ts->id); if (raydium_mem_table_setting() == SUCCESS) { +#if !ENABLE_FW_LOADER u32_image_version = (g_rad_para_image[PARA_FW_VERSION_OFFSET] << 24) | (g_rad_para_image[PARA_FW_VERSION_OFFSET + 1] << 16) | @@ -885,45 +1012,20 @@ int raydium_fw_update_check(unsigned short u16_i2c_data) g_rad_para_image[PARA_FW_VERSION_OFFSET + 3]; LOGD(LOG_INFO, "[touch]RAD Image FW ver : 0x%x\n", u32_image_version); +#endif } else { LOGD(LOG_ERR, "[touch]Mem setting failed, Stop fw upgrade!\n"); - return FAIL; + return i32_ret; } #ifdef FW_UPDATE_EN - if (u32_fw_version != u32_image_version) { - LOGD(LOG_INFO, "[touch]FW need update.\n"); - g_u8_raydium_flag |= ENG_MODE; - i32_ret = raydium_burn_fw(g_raydium_ts->client); - if (i32_ret < 0) - LOGD(LOG_ERR, "[touch]FW update fail:%d\n", i32_ret); +#if !ENABLE_FW_LOADER + i32_ret = raydium_fw_update_check(u32_image_version); + if (i32_ret < 0) + LOGD(LOG_ERR, "[touch]fw update fail!\n"); +#endif - g_u8_raydium_flag &= ~ENG_MODE; - mutex_lock(&g_raydium_ts->lock); - i32_ret = raydium_i2c_pda2_set_page(g_raydium_ts->client, - g_raydium_ts->is_suspend, - RAYDIUM_PDA2_PAGE_0); - if (i32_ret < 0) - goto exit_error; - - i32_ret = raydium_i2c_pda2_read(g_raydium_ts->client, - RAYDIUM_PDA2_FW_VERSION_ADDR, - u8_rbuffer, - 4); - if (i32_ret < 0) - goto exit_error; - - mutex_unlock(&g_raydium_ts->lock); - u32_fw_version = (u8_rbuffer[0] << 24) | - (u8_rbuffer[1] << 16) | - (u8_rbuffer[2] << 8) | - u8_rbuffer[3]; - LOGD(LOG_INFO, "[touch]RAD FW ver is 0x%x\n", - u32_fw_version); - g_raydium_ts->fw_version = u32_fw_version; - } else - LOGD(LOG_INFO, "[touch]FW is the latest version.\n"); #endif @@ -1133,3 +1235,5 @@ ERROR_EXIT: return ERROR; } + + diff --git a/raydium/raydium_sysfs.c b/raydium/raydium_sysfs.c index f04c89cdda..6317724379 100644 --- a/raydium/raydium_sysfs.c +++ b/raydium/raydium_sysfs.c @@ -536,6 +536,7 @@ static ssize_t raydium_mem_store(struct device *dev, struct device_attribute *attr, const char *p_i8_buf, size_t count) { +#if !ENABLE_FW_LOADER int i32_ret = 0; unsigned char u8_type = 0; unsigned int u32_image_version; @@ -598,7 +599,7 @@ static ssize_t raydium_mem_store(struct device *dev, kfree(g_rad_para_image); g_rad_para_image = NULL; } - +#endif return count; } From 66adec3dd9bbd0768e8bbc5931ebb35a6c3823a1 Mon Sep 17 00:00:00 2001 From: ppadasal Date: Wed, 21 Sep 2022 15:51:27 +0530 Subject: [PATCH 052/170] touchscreen: raydium: Setting up of DRM notifier Implementation of DRM notifier callbacks and registration of callback during touch probing. Fetching of the active DRM panel from the DSI panels in device tree and storing in core platform data structure. Change-Id: I392bd6df50ff5baadf0a13a1da9f61902e575eda Signed-off-by: ppadasal --- raydium/drv_interface.h | 2 - raydium/raydium_driver.c | 247 ++++++++++++++++++++++++++++++++++----- raydium/raydium_driver.h | 35 +++++- 3 files changed, 252 insertions(+), 32 deletions(-) diff --git a/raydium/drv_interface.h b/raydium/drv_interface.h index 12bacc96d4..0fee291a0f 100644 --- a/raydium/drv_interface.h +++ b/raydium/drv_interface.h @@ -32,8 +32,6 @@ #define __O volatile #define __IO volatile -#define FALSE 0x00 -#define TRUE 0x01 #define WORD 4 diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 3b9a82fc79..dadabd243a 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #if defined(CONFIG_FB) #include @@ -60,6 +62,10 @@ static int raydium_enable_regulator(struct raydium_ts_data *cd, bool en); const struct attribute_group raydium_attr_group; #endif /*end of CONFIG_RM_SYSFS_DEBUG*/ +#if defined(CONFIG_DRM) + static struct drm_panel *active_panel; +#endif + unsigned char g_u8_addr; unsigned char g_u8_raydium_flag; unsigned char g_u8_i2c_mode; @@ -156,7 +162,7 @@ static int raydium_gpio_configure(bool on) msleep(RAYDIUM_RESET_INTERVAL_10MSEC); if (i32_err) { LOGD(LOG_ERR, - "[touch]set_direction for irq gpio failed\n"); + "[touch]set_direction for rst gpio failed\n"); goto err_rst_gpio_dir; } @@ -687,14 +693,13 @@ void raydium_irq_control(bool enable) } /* Clear interrupts first */ - if (g_raydium_ts->blank != FB_BLANK_POWERDOWN) { + if (g_raydium_ts->blank != DRM_PANEL_BLANK_POWERDOWN) { if (g_u8_i2c_mode == PDA2_MODE) { mutex_lock(&g_raydium_ts->lock); if (raydium_i2c_pda2_set_page(g_raydium_ts->client, - g_raydium_ts->is_suspend, - RAYDIUM_PDA2_PAGE_0) < 0) - LOGD(LOG_ERR, "[touch]set page fail%s\n", - __func__); + g_raydium_ts->is_suspend, + RAYDIUM_PDA2_PAGE_0) < 0) + LOGD(LOG_ERR, "[touch]set page fail%s\n", __func__); mutex_unlock(&g_raydium_ts->lock); usleep_range(500, 1500); } @@ -1072,7 +1077,7 @@ int raydium_read_touchdata(unsigned char *p_u8_tp_status, unsigned char *p_u8_b unsigned char u8_retry; unsigned char u8_read_size; unsigned char u8_read_buf[MAX_REPORT_PACKET_SIZE]; - +// int i = 0; u8_retry = 3; mutex_lock(&g_raydium_ts->lock); @@ -1182,6 +1187,10 @@ static void raydium_work_handler(struct work_struct *work) #ifdef GESTURE_EN unsigned char u8_i; + LOGD(LOG_DEBUG, "[touch]ts->blank:%x, g_u8_i2c_mode:%x\n", + g_raydium_ts->blank, g_u8_i2c_mode); + LOGD(LOG_DEBUG, "[touch]u8_tp_status:%x, g_raydium_ts->is_palm:%x\n", + u8_tp_status[POS_GES_STATUS], g_raydium_ts->is_palm); if (g_u8_i2c_mode == PDA2_MODE) { i32_ret = raydium_read_touchdata(u8_tp_status, u8_buf); @@ -1192,7 +1201,7 @@ static void raydium_work_handler(struct work_struct *work) } } /*when display on can use palm to suspend*/ - if (g_raydium_ts->blank == FB_BLANK_UNBLANK) { + if (g_raydium_ts->blank == DRM_PANEL_BLANK_UNBLANK) { if (u8_tp_status[POS_GES_STATUS] == RAD_PALM_ENABLE) { if (g_raydium_ts->is_palm == 0) { /* release all touches*/ @@ -1230,11 +1239,15 @@ static void raydium_work_handler(struct work_struct *work) g_raydium_ts->is_palm = 0; /*goto exit;*/ } - } else if (g_raydium_ts->blank == FB_BLANK_VSYNC_SUSPEND || - g_raydium_ts->blank == FB_BLANK_POWERDOWN) { + } + //else if (g_raydium_ts->blank == DRM_PANEL_BLANK_LP || + // g_raydium_ts->blank == DRM_PANEL_BLANK_POWERDOWN) + { + LOGD(LOG_INFO, "[touch] elseif u8_tp_status:%x\n", u8_tp_status[POS_GES_STATUS]); /*need check small area*/ - if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP - && g_u8_wakeup_flag == false) { + //if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP + // && g_u8_wakeup_flag == false) { + if (u8_tp_status[POS_GES_STATUS] == 0) { input_report_key(g_raydium_ts->input_dev, KEY_POWER, true); usleep_range(9500, 10500); input_sync(g_raydium_ts->input_dev); @@ -1341,7 +1354,7 @@ exit_error: static void raydium_ts_do_suspend(void) { unsigned char u8_i = 0; - int rc; + int rc = 0; if (g_u8_raw_data_type == 0) g_u8_resetflag = false; @@ -1353,11 +1366,10 @@ static void raydium_ts_do_suspend(void) rc = raydium_enable_regulator(g_raydium_ts, false); if (rc < 0) { LOGD(LOG_ERR, "[touch]%s:Failed to disable regulators:rc=%d\n", - __func__, rc); + __func__, rc); } LOGD(LOG_INFO, "[touch]%s:voltage regulators disabled:rc=%d\n", - __func__, rc); - + __func__, rc); /*#ifndef GESTURE_EN*/ raydium_irq_control(DISABLE); /*#endif*/ @@ -1399,6 +1411,7 @@ static void raydium_ts_do_resume(void) int i32_ret = 0; unsigned char u8_retry = 0; #endif + int rc = 0; LOGD(LOG_INFO, "[touch]%s, %d.\n", __func__, g_raydium_ts->is_suspend); @@ -1438,13 +1451,14 @@ static void raydium_ts_do_resume(void) g_u8_checkflag = false; } #endif + rc = raydium_enable_regulator(g_raydium_ts, true); if (rc < 0) { - LOGD(LOG_ERR, "[touch]%s:Failed to disable regulators:rc=%d\n", - __func__, rc); - } - LOGD(LOG_INFO, "[touch]%s:voltage regulators disabled:rc=%d\n", + LOGD(LOG_ERR, "[touch]%s: failed to enable regulators: rc=%d\n", __func__, rc); + } + LOGD(LOG_INFO, "[touch]%s: voltage regulators enabled: rc=%d\n", + __func__, rc); raydium_irq_control(ENABLE); #ifdef GESTURE_EN if (device_may_wakeup(&g_raydium_ts->client->dev)) { @@ -1571,6 +1585,121 @@ static int raydium_ts_resume(struct device *dev) } #endif /*end of CONFIG_FB*/ + +#if defined(CONFIG_DRM) +/******************************************************************************* + * FUNCTION: drm_notifier_callback + * + * SUMMARY: Call back function for DRM notifier to allow to call + * resume/suspend attention list. + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * self - pointer to notifier_block structure + * event - event type of fb notifier + * data - pointer to fb_event structure + ******************************************************************************/ +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct raydium_ts_data *g_raydium_ts = + container_of(self, struct raydium_ts_data, fb_notif); + struct drm_panel_notifier *evdata = data; + int *blank; + + LOGD(LOG_INFO, "%s: DRM notifier called!\n", __func__); + + if (!evdata) + goto exit; + + if (!(event == DRM_PANEL_EARLY_EVENT_BLANK || + event == DRM_PANEL_EVENT_BLANK)) { + LOGD(LOG_INFO, "%s: Event(%lu) do not need process\n", + __func__, event); + goto exit; + } + + blank = evdata->data; + LOGD(LOG_INFO, "%s: DRM event:%lu,blank:%d fb_state %d ", + __func__, event, *blank, g_raydium_ts->fb_state); + LOGD(LOG_INFO, "%s: DRM Power - %s - FB state %d ", + __func__, (*blank == DRM_PANEL_BLANK_UNBLANK)?"UP":"DOWN", g_raydium_ts->fb_state); + + if (*blank == DRM_PANEL_BLANK_UNBLANK) { + LOGD(LOG_INFO, "%s: UNBLANK!\n", __func__); + + if (event == DRM_PANEL_EARLY_EVENT_BLANK) { + LOGD(LOG_INFO, "%s: resume: event = %lu, not care\n", + __func__, event); + } else if (event == DRM_PANEL_EVENT_BLANK) { + if (g_raydium_ts->fb_state != FB_ON) { + LOGD(LOG_INFO, "%s: Resume notifier called!\n", + __func__); + +#if defined(CONFIG_PM) + raydium_ts_resume(&g_raydium_ts->client->dev); +#endif + g_raydium_ts->fb_state = FB_ON; + LOGD(LOG_INFO, "%s: Resume notified!\n", __func__); + } + } + } else if (*blank == DRM_PANEL_BLANK_LP || *blank == DRM_PANEL_BLANK_POWERDOWN) { + LOGD(LOG_INFO, "%s: LOWPOWER!\n", __func__); + if (event == DRM_PANEL_EARLY_EVENT_BLANK) { + if (g_raydium_ts->fb_state != FB_OFF) { + LOGD(LOG_INFO, "%s: Suspend notifier called!\n", + __func__); +#if defined(CONFIG_PM) + raydium_ts_suspend(&g_raydium_ts->client->dev); +#endif + g_raydium_ts->fb_state = FB_OFF; + LOGD(LOG_INFO, "%s: Suspend notified!\n", __func__); + } + } else if (event == DRM_PANEL_EVENT_BLANK) { + LOGD(LOG_INFO, "%s: suspend: event = %lu, not care\n", + __func__, event); + } + } else { + LOGD(LOG_INFO, "%s: DRM BLANK(%d) do not need process\n", + __func__, *blank); + } +exit: + return 0; +} + +/******************************************************************************* + * FUNCTION: raydium_setup_drm_notifier + * + * SUMMARY: Set up call back function into drm notifier. + * + * PARAMETERS: + * g_raydium_ts - pointer to core data + *******************************************************************************/ +static void raydium_setup_drm_notifier(struct raydium_ts_data *g_raydium_ts) +{ + g_raydium_ts->fb_state = FB_ON; + g_raydium_ts->fb_notif.notifier_call = drm_notifier_callback; + LOGD(LOG_INFO, "%s: Setting up drm notifier\n", __func__); + + if (!active_panel) + LOGD(LOG_ERR, "%s: Active panel not registered!\n", __func__); + + if (active_panel && drm_panel_notifier_register(active_panel, + &g_raydium_ts->fb_notif) < 0) + LOGD(LOG_ERR, "%s: Register notifier failed!\n", __func__); +} +#endif /*end of CONFIG_DRM*/ + +/******************************************************************************* + * FUNCTION: raydium_setup_drm_notifier + * + * SUMMARY: Set up call back function into fb notifier. + * + * PARAMETERS: + * g_raydium_ts - pointer to core data + *******************************************************************************/ #if defined(CONFIG_FB) static int fb_notifier_callback(struct notifier_block *self, unsigned long event, @@ -1699,10 +1828,55 @@ static int raydium_get_dt_coords(struct device *dev, char *name, return 0; } +/******************************************************************************* + * FUNCTION: raydium_check_dsi_panel_dt + * + * SUMMARY: Get the DSI active panel information from dtsi + * + * RETURN: + * 0 = success + * !0 = fail + * + * PARAMETERS: + * np - pointer to device_node structure + * active_panel - name of active DSI panel + ******************************************************************************/ + +static int raydium_check_dsi_panel_dt(struct device_node *np, struct drm_panel **active_panel) +{ + int i = 0; + int count = 0; + struct device_node *node = NULL; + struct drm_panel *panel = NULL; + + count = of_count_phandle_with_args(np, "panel", NULL); + pr_info("%s: Active panel count: %d\n", __func__, count); + if (count <= 0) + return 0; + + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + + if (node != NULL) + pr_info("%s: Node handle successfully parsed !\n", __func__); + panel = of_drm_find_panel(node); + of_node_put(node); + + if (!IS_ERR(panel)) { + pr_info("%s: Active panel selected !\n", __func__); + *active_panel = panel; + return 0; + } + } + pr_err("%s: Active panel NOT selected !\n", __func__); + return 0; +} + static int raydium_parse_dt(struct device *dev, struct raydium_ts_platform_data *pdata) { struct device_node *np = dev->of_node; + struct drm_panel *active_panel = NULL; int rc = 0; u32 temp_val = 0; @@ -1731,6 +1905,17 @@ static int raydium_parse_dt(struct device *dev, if ((s32)(pdata->irq_gpio) < 0) return pdata->irq_gpio; + rc = raydium_check_dsi_panel_dt(np, &active_panel); + pr_info("%s: Panel not selected, rc=%d\n", __func__, rc); + if (rc) { + pr_err("%s: Panel not selected, rc=%d\n", __func__, rc); + if (rc == -EPROBE_DEFER) { + pr_err("%s: Probe defer selected, rc=%d\n", __func__, rc); + return rc; + } + } + pdata->active_panel = active_panel; + pr_info("%s: Successful insert of active panel in core data\n", __func__); rc = of_property_read_u32(np, "raydium,hard-reset-delay-ms", &temp_val); @@ -1844,7 +2029,6 @@ exit_error: mutex_unlock(&g_raydium_ts->lock); return i32_ret; } - static int raydium_get_regulator(struct raydium_ts_data *cd, bool get) { int rc; @@ -1966,9 +2150,8 @@ exit: #endif return rc; } - static int raydium_ts_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct raydium_ts_platform_data *pdata = (struct raydium_ts_platform_data *)client->dev.platform_data; @@ -2078,7 +2261,10 @@ static int raydium_ts_probe(struct i2c_client *client, ret = -EPROBE_DEFER; goto exit_check_i2c; } - +#ifdef CONFIG_DRM + /* Setup active dsi panel */ + active_panel = pdata->active_panel; +#endif /*input device initialization*/ input_dev = input_allocate_device(); if (!input_dev) { @@ -2127,6 +2313,11 @@ static int raydium_ts_probe(struct i2c_client *client, LOGD(LOG_DEBUG, "[touch]client irq : %d, pdata flags : %d\n", client->irq, pdata->irqflags); +#if defined(CONFIG_DRM) + LOGD(LOG_DEBUG, "%s: Probe: Setup drm notifier\n", __func__); + raydium_setup_drm_notifier(g_raydium_ts); +#endif/*end of CONFIG_DRM*/ + g_raydium_ts->irq = gpio_to_irq(pdata->irq_gpio); ret = request_threaded_irq(g_raydium_ts->irq, NULL, raydium_ts_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_NO_SUSPEND, @@ -2191,11 +2382,11 @@ exit_check_i2c: gpio_free(pdata->irq_gpio); err_gpio_req: - raydium_get_regulator(g_raydium_ts, false); + raydium_enable_regulator(g_raydium_ts, false); + error_get_regulator: raydium_get_regulator(g_raydium_ts, false); error_alloc_data: - parse_dt_failed: exit_check_functionality_failed: return ret; @@ -2209,6 +2400,9 @@ static int raydium_ts_remove(struct i2c_client *client) raydium_unregister_notifier(); #elif defined(CONFIG_HAS_EARLYSUSPEND) unregister_early_suspend(&g_raydium_ts->early_suspend); +#elif defined(CONFIG_DRM) + if (active_panel) + drm_panel_notifier_unregister(active_panel, &g_raydium_ts->fb_notifier); #endif/*end of CONFIG_FB*/ input_unregister_device(g_raydium_ts->input_dev); input_free_device(g_raydium_ts->input_dev); @@ -2231,7 +2425,6 @@ static int raydium_ts_remove(struct i2c_client *client) raydium_enable_regulator(g_raydium_ts, false); raydium_get_regulator(g_raydium_ts, false); - kfree(g_raydium_ts); i2c_set_clientdata(client, NULL); return 0; diff --git a/raydium/raydium_driver.h b/raydium/raydium_driver.h index ed6a4f0ff5..adc3d78616 100644 --- a/raydium/raydium_driver.h +++ b/raydium/raydium_driver.h @@ -244,13 +244,40 @@ #define RAD_SELFTEST #define PARA_FW_VERSION_OFFSET 4 -#define ENABLE_FW_LOADER 1 -#define FW_NAME "RM6D030.bin" +#define ENABLE_FW_LOADER 1 +#define FW_NAME "RM6D030.bin" #define PINCTRL_STATE_ACTIVE "pmx_ts_active" #define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" #define PINCTRL_STATE_RELEASE "pmx_ts_release" +/* Power Management Macros Enablement */ + +#ifndef CONFIG_PM +#define CONFIG_PM +#endif + + +#ifndef CONFIG_DRM +#define CONFIG_DRM +#endif + +#include +#include +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#elif defined(CONFIG_DRM) +#include +#endif + + +enum raydium_fb_state { + FB_ON, + FB_OFF, +}; + + struct raydium_ts_data { unsigned int irq; unsigned int irq_gpio; @@ -276,9 +303,10 @@ struct raydium_ts_data { bool irq_enabled; bool irq_wake; -#if defined(CONFIG_FB) +#if defined(CONFIG_FB) || defined(CONFIG_DRM) struct notifier_block fb_notif; int blank; + enum raydium_fb_state fb_state; #elif defined(CONFIG_HAS_EARLYSUSPEND) struct early_suspend early_suspend; #endif /*end of CONFIG_FB*/ @@ -323,6 +351,7 @@ struct raydium_ts_platform_data { u32 soft_rst_dly; u32 num_max_touches; u32 fw_id; + struct drm_panel *active_panel; }; /* TODO: Using struct+memcpy instead of array+offset*/ From 37908e1829bd9275938e70a54a4953201435c9d5 Mon Sep 17 00:00:00 2001 From: ppadasal Date: Wed, 21 Sep 2022 16:08:38 +0530 Subject: [PATCH 053/170] touchscreen: raydium: temporary workaround for touch to wake wakeup gesture status is not observed during touch to wake, hance simulating power key for every interrupt. Change-Id: I1e0221bfbd7f1256971a679a055145c314235dff Signed-off-by: ppadasal --- raydium/raydium_driver.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index dadabd243a..04a923575d 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -1077,7 +1077,6 @@ int raydium_read_touchdata(unsigned char *p_u8_tp_status, unsigned char *p_u8_b unsigned char u8_retry; unsigned char u8_read_size; unsigned char u8_read_buf[MAX_REPORT_PACKET_SIZE]; -// int i = 0; u8_retry = 3; mutex_lock(&g_raydium_ts->lock); @@ -1240,13 +1239,13 @@ static void raydium_work_handler(struct work_struct *work) /*goto exit;*/ } } - //else if (g_raydium_ts->blank == DRM_PANEL_BLANK_LP || - // g_raydium_ts->blank == DRM_PANEL_BLANK_POWERDOWN) + /*else if (g_raydium_ts->blank == DRM_PANEL_BLANK_LP ||*/ + /*g_raydium_ts->blank == DRM_PANEL_BLANK_POWERDOWN)*/ { LOGD(LOG_INFO, "[touch] elseif u8_tp_status:%x\n", u8_tp_status[POS_GES_STATUS]); /*need check small area*/ - //if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP - // && g_u8_wakeup_flag == false) { + /*if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP*/ + /* && g_u8_wakeup_flag == false) {*/ if (u8_tp_status[POS_GES_STATUS] == 0) { input_report_key(g_raydium_ts->input_dev, KEY_POWER, true); usleep_range(9500, 10500); From 6110f99f5171812c93d6b5f446c8586d97d193fa Mon Sep 17 00:00:00 2001 From: ppadasal Date: Wed, 21 Sep 2022 16:15:03 +0530 Subject: [PATCH 054/170] touchscreen: raydium: Active panel not select Active panel not select, display will probe first, then touch probe. Change-Id: Ib303b87eb265c6c58b37fb1e631153321b282f4b Signed-off-by: ppadasal --- raydium/raydium_driver.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 04a923575d..4079e7f220 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -1680,14 +1680,14 @@ static void raydium_setup_drm_notifier(struct raydium_ts_data *g_raydium_ts) { g_raydium_ts->fb_state = FB_ON; g_raydium_ts->fb_notif.notifier_call = drm_notifier_callback; - LOGD(LOG_INFO, "%s: Setting up drm notifier\n", __func__); + LOGD(LOG_INFO, "[touch]%s: Setting up drm notifier\n", __func__); if (!active_panel) - LOGD(LOG_ERR, "%s: Active panel not registered!\n", __func__); + LOGD(LOG_ERR, "[touch]%s: Active panel not registered!\n", __func__); if (active_panel && drm_panel_notifier_register(active_panel, &g_raydium_ts->fb_notif) < 0) - LOGD(LOG_ERR, "%s: Register notifier failed!\n", __func__); + LOGD(LOG_ERR, "[touch]%s: Register notifier failed!\n", __func__); } #endif /*end of CONFIG_DRM*/ @@ -1846,10 +1846,10 @@ static int raydium_check_dsi_panel_dt(struct device_node *np, struct drm_panel * int i = 0; int count = 0; struct device_node *node = NULL; - struct drm_panel *panel = NULL; + struct drm_panel *panel; count = of_count_phandle_with_args(np, "panel", NULL); - pr_info("%s: Active panel count: %d\n", __func__, count); + LOGD(LOG_ERR, "[touch]%s: Active panel count: %d\n", __func__, count); if (count <= 0) return 0; @@ -1857,18 +1857,19 @@ static int raydium_check_dsi_panel_dt(struct device_node *np, struct drm_panel * node = of_parse_phandle(np, "panel", i); if (node != NULL) - pr_info("%s: Node handle successfully parsed !\n", __func__); + LOGD(LOG_ERR, "[touch]%s: Node handle successfully parsed !\n", __func__); panel = of_drm_find_panel(node); of_node_put(node); if (!IS_ERR(panel)) { - pr_info("%s: Active panel selected !\n", __func__); + LOGD(LOG_ERR, "[touch]%s: Active panel selected !\n", __func__); *active_panel = panel; return 0; } } - pr_err("%s: Active panel NOT selected !\n", __func__); - return 0; + LOGD(LOG_ERR, "[touch]%s: Active panel NOT selected !\n", __func__); + + return PTR_ERR(panel); } static int raydium_parse_dt(struct device *dev, @@ -1905,16 +1906,16 @@ static int raydium_parse_dt(struct device *dev, return pdata->irq_gpio; rc = raydium_check_dsi_panel_dt(np, &active_panel); - pr_info("%s: Panel not selected, rc=%d\n", __func__, rc); + LOGD(LOG_ERR, "[touch]%s: Panel not selected, rc=%d\n", __func__, rc); if (rc) { - pr_err("%s: Panel not selected, rc=%d\n", __func__, rc); + LOGD(LOG_ERR, "[touch]%s: Panel not selected, rc=%d\n", __func__, rc); if (rc == -EPROBE_DEFER) { - pr_err("%s: Probe defer selected, rc=%d\n", __func__, rc); + LOGD(LOG_ERR, "[touch]%s: Probe defer selected, rc=%d\n", __func__, rc); return rc; } } pdata->active_panel = active_panel; - pr_info("%s: Successful insert of active panel in core data\n", __func__); + LOGD(LOG_ERR, "[touch]%s: Successful insert of active panel in core data\n", __func__); rc = of_property_read_u32(np, "raydium,hard-reset-delay-ms", &temp_val); @@ -2260,6 +2261,13 @@ static int raydium_ts_probe(struct i2c_client *client, ret = -EPROBE_DEFER; goto exit_check_i2c; } + + if (!pdata->active_panel) { +// LOGD(LOG_ERR, "[touch]active_panel null, check again!\n"); + raydium_check_dsi_panel_dt(client->dev.of_node, + &pdata->active_panel); + } + #ifdef CONFIG_DRM /* Setup active dsi panel */ active_panel = pdata->active_panel; From 8402dbcdd96064c01a3f81b1d4fdea81a1eb4d4e Mon Sep 17 00:00:00 2001 From: ppadasal Date: Wed, 21 Sep 2022 16:28:15 +0530 Subject: [PATCH 055/170] touchscreen: raydium: Click trigger open camera Click should not report power key, trigger usercase open camera. Change-Id: I255d072d1b4f532d6d9e3dbf50310f1bc96b3416 Signed-off-by: ppadasal --- raydium/raydium_driver.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 4079e7f220..8d3e196a52 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -1238,15 +1238,13 @@ static void raydium_work_handler(struct work_struct *work) g_raydium_ts->is_palm = 0; /*goto exit;*/ } - } - /*else if (g_raydium_ts->blank == DRM_PANEL_BLANK_LP ||*/ - /*g_raydium_ts->blank == DRM_PANEL_BLANK_POWERDOWN)*/ - { + } else if (g_raydium_ts->blank == DRM_PANEL_BLANK_LP || + g_raydium_ts->blank == DRM_PANEL_BLANK_POWERDOWN) { LOGD(LOG_INFO, "[touch] elseif u8_tp_status:%x\n", u8_tp_status[POS_GES_STATUS]); /*need check small area*/ - /*if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP*/ - /* && g_u8_wakeup_flag == false) {*/ - if (u8_tp_status[POS_GES_STATUS] == 0) { + if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP + && g_u8_wakeup_flag == false) { + /*if (u8_tp_status[POS_GES_STATUS] == 0) {*/ input_report_key(g_raydium_ts->input_dev, KEY_POWER, true); usleep_range(9500, 10500); input_sync(g_raydium_ts->input_dev); @@ -1621,6 +1619,7 @@ static int drm_notifier_callback(struct notifier_block *self, } blank = evdata->data; + g_raydium_ts->blank = (*blank); LOGD(LOG_INFO, "%s: DRM event:%lu,blank:%d fb_state %d ", __func__, event, *blank, g_raydium_ts->fb_state); LOGD(LOG_INFO, "%s: DRM Power - %s - FB state %d ", From ac338866d895a578ede220d44283ccf3585eefa7 Mon Sep 17 00:00:00 2001 From: ppadasal Date: Wed, 21 Sep 2022 16:36:09 +0530 Subject: [PATCH 056/170] touchscreen: raydium: workaround for touch to wake wakeup gesture status is not observed during touch to wake, hence simulating power key for every interrupt. Change-Id: I28143543b364786874b97b94ac0fe1774ea2dd26 Signed-off-by: ppadasal --- raydium/raydium_driver.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 8d3e196a52..125b3209da 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -1239,12 +1239,12 @@ static void raydium_work_handler(struct work_struct *work) /*goto exit;*/ } } else if (g_raydium_ts->blank == DRM_PANEL_BLANK_LP || - g_raydium_ts->blank == DRM_PANEL_BLANK_POWERDOWN) { + g_raydium_ts->blank == DRM_PANEL_BLANK_POWERDOWN) { LOGD(LOG_INFO, "[touch] elseif u8_tp_status:%x\n", u8_tp_status[POS_GES_STATUS]); /*need check small area*/ - if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP - && g_u8_wakeup_flag == false) { - /*if (u8_tp_status[POS_GES_STATUS] == 0) {*/ + /*if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP*/ + /*&& g_u8_wakeup_flag == false) {*/ + if (u8_tp_status[POS_GES_STATUS] == 0) { input_report_key(g_raydium_ts->input_dev, KEY_POWER, true); usleep_range(9500, 10500); input_sync(g_raydium_ts->input_dev); @@ -1640,7 +1640,7 @@ static int drm_notifier_callback(struct notifier_block *self, raydium_ts_resume(&g_raydium_ts->client->dev); #endif g_raydium_ts->fb_state = FB_ON; - LOGD(LOG_INFO, "%s: Resume notified!\n", __func__); + LOGD(LOG_INFO, "%s: Resume notified!\n", __func__); } } } else if (*blank == DRM_PANEL_BLANK_LP || *blank == DRM_PANEL_BLANK_POWERDOWN) { From 6c84669c98bc75684558362a40dcbbbe76ece8b1 Mon Sep 17 00:00:00 2001 From: ppadasal Date: Wed, 21 Sep 2022 16:41:34 +0530 Subject: [PATCH 057/170] touchscreen: raydium: Fix Touch_to_wake issue with AOD_OFF New DRM events were added as a part of deep sleep features, hence added changes to handle DRM events in callback function and interrupt work handler. Change-Id: If4ccfb12cb9c6df98eccc1ae5968be8e1225f711 Signed-off-by: ppadasal --- raydium/raydium_driver.c | 104 ++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 56 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 125b3209da..32f1f10716 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -58,6 +58,8 @@ struct raydium_slot_status gst_slot[MAX_TOUCH_NUM * 2]; struct raydium_slot_status gst_slot_init = {0xFF, 0, 0}; static int raydium_enable_regulator(struct raydium_ts_data *cd, bool en); + + #if (defined(CONFIG_RM_SYSFS_DEBUG)) const struct attribute_group raydium_attr_group; #endif /*end of CONFIG_RM_SYSFS_DEBUG*/ @@ -1186,9 +1188,9 @@ static void raydium_work_handler(struct work_struct *work) #ifdef GESTURE_EN unsigned char u8_i; - LOGD(LOG_DEBUG, "[touch]ts->blank:%x, g_u8_i2c_mode:%x\n", + LOGD(LOG_INFO, "[touch]ts->blank:%x, g_u8_i2c_mode:%x\n", g_raydium_ts->blank, g_u8_i2c_mode); - LOGD(LOG_DEBUG, "[touch]u8_tp_status:%x, g_raydium_ts->is_palm:%x\n", + LOGD(LOG_INFO, "[touch]u8_tp_status:%x, g_raydium_ts->is_palm:%x\n", u8_tp_status[POS_GES_STATUS], g_raydium_ts->is_palm); if (g_u8_i2c_mode == PDA2_MODE) { @@ -1199,8 +1201,26 @@ static void raydium_work_handler(struct work_struct *work) return; } } + + if (g_raydium_ts->blank == DRM_PANEL_BLANK_LP || + g_raydium_ts->blank == DRM_PANEL_BLANK_POWERDOWN || g_raydium_ts->fb_state == FB_OFF) { + LOGD(LOG_INFO, "[touch] elseif u8_tp_status:%x\n", u8_tp_status[POS_GES_STATUS]); + /*need check small area*/ + /*if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP */ + /*&& g_u8_wakeup_flag == false) { */ + if (u8_tp_status[POS_GES_STATUS] == 0) { + input_report_key(g_raydium_ts->input_dev, KEY_POWER, true); + usleep_range(9500, 10500); + input_sync(g_raydium_ts->input_dev); + + input_report_key(g_raydium_ts->input_dev, KEY_POWER, false); + input_sync(g_raydium_ts->input_dev); + LOGD(LOG_INFO, "[touch]display wake up with g_u8_resetflag true\n"); + /*goto exit;*/ + } + } /*when display on can use palm to suspend*/ - if (g_raydium_ts->blank == DRM_PANEL_BLANK_UNBLANK) { + else if (g_raydium_ts->blank == DRM_PANEL_BLANK_UNBLANK) { if (u8_tp_status[POS_GES_STATUS] == RAD_PALM_ENABLE) { if (g_raydium_ts->is_palm == 0) { /* release all touches*/ @@ -1238,22 +1258,6 @@ static void raydium_work_handler(struct work_struct *work) g_raydium_ts->is_palm = 0; /*goto exit;*/ } - } else if (g_raydium_ts->blank == DRM_PANEL_BLANK_LP || - g_raydium_ts->blank == DRM_PANEL_BLANK_POWERDOWN) { - LOGD(LOG_INFO, "[touch] elseif u8_tp_status:%x\n", u8_tp_status[POS_GES_STATUS]); - /*need check small area*/ - /*if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP*/ - /*&& g_u8_wakeup_flag == false) {*/ - if (u8_tp_status[POS_GES_STATUS] == 0) { - input_report_key(g_raydium_ts->input_dev, KEY_POWER, true); - usleep_range(9500, 10500); - input_sync(g_raydium_ts->input_dev); - - input_report_key(g_raydium_ts->input_dev, KEY_POWER, false); - input_sync(g_raydium_ts->input_dev); - LOGD(LOG_INFO, "[touch]display wake up with g_u8_resetflag true\n"); - /*goto exit;*/ - } } #else if (g_u8_i2c_mode == PDA2_MODE) { @@ -1275,6 +1279,7 @@ static irqreturn_t raydium_ts_interrupt(int irq, void *dev_id) bool result = false; LOGD(LOG_DEBUG, "[touch]%s\n", __func__); + /*For bootloader wrt/erase flash and software reset interrupt*/ if ((g_u8_raydium_flag & ENG_MODE) != 0) { LOGD(LOG_INFO, "[touch]RAD_ENG_MODE\n"); @@ -1351,7 +1356,8 @@ exit_error: static void raydium_ts_do_suspend(void) { unsigned char u8_i = 0; - int rc = 0; + + LOGD(LOG_INFO, "[touch]%s.\n", __func__); if (g_u8_raw_data_type == 0) g_u8_resetflag = false; @@ -1359,14 +1365,6 @@ static void raydium_ts_do_suspend(void) LOGD(LOG_WARNING, "[touch]Already in suspend state\n"); return; } - - rc = raydium_enable_regulator(g_raydium_ts, false); - if (rc < 0) { - LOGD(LOG_ERR, "[touch]%s:Failed to disable regulators:rc=%d\n", - __func__, rc); - } - LOGD(LOG_INFO, "[touch]%s:voltage regulators disabled:rc=%d\n", - __func__, rc); /*#ifndef GESTURE_EN*/ raydium_irq_control(DISABLE); /*#endif*/ @@ -1375,8 +1373,6 @@ static void raydium_ts_do_suspend(void) if (!cancel_work_sync(&g_raydium_ts->work)) LOGD(LOG_DEBUG, "[touch]workqueue is empty!\n"); - LOGD(LOG_INFO, "[touch]%s.\n", __func__); - /* release all touches */ for (u8_i = 0; u8_i < g_raydium_ts->u8_max_touchs; u8_i++) { input_mt_slot(g_raydium_ts->input_dev, u8_i); @@ -1408,8 +1404,6 @@ static void raydium_ts_do_resume(void) int i32_ret = 0; unsigned char u8_retry = 0; #endif - int rc = 0; - LOGD(LOG_INFO, "[touch]%s, %d.\n", __func__, g_raydium_ts->is_suspend); if (g_raydium_ts->is_suspend == 0) { @@ -1448,14 +1442,6 @@ static void raydium_ts_do_resume(void) g_u8_checkflag = false; } #endif - - rc = raydium_enable_regulator(g_raydium_ts, true); - if (rc < 0) { - LOGD(LOG_ERR, "[touch]%s: failed to enable regulators: rc=%d\n", - __func__, rc); - } - LOGD(LOG_INFO, "[touch]%s: voltage regulators enabled: rc=%d\n", - __func__, rc); raydium_irq_control(ENABLE); #ifdef GESTURE_EN if (device_may_wakeup(&g_raydium_ts->client->dev)) { @@ -1483,10 +1469,11 @@ static int raydium_ts_resume(struct device *dev) return 0; } + static const struct dev_pm_ops raydium_ts_pm_ops = { #if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)) - .suspend = raydium_ts_suspend, - .resume = raydium_ts_resume, + .suspend = raydium_ts_suspend, + .resume = raydium_ts_resume, #endif /*end of CONFIG_PM*/ }; @@ -1635,20 +1622,30 @@ static int drm_notifier_callback(struct notifier_block *self, if (g_raydium_ts->fb_state != FB_ON) { LOGD(LOG_INFO, "%s: Resume notifier called!\n", __func__); +#ifdef GESTURE_EN + /* clear palm status */ + g_raydium_ts->is_palm = 0; +#endif #if defined(CONFIG_PM) raydium_ts_resume(&g_raydium_ts->client->dev); #endif g_raydium_ts->fb_state = FB_ON; - LOGD(LOG_INFO, "%s: Resume notified!\n", __func__); + LOGD(LOG_INFO, "%s: Resume notified!\n", __func__); } } - } else if (*blank == DRM_PANEL_BLANK_LP || *blank == DRM_PANEL_BLANK_POWERDOWN) { + } else if (*blank == DRM_PANEL_BLANK_LP || *blank == DRM_PANEL_BLANK_POWERDOWN + || *blank == DRM_PANEL_BLANK_FPS_CHANGE) { LOGD(LOG_INFO, "%s: LOWPOWER!\n", __func__); if (event == DRM_PANEL_EARLY_EVENT_BLANK) { if (g_raydium_ts->fb_state != FB_OFF) { LOGD(LOG_INFO, "%s: Suspend notifier called!\n", __func__); +#ifdef GESTURE_EN + /* clear palm status */ + g_raydium_ts->is_palm = 0; +#endif + #if defined(CONFIG_PM) raydium_ts_suspend(&g_raydium_ts->client->dev); #endif @@ -1842,13 +1839,13 @@ static int raydium_get_dt_coords(struct device *dev, char *name, static int raydium_check_dsi_panel_dt(struct device_node *np, struct drm_panel **active_panel) { - int i = 0; + int i = 0, rc = 0; int count = 0; struct device_node *node = NULL; struct drm_panel *panel; count = of_count_phandle_with_args(np, "panel", NULL); - LOGD(LOG_ERR, "[touch]%s: Active panel count: %d\n", __func__, count); + LOGD(LOG_INFO, "[touch]%s: Active panel count: %d\n", __func__, count); if (count <= 0) return 0; @@ -1867,8 +1864,8 @@ static int raydium_check_dsi_panel_dt(struct device_node *np, struct drm_panel * } } LOGD(LOG_ERR, "[touch]%s: Active panel NOT selected !\n", __func__); - - return PTR_ERR(panel); + rc = PTR_ERR(panel); + return rc; } static int raydium_parse_dt(struct device *dev, @@ -2149,6 +2146,7 @@ exit: #endif return rc; } + static int raydium_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -2208,6 +2206,8 @@ static int raydium_ts_probe(struct i2c_client *client, g_raydium_ts->y_max = pdata->y_max - 1; g_raydium_ts->is_suspend = 0; g_raydium_ts->is_sleep = 0; + + #ifdef GESTURE_EN g_raydium_ts->is_palm = 0; #endif @@ -2260,13 +2260,6 @@ static int raydium_ts_probe(struct i2c_client *client, ret = -EPROBE_DEFER; goto exit_check_i2c; } - - if (!pdata->active_panel) { -// LOGD(LOG_ERR, "[touch]active_panel null, check again!\n"); - raydium_check_dsi_panel_dt(client->dev.of_node, - &pdata->active_panel); - } - #ifdef CONFIG_DRM /* Setup active dsi panel */ active_panel = pdata->active_panel; @@ -2328,7 +2321,6 @@ static int raydium_ts_probe(struct i2c_client *client, ret = request_threaded_irq(g_raydium_ts->irq, NULL, raydium_ts_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_NO_SUSPEND, client->dev.driver->name, g_raydium_ts); - if (ret < 0) { LOGD(LOG_ERR, "[touch]raydium_probe: request irq failed\n"); goto exit_irq_request_failed; From ddda48faec567700db30d20c4e9bc56855527b04 Mon Sep 17 00:00:00 2001 From: ppadasal Date: Wed, 21 Sep 2022 16:44:21 +0530 Subject: [PATCH 058/170] touchscreen: raydium: Both regulators turn off Add the shutdown callback() in i2c_driver struct. Keep both regulators should be turn off in release sequence. Change-Id: Icbfa8d9c9226bce8bf2286f99a18d3385e3b4d45 Signed-off-by: ppadasal --- raydium/raydium_driver.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 32f1f10716..4cbf707ce7 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -2391,6 +2391,43 @@ exit_check_functionality_failed: } + +void raydium_ts_shutdown(struct i2c_client *client) +{ + +#if defined(CONFIG_FB) + raydium_unregister_notifier(); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&g_raydium_ts->early_suspend); +#elif defined(CONFIG_DRM) + if (active_panel) + drm_panel_notifier_unregister(active_panel, &g_raydium_ts->fb_notifier); +#endif/*end of CONFIG_FB*/ + input_unregister_device(g_raydium_ts->input_dev); + input_free_device(g_raydium_ts->input_dev); + gpio_free(g_raydium_ts->rst_gpio); + +#ifdef CONFIG_RM_SYSFS_DEBUG + raydium_release_sysfs(client); +#endif /*end of CONFIG_RM_SYSFS_DEBUG*/ + + free_irq(client->irq, g_raydium_ts); + + if (gpio_is_valid(g_raydium_ts->rst_gpio)) + gpio_free(g_raydium_ts->rst_gpio); + + if (gpio_is_valid(g_raydium_ts->irq_gpio)) + gpio_free(g_raydium_ts->irq_gpio); + + cancel_work_sync(&g_raydium_ts->work); + destroy_workqueue(g_raydium_ts->workqueue); + + raydium_enable_regulator(g_raydium_ts, false); + raydium_get_regulator(g_raydium_ts, false); + + i2c_set_clientdata(client, NULL); +} + static int raydium_ts_remove(struct i2c_client *client) { @@ -2447,6 +2484,7 @@ static const struct of_device_id raydium_match_table[] = { static struct i2c_driver raydium_ts_driver = { .probe = raydium_ts_probe, .remove = raydium_ts_remove, + .shutdown = raydium_ts_shutdown, .id_table = raydium_ts_id, .driver = { .name = RAYDIUM_NAME, From 73a5d93a4c47967c5426471c1216f17c067f432f Mon Sep 17 00:00:00 2001 From: ppadasal Date: Wed, 21 Sep 2022 16:45:51 +0530 Subject: [PATCH 059/170] touchscreen: raydium: Change Input Key event Changed key event from KEY_POWER to KEY_WAKEUP. Change-Id: Ifeed075504a72426af600291457b46f1d0b336ae Signed-off-by: ppadasal --- raydium/raydium_driver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 4cbf707ce7..c03f63162c 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -1209,11 +1209,11 @@ static void raydium_work_handler(struct work_struct *work) /*if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP */ /*&& g_u8_wakeup_flag == false) { */ if (u8_tp_status[POS_GES_STATUS] == 0) { - input_report_key(g_raydium_ts->input_dev, KEY_POWER, true); + input_report_key(g_raydium_ts->input_dev, KEY_WAKEUP, true); usleep_range(9500, 10500); input_sync(g_raydium_ts->input_dev); - input_report_key(g_raydium_ts->input_dev, KEY_POWER, false); + input_report_key(g_raydium_ts->input_dev, KEY_WAKEUP, false); input_sync(g_raydium_ts->input_dev); LOGD(LOG_INFO, "[touch]display wake up with g_u8_resetflag true\n"); /*goto exit;*/ From 558808c3561fbd4db213292c373ae9ae87d1cd03 Mon Sep 17 00:00:00 2001 From: ppadasal Date: Wed, 21 Sep 2022 16:46:54 +0530 Subject: [PATCH 060/170] touchscreen: raydium: Change in Key event Changed key event from KEY_POWER to KEY_WAKEUP in Probe Sequence. Change-Id: Ifb93b0529af727443d86fd8fad47fed7c70ad48d Signed-off-by: ppadasal --- raydium/raydium_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index c03f63162c..967b58601a 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -2286,7 +2286,7 @@ static int raydium_ts_probe(struct i2c_client *client, #ifdef GESTURE_EN input_set_capability(input_dev, EV_KEY, KEY_SLEEP); - input_set_capability(input_dev, EV_KEY, KEY_POWER); + input_set_capability(input_dev, EV_KEY, KEY_WAKEUP); #endif /*suspend/resume routine*/ From 6414fb04bfec5a444010b6687028db5cdc9e1add Mon Sep 17 00:00:00 2001 From: ppadasal Date: Wed, 21 Sep 2022 16:48:25 +0530 Subject: [PATCH 061/170] touchscreen: raydium: Lower log level Lower log level in work handler. Change-Id: Idcd1ce78ce979318542be5684fc286135af5d322 Signed-off-by: ppadasal --- raydium/raydium_driver.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 967b58601a..b21a790e38 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -1188,9 +1188,9 @@ static void raydium_work_handler(struct work_struct *work) #ifdef GESTURE_EN unsigned char u8_i; - LOGD(LOG_INFO, "[touch]ts->blank:%x, g_u8_i2c_mode:%x\n", + LOGD(LOG_DEBUG, "[touch]ts->blank:%x, g_u8_i2c_mode:%x\n", g_raydium_ts->blank, g_u8_i2c_mode); - LOGD(LOG_INFO, "[touch]u8_tp_status:%x, g_raydium_ts->is_palm:%x\n", + LOGD(LOG_DEBUG, "[touch]u8_tp_status:%x, g_raydium_ts->is_palm:%x\n", u8_tp_status[POS_GES_STATUS], g_raydium_ts->is_palm); if (g_u8_i2c_mode == PDA2_MODE) { @@ -1204,7 +1204,7 @@ static void raydium_work_handler(struct work_struct *work) if (g_raydium_ts->blank == DRM_PANEL_BLANK_LP || g_raydium_ts->blank == DRM_PANEL_BLANK_POWERDOWN || g_raydium_ts->fb_state == FB_OFF) { - LOGD(LOG_INFO, "[touch] elseif u8_tp_status:%x\n", u8_tp_status[POS_GES_STATUS]); + LOGD(LOG_DEBUG, "[touch] elseif u8_tp_status:%x\n", u8_tp_status[POS_GES_STATUS]); /*need check small area*/ /*if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP */ /*&& g_u8_wakeup_flag == false) { */ @@ -1215,7 +1215,7 @@ static void raydium_work_handler(struct work_struct *work) input_report_key(g_raydium_ts->input_dev, KEY_WAKEUP, false); input_sync(g_raydium_ts->input_dev); - LOGD(LOG_INFO, "[touch]display wake up with g_u8_resetflag true\n"); + LOGD(LOG_DEBUG, "[touch]display wake up with g_u8_resetflag true\n"); /*goto exit;*/ } } From bd4d865d2d87101be5b0c7fe7fb3e2475d0f99f5 Mon Sep 17 00:00:00 2001 From: Yu Wu Date: Fri, 23 Sep 2022 14:05:50 +0800 Subject: [PATCH 062/170] touch: goodix: Remove duplicate reset operation In power_on function, it already contain reset operation, so no need to do reset again after power_on, to save time when do resume operation. Change-Id: Iba0c5a666b3e25df1e85afa6d5812666ce273922 Signed-off-by: Yu Wu --- goodix_berlin_driver/goodix_brl_hw.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/goodix_berlin_driver/goodix_brl_hw.c b/goodix_berlin_driver/goodix_brl_hw.c index d4646dad3d..f7b2afda7e 100644 --- a/goodix_berlin_driver/goodix_brl_hw.c +++ b/goodix_berlin_driver/goodix_brl_hw.c @@ -287,12 +287,6 @@ int brl_resume(struct goodix_ts_core *cd) ts_err("failed power on"); return ret; } - - ret = cd->hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); - if (ret) { - ts_err("failed reset tp"); - return ret; - } #endif return ret; From c12b22927b65f014105494eed9a358d4a312b4cc Mon Sep 17 00:00:00 2001 From: Sachin Kumar Garg Date: Mon, 26 Sep 2022 11:08:29 +0530 Subject: [PATCH 063/170] touch: Inclusion of wakeup key Adding of wakeup key to wakeup the system during touch after the display goes to ambient mode. Change-Id: Ieceb63b093016eda71e1e053ccddce93bdf65447 Signed-off-by: Sachin Kumar Garg --- pt/pt_mt_common.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pt/pt_mt_common.c b/pt/pt_mt_common.c index d1a9ade148..50a8981a6c 100644 --- a/pt/pt_mt_common.c +++ b/pt/pt_mt_common.c @@ -470,13 +470,13 @@ static void pt_mt_send_dummy_event(struct pt_core_data *cd, switch (cd->gesture_id) { case GESTURE_DOUBLE_TAP: - key_value = KEY_F1; + key_value = KEY_WAKEUP; break; case GESTURE_TWO_FINGERS_SLIDE: - key_value = KEY_F2; + key_value = KEY_WAKEUP; break; case GESTURE_TOUCH_DETECTED: - key_value = KEY_F3; + key_value = KEY_WAKEUP; break; case GESTURE_PUSH_BUTTON: key_value = KEY_F4; @@ -499,7 +499,7 @@ static void pt_mt_send_dummy_event(struct pt_core_data *cd, if (key_value > 0) { input_report_key(md->input, key_value, 1); - mdelay(10); + input_sync(md->input); input_report_key(md->input, key_value, 0); input_sync(md->input); } @@ -837,6 +837,7 @@ static int pt_setup_input_device(struct device *dev) input_set_capability(md->input, EV_KEY, KEY_F6); input_set_capability(md->input, EV_KEY, KEY_F7); input_set_capability(md->input, EV_KEY, KEY_F8); + input_set_capability(md->input, EV_KEY, KEY_WAKEUP); #endif return rc; } From 01206d278d32ff2760097be284b0350f3ae96c15 Mon Sep 17 00:00:00 2001 From: Sachin Kumar Garg Date: Tue, 27 Sep 2022 10:08:22 +0530 Subject: [PATCH 064/170] touch: Correct handling of core suspend issue Added correct handling of return codes and retrying for successful core suspend. Change-Id: I1c8f19fa55f5d51b5195242fb308f59f08330789 Signed-off-by: Sachin Kumar Garg --- pt/pt_core.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index 44a43453fb..f6c3f887a3 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -7666,14 +7666,29 @@ static int pt_core_sleep_(struct pt_core_data *cd) cancel_work_sync(&cd->enum_work); pt_stop_wd_timer(cd); - if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture) && cd->runtime) + if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture) && cd->runtime) { rc = pt_put_device_into_easy_wakeup_(cd); - else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) + if (rc) + pr_err("%s: Easy wakeup error detected :rc=%d\n", __func__, rc); + } else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) { + pt_debug(cd->dev, DL_INFO, + "%s: Entering into power off mode:\n", __func__); rc = pt_core_poweroff_device_(cd); - else if (cd->cpdata->flags & PT_CORE_FLAG_DEEP_STANDBY) + if (rc) + pr_err("%s: Power off error detected :rc=%d\n", __func__, rc); + } else if (cd->cpdata->flags & PT_CORE_FLAG_DEEP_STANDBY) { + pt_debug(cd->dev, DL_INFO, + "%s: Entering into deep standby mode:\n", __func__); rc = pt_put_device_into_deep_standby_(cd); - else + if (rc) + pr_err("%s: Deep standby error detected :rc=%d\n", __func__, rc); + } else { + pt_debug(cd->dev, DL_INFO, + "%s: Entering into deep sleep mode:\n", __func__); rc = pt_put_device_into_deep_sleep_(cd); + if (rc) + pr_err("%s: Deep sleep error detected :rc=%d\n", __func__, rc); + } mutex_lock(&cd->system_lock); cd->sleep_state = SS_SLEEP_ON; @@ -10512,7 +10527,7 @@ static int pt_core_suspend_(struct device *dev) pt_debug(dev, DL_INFO, "%s: Entering into suspend mode:\n", __func__); rc = pt_core_sleep(cd); - if (rc < 0) { + if (rc) { pt_debug(dev, DL_ERROR, "%s: Error on sleep\n", __func__); return -EAGAIN; } @@ -10621,7 +10636,12 @@ static int pt_core_resume_(struct device *dev) } exit: - pt_core_wake(cd); + rc = pt_core_wake(cd); + if (rc) { + dev_err(dev, "%s: Failed to wake up: rc=%d\n", + __func__, rc); + return -EAGAIN; + } return 0; } From 9beefc5e6fd543a7805cc52d546a61711b536384 Mon Sep 17 00:00:00 2001 From: Sachin Kumar Garg Date: Tue, 27 Sep 2022 10:15:10 +0530 Subject: [PATCH 065/170] touch: Correct return codes for S/R Adding correct condition of return codes for core suspend and resume. Change-Id: I6b9bf0fa1032f2d717535c48ec5e3ba5711b7d90 Signed-off-by: Sachin Kumar Garg --- pt/pt_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index f6c3f887a3..51bf487c50 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -10527,7 +10527,7 @@ static int pt_core_suspend_(struct device *dev) pt_debug(dev, DL_INFO, "%s: Entering into suspend mode:\n", __func__); rc = pt_core_sleep(cd); - if (rc) { + if (rc < 0) { pt_debug(dev, DL_ERROR, "%s: Error on sleep\n", __func__); return -EAGAIN; } @@ -10537,7 +10537,7 @@ static int pt_core_suspend_(struct device *dev) dev_err(dev, "%s: Failed to disable regulators: rc=%d\n", __func__, rc); } - dev_info(dev, "%s: Sayantan1: Voltage regulators disabled: rc=%d\n", + dev_info(dev, "%s: Voltage regulators disabled: rc=%d\n", __func__, rc); if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture) && !cd->runtime) @@ -10557,7 +10557,7 @@ static int pt_core_suspend_(struct device *dev) __func__); } - return rc; + return 0; } /******************************************************************************* @@ -10637,7 +10637,7 @@ static int pt_core_resume_(struct device *dev) exit: rc = pt_core_wake(cd); - if (rc) { + if (rc < 0) { dev_err(dev, "%s: Failed to wake up: rc=%d\n", __func__, rc); return -EAGAIN; From 36097dc556427803ab1e3bafbaa41a93774dd913 Mon Sep 17 00:00:00 2001 From: Sachin Kumar Garg Date: Tue, 27 Sep 2022 10:20:24 +0530 Subject: [PATCH 066/170] touch: Enabling Touch-to-Wake when AOD off Enablement of Touch to Wake when contextual mode is off. Change-Id: I94df963268daae4c50798c43fcb7c151bb2b0186 Signed-off-by: Sachin Kumar Garg --- pt/pt_core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index 51bf487c50..c12aab2c21 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -12472,8 +12472,11 @@ static int drm_notifier_callback(struct notifier_block *self, pt_debug(cd->dev, DL_INFO, "%s: Resume notified!\n", __func__); } } - } else if (*blank == DRM_PANEL_BLANK_LP) { - pt_debug(cd->dev, DL_INFO, "%s: LOWPOWER!\n", __func__); + } else if (*blank == DRM_PANEL_BLANK_LP || *blank == DRM_PANEL_BLANK_POWERDOWN) { + if (*blank == DRM_PANEL_BLANK_LP) + pt_debug(cd->dev, DL_INFO, "%s: LOWPOWER!\n", __func__); + else + pt_debug(cd->dev, DL_INFO, "%s: POWERDOWN!\n", __func__); if (event == DRM_PANEL_EARLY_EVENT_BLANK) { if (cd->fb_state != FB_OFF) { #if defined(CONFIG_PM_SLEEP) From 13fe897650bfeee666b5090afe63b9c4eea3633d Mon Sep 17 00:00:00 2001 From: Sachin Kumar Garg Date: Tue, 27 Sep 2022 10:30:56 +0530 Subject: [PATCH 067/170] touch: fix suspend resume issues run time suspend and rum time resume used for DRM events callbacks, which is updating suspend and resume count incorrectly. Added workQueue support to avoid executing logic in DRM event callback handler. Change-Id: I2985fb83aec5a72a0b9a604b424c4f06bfc06956 Signed-off-by: Sachin Kumar Garg --- pt/pt_core.c | 299 +++++++++++++++++++++++++++++++++++++++++++-------- pt/pt_regs.h | 9 +- 2 files changed, 261 insertions(+), 47 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index c12aab2c21..97553c06fc 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -7653,10 +7653,13 @@ static int pt_core_sleep_(struct pt_core_data *cd) int rc = 0; mutex_lock(&cd->system_lock); - if (cd->sleep_state == SS_SLEEP_OFF) { + pt_debug(cd->dev, DL_INFO, "%s - sleep_state %d\n", __func__, cd->sleep_state); + if (cd->sleep_state == SS_SLEEP_OFF || cd->sleep_state == SS_SLEEP_NONE) { cd->sleep_state = SS_SLEEPING; } else { mutex_unlock(&cd->system_lock); + pt_debug(cd->dev, DL_INFO, + "%s - Skip slee[ state %d\n", __func__, cd->sleep_state); return 1; } mutex_unlock(&cd->system_lock); @@ -7666,13 +7669,8 @@ static int pt_core_sleep_(struct pt_core_data *cd) cancel_work_sync(&cd->enum_work); pt_stop_wd_timer(cd); - if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture) && cd->runtime) { - rc = pt_put_device_into_easy_wakeup_(cd); - if (rc) - pr_err("%s: Easy wakeup error detected :rc=%d\n", __func__, rc); - } else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) { - pt_debug(cd->dev, DL_INFO, - "%s: Entering into power off mode:\n", __func__); + if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) { + pt_debug(cd->dev, DL_INFO, "%s: Entering into power off mode:\n", __func__); rc = pt_core_poweroff_device_(cd); if (rc) pr_err("%s: Power off error detected :rc=%d\n", __func__, rc); @@ -7697,6 +7695,86 @@ static int pt_core_sleep_(struct pt_core_data *cd) return rc; } +/******************************************************************************* + * FUNCTION: pt_core_easywake_on_ + * + * SUMMARY: Suspend the device with easy wake on the + * configuration in the core platform data structure. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_core_easywake_on_(struct pt_core_data *cd) +{ + int rc = 0; + + mutex_lock(&cd->system_lock); + + if (cd->sleep_state == SS_SLEEP_ON) { + mutex_unlock(&cd->system_lock); + pt_debug(cd->dev, DL_INFO, "%s - Skip sleep state %d\n", + __func__, cd->sleep_state); + return 1; + } + mutex_unlock(&cd->system_lock); + + /* Ensure watchdog and startup works stopped */ + pt_stop_wd_timer(cd); + cancel_work_sync(&cd->enum_work); + pt_stop_wd_timer(cd); + + if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) { + rc = pt_put_device_into_easy_wakeup_(cd); + pt_debug(cd->dev, DL_INFO, "%s :Entering into easywakeup: rc=%d\n", __func__, rc); + if (rc) + pr_err("%s: Easy wakeup error detected :rc=%d\n", __func__, rc); + } + + return rc; +} + + +/******************************************************************************* + * FUNCTION: pt_core_easywake_on + * + * SUMMARY: Protected call to pt_core_easywake_on_ by exclusive access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_core_easywake_on(struct pt_core_data *cd) +{ + int rc = 0; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_core_easywake_on_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: fail to release exclusive\n", __func__); + else + pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n", + __func__); + + return rc; +} + + /******************************************************************************* * FUNCTION: pt_core_sleep * @@ -8420,7 +8498,7 @@ static int pt_read_input(struct pt_core_data *cd) */ mutex_lock(&cd->system_lock); - if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture) && cd->runtime) { + if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) { if (cd->sleep_state == SS_SLEEP_ON) { mutex_unlock(&cd->system_lock); if (!dev->power.is_suspended) @@ -9014,13 +9092,16 @@ static int pt_core_wake_device_from_deep_sleep_( ******************************************************************************/ static int pt_core_wake_device_from_easy_wake_(struct pt_core_data *cd) { + int rc = 0; + mutex_lock(&cd->system_lock); cd->wait_until_wake = 1; mutex_unlock(&cd->system_lock); wake_up(&cd->wait_q); msleep(20); - return pt_core_wake_device_from_deep_sleep_(cd); + rc = pt_core_wake_device_from_deep_sleep_(cd); + return rc; } /******************************************************************************* @@ -9480,6 +9561,81 @@ exit: return rc; } +/******************************************************************************* + * FUNCTION: pt_core_easywake_off_ + * + * SUMMARY: Resume the device with a power on or wake from deep sleep based on + * the configuration in the core platform data structure. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_core_easywake_off_(struct pt_core_data *cd) +{ + int rc = 0; + + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_OFF) { + mutex_unlock(&cd->system_lock); + pt_debug(cd->dev, DL_INFO, + "%s - %d skip wakeoff\n", __func__, cd->sleep_state); + return 1; + } + mutex_unlock(&cd->system_lock); + + if (!(cd->cpdata->flags & PT_CORE_FLAG_SKIP_RESUME)) { + if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) + rc = pt_core_wake_device_from_easy_wake_(cd); + if (rc < 0) + pt_debug(cd->dev, DL_ERROR, + "%s - %d failed %d\n", __func__, rc); + } + + pt_start_wd_timer(cd); + return rc; +} + +/******************************************************************************* + * FUNCTION: pt_core_easywake_off + * + * SUMMARY: Protected call to pt_core_easywake_off by exclusive access to the DUT. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static int pt_core_easywake_off(struct pt_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, + "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = pt_core_easywake_off_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + pt_debug(cd->dev, DL_ERROR, "%s: fail to release exclusive\n", + __func__); + else + pt_debug(cd->dev, DL_DEBUG, "%s: pass release exclusive\n", + __func__); + + return rc; +} + + /******************************************************************************* * FUNCTION: pt_core_wake_ * @@ -9498,18 +9654,18 @@ static int pt_core_wake_(struct pt_core_data *cd) int rc = 0; mutex_lock(&cd->system_lock); - if (cd->sleep_state == SS_SLEEP_ON) { + if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_SLEEP_NONE) { cd->sleep_state = SS_WAKING; } else { mutex_unlock(&cd->system_lock); + pt_debug(cd->dev, DL_INFO, + "%s - skip wake sleep state %d\n", __func__, cd->sleep_state); return 1; } mutex_unlock(&cd->system_lock); if (!(cd->cpdata->flags & PT_CORE_FLAG_SKIP_RESUME)) { - if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture) && cd->runtime) - rc = pt_core_wake_device_from_easy_wake_(cd); - else if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) { + if (cd->cpdata->flags & PT_CORE_FLAG_POWEROFF_ON_SLEEP) { pt_debug(cd->dev, DL_INFO, "%s: Entering into poweron mode:\n", __func__); rc = pt_core_poweron_device_(cd); @@ -10454,13 +10610,11 @@ static int pt_core_rt_suspend(struct device *dev) dev_info(dev, "%s: Entering into runtime suspend mode:\n", __func__); + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) return 0; - if (cd->sleep_state == SS_SLEEP_OFF) - cd->runtime = 1; - - rc = pt_core_sleep(cd); + rc = pt_core_easywake_on(cd); if (rc < 0) { pt_debug(dev, DL_ERROR, "%s: Error on sleep\n", __func__); return -EAGAIN; @@ -10491,15 +10645,12 @@ static int pt_core_rt_resume(struct device *dev) if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) return 0; - rc = pt_core_wake(cd); + rc = pt_core_easywake_off(cd); if (rc < 0) { pt_debug(dev, DL_ERROR, "%s: Error on wake\n", __func__); return -EAGAIN; } - if (cd->sleep_state == SS_SLEEP_OFF) - cd->runtime = 0; - return 0; } #endif /* CONFIG_PM_SLEEP */ @@ -10527,20 +10678,17 @@ static int pt_core_suspend_(struct device *dev) pt_debug(dev, DL_INFO, "%s: Entering into suspend mode:\n", __func__); rc = pt_core_sleep(cd); - if (rc < 0) { + if (rc) { pt_debug(dev, DL_ERROR, "%s: Error on sleep\n", __func__); return -EAGAIN; } +#ifdef REGULATOR rc = pt_enable_regulator(cd, false); - if (rc) { - dev_err(dev, "%s: Failed to disable regulators: rc=%d\n", - __func__, rc); - } - dev_info(dev, "%s: Voltage regulators disabled: rc=%d\n", - __func__, rc); - - if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture) && !cd->runtime) + if (rc) + dev_err(dev, "%s: Failed to disable regulators: rc=%d\n", __func__, rc); +#endif + if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) return 0; /* Required to prevent interrupts before bus awake */ @@ -10557,7 +10705,7 @@ static int pt_core_suspend_(struct device *dev) __func__); } - return 0; + return rc; } /******************************************************************************* @@ -10577,10 +10725,14 @@ static int pt_core_suspend_(struct device *dev) static int pt_core_suspend(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); + int rc = 0; + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) return 0; - return pt_core_suspend_(dev); + rc = pt_core_suspend_(dev); + pt_debug(dev, DL_WARN, "%s Exit - rc = %d\n", __func__, rc); + return rc; } /******************************************************************************* @@ -10603,15 +10755,19 @@ static int pt_core_resume_(struct device *dev) dev_info(dev, "%s: Entering into resume mode:\n", __func__); + +#ifdef REGULATOR rc = pt_enable_regulator(cd, true); if (rc < 0) { dev_err(dev, "%s: Failed to enable regulators: rc=%d\n", __func__, rc); } +#endif + dev_info(dev, "%s: Voltage regulator enabled: rc=%d\n", __func__, rc); - if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture) && !cd->runtime) + if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) goto exit; /* @@ -10637,7 +10793,7 @@ static int pt_core_resume_(struct device *dev) exit: rc = pt_core_wake(cd); - if (rc < 0) { + if (rc) { dev_err(dev, "%s: Failed to wake up: rc=%d\n", __func__, rc); return -EAGAIN; @@ -12418,6 +12574,44 @@ static void pt_setup_early_suspend(struct pt_core_data *cd) } #elif defined(CONFIG_DRM) +static void pt_resume_work(struct work_struct *work) +{ + struct pt_core_data *pt_data = container_of(work, struct pt_core_data, + resume_work); + int rc = 0; + + if (pt_data->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) + return; + + rc = pt_core_easywake_off(pt_data); + if (rc < 0) { + pt_debug(pt_data->dev, DL_ERROR, + "%s: Error on wake\n", __func__); + return; + } + +} + +static void pt_suspend_work(struct work_struct *work) +{ + struct pt_core_data *pt_data = container_of(work, struct pt_core_data, + suspend_work); + int rc = 0; + + pt_debug(pt_data->dev, DL_INFO, "%s Start\n", __func__); + + if (pt_data->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) + return; + + rc = pt_core_easywake_on(pt_data); + if (rc < 0) { + pt_debug(pt_data->dev, DL_ERROR, "%s: Error on sleep\n", __func__); + return; + } + pt_debug(pt_data->dev, DL_INFO, "%s Exit\n", __func__); + +} + /******************************************************************************* * FUNCTION: drm_notifier_callback * @@ -12453,7 +12647,11 @@ static int drm_notifier_callback(struct notifier_block *self, } blank = evdata->data; - pt_debug(cd->dev, DL_INFO, "%s: DRM event:%lu,blank:%d", __func__, event, *blank); + pt_debug(cd->dev, DL_INFO, "%s: DRM event:%lu,blank:%d fb_state %d sleep state %d ", + __func__, event, *blank, cd->fb_state, cd->sleep_state); + pt_debug(cd->dev, DL_INFO, "%s: DRM Power - %s - FB state %d ", + __func__, (*blank == DRM_PANEL_BLANK_UNBLANK)?"UP":"DOWN", cd->fb_state); + if (*blank == DRM_PANEL_BLANK_UNBLANK) { pt_debug(cd->dev, DL_INFO, "%s: UNBLANK!\n", __func__); if (event == DRM_PANEL_EARLY_EVENT_BLANK) { @@ -12461,7 +12659,9 @@ static int drm_notifier_callback(struct notifier_block *self, __func__, event); } else if (event == DRM_PANEL_EVENT_BLANK) { if (cd->fb_state != FB_ON) { - call_atten_cb(cd, PT_ATTEN_RESUME, 0); + pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n", + __func__); + queue_work(cd->pt_workqueue, &cd->resume_work); #if defined(CONFIG_PM_SLEEP) pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n", __func__); @@ -12472,11 +12672,8 @@ static int drm_notifier_callback(struct notifier_block *self, pt_debug(cd->dev, DL_INFO, "%s: Resume notified!\n", __func__); } } - } else if (*blank == DRM_PANEL_BLANK_LP || *blank == DRM_PANEL_BLANK_POWERDOWN) { - if (*blank == DRM_PANEL_BLANK_LP) - pt_debug(cd->dev, DL_INFO, "%s: LOWPOWER!\n", __func__); - else - pt_debug(cd->dev, DL_INFO, "%s: POWERDOWN!\n", __func__); + } else if (*blank == DRM_PANEL_BLANK_LP) { + pt_debug(cd->dev, DL_INFO, "%s: LOWPOWER!\n", __func__); if (event == DRM_PANEL_EARLY_EVENT_BLANK) { if (cd->fb_state != FB_OFF) { #if defined(CONFIG_PM_SLEEP) @@ -12485,7 +12682,8 @@ static int drm_notifier_callback(struct notifier_block *self, if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) pt_core_suspend_(cd->dev); #endif - call_atten_cb(cd, PT_ATTEN_SUSPEND, 0); + cancel_work_sync(&cd->resume_work); + queue_work(cd->pt_workqueue, &cd->suspend_work); cd->fb_state = FB_OFF; pt_debug(cd->dev, DL_INFO, "%s: Suspend notified!\n", __func__); } @@ -12511,7 +12709,7 @@ exit: ******************************************************************************/ static void pt_setup_drm_notifier(struct pt_core_data *cd) { - cd->fb_state = FB_ON; + cd->fb_state = FB_NONE; cd->fb_notifier.notifier_call = drm_notifier_callback; pt_debug(cd->dev, DL_INFO, "%s: Setting up drm notifier\n", __func__); @@ -12519,6 +12717,17 @@ static void pt_setup_drm_notifier(struct pt_core_data *cd) pt_debug(cd->dev, DL_ERROR, "%s: Active panel not registered!\n", __func__); + cd->pt_workqueue = create_singlethread_workqueue("ts_wq"); + if (!cd->pt_workqueue) { + pt_debug(cd->dev, DL_ERROR, + "%s: worker thread creation failed !\n", __func__); + } + + if (cd->pt_workqueue) { + INIT_WORK(&cd->resume_work, pt_resume_work); + INIT_WORK(&cd->suspend_work, pt_suspend_work); + } + if (active_panel && drm_panel_notifier_register(active_panel, &cd->fb_notifier) < 0) @@ -17082,7 +17291,6 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, cd->watchdog_enabled = 0; cd->startup_retry_count = 0; cd->core_probe_complete = 0; - cd->runtime = 0; cd->fw_system_mode = FW_SYS_MODE_BOOT; cd->pip_cmd_timeout = PT_PIP_CMD_DEFAULT_TIMEOUT; cd->pip_cmd_timeout_default = PT_PIP_CMD_DEFAULT_TIMEOUT; @@ -17092,6 +17300,7 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, cd->cal_cache_in_host = PT_FEATURE_DISABLE; cd->multi_chip = PT_FEATURE_DISABLE; cd->tthe_hid_usb_format = PT_FEATURE_DISABLE; + cd->sleep_state = SS_SLEEP_NONE; if (cd->cpdata->config_dut_generation == CONFIG_DUT_PIP2_CAPABLE) { cd->set_dut_generation = true; diff --git a/pt/pt_regs.h b/pt/pt_regs.h index acfca02126..15778f1737 100644 --- a/pt/pt_regs.h +++ b/pt/pt_regs.h @@ -175,7 +175,7 @@ enum PT_DEBUG_LEVEL { DL_DEBUG = 4, DL_MAX }; -#define PT_INITIAL_DEBUG_LEVEL DL_WARN +#define PT_INITIAL_DEBUG_LEVEL DL_MAX /* Startup DUT enum status bitmask */ enum PT_STARTUP_STATUS { @@ -1103,6 +1103,7 @@ enum pt_atten_type { }; enum pt_sleep_state { + SS_SLEEP_NONE, SS_SLEEP_OFF, SS_SLEEP_ON, SS_SLEEPING, @@ -1110,6 +1111,7 @@ enum pt_sleep_state { }; enum pt_fb_state { + FB_NONE, FB_ON, FB_OFF, }; @@ -1440,6 +1442,10 @@ struct pt_core_data { struct list_head module_list; /* List of probed modules */ char core_id[20]; struct device *dev; + struct workqueue_struct *pt_workqueue; + struct work_struct suspend_work; + struct work_struct resume_work; + struct list_head atten_list[PT_ATTEN_NUM_ATTEN]; struct list_head param_list; struct mutex module_list_lock; @@ -1467,7 +1473,6 @@ struct pt_core_data { bool irq_wake; bool irq_disabled; bool hw_detected; - bool runtime; u8 easy_wakeup_gesture; #ifdef EASYWAKE_TSG6 u8 gesture_id; From a6675f0aeed4fe35d9883967c09752c18fb1090c Mon Sep 17 00:00:00 2001 From: Sachin Kumar Garg Date: Tue, 27 Sep 2022 10:36:22 +0530 Subject: [PATCH 068/170] touch: offload fw boot up to workqueue touch faststartup is consuming more than 700ms during power on, created workqueue to handle touch firmware boot up sequence. Change-Id: I67a288f41001052e5dcdb2ed465055b3a23d3233 Signed-off-by: Sachin Kumar Garg --- pt/pt_core.c | 194 ++++++++++++++++++++++++++++++++++++++------------- pt/pt_regs.h | 5 ++ 2 files changed, 152 insertions(+), 47 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index 97553c06fc..3d0edf2da6 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -41,6 +41,11 @@ #define FT_I2C_VTG_MIN_UV 1800000 #define FT_I2C_VTG_MAX_UV 1800000 +#define PWR_SUSPEND_LOAD_UA 165 +#define I2C_SUSPEND_LOAD_UA 100 +#define PWR_ACTIVE_LOAD_MA 12000 +#define I2C_ACTIVE_LOAD_MA 30000 + #define PT_CORE_STARTUP_RETRY_COUNT 3 #define PT_STATUS_STR_LEN (50) @@ -51,6 +56,7 @@ static struct drm_panel *active_panel; MODULE_FIRMWARE(PT_FW_FILE_NAME); +static int pt_lpm_regulator(struct regulator *reg, int load_uA); static const char *pt_driver_core_name = PT_CORE_NAME; static const char *pt_driver_core_version = PT_DRIVER_VERSION; static const char *pt_driver_core_date = PT_DRIVER_DATE; @@ -7660,7 +7666,7 @@ static int pt_core_sleep_(struct pt_core_data *cd) mutex_unlock(&cd->system_lock); pt_debug(cd->dev, DL_INFO, "%s - Skip slee[ state %d\n", __func__, cd->sleep_state); - return 1; + return 0; } mutex_unlock(&cd->system_lock); @@ -7714,11 +7720,11 @@ static int pt_core_easywake_on_(struct pt_core_data *cd) mutex_lock(&cd->system_lock); - if (cd->sleep_state == SS_SLEEP_ON) { + if (cd->sleep_state == SS_EASY_WAKING_ON) { mutex_unlock(&cd->system_lock); pt_debug(cd->dev, DL_INFO, "%s - Skip sleep state %d\n", __func__, cd->sleep_state); - return 1; + return 0; } mutex_unlock(&cd->system_lock); @@ -7733,6 +7739,9 @@ static int pt_core_easywake_on_(struct pt_core_data *cd) if (rc) pr_err("%s: Easy wakeup error detected :rc=%d\n", __func__, rc); } + mutex_lock(&cd->system_lock); + cd->sleep_state = SS_EASY_WAKING_ON; + mutex_unlock(&cd->system_lock); return rc; } @@ -8499,7 +8508,7 @@ static int pt_read_input(struct pt_core_data *cd) mutex_lock(&cd->system_lock); if (IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) { - if (cd->sleep_state == SS_SLEEP_ON) { + if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_EASY_WAKING_ON) { mutex_unlock(&cd->system_lock); if (!dev->power.is_suspended) goto read; @@ -9579,11 +9588,11 @@ static int pt_core_easywake_off_(struct pt_core_data *cd) int rc = 0; mutex_lock(&cd->system_lock); - if (cd->sleep_state == SS_SLEEP_OFF) { + if (cd->sleep_state == SS_EASY_WAKING_OFF) { mutex_unlock(&cd->system_lock); pt_debug(cd->dev, DL_INFO, "%s - %d skip wakeoff\n", __func__, cd->sleep_state); - return 1; + return 0; } mutex_unlock(&cd->system_lock); @@ -9595,6 +9604,9 @@ static int pt_core_easywake_off_(struct pt_core_data *cd) "%s - %d failed %d\n", __func__, rc); } + mutex_lock(&cd->system_lock); + cd->sleep_state = SS_EASY_WAKING_OFF; + mutex_unlock(&cd->system_lock); pt_start_wd_timer(cd); return rc; } @@ -9660,7 +9672,7 @@ static int pt_core_wake_(struct pt_core_data *cd) mutex_unlock(&cd->system_lock); pt_debug(cd->dev, DL_INFO, "%s - skip wake sleep state %d\n", __func__, cd->sleep_state); - return 1; + return 0; } mutex_unlock(&cd->system_lock); @@ -9701,7 +9713,7 @@ static int pt_core_wake_(struct pt_core_data *cd) ******************************************************************************/ static int pt_core_wake(struct pt_core_data *cd) { - int rc; + int rc = 0; rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { @@ -10512,6 +10524,49 @@ regulator_put: return rc; } +static int pt_enable_vdd_regulator(struct pt_core_data *cd, bool en) +{ + int rc; + + if (!en) { + rc = 0; + goto disable_vdd_reg; + } + + if (cd->vdd) { + if (regulator_count_voltages(cd->vdd) > 0) { + rc = regulator_set_voltage(cd->vdd, FT_VTG_MIN_UV, + FT_VTG_MAX_UV); + if (rc) { + dev_err(cd->dev, + "Regulator set_vtg failed vdd rc=%d\n", rc); + goto exit; + } + } + + rc = regulator_enable(cd->vdd); + if (rc) { + dev_err(cd->dev, + "Regulator vdd enable failed rc=%d\n", rc); + goto exit; + } + } + + return 0; + +disable_vdd_reg: + if (cd->vdd) { + if (regulator_count_voltages(cd->vdd) > 0) + regulator_set_voltage(cd->vdd, FT_VTG_MIN_UV, + FT_VTG_MAX_UV); + + regulator_disable(cd->vdd); + } + +exit: + return rc; +} + static int pt_enable_regulator(struct pt_core_data *cd, bool en) { int rc; @@ -10606,19 +10661,9 @@ exit: static int pt_core_rt_suspend(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); - int rc = 0; - dev_info(dev, "%s: Entering into runtime suspend mode:\n", - __func__); - - if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) - return 0; - - rc = pt_core_easywake_on(cd); - if (rc < 0) { - pt_debug(dev, DL_ERROR, "%s: Error on sleep\n", __func__); - return -EAGAIN; - } + pt_debug(dev, DL_DEBUG, "%s Skip - probe state %d\n", + __func__, cd->core_probe_complete); return 0; } @@ -10638,18 +10683,8 @@ static int pt_core_rt_suspend(struct device *dev) static int pt_core_rt_resume(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); - int rc = 0; - - dev_info(dev, "%s: Entering into runtime resume mode:\n", - __func__); - if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) - return 0; - - rc = pt_core_easywake_off(cd); - if (rc < 0) { - pt_debug(dev, DL_ERROR, "%s: Error on wake\n", __func__); - return -EAGAIN; - } + pt_debug(dev, DL_DEBUG, "%s Skip - probe state %d\n", + __func__, cd->core_probe_complete); return 0; } @@ -10683,11 +10718,17 @@ static int pt_core_suspend_(struct device *dev) return -EAGAIN; } -#ifdef REGULATOR - rc = pt_enable_regulator(cd, false); - if (rc) - dev_err(dev, "%s: Failed to disable regulators: rc=%d\n", __func__, rc); -#endif + rc = pt_enable_vdd_regulator(cd, false); + if (rc) { + dev_err(dev, "%s: Failed to disable vdd regulators: rc=%d\n", + __func__, rc); + } + rc = pt_lpm_regulator(cd->vcc_i2c, I2C_ACTIVE_LOAD_MA); + if (rc) { + dev_err(dev, "%s: Failed to enter to lpm mode rc=%d\n", + __func__, rc); + } + if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) return 0; @@ -10708,6 +10749,13 @@ static int pt_core_suspend_(struct device *dev) return rc; } +static int pt_lpm_regulator(struct regulator *reg, int load_uA) +{ + + return (regulator_count_voltages(reg) > 0) ? + regulator_set_load(reg, load_uA) : 0; +} + /******************************************************************************* * FUNCTION: pt_core_suspend * @@ -10732,7 +10780,7 @@ static int pt_core_suspend(struct device *dev) rc = pt_core_suspend_(dev); pt_debug(dev, DL_WARN, "%s Exit - rc = %d\n", __func__, rc); - return rc; + return 0; } /******************************************************************************* @@ -10756,13 +10804,17 @@ static int pt_core_resume_(struct device *dev) dev_info(dev, "%s: Entering into resume mode:\n", __func__); -#ifdef REGULATOR - rc = pt_enable_regulator(cd, true); + rc = pt_lpm_regulator(cd->vcc_i2c, I2C_ACTIVE_LOAD_MA); if (rc < 0) { - dev_err(dev, "%s: Failed to enable regulators: rc=%d\n", + dev_err(dev, "%s: Failed to exit lpm mode: rc=%d\n", + __func__, rc); + } + + rc = pt_enable_vdd_regulator(cd, true); + if (rc < 0) { + dev_err(dev, "%s: Failed to enable vdd regulators: rc=%d\n", __func__, rc); } -#endif dev_info(dev, "%s: Voltage regulator enabled: rc=%d\n", __func__, rc); @@ -10799,9 +10851,53 @@ exit: return -EAGAIN; } - return 0; + return rc; } +/******************************************************************************* + * FUNCTION: resume_offload_work + * + * SUMMARY: Wrapper function of pt_core_resume_() to avoid TP to be waken/slept + * along with kernel power state even the display is off based on the check of + * TTDL core platform flag. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to core device + ******************************************************************************/ +static void pt_resume_offload_work(struct work_struct *work) + +{ + int rc = 0; + int retry_count = 10; + struct pt_core_data *pt_data = container_of(work, struct pt_core_data, + resume_offload_work); + + do { + retry_count--; + rc = pt_core_resume_(pt_data->dev); + if (rc < 0) + pt_debug(pt_data->dev, DL_ERROR, + "%s: Error on wake\n", __func__); + } while (retry_count && rc < 0); + +#ifdef TOUCH_TO_WAKE_POWER_FEATURE_WORK_AROUND + rc = pt_core_easywake_on(pt_data); + if (rc < 0) { + pt_debug(pt_data->dev, DL_ERROR, + "%s: Error on enable touch to wake key\n", + __func__); + return; + } + pt_data->fb_state = FB_OFF; + pt_debug(pt_data->dev, DL_INFO, "%s End\n", __func__); +#endif +} + + /******************************************************************************* * FUNCTION: pt_core_resume * @@ -10819,10 +10915,14 @@ exit: static int pt_core_resume(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); + int rc = 0; if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) return 0; - return pt_core_resume_(dev); + queue_work(cd->pt_workqueue, &cd->resume_offload_work); + pt_debug(cd->dev, DL_ERROR, "%s End\n", __func__); + + return rc; } #endif @@ -12587,9 +12687,8 @@ static void pt_resume_work(struct work_struct *work) if (rc < 0) { pt_debug(pt_data->dev, DL_ERROR, "%s: Error on wake\n", __func__); - return; } - + return; } static void pt_suspend_work(struct work_struct *work) @@ -12609,7 +12708,7 @@ static void pt_suspend_work(struct work_struct *work) return; } pt_debug(pt_data->dev, DL_INFO, "%s Exit\n", __func__); - + return; } /******************************************************************************* @@ -17649,6 +17748,7 @@ skip_enum: #elif defined(CONFIG_DRM) pt_debug(dev, DL_ERROR, "%s: Probe: Setup drm notifier\n", __func__); pt_setup_drm_notifier(cd); + INIT_WORK(&cd->resume_offload_work, pt_resume_offload_work); #elif defined(CONFIG_FB) pt_setup_fb_notifier(cd); #endif diff --git a/pt/pt_regs.h b/pt/pt_regs.h index 15778f1737..34ddba3285 100644 --- a/pt/pt_regs.h +++ b/pt/pt_regs.h @@ -143,6 +143,8 @@ #define TT7XXX_EXAMPLE #endif +#define TOUCH_TO_WAKE_POWER_FEATURE_WORK_AROUND + /* * The largest PIP message is the PIP2 FILE_WRITE which has: * 2 byte register @@ -1108,6 +1110,8 @@ enum pt_sleep_state { SS_SLEEP_ON, SS_SLEEPING, SS_WAKING, + SS_EASY_WAKING_ON, + SS_EASY_WAKING_OFF, }; enum pt_fb_state { @@ -1443,6 +1447,7 @@ struct pt_core_data { char core_id[20]; struct device *dev; struct workqueue_struct *pt_workqueue; + struct work_struct resume_offload_work; struct work_struct suspend_work; struct work_struct resume_work; From 0bc0c0ab129fd69700f849e98e818a0a9bc2acc3 Mon Sep 17 00:00:00 2001 From: Srinu Gorle Date: Thu, 9 Dec 2021 16:45:52 +0530 Subject: [PATCH 069/170] touch: configure regulators individually added support to configure touch regulators individually Change-Id: Ie0fea5717e6f0ecedcb52f38cd7985922ca0243c Signed-off-by: Sayantan Majumder --- pt/pt_core.c | 101 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 24 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index 3d0edf2da6..84500bbb3f 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -56,7 +56,13 @@ static struct drm_panel *active_panel; MODULE_FIRMWARE(PT_FW_FILE_NAME); -static int pt_lpm_regulator(struct regulator *reg, int load_uA); +#ifdef ENABLE_VDD_REG_ONLY +static int pt_enable_vdd_regulator(struct pt_core_data *cd, bool en); +#endif + +#ifdef ENABLE_I2C_REG_ONLY +static int pt_enable_i2c_regulator(struct pt_core_data *cd, bool en); +#endif static const char *pt_driver_core_name = PT_CORE_NAME; static const char *pt_driver_core_version = PT_DRIVER_VERSION; static const char *pt_driver_core_date = PT_DRIVER_DATE; @@ -10524,13 +10530,60 @@ regulator_put: return rc; } +#ifdef ENABLE_I2C_REG_ONLY +static int pt_enable_i2c_regulator(struct pt_core_data *cd, bool en) +{ + int rc; + + if (!en) { + rc = 0; + goto disable_vcc_i2c_reg_only; + } + + if (cd->vcc_i2c) { + if (regulator_count_voltages(cd->vcc_i2c) > 0) { + rc = regulator_set_voltage(cd->vcc_i2c, FT_I2C_VTG_MIN_UV, + FT_I2C_VTG_MAX_UV); + if (rc) { + dev_err(cd->dev, + "Regulator set_vtg failed vcc_i2c rc=%d\n", rc); + goto disable_vcc_i2c_reg_only; + } + } + + rc = regulator_enable(cd->vcc_i2c); + if (rc) { + dev_err(cd->dev, + "Regulator vcc_i2c enable failed rc=%d\n", rc); + goto disable_vcc_i2c_reg_only; + } + } + + + return 0; + +disable_vcc_i2c_reg_only: + if (cd->vcc_i2c) { + if (regulator_count_voltages(cd->vcc_i2c) > 0) + regulator_set_voltage(cd->vcc_i2c, FT_I2C_VTG_MIN_UV, + FT_I2C_VTG_MAX_UV); + + regulator_disable(cd->vcc_i2c); + } + + return rc; +} + +#endif + +#ifdef ENABLE_VDD_REG_ONLY static int pt_enable_vdd_regulator(struct pt_core_data *cd, bool en) { int rc; if (!en) { rc = 0; - goto disable_vdd_reg; + goto disable_vdd_reg_only; } if (cd->vdd) { @@ -10540,7 +10593,7 @@ static int pt_enable_vdd_regulator(struct pt_core_data *cd, bool en) if (rc) { dev_err(cd->dev, "Regulator set_vtg failed vdd rc=%d\n", rc); - goto exit; + goto disable_vdd_reg_only; } } @@ -10548,13 +10601,13 @@ static int pt_enable_vdd_regulator(struct pt_core_data *cd, bool en) if (rc) { dev_err(cd->dev, "Regulator vdd enable failed rc=%d\n", rc); - goto exit; + goto disable_vdd_reg_only; } } return 0; -disable_vdd_reg: +disable_vdd_reg_only: if (cd->vdd) { if (regulator_count_voltages(cd->vdd) > 0) regulator_set_voltage(cd->vdd, FT_VTG_MIN_UV, @@ -10563,9 +10616,9 @@ disable_vdd_reg: regulator_disable(cd->vdd); } -exit: return rc; } +#endif static int pt_enable_regulator(struct pt_core_data *cd, bool en) { @@ -10718,16 +10771,20 @@ static int pt_core_suspend_(struct device *dev) return -EAGAIN; } +#ifdef ENABLE_VDD_REG_ONLY rc = pt_enable_vdd_regulator(cd, false); if (rc) { dev_err(dev, "%s: Failed to disable vdd regulators: rc=%d\n", __func__, rc); } - rc = pt_lpm_regulator(cd->vcc_i2c, I2C_ACTIVE_LOAD_MA); - if (rc) { - dev_err(dev, "%s: Failed to enter to lpm mode rc=%d\n", - __func__, rc); - } +#endif +#ifdef ENABLE_I2C_REG_ONLY + rc = pt_enable_i2c_regulator(cd, false); + if (rc) { + dev_err(dev, "%s: Failed to disable vdd regulators: rc=%d\n", + __func__, rc); + } +#endif if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) return 0; @@ -10749,13 +10806,6 @@ static int pt_core_suspend_(struct device *dev) return rc; } -static int pt_lpm_regulator(struct regulator *reg, int load_uA) -{ - - return (regulator_count_voltages(reg) > 0) ? - regulator_set_load(reg, load_uA) : 0; -} - /******************************************************************************* * FUNCTION: pt_core_suspend * @@ -10804,17 +10854,20 @@ static int pt_core_resume_(struct device *dev) dev_info(dev, "%s: Entering into resume mode:\n", __func__); - rc = pt_lpm_regulator(cd->vcc_i2c, I2C_ACTIVE_LOAD_MA); - if (rc < 0) { - dev_err(dev, "%s: Failed to exit lpm mode: rc=%d\n", - __func__, rc); - } - +#ifdef ENABLE_VDD_REG_ONLY rc = pt_enable_vdd_regulator(cd, true); if (rc < 0) { dev_err(dev, "%s: Failed to enable vdd regulators: rc=%d\n", __func__, rc); } +#endif +#ifdef ENABLE_I2C_REG_ONLY + rc = pt_enable_i2c_regulator(cd, true); + if (rc < 0) { + dev_err(dev, "%s: Failed to enable vdd regulators: rc=%d\n", + __func__, rc); + } +#endif dev_info(dev, "%s: Voltage regulator enabled: rc=%d\n", __func__, rc); From 55fb326d7083fdcc1ddb887c6e37901c2dbbfaf0 Mon Sep 17 00:00:00 2001 From: Srinu Gorle Date: Thu, 9 Dec 2021 16:55:06 +0530 Subject: [PATCH 070/170] touch: disable both LDO29 and LDO21 regulators Enable and disable both LDO29 and LDO21 regulators during power suspend and resume sequence. Change-Id: I9e5058d6191d536bc80d5b59aa23a5dac9c9babb Signed-off-by: Sayantan Majumder --- pt/pt_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pt/pt_core.c b/pt/pt_core.c index 84500bbb3f..bfec36fff2 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -56,6 +56,8 @@ static struct drm_panel *active_panel; MODULE_FIRMWARE(PT_FW_FILE_NAME); +#define ENABLE_VDD_REG_ONLY +#define ENABLE_I2C_REG_ONLY #ifdef ENABLE_VDD_REG_ONLY static int pt_enable_vdd_regulator(struct pt_core_data *cd, bool en); #endif From 9fbc77acce2de793c174a615b76598d93e2f658b Mon Sep 17 00:00:00 2001 From: Srinu Gorle Date: Wed, 15 Dec 2021 11:34:46 +0530 Subject: [PATCH 071/170] touch: Fix easywake and suspend concurrencies If target already in easywake on mode then power suspend is getting skipped. Corrected state checks in power suspend to allow suspend on top of easywake. Change-Id: I64decc54c4b6f0d7b15a4f687bf25ceb42150549 Signed-off-by: Sayantan Majumder --- pt/pt_core.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index bfec36fff2..47b3f83e2f 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -7668,13 +7668,13 @@ static int pt_core_sleep_(struct pt_core_data *cd) mutex_lock(&cd->system_lock); pt_debug(cd->dev, DL_INFO, "%s - sleep_state %d\n", __func__, cd->sleep_state); - if (cd->sleep_state == SS_SLEEP_OFF || cd->sleep_state == SS_SLEEP_NONE) { - cd->sleep_state = SS_SLEEPING; - } else { + if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_SLEEPING) { mutex_unlock(&cd->system_lock); pt_debug(cd->dev, DL_INFO, "%s - Skip slee[ state %d\n", __func__, cd->sleep_state); - return 0; + return 0; + } else { + cd->sleep_state = SS_SLEEPING; } mutex_unlock(&cd->system_lock); @@ -9674,13 +9674,14 @@ static int pt_core_wake_(struct pt_core_data *cd) int rc = 0; mutex_lock(&cd->system_lock); - if (cd->sleep_state == SS_SLEEP_ON || cd->sleep_state == SS_SLEEP_NONE) { - cd->sleep_state = SS_WAKING; - } else { + + if (cd->sleep_state == SS_SLEEP_OFF || cd->sleep_state == SS_WAKING) { mutex_unlock(&cd->system_lock); pt_debug(cd->dev, DL_INFO, "%s - skip wake sleep state %d\n", __func__, cd->sleep_state); return 0; + } else { + cd->sleep_state = SS_WAKING; } mutex_unlock(&cd->system_lock); @@ -12826,7 +12827,7 @@ static int drm_notifier_callback(struct notifier_block *self, pt_debug(cd->dev, DL_INFO, "%s: Resume notified!\n", __func__); } } - } else if (*blank == DRM_PANEL_BLANK_LP) { + } else if (*blank == DRM_PANEL_BLANK_LP || *blank == DRM_PANEL_BLANK_POWERDOWN) { pt_debug(cd->dev, DL_INFO, "%s: LOWPOWER!\n", __func__); if (event == DRM_PANEL_EARLY_EVENT_BLANK) { if (cd->fb_state != FB_OFF) { From c54d6873641022b00291e783d27d21e5cb17da6c Mon Sep 17 00:00:00 2001 From: Srinu Gorle Date: Wed, 15 Dec 2021 18:39:32 +0530 Subject: [PATCH 072/170] touch: Add workqueue for suspend Added work Queues to handle suspend functionality. Change-Id: I1a7799056cd31d29b36172b5a0c509c9f6312dcd Signed-off-by: Sayantan Majumder --- pt/pt_core.c | 44 +++++++++++++++++++++++++++++++++++++------- pt/pt_regs.h | 1 + 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index 47b3f83e2f..c4802f631a 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -10825,15 +10825,16 @@ static int pt_core_suspend_(struct device *dev) ******************************************************************************/ static int pt_core_suspend(struct device *dev) { - struct pt_core_data *cd = dev_get_drvdata(dev); - int rc = 0; + struct pt_core_data *cd = dev_get_drvdata(dev); + int rc = 0; - if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) - return 0; + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) + return 0; - rc = pt_core_suspend_(dev); - pt_debug(dev, DL_WARN, "%s Exit - rc = %d\n", __func__, rc); - return 0; + queue_work(cd->pt_workqueue, &cd->suspend_offload_work); + pt_debug(cd->dev, DL_ERROR, "%s End\n", __func__); + + return rc; } /******************************************************************************* @@ -10910,6 +10911,34 @@ exit: return rc; } +/******************************************************************************* + * FUNCTION: suspend_offload_work + * + * SUMMARY: Wrapper function of pt_core_suspend() to avoid TP to be waken/slept + * along with kernel power state even the display is off based on the check of + * TTDL core platform flag. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to core device + ******************************************************************************/ +static void pt_suspend_offload_work(struct work_struct *work) + +{ + int rc = 0; + struct pt_core_data *cd = container_of(work, struct pt_core_data, + suspend_offload_work); + + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) + return; + + rc = pt_core_suspend_(cd->dev); + pt_debug(cd->dev, DL_WARN, "%s Exit - rc = %d\n", __func__, rc); +} + /******************************************************************************* * FUNCTION: resume_offload_work * @@ -17805,6 +17834,7 @@ skip_enum: pt_debug(dev, DL_ERROR, "%s: Probe: Setup drm notifier\n", __func__); pt_setup_drm_notifier(cd); INIT_WORK(&cd->resume_offload_work, pt_resume_offload_work); + INIT_WORK(&cd->suspend_offload_work, pt_suspend_offload_work); #elif defined(CONFIG_FB) pt_setup_fb_notifier(cd); #endif diff --git a/pt/pt_regs.h b/pt/pt_regs.h index 34ddba3285..c5a87ff059 100644 --- a/pt/pt_regs.h +++ b/pt/pt_regs.h @@ -1448,6 +1448,7 @@ struct pt_core_data { struct device *dev; struct workqueue_struct *pt_workqueue; struct work_struct resume_offload_work; + struct work_struct suspend_offload_work; struct work_struct suspend_work; struct work_struct resume_work; From 78f5b6be0d3867a304351e8eac7930ff0b0106f2 Mon Sep 17 00:00:00 2001 From: Sayantan Majumder Date: Tue, 11 Jan 2022 18:18:38 +0530 Subject: [PATCH 073/170] touch: Adding default logging level Updating the logging level to default from maximum Change-Id: I25f822350d71e7349b9bc6337d754115d8fe0af1 Signed-off-by: Sayantan Majumder --- pt/pt_regs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pt/pt_regs.h b/pt/pt_regs.h index c5a87ff059..946649efa3 100644 --- a/pt/pt_regs.h +++ b/pt/pt_regs.h @@ -177,7 +177,7 @@ enum PT_DEBUG_LEVEL { DL_DEBUG = 4, DL_MAX }; -#define PT_INITIAL_DEBUG_LEVEL DL_MAX +#define PT_INITIAL_DEBUG_LEVEL DL_WARN /* Startup DUT enum status bitmask */ enum PT_STARTUP_STATUS { From 34a427184763871c7c06470e607555d1615624dd Mon Sep 17 00:00:00 2001 From: Zhenbin Tan Date: Tue, 18 Jan 2022 16:19:27 +0800 Subject: [PATCH 074/170] touch: device exit TWM probe fail When device exit TWM mode, poweron init wait for reset timeout, casue probe fail, add probe retry. Change-Id: I2b323eb57cd04bfcc64f136a389b0af49f8b0162 Signed-off-by: Sayantan Majumder --- pt/pt_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pt/pt_core.c b/pt/pt_core.c index c4802f631a..da04477445 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -17791,6 +17791,7 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, pt_debug(cd->dev, DL_ERROR, "%s: Enumeration Failed r=%d\n", __func__, rc); /* For PtSBC don't error out, allow TTDL to stay up */ + rc = -EPROBE_DEFER; goto error_after_startup; } /* Suspend scanning until probe is complete to avoid asyc touches */ From b29e79f70268ed8498e8054f0d23d80d0122168a Mon Sep 17 00:00:00 2001 From: Anand Abhishek Date: Thu, 29 Sep 2022 13:32:08 +0530 Subject: [PATCH 075/170] touch: pt: cancel all pending works In some scenarios, back to back suspend and resume calls causing concurrency issues. This change cancel pending queued works before executing current task. Change-Id: I1c0971b68a08a4c634767ea1bb6d783f495a1093 Signed-off-by: Srinu Gorle Signed-off-by: Anand Abhishek --- pt/pt_core.c | 25 +++++++++++++++++++++++++ pt/pt_regs.h | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index da04477445..d304ef46bf 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -10831,6 +10831,11 @@ static int pt_core_suspend(struct device *dev) if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) return 0; + cancel_work_sync(&cd->resume_offload_work); + cancel_work_sync(&cd->suspend_offload_work); + cancel_work_sync(&cd->resume_work); + cancel_work_sync(&cd->suspend_work); + queue_work(cd->pt_workqueue, &cd->suspend_offload_work); pt_debug(cd->dev, DL_ERROR, "%s End\n", __func__); @@ -11004,6 +11009,11 @@ static int pt_core_resume(struct device *dev) if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) return 0; + cancel_work_sync(&cd->resume_offload_work); + cancel_work_sync(&cd->suspend_offload_work); + cancel_work_sync(&cd->resume_work); + cancel_work_sync(&cd->suspend_work); + queue_work(cd->pt_workqueue, &cd->resume_offload_work); pt_debug(cd->dev, DL_ERROR, "%s End\n", __func__); @@ -12845,6 +12855,11 @@ static int drm_notifier_callback(struct notifier_block *self, if (cd->fb_state != FB_ON) { pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n", __func__); + cancel_work_sync(&cd->resume_offload_work); + cancel_work_sync(&cd->suspend_offload_work); + cancel_work_sync(&cd->resume_work); + cancel_work_sync(&cd->suspend_work); + queue_work(cd->pt_workqueue, &cd->resume_work); #if defined(CONFIG_PM_SLEEP) pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n", @@ -12866,7 +12881,11 @@ static int drm_notifier_callback(struct notifier_block *self, if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) pt_core_suspend_(cd->dev); #endif + cancel_work_sync(&cd->resume_offload_work); + cancel_work_sync(&cd->suspend_offload_work); cancel_work_sync(&cd->resume_work); + cancel_work_sync(&cd->suspend_work); + queue_work(cd->pt_workqueue, &cd->suspend_work); cd->fb_state = FB_OFF; pt_debug(cd->dev, DL_INFO, "%s: Suspend notified!\n", __func__); @@ -17937,6 +17956,12 @@ int pt_release(struct pt_core_data *cd) call_atten_cb(cd, PT_ATTEN_CANCEL_LOADER, 0); cancel_work_sync(&cd->ttdl_restart_work); cancel_work_sync(&cd->enum_work); + cancel_work_sync(&cd->resume_offload_work); + cancel_work_sync(&cd->suspend_offload_work); + cancel_work_sync(&cd->resume_work); + cancel_work_sync(&cd->suspend_work); + destroy_workqueue(cd->pt_workqueue); + pt_stop_wd_timer(cd); pt_release_modules(cd); diff --git a/pt/pt_regs.h b/pt/pt_regs.h index 946649efa3..c5a87ff059 100644 --- a/pt/pt_regs.h +++ b/pt/pt_regs.h @@ -177,7 +177,7 @@ enum PT_DEBUG_LEVEL { DL_DEBUG = 4, DL_MAX }; -#define PT_INITIAL_DEBUG_LEVEL DL_WARN +#define PT_INITIAL_DEBUG_LEVEL DL_MAX /* Startup DUT enum status bitmask */ enum PT_STARTUP_STATUS { From 9837fef63e8de0e7cce3e2eed2455e6d4a2d6f09 Mon Sep 17 00:00:00 2001 From: Anand Abhishek Date: Thu, 29 Sep 2022 13:59:59 +0530 Subject: [PATCH 076/170] touch: pt: set active current Set required current loads to LDO's for active and RBSC modes. Change-Id: Ia4f338c6ca8fdaa981cd7dda959ac887b2fd4fbc Signed-off-by: Srinu Gorle Signed-off-by: Anand Abhishek --- pt/pt_core.c | 77 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index d304ef46bf..4a45bccb83 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -10536,43 +10536,35 @@ regulator_put: #ifdef ENABLE_I2C_REG_ONLY static int pt_enable_i2c_regulator(struct pt_core_data *cd, bool en) { - int rc; + int rc = 0; + pt_debug(cd->dev, DL_INFO, "%s: Enter flag = %d\n", __func__, en); if (!en) { rc = 0; goto disable_vcc_i2c_reg_only; } if (cd->vcc_i2c) { - if (regulator_count_voltages(cd->vcc_i2c) > 0) { - rc = regulator_set_voltage(cd->vcc_i2c, FT_I2C_VTG_MIN_UV, - FT_I2C_VTG_MAX_UV); - if (rc) { - dev_err(cd->dev, - "Regulator set_vtg failed vcc_i2c rc=%d\n", rc); - goto disable_vcc_i2c_reg_only; - } - } + rc = regulator_set_load(cd->vcc_i2c, I2C_ACTIVE_LOAD_MA); + if (rc < 0) + pt_debug(cd->dev, DL_INFO, + "%s: I2c unable to set active current rc = %d\n", __func__, rc); - rc = regulator_enable(cd->vcc_i2c); - if (rc) { - dev_err(cd->dev, - "Regulator vcc_i2c enable failed rc=%d\n", rc); - goto disable_vcc_i2c_reg_only; - } + pt_debug(cd->dev, DL_INFO, "%s: i2c set I2C_ACTIVE_LOAD_MA rc = %d\n", + __func__, rc); } - return 0; disable_vcc_i2c_reg_only: if (cd->vcc_i2c) { - if (regulator_count_voltages(cd->vcc_i2c) > 0) - regulator_set_voltage(cd->vcc_i2c, FT_I2C_VTG_MIN_UV, - FT_I2C_VTG_MAX_UV); + rc = regulator_set_load(cd->vcc_i2c, I2C_SUSPEND_LOAD_UA); + if (rc < 0) + pt_debug(cd->dev, DL_INFO, "%s: i2c unable to set 0 uAm rc = %d\n", + __func__, rc); - regulator_disable(cd->vcc_i2c); } + pt_debug(cd->dev, DL_INFO, "%s: Exit rc = %d I2C_SUSPEND_LOAD_UA\n", __func__, rc); return rc; } @@ -10582,8 +10574,9 @@ disable_vcc_i2c_reg_only: #ifdef ENABLE_VDD_REG_ONLY static int pt_enable_vdd_regulator(struct pt_core_data *cd, bool en) { - int rc; + int rc = 0, status = 0; + pt_debug(cd->dev, DL_INFO, "%s: Enter flag = %d\n", __func__, en); if (!en) { rc = 0; goto disable_vdd_reg_only; @@ -10606,6 +10599,13 @@ static int pt_enable_vdd_regulator(struct pt_core_data *cd, bool en) "Regulator vdd enable failed rc=%d\n", rc); goto disable_vdd_reg_only; } + + rc = regulator_set_load(cd->vdd, PWR_ACTIVE_LOAD_MA); + if (rc < 0) + pt_debug(cd->dev, DL_INFO, "%s: vdd unable to set active current rc = %d\n", + __func__, rc); + + pt_debug(cd->dev, DL_INFO, "%s: vdd regulator enabled rc = %d\n", __func__, rc); } return 0; @@ -10616,9 +10616,17 @@ disable_vdd_reg_only: regulator_set_voltage(cd->vdd, FT_VTG_MIN_UV, FT_VTG_MAX_UV); - regulator_disable(cd->vdd); - } + rc = regulator_set_load(cd->vdd, 0); + if (rc < 0) + pt_debug(cd->dev, DL_INFO, "%s: vdd unable to set 0 uAm rc = %d\n", + __func__, rc); + status = regulator_disable(cd->vdd); + if (!en) + rc = status; + pt_debug(cd->dev, DL_INFO, "%s: vdd regulator disabled rc = %d\n", __func__, rc); + } + pt_debug(cd->dev, DL_INFO, "%s: Exit rc = %d\n", __func__, rc); return rc; } #endif @@ -10766,11 +10774,11 @@ static int pt_core_suspend_(struct device *dev) int rc; struct pt_core_data *cd = dev_get_drvdata(dev); - pt_debug(dev, DL_INFO, "%s: Entering into suspend mode:\n", - __func__); + pt_debug(dev, DL_INFO, "%s: Enter\n", __func__); rc = pt_core_sleep(cd); if (rc) { - pt_debug(dev, DL_ERROR, "%s: Error on sleep\n", __func__); + pt_debug(dev, DL_ERROR, "%s: Error on sleep rc =%d\n", + __func__, rc); return -EAGAIN; } @@ -10806,6 +10814,7 @@ static int pt_core_suspend_(struct device *dev) __func__); } + pt_debug(dev, DL_INFO, "%s: Exit :\n", __func__); return rc; } @@ -10836,8 +10845,8 @@ static int pt_core_suspend(struct device *dev) cancel_work_sync(&cd->resume_work); cancel_work_sync(&cd->suspend_work); - queue_work(cd->pt_workqueue, &cd->suspend_offload_work); - pt_debug(cd->dev, DL_ERROR, "%s End\n", __func__); + rc = pt_core_suspend_(cd->dev); + pt_debug(cd->dev, DL_INFO, "%s Exit - rc = %d\n", __func__, rc); return rc; } @@ -10937,6 +10946,7 @@ static void pt_suspend_offload_work(struct work_struct *work) struct pt_core_data *cd = container_of(work, struct pt_core_data, suspend_offload_work); + pt_debug(cd->dev, DL_INFO, "%s start\n", __func__); if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) return; @@ -10966,6 +10976,7 @@ static void pt_resume_offload_work(struct work_struct *work) struct pt_core_data *pt_data = container_of(work, struct pt_core_data, resume_offload_work); + pt_debug(pt_data->dev, DL_INFO, "%s start\n", __func__); do { retry_count--; rc = pt_core_resume_(pt_data->dev); @@ -11015,7 +11026,7 @@ static int pt_core_resume(struct device *dev) cancel_work_sync(&cd->suspend_work); queue_work(cd->pt_workqueue, &cd->resume_offload_work); - pt_debug(cd->dev, DL_ERROR, "%s End\n", __func__); + pt_debug(cd->dev, DL_INFO, "%s workqueued\n", __func__); return rc; } @@ -12775,6 +12786,7 @@ static void pt_resume_work(struct work_struct *work) resume_work); int rc = 0; + pt_debug(pt_data->dev, DL_INFO, "%s start ", __func__); if (pt_data->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) return; @@ -12783,6 +12795,7 @@ static void pt_resume_work(struct work_struct *work) pt_debug(pt_data->dev, DL_ERROR, "%s: Error on wake\n", __func__); } + pt_debug(pt_data->dev, DL_INFO, "%s touch to wake disabled ", __func__); return; } @@ -12792,7 +12805,7 @@ static void pt_suspend_work(struct work_struct *work) suspend_work); int rc = 0; - pt_debug(pt_data->dev, DL_INFO, "%s Start\n", __func__); + pt_debug(pt_data->dev, DL_INFO, "%s start\n", __func__); if (pt_data->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) return; @@ -12802,7 +12815,7 @@ static void pt_suspend_work(struct work_struct *work) pt_debug(pt_data->dev, DL_ERROR, "%s: Error on sleep\n", __func__); return; } - pt_debug(pt_data->dev, DL_INFO, "%s Exit\n", __func__); + pt_debug(pt_data->dev, DL_INFO, "%s Exit touch to wake enabled\n", __func__); return; } From 9ec2c285b697cc6edb1f5e75fd8af1f26d462384 Mon Sep 17 00:00:00 2001 From: Anand Abhishek Date: Thu, 29 Sep 2022 14:30:34 +0530 Subject: [PATCH 077/170] touch: pt: disable power off feature enable touch to wake feature for all power modes. Change-Id: Ic12fb06cb3d436d41f373f57eaee91c5f20a11eb Signed-off-by: Srinu Gorle Signed-off-by: Anand Abhishek --- pt/pt_core.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index 4a45bccb83..081a0282a3 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -10834,21 +10834,27 @@ static int pt_core_suspend_(struct device *dev) ******************************************************************************/ static int pt_core_suspend(struct device *dev) { - struct pt_core_data *cd = dev_get_drvdata(dev); - int rc = 0; + struct pt_core_data *cd = dev_get_drvdata(dev); + int rc = 0, status = 0; - if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) - return 0; + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) + return 0; - cancel_work_sync(&cd->resume_offload_work); - cancel_work_sync(&cd->suspend_offload_work); - cancel_work_sync(&cd->resume_work); - cancel_work_sync(&cd->suspend_work); + pt_debug(cd->dev, DL_INFO, "%s start\n", __func__); + cancel_work_sync(&cd->resume_work); + cancel_work_sync(&cd->suspend_work); - rc = pt_core_suspend_(cd->dev); - pt_debug(cd->dev, DL_INFO, "%s Exit - rc = %d\n", __func__, rc); + rc = pt_core_easywake_on(cd); + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, "%s: Error on sleep\n", __func__); + return true; + } + cd->fb_state = FB_OFF; - return rc; + status = pt_enable_i2c_regulator(cd, false); + pt_debug(cd->dev, DL_INFO, "%s Exit - rc = %d\n", __func__, status); + + return rc; } /******************************************************************************* @@ -11016,17 +11022,15 @@ static void pt_resume_offload_work(struct work_struct *work) static int pt_core_resume(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); - int rc = 0; + int rc = 0, status = 0; if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) return 0; - cancel_work_sync(&cd->resume_offload_work); - cancel_work_sync(&cd->suspend_offload_work); - cancel_work_sync(&cd->resume_work); - cancel_work_sync(&cd->suspend_work); + pt_debug(cd->dev, DL_INFO, "%s start\n", __func__); + status = pt_enable_i2c_regulator(cd, true); + pt_debug(cd->dev, DL_INFO, "%s i2c regulator rc %d\n", __func__, status); - queue_work(cd->pt_workqueue, &cd->resume_offload_work); - pt_debug(cd->dev, DL_INFO, "%s workqueued\n", __func__); + pt_debug(cd->dev, DL_INFO, "%s End\n", __func__); return rc; } From 85f841d111042728b170451f6685e2f3c9ab48e6 Mon Sep 17 00:00:00 2001 From: Anand Abhishek Date: Thu, 29 Sep 2022 14:47:58 +0530 Subject: [PATCH 078/170] touch: pt: deep sleep feature Enabled deep sleep feature for pt touch sensor. Change-Id: If598192b2e1fc3535c4f9c9e0a6c56bd712af005 Signed-off-by: Srikanth Katteboina Signed-off-by: Anand Abhishek --- pt/pt_core.c | 167 ++++++++++++++++++--------------------------------- pt/pt_regs.h | 1 + 2 files changed, 58 insertions(+), 110 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index 081a0282a3..a60fc2d4eb 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "pt_regs.h" #define PINCTRL_STATE_ACTIVE "pmx_ts_active" @@ -56,11 +57,7 @@ static struct drm_panel *active_panel; MODULE_FIRMWARE(PT_FW_FILE_NAME); -#define ENABLE_VDD_REG_ONLY #define ENABLE_I2C_REG_ONLY -#ifdef ENABLE_VDD_REG_ONLY -static int pt_enable_vdd_regulator(struct pt_core_data *cd, bool en); -#endif #ifdef ENABLE_I2C_REG_ONLY static int pt_enable_i2c_regulator(struct pt_core_data *cd, bool en); @@ -10571,66 +10568,6 @@ disable_vcc_i2c_reg_only: #endif -#ifdef ENABLE_VDD_REG_ONLY -static int pt_enable_vdd_regulator(struct pt_core_data *cd, bool en) -{ - int rc = 0, status = 0; - - pt_debug(cd->dev, DL_INFO, "%s: Enter flag = %d\n", __func__, en); - if (!en) { - rc = 0; - goto disable_vdd_reg_only; - } - - if (cd->vdd) { - if (regulator_count_voltages(cd->vdd) > 0) { - rc = regulator_set_voltage(cd->vdd, FT_VTG_MIN_UV, - FT_VTG_MAX_UV); - if (rc) { - dev_err(cd->dev, - "Regulator set_vtg failed vdd rc=%d\n", rc); - goto disable_vdd_reg_only; - } - } - - rc = regulator_enable(cd->vdd); - if (rc) { - dev_err(cd->dev, - "Regulator vdd enable failed rc=%d\n", rc); - goto disable_vdd_reg_only; - } - - rc = regulator_set_load(cd->vdd, PWR_ACTIVE_LOAD_MA); - if (rc < 0) - pt_debug(cd->dev, DL_INFO, "%s: vdd unable to set active current rc = %d\n", - __func__, rc); - - pt_debug(cd->dev, DL_INFO, "%s: vdd regulator enabled rc = %d\n", __func__, rc); - } - - return 0; - -disable_vdd_reg_only: - if (cd->vdd) { - if (regulator_count_voltages(cd->vdd) > 0) - regulator_set_voltage(cd->vdd, FT_VTG_MIN_UV, - FT_VTG_MAX_UV); - - rc = regulator_set_load(cd->vdd, 0); - if (rc < 0) - pt_debug(cd->dev, DL_INFO, "%s: vdd unable to set 0 uAm rc = %d\n", - __func__, rc); - - status = regulator_disable(cd->vdd); - if (!en) - rc = status; - pt_debug(cd->dev, DL_INFO, "%s: vdd regulator disabled rc = %d\n", __func__, rc); - } - pt_debug(cd->dev, DL_INFO, "%s: Exit rc = %d\n", __func__, rc); - return rc; -} -#endif - static int pt_enable_regulator(struct pt_core_data *cd, bool en) { int rc; @@ -10782,21 +10719,7 @@ static int pt_core_suspend_(struct device *dev) return -EAGAIN; } -#ifdef ENABLE_VDD_REG_ONLY - rc = pt_enable_vdd_regulator(cd, false); - if (rc) { - dev_err(dev, "%s: Failed to disable vdd regulators: rc=%d\n", - __func__, rc); - } -#endif -#ifdef ENABLE_I2C_REG_ONLY - rc = pt_enable_i2c_regulator(cd, false); - if (rc) { - dev_err(dev, "%s: Failed to disable vdd regulators: rc=%d\n", - __func__, rc); - } -#endif - + rc = pt_enable_regulator(cd, false); if (!IS_EASY_WAKE_CONFIGURED(cd->easy_wakeup_gesture)) return 0; @@ -10844,16 +10767,17 @@ static int pt_core_suspend(struct device *dev) cancel_work_sync(&cd->resume_work); cancel_work_sync(&cd->suspend_work); - rc = pt_core_easywake_on(cd); - if (rc < 0) { - pt_debug(cd->dev, DL_ERROR, "%s: Error on sleep\n", __func__); - return true; + if (mem_sleep_current == PM_SUSPEND_MEM) { + rc = pt_core_suspend_(cd->dev); + cd->quick_boot = true; + } else { + rc = pt_core_easywake_on(cd); + if (rc < 0) + pt_debug(cd->dev, DL_ERROR, "%s: Error on sleep\n", __func__); + cd->fb_state = FB_OFF; + status = pt_enable_i2c_regulator(cd, false); + pt_debug(cd->dev, DL_INFO, "%s Exit - rc = %d\n", __func__, status); } - cd->fb_state = FB_OFF; - - status = pt_enable_i2c_regulator(cd, false); - pt_debug(cd->dev, DL_INFO, "%s Exit - rc = %d\n", __func__, status); - return rc; } @@ -10877,22 +10801,7 @@ static int pt_core_resume_(struct device *dev) dev_info(dev, "%s: Entering into resume mode:\n", __func__); - -#ifdef ENABLE_VDD_REG_ONLY - rc = pt_enable_vdd_regulator(cd, true); - if (rc < 0) { - dev_err(dev, "%s: Failed to enable vdd regulators: rc=%d\n", - __func__, rc); - } -#endif -#ifdef ENABLE_I2C_REG_ONLY - rc = pt_enable_i2c_regulator(cd, true); - if (rc < 0) { - dev_err(dev, "%s: Failed to enable vdd regulators: rc=%d\n", - __func__, rc); - } -#endif - + rc = pt_enable_regulator(cd, true); dev_info(dev, "%s: Voltage regulator enabled: rc=%d\n", __func__, rc); @@ -10930,6 +10839,30 @@ exit: return rc; } +/******************************************************************************* + * FUNCTION: pt_core_restore + * + * SUMMARY: Wrapper function with device suspend/resume stratergy to call + * pt_core_wake. This function may enable IRQ before wake up. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to core device + ******************************************************************************/ +static int pt_core_restore(struct device *dev) +{ + int rc = 0; + struct pt_core_data *cd = dev_get_drvdata(dev); + + dev_info(dev, "%s: Entering into resume mode:\n", + __func__); + + queue_work(cd->pt_workqueue, &cd->resume_offload_work); + return rc; +} /******************************************************************************* * FUNCTION: suspend_offload_work @@ -11002,6 +10935,8 @@ static void pt_resume_offload_work(struct work_struct *work) pt_data->fb_state = FB_OFF; pt_debug(pt_data->dev, DL_INFO, "%s End\n", __func__); #endif + pt_data->quick_boot = false; + pt_debug(pt_data->dev, DL_INFO, "%s Exit\n", __func__); } @@ -11022,16 +10957,22 @@ static void pt_resume_offload_work(struct work_struct *work) static int pt_core_resume(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); - int rc = 0, status = 0; + int rc = 0; + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) return 0; - pt_debug(cd->dev, DL_INFO, "%s start\n", __func__); - status = pt_enable_i2c_regulator(cd, true); - pt_debug(cd->dev, DL_INFO, "%s i2c regulator rc %d\n", __func__, status); + if (mem_sleep_current == PM_SUSPEND_MEM) { + rc = pt_core_restore(cd->dev); + } else { + pt_debug(cd->dev, DL_INFO, "%s start\n", __func__); + rc = pt_enable_i2c_regulator(cd, true); + pt_debug(cd->dev, DL_INFO, "%s i2c regulator rc %d\n", __func__, rc); - pt_debug(cd->dev, DL_INFO, "%s End\n", __func__); + pt_debug(cd->dev, DL_INFO, "%s End\n", __func__); + } + pt_debug(cd->dev, DL_INFO, "%s End rc = %d\n", __func__, rc); return rc; } #endif @@ -11077,6 +11018,8 @@ static int pt_pm_notifier(struct notifier_block *nb, const struct dev_pm_ops pt_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(pt_core_suspend, pt_core_resume) + .freeze = pt_core_suspend, + .restore = pt_core_restore, SET_RUNTIME_PM_OPS(pt_core_rt_suspend, pt_core_rt_resume, NULL) }; @@ -12857,6 +12800,9 @@ static int drm_notifier_callback(struct notifier_block *self, goto exit; } + if (cd->quick_boot) + goto exit; + blank = evdata->data; pt_debug(cd->dev, DL_INFO, "%s: DRM event:%lu,blank:%d fb_state %d sleep state %d ", __func__, event, *blank, cd->fb_state, cd->sleep_state); @@ -17520,7 +17466,8 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, cd->cal_cache_in_host = PT_FEATURE_DISABLE; cd->multi_chip = PT_FEATURE_DISABLE; cd->tthe_hid_usb_format = PT_FEATURE_DISABLE; - cd->sleep_state = SS_SLEEP_NONE; + cd->sleep_state = SS_SLEEP_NONE; + cd->quick_boot = false; if (cd->cpdata->config_dut_generation == CONFIG_DUT_PIP2_CAPABLE) { cd->set_dut_generation = true; diff --git a/pt/pt_regs.h b/pt/pt_regs.h index c5a87ff059..e9756834b9 100644 --- a/pt/pt_regs.h +++ b/pt/pt_regs.h @@ -1585,6 +1585,7 @@ struct pt_core_data { bool bridge_mode; bool hw_detect_enabled; #endif + bool quick_boot; }; struct gd_sensor { From a362b2715620cdcde90a868cdee71808b811948f Mon Sep 17 00:00:00 2001 From: Anand Abhishek Date: Thu, 29 Sep 2022 15:07:32 +0530 Subject: [PATCH 079/170] touch: pt: Add default log level Change the log level from Maximum to default. Change-Id: I61efe9747e18d8f1775a385cfb9187a1e5153a30 Signed-off-by: Zhenbin Tan Signed-off-by: Anand Abhishek --- pt/pt_regs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pt/pt_regs.h b/pt/pt_regs.h index e9756834b9..a23a83767c 100644 --- a/pt/pt_regs.h +++ b/pt/pt_regs.h @@ -177,7 +177,7 @@ enum PT_DEBUG_LEVEL { DL_DEBUG = 4, DL_MAX }; -#define PT_INITIAL_DEBUG_LEVEL DL_MAX +#define PT_INITIAL_DEBUG_LEVEL DL_WARN /* Startup DUT enum status bitmask */ enum PT_STARTUP_STATUS { From a3036855e2bc442e56beb89fdd36e114ad6ccf89 Mon Sep 17 00:00:00 2001 From: Anand Abhishek Date: Thu, 29 Sep 2022 15:29:11 +0530 Subject: [PATCH 080/170] touch: pt: Suspend using debugfs Enablement of debugfs manual POWER mode suspend for touch Change-Id: I21c20f07b89db5135e43373ef0eaa2d76d26d6ef Signed-off-by: Sayantan Majumder Signed-off-by: Anand Abhishek --- pt/pt_core.c | 26 +++++++++++++++++++------- pt/pt_regs.h | 1 + 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index a60fc2d4eb..2f0699d4ed 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -10594,6 +10594,7 @@ static int pt_enable_regulator(struct pt_core_data *cd, bool en) "Regulator vdd enable failed rc=%d\n", rc); goto exit; } + dev_info(cd->dev, "%s: VDD regulator enabled:\n", __func__); } if (cd->vcc_i2c) { @@ -10613,6 +10614,7 @@ static int pt_enable_regulator(struct pt_core_data *cd, bool en) "Regulator vcc_i2c enable failed rc=%d\n", rc); goto disable_vdd_reg; } + dev_info(cd->dev, "%s: VCC I2C regulator enabled:\n", __func__); } return 0; @@ -10624,6 +10626,8 @@ disable_vcc_i2c_reg: FT_I2C_VTG_MAX_UV); regulator_disable(cd->vcc_i2c); + dev_info(cd->dev, "%s: VCC I2C regulator disabled:\n", __func__); + } disable_vdd_reg: @@ -10633,6 +10637,7 @@ disable_vdd_reg: FT_VTG_MAX_UV); regulator_disable(cd->vdd); + dev_info(cd->dev, "%s: VDD regulator disabled:\n", __func__); } exit: @@ -10760,7 +10765,7 @@ static int pt_core_suspend(struct device *dev) struct pt_core_data *cd = dev_get_drvdata(dev); int rc = 0, status = 0; - if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) + if (cd->drv_debug_suspend || (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP)) return 0; pt_debug(cd->dev, DL_INFO, "%s start\n", __func__); @@ -10959,7 +10964,7 @@ static int pt_core_resume(struct device *dev) struct pt_core_data *cd = dev_get_drvdata(dev); int rc = 0; - if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP) + if (cd->drv_debug_suspend || (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP)) return 0; if (mem_sleep_current == PM_SUSPEND_MEM) { @@ -12800,7 +12805,7 @@ static int drm_notifier_callback(struct notifier_block *self, goto exit; } - if (cd->quick_boot) + if (cd->quick_boot || cd->drv_debug_suspend) goto exit; blank = evdata->data; @@ -14063,24 +14068,30 @@ static ssize_t pt_drv_debug_store(struct device *dev, case PT_DRV_DBG_SUSPEND: /* 4 */ pt_debug(dev, DL_INFO, "%s: TTDL: Core Sleep\n", __func__); wd_disabled = 1; - rc = pt_core_sleep(cd); + rc = pt_core_suspend_(cd->dev); if (rc) pt_debug(dev, DL_ERROR, "%s: Suspend failed rc=%d\n", __func__, rc); - else + else { pt_debug(dev, DL_INFO, "%s: Suspend succeeded\n", __func__); + cd->drv_debug_suspend = true; + pt_debug(dev, DL_INFO, "%s: Debugfs flag set:\n", __func__); + } break; case PT_DRV_DBG_RESUME: /* 5 */ pt_debug(dev, DL_INFO, "%s: TTDL: Wake\n", __func__); - rc = pt_core_wake(cd); + rc = pt_core_resume_(cd->dev); if (rc) pt_debug(dev, DL_ERROR, "%s: Resume failed rc=%d\n", __func__, rc); - else + else { pt_debug(dev, DL_INFO, "%s: Resume succeeded\n", __func__); + cd->drv_debug_suspend = false; + pt_debug(dev, DL_INFO, "%s: Debugfs flag reset:\n", __func__); + } break; case PIP1_BL_CMD_ID_VERIFY_APP_INTEGRITY: /* BL - 49 */ pt_debug(dev, DL_INFO, "%s: Cmd: verify app integ\n", __func__); @@ -17468,6 +17479,7 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, cd->tthe_hid_usb_format = PT_FEATURE_DISABLE; cd->sleep_state = SS_SLEEP_NONE; cd->quick_boot = false; + cd->drv_debug_suspend = false; if (cd->cpdata->config_dut_generation == CONFIG_DUT_PIP2_CAPABLE) { cd->set_dut_generation = true; diff --git a/pt/pt_regs.h b/pt/pt_regs.h index a23a83767c..c79746dcc3 100644 --- a/pt/pt_regs.h +++ b/pt/pt_regs.h @@ -1586,6 +1586,7 @@ struct pt_core_data { bool hw_detect_enabled; #endif bool quick_boot; + bool drv_debug_suspend; }; struct gd_sensor { From 7e6fdfcf0e43d8f740bd72516dc45d0970fec7ff Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Mon, 17 Oct 2022 00:03:25 +0530 Subject: [PATCH 081/170] touch: Add the IRQF_NO_SUSPEND flag Add the IRQF_NO_SUSPEND flag in requested_thread_irq(). Enable Touch_to_wake feature during RBSC mode. Change-Id: I7999b521bd88f4b92341eaa07ba6ed7a2d692132 Signed-off-by: Surya Teja Kudiri --- pt/pt_platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pt/pt_platform.c b/pt/pt_platform.c index 3b2c8e88fe..35734fab2c 100644 --- a/pt/pt_platform.c +++ b/pt/pt_platform.c @@ -1039,7 +1039,7 @@ int pt_setup_irq(struct pt_core_platform_data *pdata, int on, /* use edge triggered interrupts */ irq_flags = IRQF_TRIGGER_FALLING; rc = request_threaded_irq(cd->irq, NULL, pt_irq, - irq_flags | IRQF_ONESHOT, dev_name(dev), cd); + irq_flags | IRQF_ONESHOT | IRQF_NO_SUSPEND, dev_name(dev), cd); if (rc < 0) pt_debug(dev, DL_ERROR, "%s: Error, could not request irq\n", __func__); From bd6a84f66914c890a7804580d7cee73ed3b852c2 Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Mon, 17 Oct 2022 00:18:29 +0530 Subject: [PATCH 082/170] touch: Fix Touch_to_wake during RBSC mode Calling enable_irq_wake() causes suspend_device_irqs() to treat the given IRQ in a special way.the IRQ remains enabled, by on the first interrupt it will be disabled, marked as pending and "suspended" so that it will be re-enabled by resume_device_irqs() during the subsequent system resume. Change-Id: Id129a8b4e0eddc07000793986b119a0a7b5530ef Signed-off-by: Srikanth Katteboina Signed-off-by: Surya Teja Kudiri --- pt/pt_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pt/pt_core.c b/pt/pt_core.c index 2f0699d4ed..984fa6a828 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -17721,6 +17721,10 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, pt_debug(dev, DL_ERROR, "%s: Error, device_init_wakeup rc:%d\n", __func__, rc); + if (!enable_irq_wake(cd->irq)) { + cd->irq_wake = 1; + pt_debug(cd->dev, DL_WARN, "%s Device MAY wakeup\n", __func__); + } pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); From acf59ba335a3a9b8c5453e727bf26108d2878536 Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Mon, 17 Oct 2022 00:30:45 +0530 Subject: [PATCH 083/170] touch: Fix L29 is ON in TWM Add the regulator_set_load() in pt_release(). Keep both regulators by using regulator_set_load API's should be turn off in release sequence. Change-Id: Ie041e7f8e666d1dae1d7a4e958e92ae8dcc32699 Signed-off-by: Srikanth Katteboina Signed-off-by: Surya Teja Kudiri --- pt/pt_core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pt/pt_core.c b/pt/pt_core.c index 984fa6a828..079eb04e96 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -17988,6 +17988,11 @@ int pt_release(struct pt_core_data *cd) cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev); dev_set_drvdata(dev, NULL); pt_del_core(dev); + if (cd->vcc_i2c) + regulator_set_load(cd->vcc_i2c, 0); + + if (cd->vdd) + regulator_set_load(cd->vdd, 0); pt_enable_regulator(cd, false); pt_get_regulator(cd, false); pt_free_si_ptrs(cd); From d02718230b03c7bab5904909d3b0c3b290ed36fc Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Mon, 17 Oct 2022 00:34:21 +0530 Subject: [PATCH 084/170] touch: irregular count on deep sleep feature regulator count would be increment 1 for enable() and get decrement 1 for every disable(). Change-Id: I7c40e6363e1ffba035ab4b71e780856bcabbdc87 Signed-off-by: Srikanth Katteboina Signed-off-by: Surya Teja Kudiri --- pt/pt_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pt/pt_core.c b/pt/pt_core.c index 079eb04e96..9cec02b83f 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -10839,6 +10839,7 @@ exit: if (rc) { dev_err(dev, "%s: Failed to wake up: rc=%d\n", __func__, rc); + pt_enable_regulator(cd, false); return -EAGAIN; } From 704b21a0965edca8f69e9ae2b2c3da8707e688d3 Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Mon, 17 Oct 2022 00:38:34 +0530 Subject: [PATCH 085/170] touch: Fix L29 is OFF in TWM Add the shutdown callback() in i2c_driver struct. Keep both regulators should be turn off in release sequence. Change-Id: I82fb31a786ae7ff867b6727eb6fb5991842951a4 Signed-off-by: Srikanth Katteboina Signed-off-by: Surya Teja Kudiri --- pt/pt_i2c.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/pt/pt_i2c.c b/pt/pt_i2c.c index 5ce74720d9..6e70fc1148 100644 --- a/pt/pt_i2c.c +++ b/pt/pt_i2c.c @@ -241,6 +241,31 @@ static int pt_i2c_remove(struct i2c_client *client) return 0; } +/******************************************************************************* + * FUNCTION: pt_i2c_shutdown + * + * SUMMARY: Shutdown functon for the I2C module + * + * PARAMETERS: + * *client - pointer to i2c client structure + ******************************************************************************/ +void pt_i2c_shutdown(struct i2c_client *client) +{ +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT + const struct of_device_id *match; +#endif + struct device *dev = &client->dev; + struct pt_core_data *cd = i2c_get_clientdata(client); + + pt_release(cd); + +#ifdef CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT + match = of_match_device(of_match_ptr(pt_i2c_of_match), dev); + if (match) + pt_devtree_clean_pdata(dev); +#endif + +} static const struct i2c_device_id pt_i2c_id[] = { { PT_I2C_NAME, 0, }, @@ -259,6 +284,7 @@ static struct i2c_driver pt_i2c_driver = { }, .probe = pt_i2c_probe, .remove = pt_i2c_remove, + .shutdown = pt_i2c_shutdown, .id_table = pt_i2c_id, }; From 7d31f1b40bfe99dfa66ee2e295921fd4f5252ea1 Mon Sep 17 00:00:00 2001 From: Sayantan Majumder Date: Mon, 17 Oct 2022 00:45:50 +0530 Subject: [PATCH 086/170] touchs: Adding synchronization for interrupt handling Depending on the device state, adding proper synchronization for interrupt handling. Change-Id: Id7cbbb02f1b06bd01fe66a208b6484a415e90b14 Signed-off-by: Sayantan Majumder Signed-off-by: Surya Teja Kudiri --- pt/pt_core.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index 9cec02b83f..d4172804c8 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -7562,9 +7562,6 @@ static int pt_put_device_into_easy_wakeup_(struct pt_core_data *cd) int rc = 0; u8 status = 0; - mutex_lock(&cd->system_lock); - cd->wait_until_wake = 0; - mutex_unlock(&cd->system_lock); rc = pt_hid_output_enter_easywake_state_(cd, cd->easy_wakeup_gesture, &status); @@ -9108,11 +9105,6 @@ static int pt_core_wake_device_from_easy_wake_(struct pt_core_data *cd) { int rc = 0; - mutex_lock(&cd->system_lock); - cd->wait_until_wake = 1; - mutex_unlock(&cd->system_lock); - wake_up(&cd->wait_q); - msleep(20); rc = pt_core_wake_device_from_deep_sleep_(cd); return rc; @@ -10763,26 +10755,29 @@ static int pt_core_suspend_(struct device *dev) static int pt_core_suspend(struct device *dev) { struct pt_core_data *cd = dev_get_drvdata(dev); - int rc = 0, status = 0; + int rc = 0; if (cd->drv_debug_suspend || (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP)) return 0; - pt_debug(cd->dev, DL_INFO, "%s start\n", __func__); + pt_debug(cd->dev, DL_INFO, "%s Suspend start\n", __func__); cancel_work_sync(&cd->resume_work); cancel_work_sync(&cd->suspend_work); + mutex_lock(&cd->system_lock); + cd->wait_until_wake = 0; + mutex_unlock(&cd->system_lock); + if (mem_sleep_current == PM_SUSPEND_MEM) { rc = pt_core_suspend_(cd->dev); cd->quick_boot = true; } else { - rc = pt_core_easywake_on(cd); + rc = pt_enable_i2c_regulator(cd, false); if (rc < 0) - pt_debug(cd->dev, DL_ERROR, "%s: Error on sleep\n", __func__); - cd->fb_state = FB_OFF; - status = pt_enable_i2c_regulator(cd, false); - pt_debug(cd->dev, DL_INFO, "%s Exit - rc = %d\n", __func__, status); + pt_debug(cd->dev, DL_ERROR, + "%s: Error on disabling i2c regulator\n", __func__); } + pt_debug(cd->dev, DL_INFO, "%s Suspend exit - rc = %d\n", __func__, rc); return rc; } @@ -10968,16 +10963,20 @@ static int pt_core_resume(struct device *dev) if (cd->drv_debug_suspend || (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP)) return 0; + if (mem_sleep_current == PM_SUSPEND_MEM) { rc = pt_core_restore(cd->dev); } else { pt_debug(cd->dev, DL_INFO, "%s start\n", __func__); rc = pt_enable_i2c_regulator(cd, true); pt_debug(cd->dev, DL_INFO, "%s i2c regulator rc %d\n", __func__, rc); - - pt_debug(cd->dev, DL_INFO, "%s End\n", __func__); } + mutex_lock(&cd->system_lock); + cd->wait_until_wake = 1; + mutex_unlock(&cd->system_lock); + wake_up(&cd->wait_q); + pt_debug(cd->dev, DL_INFO, "%s End rc = %d\n", __func__, rc); return rc; } From 32642e58bc1c0226514041960773938624128af2 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Mon, 17 Oct 2022 01:14:53 +0530 Subject: [PATCH 087/170] touch: KW patch has been fixed Applied all the changes from the patch. Tested the code on target for touch to wake functionality. Change-Id: I393b29ab570fc70c564dfc069864f1b627f5d389 Signed-off-by: Surya Teja Kudiri Signed-off-by: Surya Teja Kudiri --- pt/pt_core.c | 43 +++++++++++++++------- pt/pt_device_access.c | 86 +++++++++++++++++++++++++------------------ pt/pt_devtree.c | 2 +- 3 files changed, 80 insertions(+), 51 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index d4172804c8..02610bfbb8 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -570,6 +570,9 @@ static int pt_hid_exec_cmd_(struct pt_core_data *cd, + (hid_cmd->has_data_register ? 2 : 0) /* Data register */ + hid_cmd->write_length; /* Data length */ + if (cmd_length < 4 || cmd_length > PT_MAX_PIP1_MSG_SIZE) + return -EPROTO; + cmd = kzalloc(cmd_length, GFP_KERNEL); if (!cmd) return -ENOMEM; @@ -2122,13 +2125,16 @@ static int _pt_request_pip2_send_cmd(struct device *dev, struct pt_core_data *cd = dev_get_drvdata(dev); struct pip2_cmd_structure pip2_cmd; int rc = 0; - int i = 0; - int j = 0; + u16 i = 0; + u16 j = 0; u16 write_len; u8 *write_buf = NULL; u16 read_len; u8 extra_bytes; + if (report_body_len > 247) + return -EPROTO; + memset(&pip2_cmd, 0, sizeof(pip2_cmd)); /* Hard coded register for PIP2.x */ pip2_cmd.reg[0] = 0x01; @@ -2255,8 +2261,8 @@ static int _pt_pip2_send_cmd_no_int(struct device *dev, int max_retry = 0; int retry = 0; int rc = 0; - int i = 0; - int j = 0; + u16 i = 0; + u16 j = 0; u16 write_len; u8 *write_buf = NULL; u16 read_len; @@ -2270,6 +2276,9 @@ static int _pt_pip2_send_cmd_no_int(struct device *dev, struct pt_core_data *cd = dev_get_drvdata(dev); struct pip2_cmd_structure pip2_cmd; + if (report_body_len > 247) + return -EPROTO; + if (protect == PT_CORE_CMD_PROTECTED) { rc = request_exclusive(cd, cd->dev, PT_REQUEST_EXCLUSIVE_TIMEOUT); @@ -3776,7 +3785,8 @@ static int pt_pip1_read_data_block_(struct pt_core_data *cd, if (length == 0 || *actual_read_len == 0) return 0; - if (read_buf_size >= *actual_read_len) + if (read_buf_size >= *actual_read_len && + *actual_read_len < PT_MAX_PIP2_MSG_SIZE) memcpy(read_buf, &cd->response_buf[10], *actual_read_len); else return -EPROTO; @@ -3848,7 +3858,7 @@ static int pt_pip1_write_data_block_(struct pt_core_data *cd, u8 *security_key, u16 *actual_write_len) { /* row_number + write_len + ebid + security_key + crc */ - int full_write_length = 2 + 2 + 1 + write_length + 8 + 2; + u16 full_write_length = 2 + 2 + 1 + write_length + 8 + 2; u8 *full_write_buf; u8 cmd_offset = 0; u16 crc; @@ -3862,6 +3872,9 @@ static int pt_pip1_write_data_block_(struct pt_core_data *cd, .timeout_ms = PT_PIP1_CMD_WRITE_CONF_BLOCK_TIMEOUT, }; + if (write_length > PT_CAL_DATA_ROW_SIZE) + return -EINVAL; + full_write_buf = kzalloc(full_write_length, GFP_KERNEL); if (!full_write_buf) return -ENOMEM; @@ -4792,7 +4805,7 @@ static int pt_pip_calibrate_ext_(struct pt_core_data *cd, * When doing a calibration on a flashless DUT, save CAL data in * the TTDL cache on any successful calibration */ - if (*status == 0 && cd->cal_cache_in_host) { + if (cd->response_buf[5] == 0 && cd->cal_cache_in_host) { pt_debug(cd->dev, DL_INFO, "%s: Retrieve and Save CAL\n", __func__); rc = _pt_manage_local_cal_data(cd->dev, PT_CAL_DATA_SAVE, @@ -6982,6 +6995,7 @@ static int _pt_request_hw_version(struct device *dev, char *hw_version) goto exit_error; } + memset(return_data, 0, ARRAY_SIZE(return_data)); /* For Parade TC or TT parts */ if (cd->active_dut_generation == DUT_PIP2_CAPABLE) { rc = _pt_request_pip2_send_cmd(dev, @@ -8410,7 +8424,7 @@ static int pt_parse_input(struct pt_core_data *cd) pt_debug(cd->dev, DL_WARN, "%s: DUT - Empty buffer detected\n", __func__); return 0; - } else if (size > PT_MAX_INPUT) { + } else if (size > PT_MAX_INPUT || size < 0) { pt_debug(cd->dev, DL_ERROR, "%s: DUT - Unexpected len field in active bus data!\n", __func__); @@ -13613,7 +13627,7 @@ static ssize_t pt_pip2_cmd_rsp_store(struct device *dev, length = _pt_ic_parse_input_hex(dev, buf, size, input_data, PT_MAX_PIP2_MSG_SIZE); - if (length <= 0 || length > PT_MAX_PIP2_MSG_SIZE) { + if (length <= 0 || length > (PT_MAX_PIP2_MSG_SIZE - 2)) { pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n", __func__); rc = -EINVAL; @@ -16273,11 +16287,12 @@ exit: if (boot_err) *boot_err = last_err; - pt_debug(cd->dev, DL_INFO, "%s: %s=0x%02X, %s=0x%02X, %s=0x%02X\n", - __func__, - "Detected", detected, - "slave_irq_toggled", *slave_irq_toggled, - "slave_bus_toggled", *slave_bus_toggled); + if (slave_irq_toggled && slave_bus_toggled) + pt_debug(cd->dev, DL_INFO, "%s: %s=0x%02X, %s=0x%02X, %s=0x%02X\n", + __func__, + "Detected", detected, + "slave_irq_toggled", *slave_irq_toggled, + "slave_bus_toggled", *slave_bus_toggled); return rc; } diff --git a/pt/pt_device_access.c b/pt/pt_device_access.c index 80da0c98d8..8cb915231e 100644 --- a/pt/pt_device_access.c +++ b/pt/pt_device_access.c @@ -1238,27 +1238,27 @@ static int pt_get_cmcp_info(struct pt_device_access_data *dad, } cm_ave_data_panel /= (tx_num * rx_num); cmcp_info->cm_ave_data_panel = cm_ave_data_panel; - } - /* Calculate gradient panel sensor column/row here */ - calculate_gd_info(gd_sensor_col, gd_sensor_row, tx_num, rx_num, - cm_data_panel, 1, 1); - for (i = 0; i < tx_num; i++) { - pt_debug(dev, DL_DEBUG, - "i=%d max=%d,min=%d,ave=%d, gradient=%d", i, - gd_sensor_col[i].cm_max, - gd_sensor_col[i].cm_min, - gd_sensor_col[i].cm_ave, - gd_sensor_col[i].gradient_val); - } + /* Calculate gradient panel sensor column/row here */ + calculate_gd_info(gd_sensor_col, gd_sensor_row, tx_num, rx_num, + cm_data_panel, 1, 1); + for (i = 0; i < tx_num; i++) { + pt_debug(dev, DL_DEBUG, + "i=%d max=%d,min=%d,ave=%d, gradient=%d", i, + gd_sensor_col[i].cm_max, + gd_sensor_col[i].cm_min, + gd_sensor_col[i].cm_ave, + gd_sensor_col[i].gradient_val); + } - for (i = 0; i < rx_num; i++) { - pt_debug(dev, DL_DEBUG, - "i=%d max=%d,min=%d,ave=%d, gradient=%d", i, - gd_sensor_row[i].cm_max, - gd_sensor_row[i].cm_min, - gd_sensor_row[i].cm_ave, - gd_sensor_row[i].gradient_val); + for (i = 0; i < rx_num; i++) { + pt_debug(dev, DL_DEBUG, + "i=%d max=%d,min=%d,ave=%d, gradient=%d", i, + gd_sensor_row[i].cm_max, + gd_sensor_row[i].cm_min, + gd_sensor_row[i].cm_ave, + gd_sensor_row[i].gradient_val); + } } /*Get cp data*/ @@ -1294,12 +1294,12 @@ static int pt_get_cmcp_info(struct pt_device_access_data *dad, pt_debug(dev, DL_DEBUG, "cp_tx_cal_data_panel[%d]=%d\n", i, cp_tx_cal_data_panel[i]); } - } - /*get cp_sensor_tx_delta,using the first sensor cal value for temp */ - /*multiple 1000 to increase accuracy*/ - cmcp_info->cp_sensor_tx_delta = ABS((cp_tx_cal_data_panel[0] - - cp_tx_ave_data_panel) * 1000 / cp_tx_ave_data_panel); + /*get cp_sensor_tx_delta,using the first sensor cal value for temp */ + /*multiple 1000 to increase accuracy*/ + cmcp_info->cp_sensor_tx_delta = ABS((cp_tx_cal_data_panel[0] + - cp_tx_ave_data_panel) * 1000 / cp_tx_ave_data_panel); + } /*Get cp_rx_data_panel*/ if (cp_rx_data_panel != NULL) { @@ -1327,12 +1327,12 @@ static int pt_get_cmcp_info(struct pt_device_access_data *dad, "cp_rx_cal_data_panel[%d]=%d\n", i, cp_rx_cal_data_panel[i]); } - } - /*get cp_sensor_rx_delta,using the first sensor cal value for temp */ - /*multiple 1000 to increase accuracy*/ - cmcp_info->cp_sensor_rx_delta = ABS((cp_rx_cal_data_panel[0] - - cp_rx_ave_data_panel) * 1000 / cp_rx_ave_data_panel); + /*get cp_sensor_rx_delta,using the first sensor cal value for temp */ + /*multiple 1000 to increase accuracy*/ + cmcp_info->cp_sensor_rx_delta = ABS((cp_rx_cal_data_panel[0] + - cp_rx_ave_data_panel) * 1000 / cp_rx_ave_data_panel); + } if (btn_num == 0) { pt_debug(dev, DL_INFO, "%s: Skip Button Test\n", __func__); @@ -2101,9 +2101,9 @@ int save_engineering_data(struct device *dev, char *out_buf, int index, { int i; int j; - int tx_num = cmcp_info->tx_num; - int rx_num = cmcp_info->rx_num; - int btn_num = cmcp_info->btn_num; + int tx_num = 0; + int rx_num = 0; + int btn_num = 0; int tmp = 0; uint32_t fw_revision_control; uint32_t fw_config_ver; @@ -2111,6 +2111,13 @@ int save_engineering_data(struct device *dev, char *out_buf, int index, struct pt_device_access_data *dad = pt_get_device_access_data(dev); + if ((result == NULL) || (cmcp_info == NULL)) + return -EINVAL; + + tx_num = cmcp_info->tx_num; + rx_num = cmcp_info->rx_num; + btn_num = cmcp_info->btn_num; + fw_revision_control = dad->si->ttdata.revctrl; fw_config_ver = dad->si->ttdata.fw_ver_conf; /*calculate silicon id*/ @@ -2950,12 +2957,18 @@ int result_save(struct device *dev, char *buf, int byte_left; out_buf = kzalloc(MAX_BUF_LEN, GFP_KERNEL); - if (configuration == NULL) + if (configuration == NULL) { pt_debug(dev, DL_WARN, "config is NULL"); - if (result == NULL) + return -ENOMEM; + } + if (result == NULL) { pt_debug(dev, DL_WARN, "result is NULL"); - if (cmcp_info == NULL) + return -ENOMEM; + } + if (cmcp_info == NULL) { pt_debug(dev, DL_WARN, "cmcp_info is NULL"); + return -ENOMEM; + } index = save_header(out_buf, index, result); index = save_engineering_data(dev, out_buf, index, @@ -3292,7 +3305,8 @@ int cmcp_return_one_value(struct device *dev, const char *buf, u32 *offset, } } else { /* Multiple line: line count */ - *line_num = line_count; + if (line_num) + *line_num = line_count; /* Reset for next case */ line_count = 1; } diff --git a/pt/pt_devtree.c b/pt/pt_devtree.c index 9b6261a0cf..aa54f65510 100644 --- a/pt/pt_devtree.c +++ b/pt/pt_devtree.c @@ -1106,8 +1106,8 @@ int pt_devtree_clean_pdata(struct device *adap_dev) pdata = dev_get_platdata(adap_dev); set_pdata_ptr(pdata); + free_core_pdata(pdata->core_pdata); for_each_child_of_node(adap_dev->of_node, core_node) { - free_core_pdata(pdata->core_pdata); of_node_put(core_node); for_each_child_of_node(core_node, dev_node) { rc = get_device_type(dev_node, &type); From 3127a1879a604667091f097bf1c3ede6975b1523 Mon Sep 17 00:00:00 2001 From: Simranjeet Thind Date: Thu, 27 Oct 2022 10:15:24 -0700 Subject: [PATCH 088/170] touch: pineapple: enable touch drivers for pineapple Add touch config files and enable for pineapple platform Change-Id: Ibad6ce0e661a5d2c0e8794573e18abbf1fe2b88f Signed-off-by: Simranjeet Thind --- Kbuild | 5 +++++ Makefile.am | 4 ++++ config/gki_pineappletouch.conf | 5 +++++ config/gki_pineappletouchconf.h | 8 ++++++++ 4 files changed, 22 insertions(+) create mode 100644 config/gki_pineappletouch.conf create mode 100644 config/gki_pineappletouchconf.h diff --git a/Kbuild b/Kbuild index d1e5878e0c..9524f33d86 100644 --- a/Kbuild +++ b/Kbuild @@ -16,6 +16,11 @@ endif LINUX_INC += -include $(TOUCH_ROOT)/config/gki_khajetouchconf.h #endif +#ifeq ($(CONFIG_ARCH_PINEAPPLE), y) + include $(TOUCH_ROOT)/config/gki_pineappletouch.conf + LINUX_INC += -include $(TOUCH_ROOT)/config/gki_pineappletouchconf.h +#endif + LINUX_INC += -Iinclude/linux \ -Iinclude/linux/drm \ -Iinclude/linux/gunyah \ diff --git a/Makefile.am b/Makefile.am index 106a9a89ae..17208741c0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -6,6 +6,10 @@ ifeq ($(TARGET_SUPPORT),genericarmv8) KBUILD_OPTIONS += CONFIG_ARCH_WAIPIO=y endif +ifeq ($(TARGET_SUPPORT),genericarmv8) + KBUILD_OPTIONS += CONFIG_ARCH_PINEAPPLE=y +endif + all: $(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS) diff --git a/config/gki_pineappletouch.conf b/config/gki_pineappletouch.conf new file mode 100644 index 0000000000..0849bec783 --- /dev/null +++ b/config/gki_pineappletouch.conf @@ -0,0 +1,5 @@ +export CONFIG_TOUCHSCREEN_NT36XXX_I2C=y +export CONFIG_TOUCHSCREEN_GOODIX_BRL=y +export CONFIG_TOUCHSCREEN_ATMEL_MXT=y +export CONFIG_TOUCHSCREEN_DUMMY=y +export CONFIG_MSM_TOUCH=m diff --git a/config/gki_pineappletouchconf.h b/config/gki_pineappletouchconf.h new file mode 100644 index 0000000000..934e7175b3 --- /dev/null +++ b/config/gki_pineappletouchconf.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define CONFIG_TOUCHSCREEN_NT36XXX_I2C 1 +#define CONFIG_TOUCHSCREEN_GOODIX_BRL 1 +#define CONFIG_TOUCHSCREEN_ATMEL_MXT 1 From 8664f210f698a115711763dc14994eaa866bb421 Mon Sep 17 00:00:00 2001 From: Simranjeet Thind Date: Thu, 27 Oct 2022 11:50:28 -0700 Subject: [PATCH 089/170] touch: fix compilation errors in touch drivers Change fixes compilation issues seen due kernel upgrade in touch driver files with maintaining the backward compatability. Change-Id: Iaa49186bd2c895850bc0349ccb695a2069cece84 Signed-off-by: Simranjeet Thind --- goodix_berlin_driver/goodix_brl_spi.c | 14 +++++++++++--- goodix_berlin_driver/goodix_ts_core.c | 7 ++++++- qts/qts_core.c | 9 ++++++++- synaptics_tcm/synaptics_tcm_core.c | 1 + 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/goodix_berlin_driver/goodix_brl_spi.c b/goodix_berlin_driver/goodix_brl_spi.c index 7155a1aaab..d246524a09 100644 --- a/goodix_berlin_driver/goodix_brl_spi.c +++ b/goodix_berlin_driver/goodix_brl_spi.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "goodix_ts_core.h" #define TS_DRIVER_NAME "gtx8_spi" @@ -263,11 +264,18 @@ err_pdev: return ret; } -static int goodix_spi_remove(struct spi_device *spi) -{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)) + static void goodix_spi_remove(struct spi_device *spi) + { + platform_device_unregister(goodix_pdev); + } +#else + static int goodix_spi_remove(struct spi_device *spi) + { platform_device_unregister(goodix_pdev); return 0; -} + } +#endif #ifdef CONFIG_OF static const struct of_device_id spi_matchs[] = { diff --git a/goodix_berlin_driver/goodix_ts_core.c b/goodix_berlin_driver/goodix_ts_core.c index aadf88cffd..4c2c079d4a 100644 --- a/goodix_berlin_driver/goodix_ts_core.c +++ b/goodix_berlin_driver/goodix_ts_core.c @@ -876,8 +876,13 @@ exit: static int rawdata_proc_open(struct inode *inode, struct file *file) { - return single_open_size(file, rawdata_proc_show, + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)) + return single_open_size(file, rawdata_proc_show, + pde_data(inode), PAGE_SIZE * 10); + #else + return single_open_size(file, rawdata_proc_show, PDE_DATA(inode), PAGE_SIZE * 10); + #endif } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) diff --git a/qts/qts_core.c b/qts/qts_core.c index 66d9f359fd..b49e9be9f5 100644 --- a/qts/qts_core.c +++ b/qts/qts_core.c @@ -674,6 +674,7 @@ static void qts_trusted_touch_abort_tvm(struct qts_data *qts_data) switch (vm_state) { case TVM_INTERRUPT_ENABLED: qts_irq_enable(qts_data, false); + fallthrough; case TVM_IRQ_ACCEPTED: case TVM_INTERRUPT_DISABLED: rc = gh_irq_release(qts_data->vm_info->irq_label); @@ -682,6 +683,7 @@ static void qts_trusted_touch_abort_tvm(struct qts_data *qts_data) rc = gh_irq_release_notify(qts_data->vm_info->irq_label); if (rc) pr_err("Failed to notify irq release rc:%d\n", rc); + fallthrough; case TVM_I2C_SESSION_ACQUIRED: case TVM_IOMEM_ACCEPTED: case TVM_IRQ_RELEASED: @@ -689,10 +691,12 @@ static void qts_trusted_touch_abort_tvm(struct qts_data *qts_data) pm_runtime_put_sync(qts_data->client->adapter->dev.parent); else pm_runtime_put_sync(qts_data->spi->master->dev.parent); + fallthrough; case TVM_I2C_SESSION_RELEASED: rc = qts_vm_mem_release(qts_data); if (rc) pr_err("Failed to release mem rc:%d\n", rc); + fallthrough; case TVM_IOMEM_RELEASED: case TVM_ALL_RESOURCES_LENT_NOTIFIED: case TRUSTED_TOUCH_TVM_INIT: @@ -730,6 +734,7 @@ static void qts_trusted_touch_abort_pvm(struct qts_data *qts_data) pr_err("failed to reclaim irq on pvm rc:%d\n", rc); return; } + fallthrough; case PVM_IRQ_RECLAIMED: case PVM_IOMEM_LENT: case PVM_IOMEM_LENT_NOTIFIED: @@ -741,13 +746,16 @@ static void qts_trusted_touch_abort_pvm(struct qts_data *qts_data) return; } qts_data->vm_info->vm_mem_handle = 0; + fallthrough; case PVM_IOMEM_RECLAIMED: case PVM_INTERRUPT_DISABLED: if (qts_data->vendor_ops.enable_touch_irq) qts_data->vendor_ops.enable_touch_irq(qts_data->vendor_data, true); + fallthrough; case PVM_I2C_RESOURCE_ACQUIRED: case PVM_INTERRUPT_ENABLED: qts_bus_put(qts_data); + fallthrough; case TRUSTED_TOUCH_PVM_INIT: case PVM_I2C_RESOURCE_RELEASED: atomic_set(&qts_data->trusted_touch_enabled, 0); @@ -1777,4 +1785,3 @@ qts_register_end: return rc; } EXPORT_SYMBOL(qts_client_register); - diff --git a/synaptics_tcm/synaptics_tcm_core.c b/synaptics_tcm/synaptics_tcm_core.c index f030032b7e..4428b164bb 100644 --- a/synaptics_tcm/synaptics_tcm_core.c +++ b/synaptics_tcm/synaptics_tcm_core.c @@ -1238,6 +1238,7 @@ retry: case STATUS_CONTINUED_READ: LOGD(tcm_hcd->pdev->dev.parent, "Out-of-sync continued read\n"); + fallthrough; case STATUS_IDLE: case STATUS_BUSY: tcm_hcd->payload_length = 0; From 4200da37ac751cc6215c7cf6d8ad98772e4d6540 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Thu, 15 Sep 2022 11:37:26 +0530 Subject: [PATCH 090/170] touch: enable paradetech driver Enable new paradetech driver. configuration changes to pick paradetech driver for compilation. Change-Id: I70feb9c34857166a8be1e12a3e5d85f4f27bc679 Signed-off-by: Surya Teja Kudiri --- Android.mk | 33 ++++++++++++++++++++++++++++++ Kbuild | 39 ++++++++++++++++++++++++++++++++++++ NOTICE | 19 ++++++++++++++++++ config/gki_monacotouch.conf | 5 +++++ config/gki_monacotouchconf.h | 6 ++++++ touch_driver_board.mk | 7 +++---- touch_driver_product.mk | 7 +++---- 7 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 config/gki_monacotouch.conf create mode 100644 config/gki_monacotouchconf.h diff --git a/Android.mk b/Android.mk index 9dd4750598..24066705eb 100644 --- a/Android.mk +++ b/Android.mk @@ -76,5 +76,38 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := pt_ts.ko + LOCAL_MODULE_KBUILD_NAME := pt_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := pt_i2c.ko + LOCAL_MODULE_KBUILD_NAME := pt_i2c.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := pt_device_access.ko + LOCAL_MODULE_KBUILD_NAME := pt_device_access.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### endif # DLKM check endif diff --git a/Kbuild b/Kbuild index 9524f33d86..52527c3364 100644 --- a/Kbuild +++ b/Kbuild @@ -19,6 +19,10 @@ endif #ifeq ($(CONFIG_ARCH_PINEAPPLE), y) include $(TOUCH_ROOT)/config/gki_pineappletouch.conf LINUX_INC += -include $(TOUCH_ROOT)/config/gki_pineappletouchconf.h + +#ifeq ($(CONFIG_ARCH_MONACO), y) + include $(TOUCH_ROOT)/config/gki_monacotouch.conf + LINUX_INC += -include $(TOUCH_ROOT)/config/gki_monacotouchconf.h #endif LINUX_INC += -Iinclude/linux \ @@ -141,4 +145,39 @@ ifeq ($(CONFIG_TOUCHSCREEN_SYNAPTICS_TCM), y) endif +ifeq ($(CONFIG_TOUCHSCREEN_PARADE), y) + LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_regs.h + LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_core.h + LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_platform.h + + pt_ts-y := \ + ./pt/pt_core.o \ + ./pt/pt_mt_common.o \ + ./pt/pt_platform.o \ + ./pt/pt_devtree.o \ + ./pt/pt_btn.o \ + ./pt/pt_mtb.o \ + ./pt/pt_proximity.o + + obj-$(CONFIG_MSM_TOUCH) += pt_ts.o +endif + +ifeq ($(CONFIG_TOUCHSCREEN_PARADE_I2C), y) + LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_regs.h + + pt_i2c-y := \ + ./pt/pt_i2c.o + + obj-$(CONFIG_MSM_TOUCH) += pt_i2c.o +endif + +ifeq ($(CONFIG_TOUCHSCREEN_PARADE_DEVICE_ACCESS), y) + LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_regs.h + + pt_device_access-y := \ + ./pt/pt_device_access.o + + obj-$(CONFIG_MSM_TOUCH) += pt_device_access.o +endif + CDEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/NOTICE b/NOTICE index 0b3cbd5aa5..333f0d7dbf 100644 --- a/NOTICE +++ b/NOTICE @@ -3,6 +3,25 @@ * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. */ +/* + * + * Parade TouchScreen driver. + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.paradetech.com + */ + /* * * FocalTech fts TouchScreen driver. diff --git a/config/gki_monacotouch.conf b/config/gki_monacotouch.conf new file mode 100644 index 0000000000..d16e3b095c --- /dev/null +++ b/config/gki_monacotouch.conf @@ -0,0 +1,5 @@ +export CONFIG_MSM_TOUCH=m +export CONFIG_TOUCHSCREEN_PARADE=y +export CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT=y +export CONFIG_TOUCHSCREEN_PARADE_I2C=y +export CONFIG_TOUCHSCREEN_PARADE_DEVICE_ACCESS=y diff --git a/config/gki_monacotouchconf.h b/config/gki_monacotouchconf.h new file mode 100644 index 0000000000..dd7be04589 --- /dev/null +++ b/config/gki_monacotouchconf.h @@ -0,0 +1,6 @@ +#define CONFIG_TOUCHSCREEN_PARADE 1 +#define CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT 1 +#define CONFIG_TOUCHSCREEN_PARADE_I2C 1 +#define CONFIG_TOUCHSCREEN_PARADE_DEVICE_ACCESS 1 +#define CONFIG_TOUCHSCREEN_PARADE_BUTTON 1 +#define CONFIG_TOUCHSCREEN_PARADE_PROXIMITY 1 diff --git a/touch_driver_board.mk b/touch_driver_board.mk index 635cfd7e43..d168a7bfb5 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -8,10 +8,9 @@ endif ifeq ($(TOUCH_DLKM_ENABLE), true) ifneq ($(TARGET_BOARD_AUTO),true) ifeq ($(call is-board-platform-in-list,$(TARGET_BOARD_PLATFORM)),true) - BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ - $(KERNEL_MODULES_OUT)/goodix_ts.ko \ - $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ - $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/pt_ts.ko \ + $(KERNEL_MODULES_OUT)/pt_i2c.ko \ + $(KERNEL_MODULES_OUT)/pt_device_access.ko endif endif endif diff --git a/touch_driver_product.mk b/touch_driver_product.mk index 20d33042c9..3a3e5abd5c 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -6,8 +6,7 @@ ifeq ($(TARGET_KERNEL_DLKM_DISABLE), true) endif ifeq ($(TOUCH_DLKM_ENABLE), true) - PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ - $(KERNEL_MODULES_OUT)/goodix_ts.ko \ - $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ - $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/pt_ts.ko \ + $(KERNEL_MODULES_OUT)/pt_i2c.ko \ + $(KERNEL_MODULES_OUT)/pt_device_access.ko endif From 3958a8c601aad41b1003b745edfbfd2697500b83 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Thu, 15 Sep 2022 13:11:19 +0530 Subject: [PATCH 091/170] touch: paradetech touch driver upgrade Upgrading parade tech touch driver code from kernel 5.4 to kernel 5.15. Change-Id: I0052e905813f0dc2c3a374b10cd6bcb243502673 Signed-off-by: Surya Teja Kudiri --- pt/pt_core.c | 20 ++-- pt/pt_core.h | 230 ++++++++++++++++++++++++++++++++++++++++++ pt/pt_device_access.c | 27 +++-- pt/pt_devtree.c | 2 +- pt/pt_platform.c | 2 +- pt/pt_platform.h | 69 +++++++++++++ pt/pt_regs.h | 9 +- 7 files changed, 332 insertions(+), 27 deletions(-) create mode 100644 pt/pt_core.h create mode 100644 pt/pt_platform.h diff --git a/pt/pt_core.c b/pt/pt_core.c index 02610bfbb8..dfbee333ad 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -32,6 +32,7 @@ #include #include #include "pt_regs.h" +#include #define PINCTRL_STATE_ACTIVE "pmx_ts_active" #define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" @@ -3786,7 +3787,7 @@ static int pt_pip1_read_data_block_(struct pt_core_data *cd, return 0; if (read_buf_size >= *actual_read_len && - *actual_read_len < PT_MAX_PIP2_MSG_SIZE) + *actual_read_len < PT_MAX_PIP2_MSG_SIZE) memcpy(read_buf, &cd->response_buf[10], *actual_read_len); else return -EPROTO; @@ -10782,7 +10783,7 @@ static int pt_core_suspend(struct device *dev) cd->wait_until_wake = 0; mutex_unlock(&cd->system_lock); - if (mem_sleep_current == PM_SUSPEND_MEM) { + if (pm_suspend_via_firmware()) { rc = pt_core_suspend_(cd->dev); cd->quick_boot = true; } else { @@ -10978,7 +10979,7 @@ static int pt_core_resume(struct device *dev) return 0; - if (mem_sleep_current == PM_SUSPEND_MEM) { + if (pm_suspend_via_firmware()) { rc = pt_core_restore(cd->dev); } else { pt_debug(cd->dev, DL_INFO, "%s start\n", __func__); @@ -11550,9 +11551,9 @@ int _pt_read_us_file(struct device *dev, u8 *file_path, u8 *buf, int *size) } pt_debug(dev, DL_WARN, "%s: path = %s\n", __func__, file_path); - oldfs = get_fs(); - set_fs(KERNEL_DS); - filp = filp_open(file_path, O_RDONLY, 0400); + oldfs = force_uaccess_begin(); + filp = filp_open_block(file_path, O_RDONLY, 0400); + if (IS_ERR(filp)) { pt_debug(dev, DL_ERROR, "%s: Failed to open %s\n", __func__, file_path); @@ -11593,7 +11594,11 @@ int _pt_read_us_file(struct device *dev, u8 *file_path, u8 *buf, int *size) goto exit; } filp->private_data = inode->i_private; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)) + if (filp->f_op->read(filp, buf, read_len, &(filp->f_pos)) != read_len) { +#else if (vfs_read(filp, buf, read_len, &(filp->f_pos)) != read_len) { +#endif pt_debug(dev, DL_ERROR, "%s: file read error.\n", __func__); rc = -EINVAL; goto exit; @@ -11604,8 +11609,9 @@ exit: if (filp_close(filp, NULL) != 0) pt_debug(dev, DL_ERROR, "%s: file close error.\n", __func__); err: - set_fs(oldfs); + force_uaccess_end(oldfs); return rc; + } /******************************************************************************* diff --git a/pt/pt_core.h b/pt/pt_core.h new file mode 100644 index 0000000000..792427717a --- /dev/null +++ b/pt/pt_core.h @@ -0,0 +1,230 @@ +/* + * pt_core.h + * Parade TrueTouch(TM) Standard Product Core Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.parade.com + */ + +#ifndef _LINUX_PT_CORE_H +#define _LINUX_PT_CORE_H + +#include +#include + +#define PT_I2C_NAME "pt_i2c_adapter" +#define PT_SPI_NAME "pt_spi_adapter" + +#define PT_CORE_NAME "pt_core" +#define PT_MT_NAME "pt_mt" +#define PT_BTN_NAME "pt_btn" +#define PT_PROXIMITY_NAME "pt_proximity" + +#define PT_DRIVER_NAME TTDL +#define PT_DRIVER_MAJOR 04 +#define PT_DRIVER_MINOR 11 + +#define PT_DRIVER_REVCTRL 977092 + +#define PT_DRIVER_VERSION \ +__stringify(PT_DRIVER_NAME) \ +"." __stringify(PT_DRIVER_MAJOR) \ +"." __stringify(PT_DRIVER_MINOR) \ +"." __stringify(PT_DRIVER_REVCTRL) + +#define PT_DRIVER_DATE "20201210" + +/* abs settings */ +#define PT_IGNORE_VALUE -1 + +enum pt_core_platform_flags { + PT_CORE_FLAG_NONE, + PT_CORE_FLAG_POWEROFF_ON_SLEEP = 0x02, + PT_CORE_FLAG_RESTORE_PARAMETERS = 0x04, + PT_CORE_FLAG_DEEP_STANDBY = 0x08, + PT_CORE_FLAG_SKIP_SYS_SLEEP = 0x10, + PT_CORE_FLAG_SKIP_RUNTIME = 0x20, + PT_CORE_FLAG_SKIP_RESUME = 0x40, +}; + +enum pt_core_platform_easy_wakeup_gesture { + PT_CORE_EWG_NONE, + PT_CORE_EWG_TAP_TAP, + PT_CORE_EWG_TWO_FINGER_SLIDE, + PT_CORE_EWG_RESERVED, + PT_CORE_EWG_WAKE_ON_INT_FROM_HOST = 0xFF, +}; + +enum pt_loader_platform_flags { + PT_LOADER_FLAG_NONE, + PT_LOADER_FLAG_CALIBRATE_AFTER_FW_UPGRADE, + /* Use CONFIG_VER field in TT_CFG to decide TT_CFG update */ + PT_LOADER_FLAG_CHECK_TTCONFIG_VERSION, + PT_LOADER_FLAG_CALIBRATE_AFTER_TTCONFIG_UPGRADE, +}; + +enum CONFIG_DUT_GENERATION { + CONFIG_DUT_AUTO_DETECT = 0x00, + CONFIG_DUT_PIP1_ONLY = 0x01, + CONFIG_DUT_PIP2_CAPABLE = 0x02, +}; + +enum pt_core_platform_panel_id_flags { + PT_PANEL_ID_DISABLE = 0x00, + PT_PANEL_ID_BY_BL = 0x01, + PT_PANEL_ID_BY_SYS_INFO = 0x02, + PT_PANEL_ID_BY_MFG_DATA = 0x04, +}; + +struct touch_settings { + const uint8_t *data; + uint32_t size; + uint8_t tag; +}; + +struct pt_touch_firmware { + const uint8_t *img; + uint32_t size; + const uint8_t *ver; + uint8_t vsize; + uint8_t panel_id; +}; + +struct pt_touch_config { + struct touch_settings *param_regs; + struct touch_settings *param_size; + const uint8_t *fw_ver; + uint8_t fw_vsize; + uint8_t panel_id; +}; + +struct pt_loader_platform_data { + struct pt_touch_firmware *fw; + struct pt_touch_config *ttconfig; + struct pt_touch_firmware **fws; + struct pt_touch_config **ttconfigs; + u32 flags; +}; + +typedef int (*pt_platform_read) (struct device *dev, void *buf, int size); + +#define PT_TOUCH_SETTINGS_MAX 32 + +struct pt_core_platform_data { + int irq_gpio; + u32 irq_gpio_flags; + int rst_gpio; + u32 rst_gpio_flags; + int ddi_rst_gpio; + int vddi_gpio; + int vcc_gpio; + int avdd_gpio; + int avee_gpio; + int level_irq_udelay; + u16 hid_desc_register; + u16 vendor_id; + u16 product_id; + + int (*xres)(struct pt_core_platform_data *pdata, + struct device *dev); + int (*init)(struct pt_core_platform_data *pdata, + int on, struct device *dev); + int (*power)(struct pt_core_platform_data *pdata, + int on, struct device *dev, atomic_t *ignore_irq); + int (*detect)(struct pt_core_platform_data *pdata, + struct device *dev, pt_platform_read read); + int (*irq_stat)(struct pt_core_platform_data *pdata, + struct device *dev); + int (*setup_power)(struct pt_core_platform_data *pdata, + int on, struct device *dev); + int (*setup_irq)(struct pt_core_platform_data *pdata, + int on, struct device *dev); + struct touch_settings *sett[PT_TOUCH_SETTINGS_MAX]; + u32 flags; + u8 easy_wakeup_gesture; + u8 config_dut_generation; + u8 watchdog_force_stop; + u8 panel_id_support; + + struct device_node *node; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_active; + struct pinctrl_state *pins_suspend; + struct pinctrl_state *pins_release; + + struct drm_panel *active_panel; +}; + +struct touch_framework { + const int16_t *abs; + uint8_t size; + uint8_t enable_vkeys; +} __packed; + +enum pt_mt_platform_power_state { + PT_MT_POWER_OFF = 0x00, + PT_MT_POWER_ON = 0x01 +}; + +enum pt_mt_platform_irq_state { + PT_MT_IRQ_FREE = 0x00, + PT_MT_IRQ_REG = 0x01 +}; + +enum pt_mt_platform_flags { + PT_MT_FLAG_NONE, + PT_MT_FLAG_HOVER = 0x04, + PT_MT_FLAG_FLIP = 0x08, + PT_MT_FLAG_INV_X = 0x10, + PT_MT_FLAG_INV_Y = 0x20, + PT_MT_FLAG_VKEYS = 0x40, + PT_MT_FLAG_NO_TOUCH_ON_LO = 0x80, +}; + +struct pt_mt_platform_data { + struct touch_framework *frmwrk; + unsigned short flags; + char const *inp_dev_name; + int vkeys_x; + int vkeys_y; +}; + +struct pt_btn_platform_data { + char const *inp_dev_name; +}; + +struct pt_proximity_platform_data { + struct touch_framework *frmwrk; + char const *inp_dev_name; +}; + +struct pt_platform_data { + struct pt_core_platform_data *core_pdata; + struct pt_mt_platform_data *mt_pdata; + struct pt_btn_platform_data *btn_pdata; + struct pt_proximity_platform_data *prox_pdata; + struct pt_loader_platform_data *loader_pdata; +}; + +#endif /* _LINUX_PT_CORE_H */ diff --git a/pt/pt_device_access.c b/pt/pt_device_access.c index 8cb915231e..611483a06b 100644 --- a/pt/pt_device_access.c +++ b/pt/pt_device_access.c @@ -2028,10 +2028,10 @@ static int save_header(char *out_buf, int index, struct result *result) char time_buf[100] = {0}; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)) - struct timespec ts; + struct timespec64 ts; - getnstimeofday(&ts); - rtc_time_to_tm(ts.tv_sec, &tm); + ktime_get_real_ts64(&ts); + rtc_time64_to_tm(ts.tv_sec, &tm); #else struct timex txc; @@ -2117,7 +2117,6 @@ int save_engineering_data(struct device *dev, char *out_buf, int index, tx_num = cmcp_info->tx_num; rx_num = cmcp_info->rx_num; btn_num = cmcp_info->btn_num; - fw_revision_control = dad->si->ttdata.revctrl; fw_config_ver = dad->si->ttdata.fw_ver_conf; /*calculate silicon id*/ @@ -2246,11 +2245,11 @@ int save_engineering_data(struct device *dev, char *out_buf, int index, index = prepare_print_data( out_buf, &tmp, index, 1); - for (j = 1; j < tx_num; j++) - index = prepare_print_data( - out_buf, - &cmcp_info->cm_sensor_column_delta[(j-1)*rx_num+i], - index, 1); + for (j = 1; j < tx_num; j++) + index = prepare_print_data( + out_buf, + &cmcp_info->cm_sensor_column_delta[(j-1)*rx_num+i], + index, 1); index = prepare_print_string( out_buf, "\n", index); @@ -2284,11 +2283,11 @@ int save_engineering_data(struct device *dev, char *out_buf, int index, index = prepare_print_data( out_buf, &i, index, 1); - for (j = 0; j < tx_num; j++) - index = prepare_print_data( - out_buf, - &cmcp_info->cm_sensor_row_delta[j*rx_num+i-1], - index, 1); + for (j = 0; j < tx_num; j++) + index = prepare_print_data( + out_buf, + &cmcp_info->cm_sensor_row_delta[j*rx_num+i-1], + index, 1); index = prepare_print_string( out_buf, "\n", index); diff --git a/pt/pt_devtree.c b/pt/pt_devtree.c index aa54f65510..5482c1d224 100644 --- a/pt/pt_devtree.c +++ b/pt/pt_devtree.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include "pt_platform.h" #include "pt_regs.h" #define MAX_NAME_LENGTH 64 diff --git a/pt/pt_platform.c b/pt/pt_platform.c index 35734fab2c..b91fc3f22f 100644 --- a/pt/pt_platform.c +++ b/pt/pt_platform.c @@ -28,7 +28,7 @@ */ #include "pt_regs.h" -#include +#include "pt_platform.h" #ifdef CONFIG_TOUCHSCREEN_PARADE_PLATFORM_FW_UPGRADE /* FW for Panel ID = 0x00 */ diff --git a/pt/pt_platform.h b/pt/pt_platform.h new file mode 100644 index 0000000000..17b490f759 --- /dev/null +++ b/pt/pt_platform.h @@ -0,0 +1,69 @@ +/* + * pt_platform.h + * Parade TrueTouch(TM) Standard Product Platform Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * TMA5XX + * TMA448 + * TMA445A + * TT21XXX + * TT31XXX + * TT4XXXX + * TT7XXX + * TC3XXX + * + * Copyright (C) 2015-2020 Parade Technologies + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * 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. + * + * Contact Parade Technologies at www.parade.com + */ + +#ifndef _LINUX_PT_PLATFORM_H +#define _LINUX_PT_PLATFORM_H + +#include "pt_core.h" +#include + +#if defined(CONFIG_TOUCHSCREEN_PARADE) \ + || defined(CONFIG_TOUCHSCREEN_PARADE_MODULE) +extern struct pt_loader_platform_data _pt_loader_platform_data; +extern irqreturn_t pt_irq(int irq, void *handle); + +int pt_xres(struct pt_core_platform_data *pdata, struct device *dev); +int pt_init(struct pt_core_platform_data *pdata, int on, + struct device *dev); +int pt_power(struct pt_core_platform_data *pdata, int on, + struct device *dev, atomic_t *ignore_irq); +#ifdef PT_DETECT_HW +int pt_detect(struct pt_core_platform_data *pdata, + struct device *dev, pt_platform_read read); +#else +#define pt_detect NULL +#endif +int pt_irq_stat(struct pt_core_platform_data *pdata, + struct device *dev); +int pt_setup_power(struct pt_core_platform_data *pdata, int on, + struct device *dev); +int pt_setup_irq(struct pt_core_platform_data *pdata, int on, + struct device *dev); +#else /* !CONFIG_TOUCHSCREEN_PARADE */ +static struct pt_loader_platform_data _pt_loader_platform_data; +#define pt_xres NULL +#define pt_init NULL +#define pt_power NULL +#define pt_irq_stat NULL +#define pt_detect NULL +#define pt_setup_power NULL +#define pt_setup_irq NULL +#endif /* CONFIG_TOUCHSCREEN_PARADE */ + +#endif /* _LINUX_PT_PLATFORM_H */ diff --git a/pt/pt_regs.h b/pt/pt_regs.h index c79746dcc3..0552e61afd 100644 --- a/pt/pt_regs.h +++ b/pt/pt_regs.h @@ -74,10 +74,11 @@ #include #include #include -#include +#include +#include #include #include -#include +#include "pt_core.h" #include #include @@ -1728,9 +1729,9 @@ static inline int pt_proximity_release(struct device *dev) { return 0; } static inline unsigned int pt_get_time_stamp(void) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)) - struct timespec ts; + struct timespec64 ts; - getnstimeofday(&ts); + ktime_get_real_ts64(&ts); return (ts.tv_sec*1000 + ts.tv_nsec/1000000); #else struct timeval tv; From ba902f4726fb81ddbdaada27750c2d7cb67334d5 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Thu, 15 Sep 2022 14:48:36 +0530 Subject: [PATCH 092/170] touch: add DRM support for paradetech driver Updated panel_event_notifier_register and panel_event_notifier_unregister as per kernel 5.15. Change-Id: Icc6f78df492cb2381dfea598d8fa899ef65e48a2 Signed-off-by: Surya Teja Kudiri --- pt/pt_core.c | 71 ++++++++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index dfbee333ad..35c07e582d 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -12805,41 +12805,40 @@ static void pt_suspend_work(struct work_struct *work) * event - event type of fb notifier * *data - pointer to fb_event structure ******************************************************************************/ -static int drm_notifier_callback(struct notifier_block *self, - unsigned long event, void *data) +static void drm_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data) { - struct pt_core_data *cd = - container_of(self, struct pt_core_data, fb_notifier); - struct drm_panel_notifier *evdata = data; - int *blank; + struct pt_core_data *cd = client_data; + + if(!notification) + { + pt_debug(cd->dev,DL_INFO, "%s: Invalid notification\n", __func__); + return; + } pt_debug(cd->dev, DL_INFO, "%s: DRM notifier called!\n", __func__); - if (!evdata) - goto exit; - - if (!(event == DRM_PANEL_EARLY_EVENT_BLANK || - event == DRM_PANEL_EVENT_BLANK)) { - pt_debug(cd->dev, DL_INFO, "%s: Event(%lu) do not need process\n", - __func__, event); + if (!(notification->notif_type == DRM_PANEL_EVENT_BLANK || + notification->notif_type == DRM_PANEL_EVENT_BLANK)) { + pt_debug(cd->dev, DL_INFO, "%s: Event(%d) do not need process\n", + __func__, notification->notif_type); goto exit; } if (cd->quick_boot || cd->drv_debug_suspend) goto exit; - blank = evdata->data; - pt_debug(cd->dev, DL_INFO, "%s: DRM event:%lu,blank:%d fb_state %d sleep state %d ", - __func__, event, *blank, cd->fb_state, cd->sleep_state); + pt_debug(cd->dev, DL_INFO, "%s: DRM event:%d,fb_state %d", + __func__, notification->notif_type, cd->fb_state); pt_debug(cd->dev, DL_INFO, "%s: DRM Power - %s - FB state %d ", - __func__, (*blank == DRM_PANEL_BLANK_UNBLANK)?"UP":"DOWN", cd->fb_state); + __func__, (notification->notif_type == DRM_PANEL_EVENT_UNBLANK)?"UP":"DOWN", cd->fb_state); - if (*blank == DRM_PANEL_BLANK_UNBLANK) { + if (notification->notif_type == DRM_PANEL_EVENT_UNBLANK) { pt_debug(cd->dev, DL_INFO, "%s: UNBLANK!\n", __func__); - if (event == DRM_PANEL_EARLY_EVENT_BLANK) { - pt_debug(cd->dev, DL_INFO, "%s: resume: event = %lu, not care\n", - __func__, event); - } else if (event == DRM_PANEL_EVENT_BLANK) { + if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { + pt_debug(cd->dev, DL_INFO, "%s: resume: event = %d, not care\n", + __func__, notification->notif_type); + } else if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { if (cd->fb_state != FB_ON) { pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n", __func__); @@ -12859,9 +12858,9 @@ static int drm_notifier_callback(struct notifier_block *self, pt_debug(cd->dev, DL_INFO, "%s: Resume notified!\n", __func__); } } - } else if (*blank == DRM_PANEL_BLANK_LP || *blank == DRM_PANEL_BLANK_POWERDOWN) { + } else if (notification->notif_type == DRM_PANEL_EVENT_BLANK_LP) { pt_debug(cd->dev, DL_INFO, "%s: LOWPOWER!\n", __func__); - if (event == DRM_PANEL_EARLY_EVENT_BLANK) { + if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { if (cd->fb_state != FB_OFF) { #if defined(CONFIG_PM_SLEEP) pt_debug(cd->dev, DL_INFO, "%s: Suspend notifier called!\n", @@ -12878,16 +12877,16 @@ static int drm_notifier_callback(struct notifier_block *self, cd->fb_state = FB_OFF; pt_debug(cd->dev, DL_INFO, "%s: Suspend notified!\n", __func__); } - } else if (event == DRM_PANEL_EVENT_BLANK) { - pt_debug(cd->dev, DL_INFO, "%s: suspend: event = %lu, not care\n", - __func__, event); + } else if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { + pt_debug(cd->dev, DL_INFO, "%s: suspend: event = %d, not care\n", + __func__, notification->notif_type); } } else { pt_debug(cd->dev, DL_INFO, "%s: DRM BLANK(%d) do not need process\n", - __func__, *blank); + __func__, notification->notif_type); } exit: - return 0; + return; } /******************************************************************************* @@ -12900,9 +12899,7 @@ exit: ******************************************************************************/ static void pt_setup_drm_notifier(struct pt_core_data *cd) { - cd->fb_state = FB_NONE; - cd->fb_notifier.notifier_call = drm_notifier_callback; - pt_debug(cd->dev, DL_INFO, "%s: Setting up drm notifier\n", __func__); + void *cookie = NULL; if (!active_panel) pt_debug(cd->dev, DL_ERROR, @@ -12919,11 +12916,9 @@ static void pt_setup_drm_notifier(struct pt_core_data *cd) INIT_WORK(&cd->suspend_work, pt_suspend_work); } - if (active_panel && - drm_panel_notifier_register(active_panel, - &cd->fb_notifier) < 0) - pt_debug(cd->dev, DL_ERROR, - "%s: Register notifier failed!\n", __func__); + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, + active_panel,&drm_notifier_callback, cd); } #elif defined(CONFIG_FB) /******************************************************************************* @@ -17974,7 +17969,7 @@ int pt_release(struct pt_core_data *cd) unregister_early_suspend(&cd->es); #elif defined(CONFIG_DRM) if (active_panel) - drm_panel_notifier_unregister(active_panel, &cd->fb_notifier); + panel_event_notifier_unregister(&cd->fb_notifier); #elif defined(CONFIG_FB) fb_unregister_client(&cd->fb_notifier); #endif From dd836445787dda334440a3a8978b40847f22e8bd Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Mon, 14 Nov 2022 16:06:28 +0530 Subject: [PATCH 093/170] touch-driver: add KBUILD_OPTIONS for standalone kernel compilation add KBUILD_OPTIONS for standalone kernel compilation. Change-Id: I392f3eb1f9fb7414c888da720088de96bd487660 Signed-off-by: Surya Teja Kudiri --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 05c3bcc66c..d86c29c187 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ KBUILD_OPTIONS+= TOUCH_ROOT=$(KERNEL_SRC)/$(M) +KBUILD_OPTIONS += MODNAME?=touch_dlkm all: $(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS) From 6b6fdc17bf680ecdcb3a89f0d07dab304fa97c99 Mon Sep 17 00:00:00 2001 From: Yu Wu Date: Mon, 14 Nov 2022 17:00:16 +0800 Subject: [PATCH 094/170] touch: fix compilation errors in touch drivers Remove the %s in pr_warn function which is not needed. Change-Id: Idf8346b250a51763bdb270cfa36bda416650d91e Signed-off-by: Yu Wu --- qts/qts_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qts/qts_core.c b/qts/qts_core.c index b49e9be9f5..b98c104eb3 100644 --- a/qts/qts_core.c +++ b/qts/qts_core.c @@ -177,7 +177,7 @@ static int qts_populate_vm_info(struct qts_data *qts_data) rc = of_property_read_string(np, "qts,trusted-touch-type", &vm_info->trusted_touch_type); if (rc) { - pr_warn("%s: No trusted touch type selection made\n"); + pr_warn("No trusted touch type selection mode\n"); vm_info->mem_tag = GH_MEM_NOTIFIER_TAG_TOUCH_PRIMARY; vm_info->irq_label = GH_IRQ_LABEL_TRUSTED_TOUCH_PRIMARY; rc = 0; From 21480652282c84ad3af3f6cb0b9d4f39f2d3d61a Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Wed, 16 Nov 2022 19:31:05 +0530 Subject: [PATCH 095/170] touch: Fix regression issue Fix regression issues for other SI. Change-Id: I5d39dcc4981f0f5857b96389bfd19615b4a199a4 Signed-off-by: Surya Teja Kudiri --- Kbuild | 1 + touch_driver_board.mk | 6 +++++- touch_driver_product.mk | 6 +++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Kbuild b/Kbuild index 52527c3364..5537af8095 100644 --- a/Kbuild +++ b/Kbuild @@ -19,6 +19,7 @@ endif #ifeq ($(CONFIG_ARCH_PINEAPPLE), y) include $(TOUCH_ROOT)/config/gki_pineappletouch.conf LINUX_INC += -include $(TOUCH_ROOT)/config/gki_pineappletouchconf.h +#endif #ifeq ($(CONFIG_ARCH_MONACO), y) include $(TOUCH_ROOT)/config/gki_monacotouch.conf diff --git a/touch_driver_board.mk b/touch_driver_board.mk index d168a7bfb5..4ed664509d 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -8,7 +8,11 @@ endif ifeq ($(TOUCH_DLKM_ENABLE), true) ifneq ($(TARGET_BOARD_AUTO),true) ifeq ($(call is-board-platform-in-list,$(TARGET_BOARD_PLATFORM)),true) - BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/pt_ts.ko \ + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ + $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ + $(KERNEL_MODULES_OUT)/pt_ts.ko \ $(KERNEL_MODULES_OUT)/pt_i2c.ko \ $(KERNEL_MODULES_OUT)/pt_device_access.ko endif diff --git a/touch_driver_product.mk b/touch_driver_product.mk index 3a3e5abd5c..0d5d2c225a 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -6,7 +6,11 @@ ifeq ($(TARGET_KERNEL_DLKM_DISABLE), true) endif ifeq ($(TOUCH_DLKM_ENABLE), true) - PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/pt_ts.ko \ + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ + $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ + $(KERNEL_MODULES_OUT)/pt_ts.ko \ $(KERNEL_MODULES_OUT)/pt_i2c.ko \ $(KERNEL_MODULES_OUT)/pt_device_access.ko endif From 333d940f96f3201bf38108ad8f7225f10ea7e001 Mon Sep 17 00:00:00 2001 From: Ritesh Kumar Date: Tue, 15 Nov 2022 23:38:55 +0530 Subject: [PATCH 096/170] touch: goodix: fix suspend resume sequence for dual touch case Touch resume and suspend is done only when core_module_prob_sate global variable is set to probed state. In case of dual touch, if secondary probe fails, core_module_prob_sate is updated to probe failed state. Due to this, suspend or resume calls for primary touch returns early even though primary touch probe is successful. This leads to primary touch remain always on. To fix this, add a new ready variable in touch data structure and perform suspend or resume operations when ready flag is set to true. Change-Id: I7b93a5b3135736caa3e2b5aaf6ff7d8adf4dee9d Signed-off-by: Ritesh Kumar --- goodix_berlin_driver/goodix_ts_core.c | 5 +++-- goodix_berlin_driver/goodix_ts_core.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/goodix_berlin_driver/goodix_ts_core.c b/goodix_berlin_driver/goodix_ts_core.c index 4c2c079d4a..e61fe7e233 100644 --- a/goodix_berlin_driver/goodix_ts_core.c +++ b/goodix_berlin_driver/goodix_ts_core.c @@ -2298,7 +2298,7 @@ static int goodix_ts_suspend_helper(void *data) { struct goodix_ts_core *core_data = data; - if (!core_data || core_module_prob_sate != CORE_MODULE_PROB_SUCCESS) + if (!core_data || !core_data->ready) return 0; return goodix_ts_suspend(core_data); @@ -2308,7 +2308,7 @@ static int goodix_ts_resume_helper(void *data) { struct goodix_ts_core *core_data = data; - if (!core_data || core_module_prob_sate != CORE_MODULE_PROB_SUCCESS) + if (!core_data || !core_data->ready) return 0; return goodix_ts_resume(core_data); @@ -2590,6 +2590,7 @@ skip_to_power_gpio_setup: core_data->init_stage = CORE_INIT_STAGE1; goodix_modules.core_data = core_data; core_module_prob_sate = CORE_MODULE_PROB_SUCCESS; + core_data->ready = true; /* Try start a thread to get config-bin info */ goodix_start_later_init(core_data); diff --git a/goodix_berlin_driver/goodix_ts_core.h b/goodix_berlin_driver/goodix_ts_core.h index 929f96511b..debfddaf66 100644 --- a/goodix_berlin_driver/goodix_ts_core.h +++ b/goodix_berlin_driver/goodix_ts_core.h @@ -513,7 +513,7 @@ struct goodix_ts_core { struct notifier_block ts_notifier; struct goodix_ts_esd ts_esd; bool esd_initialized; - + bool ready; #if defined(CONFIG_DRM) struct notifier_block fb_notifier; void *notifier_cookie; From 8ef61bcc10d2b574f5d909af73815a15a6793fef Mon Sep 17 00:00:00 2001 From: Yu Wu Date: Tue, 30 Aug 2022 16:34:57 +0800 Subject: [PATCH 097/170] touch: goodix: Add more checks for brl_send_config function Add more checks in brl_send_config to ensure the config data sent by user is valid. Also add more parameter checks in brl_read_config function. Change-Id: I6ac69de62e8565dc4bee4befbe3124c60118f3a1 Signed-off-by: Yu Wu --- goodix_berlin_driver/goodix_brl_hw.c | 23 +++++++++++++++++++++-- goodix_berlin_driver/goodix_ts_utils.c | 3 ++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/goodix_berlin_driver/goodix_brl_hw.c b/goodix_berlin_driver/goodix_brl_hw.c index f7b2afda7e..791bd42f4e 100644 --- a/goodix_berlin_driver/goodix_brl_hw.c +++ b/goodix_berlin_driver/goodix_brl_hw.c @@ -497,14 +497,33 @@ static int brl_send_config(struct goodix_ts_core *cd, u8 *cfg, int len) { int ret; u8 *tmp_buf; + u16 cfg_head_len = sizeof(struct goodix_config_head) / sizeof(u8); struct goodix_ts_cmd cfg_cmd; struct goodix_ic_info_misc *misc = &cd->ic_info.misc; struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + struct goodix_config_head *cfg_head = (struct goodix_config_head *)cfg; - if (len > misc->fw_buffer_max_len) { + if (!cd || !cfg) { + ts_err("input parameter is NULL"); + return -EINVAL; + } else if (len > misc->fw_buffer_max_len) { ts_err("config len exceed limit %d > %d", len, misc->fw_buffer_max_len); return -EINVAL; + } else if (len < cfg_head_len) { + ts_err("config buffer size %d smaller than header size %d", + len, cfg_head_len); + return -EINVAL; + } else if (len != cfg_head_len + cfg_head->cfg_len) { + ts_err("config buffer size %d not equal to head %d + cfg_len %d", + len, cfg_head_len, cfg_head->cfg_len); + return -EINVAL; + } else if (checksum_cmp(cfg, cfg_head_len, CHECKSUM_MODE_U8_LE)) { + ts_err("config head checksum error"); + return -EINVAL; + } else if (checksum_cmp(cfg + cfg_head_len, cfg_head->cfg_len, CHECKSUM_MODE_U16_LE)) { + ts_err("config body checksum error"); + return -EINVAL; } tmp_buf = kzalloc(len, GFP_KERNEL); @@ -573,7 +592,7 @@ static int brl_read_config(struct goodix_ts_core *cd, u8 *cfg, int size) struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; struct goodix_config_head cfg_head; - if (!cfg) + if (!cfg || sizeof(cfg_head) > size) return -EINVAL; cfg_cmd.len = CONFIG_CND_LEN; diff --git a/goodix_berlin_driver/goodix_ts_utils.c b/goodix_berlin_driver/goodix_ts_utils.c index e1388acaa9..3916a767ea 100644 --- a/goodix_berlin_driver/goodix_ts_utils.c +++ b/goodix_berlin_driver/goodix_ts_utils.c @@ -71,7 +71,8 @@ int checksum_cmp(const u8 *data, int size, int mode) u32 i; if (((mode == CHECKSUM_MODE_U8_LE) && (size < 2)) || - ((mode == CHECKSUM_MODE_U16_LE) && (size < 4))) + ((mode == CHECKSUM_MODE_U16_LE) && (size < 4)) || + ((mode == CHECKSUM_MODE_U16_LE) && (size % 2 != 0))) return 1; if (mode == CHECKSUM_MODE_U8_LE) { From f69760205ec1887f185a24746bd65929eff99c0e Mon Sep 17 00:00:00 2001 From: Kirill Shpin Date: Mon, 5 Dec 2022 18:00:49 -0800 Subject: [PATCH 098/170] touch: removing pt touch for Pineapple target Change removes compilation of pt touch driver which is not needed for Pineapple. Change-Id: Ie79fad0dea645ab9332490eb2bb8549228ad13b4 Signed-off-by: Kirill Shpin --- Android.mk | 62 +++++++++++++++++++++-------------------- Kbuild | 54 ++++++++++++++++++----------------- touch_driver_board.mk | 21 +++++++++----- touch_driver_product.mk | 21 +++++++++----- 4 files changed, 88 insertions(+), 70 deletions(-) diff --git a/Android.mk b/Android.mk index 24066705eb..b426ef729e 100644 --- a/Android.mk +++ b/Android.mk @@ -77,37 +77,39 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### - ########################################################### - include $(CLEAR_VARS) - LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) - LOCAL_MODULE := pt_ts.ko - LOCAL_MODULE_KBUILD_NAME := pt_ts.ko - LOCAL_MODULE_TAGS := optional - #LOCAL_MODULE_DEBUG_ENABLE := true - LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) - include $(DLKM_DIR)/Build_external_kernelmodule.mk - ########################################################### + ifneq ($(TARGET_BOARD_PLATFORM), pineapple) + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := pt_ts.ko + LOCAL_MODULE_KBUILD_NAME := pt_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### - ########################################################### - include $(CLEAR_VARS) - LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) - LOCAL_MODULE := pt_i2c.ko - LOCAL_MODULE_KBUILD_NAME := pt_i2c.ko - LOCAL_MODULE_TAGS := optional - #LOCAL_MODULE_DEBUG_ENABLE := true - LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) - include $(DLKM_DIR)/Build_external_kernelmodule.mk - ########################################################### + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := pt_i2c.ko + LOCAL_MODULE_KBUILD_NAME := pt_i2c.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### - ########################################################### - include $(CLEAR_VARS) - LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) - LOCAL_MODULE := pt_device_access.ko - LOCAL_MODULE_KBUILD_NAME := pt_device_access.ko - LOCAL_MODULE_TAGS := optional - #LOCAL_MODULE_DEBUG_ENABLE := true - LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) - include $(DLKM_DIR)/Build_external_kernelmodule.mk - ########################################################### + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := pt_device_access.ko + LOCAL_MODULE_KBUILD_NAME := pt_device_access.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + endif # pineapple endif # DLKM check endif diff --git a/Kbuild b/Kbuild index 5537af8095..a73fab238c 100644 --- a/Kbuild +++ b/Kbuild @@ -146,39 +146,41 @@ ifeq ($(CONFIG_TOUCHSCREEN_SYNAPTICS_TCM), y) endif -ifeq ($(CONFIG_TOUCHSCREEN_PARADE), y) - LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_regs.h - LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_core.h - LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_platform.h +ifneq ($(CONFIG_ARCH_PINEAPPLE), y) + ifeq ($(CONFIG_TOUCHSCREEN_PARADE), y) + LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_regs.h + LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_core.h + LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_platform.h - pt_ts-y := \ - ./pt/pt_core.o \ - ./pt/pt_mt_common.o \ - ./pt/pt_platform.o \ - ./pt/pt_devtree.o \ - ./pt/pt_btn.o \ - ./pt/pt_mtb.o \ - ./pt/pt_proximity.o + pt_ts-y := \ + ./pt/pt_core.o \ + ./pt/pt_mt_common.o \ + ./pt/pt_platform.o \ + ./pt/pt_devtree.o \ + ./pt/pt_btn.o \ + ./pt/pt_mtb.o \ + ./pt/pt_proximity.o - obj-$(CONFIG_MSM_TOUCH) += pt_ts.o -endif + obj-$(CONFIG_MSM_TOUCH) += pt_ts.o + endif -ifeq ($(CONFIG_TOUCHSCREEN_PARADE_I2C), y) - LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_regs.h + ifeq ($(CONFIG_TOUCHSCREEN_PARADE_I2C), y) + LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_regs.h - pt_i2c-y := \ - ./pt/pt_i2c.o + pt_i2c-y := \ + ./pt/pt_i2c.o - obj-$(CONFIG_MSM_TOUCH) += pt_i2c.o -endif + obj-$(CONFIG_MSM_TOUCH) += pt_i2c.o + endif -ifeq ($(CONFIG_TOUCHSCREEN_PARADE_DEVICE_ACCESS), y) - LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_regs.h + ifeq ($(CONFIG_TOUCHSCREEN_PARADE_DEVICE_ACCESS), y) + LINUX_INC += -include $(TOUCH_ROOT)/pt/pt_regs.h - pt_device_access-y := \ - ./pt/pt_device_access.o + pt_device_access-y := \ + ./pt/pt_device_access.o - obj-$(CONFIG_MSM_TOUCH) += pt_device_access.o -endif + obj-$(CONFIG_MSM_TOUCH) += pt_device_access.o + endif +endif # pineapple CDEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" diff --git a/touch_driver_board.mk b/touch_driver_board.mk index 4ed664509d..641c70818f 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -8,13 +8,20 @@ endif ifeq ($(TOUCH_DLKM_ENABLE), true) ifneq ($(TARGET_BOARD_AUTO),true) ifeq ($(call is-board-platform-in-list,$(TARGET_BOARD_PLATFORM)),true) - BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ - $(KERNEL_MODULES_OUT)/goodix_ts.ko \ - $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ - $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ - $(KERNEL_MODULES_OUT)/pt_ts.ko \ - $(KERNEL_MODULES_OUT)/pt_i2c.ko \ - $(KERNEL_MODULES_OUT)/pt_device_access.ko + ifeq ($(TARGET_BOARD_PLATFORM), pineapple) + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ + $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko + else # pineapple + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ + $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ + $(KERNEL_MODULES_OUT)/pt_ts.ko \ + $(KERNEL_MODULES_OUT)/pt_i2c.ko \ + $(KERNEL_MODULES_OUT)/pt_device_access.ko + endif # pineapple endif endif endif diff --git a/touch_driver_product.mk b/touch_driver_product.mk index 0d5d2c225a..8f626141ff 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -6,11 +6,18 @@ ifeq ($(TARGET_KERNEL_DLKM_DISABLE), true) endif ifeq ($(TOUCH_DLKM_ENABLE), true) - PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ - $(KERNEL_MODULES_OUT)/goodix_ts.ko \ - $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ - $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ - $(KERNEL_MODULES_OUT)/pt_ts.ko \ - $(KERNEL_MODULES_OUT)/pt_i2c.ko \ - $(KERNEL_MODULES_OUT)/pt_device_access.ko + ifeq ($(TARGET_BOARD_PLATFORM), pineapple) + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ + $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko + else # pineapple + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ + $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ + $(KERNEL_MODULES_OUT)/pt_ts.ko \ + $(KERNEL_MODULES_OUT)/pt_i2c.ko \ + $(KERNEL_MODULES_OUT)/pt_device_access.ko + endif # pineapple endif From 2892f00748b00de5cef3a7fc69eb7cb0066887fd Mon Sep 17 00:00:00 2001 From: ppadasal Date: Wed, 7 Sep 2022 18:23:47 +0530 Subject: [PATCH 099/170] touch: enable raydium driver Enable new raydium. Configuration changes to pick raydium for compilation. Change-Id: If3a8f8c425d6702ee5e483a419aaa19907cfc013 Signed-off-by: ppadasal --- Android.mk | 11 +++++++++++ Kbuild | 27 +++++++++++++++++++++++++++ config/gki_monacotouch.conf | 1 + config/gki_monacotouchconf.h | 1 + touch_driver_board.mk | 3 ++- touch_driver_product.mk | 3 ++- 6 files changed, 44 insertions(+), 2 deletions(-) diff --git a/Android.mk b/Android.mk index b426ef729e..a815b145ba 100644 --- a/Android.mk +++ b/Android.mk @@ -66,6 +66,17 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := raydium_ts.ko + LOCAL_MODULE_KBUILD_NAME := raydium_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + ########################################################### include $(CLEAR_VARS) LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) diff --git a/Kbuild b/Kbuild index a73fab238c..699437be8b 100644 --- a/Kbuild +++ b/Kbuild @@ -136,6 +136,33 @@ ifeq ($(CONFIG_TOUCHSCREEN_DUMMY), y) obj-$(CONFIG_MSM_TOUCH) += dummy_ts.o endif +ifeq ($(CONFIG_TOUCHSCREEN_RM_TS), y) + LINUX_INC += -include $(TOUCH_ROOT)/raydium/Config.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/drv_interface.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/rad_fw_image_30.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/raydium_driver.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/raydium_selftest.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/tpselftest_30.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/f303_ic_control.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/f303_ic_reg.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/f303_ic_test.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/ic_drv_global.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/ic_drv_interface.h + + raydium_ts-y := \ + ./raydium/drv_interface.o \ + ./raydium/raydium_driver.o \ + ./raydium/raydium_fw_update.o \ + ./raydium/raydium_selftest.o \ + ./raydium/raydium_sysfs.o \ + ./raydium/chip_raydium/f303_ic_control.o \ + ./raydium/chip_raydium/f303_ic_test.o \ + ./raydium/chip_raydium/ic_drv_global.o \ + ./raydium/chip_raydium/ic_drv_interface.o + + obj-$(CONFIG_MSM_TOUCH) += raydium_ts.o +endif + ifeq ($(CONFIG_TOUCHSCREEN_SYNAPTICS_TCM), y) synaptics_tcm_ts-y := \ ./synaptics_tcm/synaptics_tcm_core.o \ diff --git a/config/gki_monacotouch.conf b/config/gki_monacotouch.conf index d16e3b095c..810b5c9c49 100644 --- a/config/gki_monacotouch.conf +++ b/config/gki_monacotouch.conf @@ -3,3 +3,4 @@ export CONFIG_TOUCHSCREEN_PARADE=y export CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT=y export CONFIG_TOUCHSCREEN_PARADE_I2C=y export CONFIG_TOUCHSCREEN_PARADE_DEVICE_ACCESS=y +export CONFIG_TOUCHSCREEN_RM_TS=y diff --git a/config/gki_monacotouchconf.h b/config/gki_monacotouchconf.h index dd7be04589..34f79b80d4 100644 --- a/config/gki_monacotouchconf.h +++ b/config/gki_monacotouchconf.h @@ -4,3 +4,4 @@ #define CONFIG_TOUCHSCREEN_PARADE_DEVICE_ACCESS 1 #define CONFIG_TOUCHSCREEN_PARADE_BUTTON 1 #define CONFIG_TOUCHSCREEN_PARADE_PROXIMITY 1 +#define CONFIG_TOUCHSCREEN_RM_TS 1 diff --git a/touch_driver_board.mk b/touch_driver_board.mk index 641c70818f..26712deb38 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -20,7 +20,8 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ $(KERNEL_MODULES_OUT)/pt_ts.ko \ $(KERNEL_MODULES_OUT)/pt_i2c.ko \ - $(KERNEL_MODULES_OUT)/pt_device_access.ko + $(KERNEL_MODULES_OUT)/pt_device_access.ko \ + $(KERNEL_MODULES_OUT)/raydium_ts.ko endif # pineapple endif endif diff --git a/touch_driver_product.mk b/touch_driver_product.mk index 8f626141ff..6f163dce7c 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -18,6 +18,7 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ $(KERNEL_MODULES_OUT)/pt_ts.ko \ $(KERNEL_MODULES_OUT)/pt_i2c.ko \ - $(KERNEL_MODULES_OUT)/pt_device_access.ko + $(KERNEL_MODULES_OUT)/pt_device_access.ko \ + $(KERNEL_MODULES_OUT)/raydium_ts.ko endif # pineapple endif From e74f386766a8ced6147a27641c88de940fbadabf Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Mon, 5 Dec 2022 19:58:45 +0530 Subject: [PATCH 100/170] touch: Add panel event notifier support Add panel event notifier support for parade tech driver. Change-Id: I347f9805b19bcd03b23a8ad34130dc1f5b05b3e8 Signed-off-by: Surya Teja Kudiri --- pt/pt_core.c | 233 +++++++++++++++++++++++++++++++++++++++++++-------- pt/pt_regs.h | 8 +- 2 files changed, 204 insertions(+), 37 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index 35c07e582d..03ac8fc970 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -32,7 +32,9 @@ #include #include #include "pt_regs.h" +#if defined(CONFIG_PANEL_NOTIFIER) #include +#endif #define PINCTRL_STATE_ACTIVE "pmx_ts_active" #define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" @@ -52,7 +54,7 @@ #define PT_STATUS_STR_LEN (50) -#if defined(CONFIG_DRM) +#if defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER) static struct drm_panel *active_panel; #endif @@ -12791,6 +12793,152 @@ static void pt_suspend_work(struct work_struct *work) return; } +#if defined(CONFIG_PANEL_NOTIFIER) +/******************************************************************************* + * FUNCTION: panel_event_notifier_callback + * + * SUMMARY: Call back function for Panel Event notifier to allow to call + * resume/suspend attention list. + * + * PARAMETERS: + * tag - type of input panel. + * *notification - pointer to notification details. + * *client_data - pointer to core data + ******************************************************************************/ +static void panel_event_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data) +{ + struct pt_core_data *cd = client_data; + + if(!notification) + { + pt_debug(cd->dev,DL_INFO, "%s: Invalid notification\n", __func__); + return; + } + + pt_debug(cd->dev, DL_INFO, "%s: DRM notifier called!\n", __func__); + if (cd->quick_boot || cd->drv_debug_suspend) + goto exit; + + pt_debug(cd->dev, DL_INFO, "%s: DRM event:%d,fb_state %d", + __func__, notification->notif_type, cd->fb_state); + pt_debug(cd->dev, DL_INFO, "%s: DRM Power - %s - FB state %d ", + __func__, (notification->notif_type == DRM_PANEL_EVENT_UNBLANK)?"UP":"DOWN", cd->fb_state); + + if (notification->notif_type == DRM_PANEL_EVENT_UNBLANK) { + pt_debug(cd->dev, DL_INFO, "%s: UNBLANK!\n", __func__); + if (notification->notif_data.early_trigger) { + pr_err("%s: resume: event = %d, not care\n", __func__, notification->notif_type); + pt_debug(cd->dev, DL_INFO, "%s: resume: event = %d, not care\n", + __func__, notification->notif_type); + } else { + pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n", + __func__); + cancel_work_sync(&cd->resume_offload_work); + cancel_work_sync(&cd->suspend_offload_work); + cancel_work_sync(&cd->resume_work); + cancel_work_sync(&cd->suspend_work); + + queue_work(cd->pt_workqueue, &cd->resume_work); +#if defined(CONFIG_PM_SLEEP) + pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n", + __func__); + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) + pt_core_resume_(cd->dev); +#endif + cd->fb_state = FB_ON; + pt_debug(cd->dev, DL_INFO, "%s: Resume notified!\n", __func__); + } + } else if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { + pt_debug(cd->dev, DL_INFO, "%s: BLANK!\n", __func__); + if (notification->notif_data.early_trigger) { +#if defined(CONFIG_PM_SLEEP) + pt_debug(cd->dev, DL_INFO, "%s: Suspend notifier called!\n", + __func__); + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) + pt_core_suspend_(cd->dev); +#endif + cancel_work_sync(&cd->resume_offload_work); + cancel_work_sync(&cd->suspend_offload_work); + cancel_work_sync(&cd->resume_work); + cancel_work_sync(&cd->suspend_work); + + queue_work(cd->pt_workqueue, &cd->suspend_work); + cd->fb_state = FB_OFF; + pt_debug(cd->dev, DL_INFO, "%s: Suspend notified!\n", __func__); + } else { + pt_debug(cd->dev, DL_INFO, "%s: suspend: event = %d, not care\n", + __func__, notification->notif_type); + } + } else if (notification->notif_type == DRM_PANEL_EVENT_BLANK_LP) { + pt_debug(cd->dev, DL_INFO, "%s: LOWPOWER!\n", __func__); + if (notification->notif_data.early_trigger) { +#if defined(CONFIG_PM_SLEEP) + pt_debug(cd->dev, DL_INFO, "%s: Suspend notifier called!\n", __func__); + if (cd->cpdata->flags & PT_CORE_FLAG_SKIP_RUNTIME) + pt_core_suspend_(cd->dev); +#endif + cancel_work_sync(&cd->resume_offload_work); + cancel_work_sync(&cd->suspend_offload_work); + cancel_work_sync(&cd->resume_work); + cancel_work_sync(&cd->suspend_work); + + queue_work(cd->pt_workqueue, &cd->suspend_work); + cd->fb_state = FB_OFF; + pt_debug(cd->dev, DL_INFO, "%s: Suspend notified!\n", __func__); + } else { + pt_debug(cd->dev, DL_INFO, "%s: suspend: event = %d, not care\n", + __func__, notification->notif_type); + } + + } else { + pt_debug(cd->dev, DL_INFO, "%s: DRM BLANK(%d) do not need process\n", + __func__, notification->notif_type); + } +exit: + return; +} + +/******************************************************************************* + * FUNCTION: pt_setup_panel_event_notifier + * + * SUMMARY: Set up call back function into drm notifier. + * + * PARAMETERS: + * *cd - pointer to core data + ******************************************************************************/ +static void pt_setup_panel_event_notifier(struct pt_core_data *cd) +{ + void *cookie = NULL; + + if (!active_panel) + pt_debug(cd->dev, DL_ERROR, + "%s: Active panel not registered!\n", __func__); + + cd->pt_workqueue = create_singlethread_workqueue("ts_wq"); + if (!cd->pt_workqueue) { + pt_debug(cd->dev, DL_ERROR, + "%s: worker thread creation failed !\n", __func__); + } + + if (cd->pt_workqueue) { + INIT_WORK(&cd->resume_work, pt_resume_work); + INIT_WORK(&cd->suspend_work, pt_suspend_work); + } + + + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, + active_panel,&panel_event_notifier_callback, cd); + + if (active_panel && !cookie) + { + pt_debug(cd->dev, DL_ERROR, + "%s: Register notifier failed!\n", __func__); + } +} +#else + /******************************************************************************* * FUNCTION: drm_notifier_callback * @@ -12805,40 +12953,41 @@ static void pt_suspend_work(struct work_struct *work) * event - event type of fb notifier * *data - pointer to fb_event structure ******************************************************************************/ -static void drm_notifier_callback(enum panel_event_notifier_tag tag, - struct panel_event_notification *notification, void *client_data) +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) { - struct pt_core_data *cd = client_data; - - if(!notification) - { - pt_debug(cd->dev,DL_INFO, "%s: Invalid notification\n", __func__); - return; - } + struct pt_core_data *cd = + container_of(self, struct pt_core_data, fb_notifier); + struct drm_panel_notifier *evdata = data; + int *blank; pt_debug(cd->dev, DL_INFO, "%s: DRM notifier called!\n", __func__); - if (!(notification->notif_type == DRM_PANEL_EVENT_BLANK || - notification->notif_type == DRM_PANEL_EVENT_BLANK)) { - pt_debug(cd->dev, DL_INFO, "%s: Event(%d) do not need process\n", - __func__, notification->notif_type); + if (!evdata) + goto exit; + + if (!(event == DRM_PANEL_EARLY_EVENT_BLANK || + event == DRM_PANEL_EVENT_BLANK)) { + pt_debug(cd->dev, DL_INFO, "%s: Event(%lu) do not need process\n", + __func__, event); goto exit; } if (cd->quick_boot || cd->drv_debug_suspend) goto exit; - pt_debug(cd->dev, DL_INFO, "%s: DRM event:%d,fb_state %d", - __func__, notification->notif_type, cd->fb_state); + blank = evdata->data; + pt_debug(cd->dev, DL_INFO, "%s: DRM event:%lu,blank:%d fb_state %d sleep state %d ", + __func__, event, *blank, cd->fb_state, cd->sleep_state); pt_debug(cd->dev, DL_INFO, "%s: DRM Power - %s - FB state %d ", - __func__, (notification->notif_type == DRM_PANEL_EVENT_UNBLANK)?"UP":"DOWN", cd->fb_state); + __func__, (*blank == DRM_PANEL_BLANK_UNBLANK)?"UP":"DOWN", cd->fb_state); - if (notification->notif_type == DRM_PANEL_EVENT_UNBLANK) { + if (*blank == DRM_PANEL_BLANK_UNBLANK) { pt_debug(cd->dev, DL_INFO, "%s: UNBLANK!\n", __func__); - if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { - pt_debug(cd->dev, DL_INFO, "%s: resume: event = %d, not care\n", - __func__, notification->notif_type); - } else if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { + if (event == DRM_PANEL_EARLY_EVENT_BLANK) { + pt_debug(cd->dev, DL_INFO, "%s: resume: event = %lu, not care\n", + __func__, event); + } else if (event == DRM_PANEL_EVENT_BLANK) { if (cd->fb_state != FB_ON) { pt_debug(cd->dev, DL_INFO, "%s: Resume notifier called!\n", __func__); @@ -12858,9 +13007,9 @@ static void drm_notifier_callback(enum panel_event_notifier_tag tag, pt_debug(cd->dev, DL_INFO, "%s: Resume notified!\n", __func__); } } - } else if (notification->notif_type == DRM_PANEL_EVENT_BLANK_LP) { + } else if (*blank == DRM_PANEL_BLANK_LP || *blank == DRM_PANEL_BLANK_POWERDOWN) { pt_debug(cd->dev, DL_INFO, "%s: LOWPOWER!\n", __func__); - if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { + if (event == DRM_PANEL_EARLY_EVENT_BLANK) { if (cd->fb_state != FB_OFF) { #if defined(CONFIG_PM_SLEEP) pt_debug(cd->dev, DL_INFO, "%s: Suspend notifier called!\n", @@ -12877,16 +13026,17 @@ static void drm_notifier_callback(enum panel_event_notifier_tag tag, cd->fb_state = FB_OFF; pt_debug(cd->dev, DL_INFO, "%s: Suspend notified!\n", __func__); } - } else if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { - pt_debug(cd->dev, DL_INFO, "%s: suspend: event = %d, not care\n", - __func__, notification->notif_type); + + } else if (event == DRM_PANEL_EVENT_BLANK) { + pt_debug(cd->dev, DL_INFO, "%s: suspend: event = %lu, not care\n", + __func__, event); } } else { pt_debug(cd->dev, DL_INFO, "%s: DRM BLANK(%d) do not need process\n", - __func__, notification->notif_type); + __func__, *blank); } exit: - return; + return 0; } /******************************************************************************* @@ -12899,7 +13049,9 @@ exit: ******************************************************************************/ static void pt_setup_drm_notifier(struct pt_core_data *cd) { - void *cookie = NULL; + cd->fb_state = FB_NONE; + cd->fb_notifier.notifier_call = drm_notifier_callback; + pt_debug(cd->dev, DL_INFO, "%s: Setting up drm notifier\n", __func__); if (!active_panel) pt_debug(cd->dev, DL_ERROR, @@ -12916,10 +13068,13 @@ static void pt_setup_drm_notifier(struct pt_core_data *cd) INIT_WORK(&cd->suspend_work, pt_suspend_work); } - cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, - PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, - active_panel,&drm_notifier_callback, cd); + if (active_panel && + drm_panel_notifier_register(active_panel, + &cd->fb_notifier) < 0) + pt_debug(cd->dev, DL_ERROR, + "%s: Register notifier failed!\n", __func__); } +#endif #elif defined(CONFIG_FB) /******************************************************************************* * FUNCTION: fb_notifier_callback @@ -17600,7 +17755,7 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, /* Set platform easywake value */ cd->easy_wakeup_gesture = cd->cpdata->easy_wakeup_gesture; -#ifdef CONFIG_DRM +#if defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER) /* Setup active dsi panel */ active_panel = cd->cpdata->active_panel; #endif @@ -17846,6 +18001,11 @@ skip_enum: #ifdef CONFIG_HAS_EARLYSUSPEND pt_setup_early_suspend(cd); +#elif defined(CONFIG_PANEL_NOTIFIER) + pt_debug(dev, DL_ERROR, "%s: Probe: Setup Panel Event notifier\n", __func__); + pt_setup_panel_event_notifier(cd); + INIT_WORK(&cd->resume_offload_work, pt_resume_offload_work); + INIT_WORK(&cd->suspend_offload_work, pt_suspend_offload_work); #elif defined(CONFIG_DRM) pt_debug(dev, DL_ERROR, "%s: Probe: Setup drm notifier\n", __func__); pt_setup_drm_notifier(cd); @@ -17967,9 +18127,12 @@ int pt_release(struct pt_core_data *cd) #ifdef CONFIG_HAS_EARLYSUSPEND unregister_early_suspend(&cd->es); -#elif defined(CONFIG_DRM) +#elif defined(CONFIG_PANEL_NOTIFIER) if (active_panel) panel_event_notifier_unregister(&cd->fb_notifier); +#elif defined(CONFIG_DRM) + if (active_panel) + drm_panel_notifier_unregister(active_panel, &cd->fb_notifier); #elif defined(CONFIG_FB) fb_unregister_client(&cd->fb_notifier); #endif diff --git a/pt/pt_regs.h b/pt/pt_regs.h index 0552e61afd..c807a6604f 100644 --- a/pt/pt_regs.h +++ b/pt/pt_regs.h @@ -45,12 +45,16 @@ #define CONFIG_DRM #endif +#if IS_ENABLED(CONFIG_QCOM_PANEL_EVENT_NOTIFIER) +#define CONFIG_PANEL_NOTIFIER +#endif + #include #include #include #ifdef CONFIG_HAS_EARLYSUSPEND #include -#elif defined(CONFIG_DRM) +#elif defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER) #include #endif @@ -1521,7 +1525,7 @@ struct pt_core_data { int raw_cmd_status; #ifdef CONFIG_HAS_EARLYSUSPEND struct early_suspend es; -#elif defined(CONFIG_FB) || defined(CONFIG_DRM) +#elif defined(CONFIG_FB) || defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER) struct notifier_block fb_notifier; enum pt_fb_state fb_state; #endif From f650cebc8149ce698ad5545983485d049d1f0b76 Mon Sep 17 00:00:00 2001 From: Yu Wu Date: Thu, 15 Dec 2022 16:12:18 +0800 Subject: [PATCH 101/170] touch: goodix: move qts initialization to after touch power on qts initialization will contain an event notifier, sometimes the qupv engine init fail and causes goodix_ts_power_on failure, it leads to touch probe function failure. In this case there is no need to register this notifier, otherwise the resume function may access resource like irqs, which are not ready because probe failure, it may cause crash. Change-Id: I65bf39ec3ca3e2ea92eb84366b75a98ba43b8816 Signed-off-by: Yu Wu --- goodix_berlin_driver/goodix_ts_core.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/goodix_berlin_driver/goodix_ts_core.c b/goodix_berlin_driver/goodix_ts_core.c index e61fe7e233..a3ffc0508d 100644 --- a/goodix_berlin_driver/goodix_ts_core.c +++ b/goodix_berlin_driver/goodix_ts_core.c @@ -2509,18 +2509,6 @@ static int goodix_ts_probe(struct platform_device *pdev) } core_data->bus = bus_interface; - qts_en = of_property_read_bool(node, "goodix,qts_en"); - if (qts_en) { - mutex_init(&core_data->tui_transition_lock); - goodix_ts_fill_qts_vendor_data(&qts_vendor_data, core_data); - - ret = qts_client_register(qts_vendor_data); - if (ret) { - pr_err("qts client register failed, rc %d\n", ret); - goto err_out; - } - core_data->qts_en = qts_en; - } if (IS_ENABLED(CONFIG_OF) && bus_interface->dev->of_node) { /* parse devicetree property */ @@ -2575,6 +2563,19 @@ static int goodix_ts_probe(struct platform_device *pdev) goto err_out; } + qts_en = of_property_read_bool(node, "goodix,qts_en"); + if (qts_en) { + mutex_init(&core_data->tui_transition_lock); + goodix_ts_fill_qts_vendor_data(&qts_vendor_data, core_data); + + ret = qts_client_register(qts_vendor_data); + if (ret) { + pr_err("qts client register failed, rc %d\n", ret); + goto err_out; + } + core_data->qts_en = qts_en; + } + #ifdef CONFIG_ARCH_QTI_VM skip_to_power_gpio_setup: #endif From 20a6810daed70c7d6561e57c88167e6bfe629457 Mon Sep 17 00:00:00 2001 From: ppadasal Date: Mon, 12 Sep 2022 11:40:44 +0530 Subject: [PATCH 102/170] touch: raydium touch driver upgrade Upgrading raydium tech touch driver code from kernel 5.4 to kernel 5.15. Change-Id: I051065cadad60ea9428f38eb18bedbcb3af47797 Signed-off-by: ppadasal --- NOTICE | 48 +++++++++++++++++++++++++ raydium/chip_raydium/f303_ic_control.c | 2 +- raydium/chip_raydium/f303_ic_test.c | 10 +++--- raydium/chip_raydium/ic_drv_global.c | 4 +-- raydium/chip_raydium/ic_drv_global.h | 2 +- raydium/chip_raydium/ic_drv_interface.c | 2 +- raydium/drv_interface.c | 37 +++++++++++++++++-- raydium/raydium_selftest.c | 39 +++++++++++++++++--- 8 files changed, 126 insertions(+), 18 deletions(-) diff --git a/NOTICE b/NOTICE index 333f0d7dbf..4c2fafdd89 100644 --- a/NOTICE +++ b/NOTICE @@ -3,6 +3,54 @@ * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. */ +/* + * + * Raydium TouchScreen driver. + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * Qualcomm Innovation Center, Inc. chooses to use it under GPLv2 + * Copyright (c) 2021 Raydium tech Ltd. + * + * 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. + * + * BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. or Linaro Ltd. nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + /* * * Parade TouchScreen driver. diff --git a/raydium/chip_raydium/f303_ic_control.c b/raydium/chip_raydium/f303_ic_control.c index 8c5a79a6a7..2b1ae81cfa 100644 --- a/raydium/chip_raydium/f303_ic_control.c +++ b/raydium/chip_raydium/f303_ic_control.c @@ -31,7 +31,7 @@ #include #endif -#include "drv_interface.h" +#include "../drv_interface.h" #include "ic_drv_global.h" #include "ic_drv_interface.h" #include "f303_ic_control.h" diff --git a/raydium/chip_raydium/f303_ic_test.c b/raydium/chip_raydium/f303_ic_test.c index 0694356601..00f0b0ca86 100644 --- a/raydium/chip_raydium/f303_ic_test.c +++ b/raydium/chip_raydium/f303_ic_test.c @@ -31,7 +31,7 @@ #endif #define RM_F303_MAX_STR_LENGTH 8 -#include "drv_interface.h" +#include "../drv_interface.h" #include "ic_drv_global.h" #include "ic_drv_interface.h" #include "f303_ic_control.h" @@ -1193,7 +1193,7 @@ STATUS ft_raw_data_checksum_check_3x(unsigned short *u16_buffer) if (u16_buffer[48] != 0x55AA) { DEBUGOUT("u16_buffer[34]:%x\r\n", u16_buffer[48]); - DEBUGOUT("[RDCSC] Pattern NG! [0x%x](0x%x)\r\n", (int)u16_buffer, u16_buffer[48]); + DEBUGOUT("[RDCSC] Pattern NG! [0x%p](0x%x)\n", u16_buffer, u16_buffer[48]); return ERROR; } @@ -1202,7 +1202,7 @@ STATUS ft_raw_data_checksum_check_3x(unsigned short *u16_buffer) u16_sum += u16_buffer[u8_i]; if (u16_buffer[49] != u16_sum) { - DEBUGOUT("[RDCSC] Check SUM NG! [0x%x](0x%x:0x%x)\r\n", (int)u16_buffer, u16_sum, u16_buffer[49]); + DEBUGOUT("[RDCSC] Check SUM NG! [0x%p](0x%x:0x%x)\n", u16_buffer, u16_sum, u16_buffer[49]); return ERROR; } @@ -1217,7 +1217,7 @@ STATUS ft_test_result_checksum_check_3x(unsigned char *u8_buffer) unsigned char u8_sum = 0; if (u8_buffer[48] != 0x5A) { - DEBUGOUT("[TRCSC] Pattern NG! [0x%x](0x%x:0x%x)\r\n", (int)u8_buffer, u8_buffer[48], u8_buffer[49]); + DEBUGOUT("[TRCSC] Pattern NG! [0x%p](0x%x:0x%x)\n", u8_buffer, u8_buffer[48], u8_buffer[49]); return ERROR; } @@ -1226,7 +1226,7 @@ STATUS ft_test_result_checksum_check_3x(unsigned char *u8_buffer) u8_sum += u8_buffer[u8_i]; if (u8_buffer[49] != u8_sum) { - DEBUGOUT("[TRCSC] Check SUM NG! [0x%x](0x%x:0x%x)\r\n", (int)u8_buffer, u8_sum, u8_buffer[49]); + DEBUGOUT("[TRCSC] Check SUM NG! [0x%p](0x%x:0x%x)\n", u8_buffer, u8_sum, u8_buffer[49]); return ERROR; } diff --git a/raydium/chip_raydium/ic_drv_global.c b/raydium/chip_raydium/ic_drv_global.c index 6de61b4698..13816efa0b 100644 --- a/raydium/chip_raydium/ic_drv_global.c +++ b/raydium/chip_raydium/ic_drv_global.c @@ -16,7 +16,7 @@ * */ -#include +#include "../Config.h" #include "ic_drv_global.h" #include "ic_drv_interface.h" #ifdef __KERNEL__ @@ -30,7 +30,7 @@ #include #endif -#include "drv_interface.h" +#include "../drv_interface.h" diff --git a/raydium/chip_raydium/ic_drv_global.h b/raydium/chip_raydium/ic_drv_global.h index ae9e884deb..3de8078751 100644 --- a/raydium/chip_raydium/ic_drv_global.h +++ b/raydium/chip_raydium/ic_drv_global.h @@ -18,7 +18,7 @@ #ifndef _DRVGLOBAL_H_ #define _DRVGLOBAL_H_ -#include +#include "../Config.h" #ifdef __KERNEL__ #include #else diff --git a/raydium/chip_raydium/ic_drv_interface.c b/raydium/chip_raydium/ic_drv_interface.c index 663e54d6b3..1d7621f5ab 100644 --- a/raydium/chip_raydium/ic_drv_interface.c +++ b/raydium/chip_raydium/ic_drv_interface.c @@ -29,7 +29,7 @@ #include #endif #include "ic_drv_interface.h" -#include "drv_interface.h" +#include "../drv_interface.h" #include "ic_drv_global.h" #if SELFTEST_2X #include "f302_ic_control.h" diff --git a/raydium/drv_interface.c b/raydium/drv_interface.c index 42af05c7fa..b065b8db01 100644 --- a/raydium/drv_interface.c +++ b/raydium/drv_interface.c @@ -2,6 +2,9 @@ * * Raydium TouchScreen driver. * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * Qualcomm Innovation Center, Inc. chooses to use it under GPLv2 * Copyright (c) 2021 Raydium tech Ltd. * * This program is free software; you can redistribute it and/or modify @@ -14,9 +17,37 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * + * BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. or Linaro Ltd. nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include +#include #include #include #include @@ -29,7 +60,6 @@ #include "raydium_driver.h" #include "chip_raydium/f303_ic_control.h" -struct timeval timer; unsigned char g_u8_m_buf[2][128]; unsigned char g_u8_ini_flash[0x400]; struct raydium_ts_data *ts; @@ -127,14 +157,15 @@ unsigned char read_flash_data(unsigned int u32_addr, unsigned short u16_lenth) unsigned int get_system_time(void) { /* unsigned int u32_timer; + * struct timeval timer; * do_gettimeofday(&timer); * u32_timer = (timer.tv_sec % 1000) * 1000 + (timer.tv_usec / 1000); * return u32_timer; */ #if (KERNEL_VERSION(5, 0, 0) <= LINUX_VERSION_CODE) - struct timespec ts; + struct timespec64 ts; - getnstimeofday(&ts); + ktime_get_ts64(&ts); return (ts.tv_sec*1000 + ts.tv_nsec/1000000); #else struct timeval tv; diff --git a/raydium/raydium_selftest.c b/raydium/raydium_selftest.c index 1ec50d4dca..b91e07a32a 100644 --- a/raydium/raydium_selftest.c +++ b/raydium/raydium_selftest.c @@ -2,6 +2,9 @@ * * Raydium TouchScreen driver. * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * Qualcomm Innovation Center, Inc. chooses to use it under GPLv2 * Copyright (c) 2021 Raydium tech Ltd. * * This program is free software; you can redistribute it and/or modify @@ -14,8 +17,36 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * + * BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. or Linaro Ltd. nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + #include #include #include @@ -69,17 +100,15 @@ int self_test_save_to_file(char *file_name, char *p_string, short len) struct file *filp = NULL; mm_segment_t old_fs; - filp = filp_open(file_name, O_RDWR | O_CREAT | O_APPEND, 0666); + filp = filp_open_block(file_name, O_RDWR | O_CREAT | O_APPEND, 0666); if (IS_ERR(filp)) { DEBUGOUT("can't open file:%s\n", RM_SELF_TEST_LOGFILE); return 0; } - old_fs = get_fs(); - set_fs(KERNEL_DS); + old_fs = force_uaccess_begin(); filp->f_op->write(filp, p_string, len, &filp->f_pos); - set_fs(old_fs); + force_uaccess_end(old_fs); filp_close(filp, NULL); - return 1; } From 786e4371371760d7444cfdd8e95e640f03e9df77 Mon Sep 17 00:00:00 2001 From: ppadasal Date: Mon, 12 Sep 2022 16:27:06 +0530 Subject: [PATCH 103/170] touch: add DRM for raydium for kernel 5.15 Updated panel_event_notifier_register and panel_event_notifier_unregister as per kernel 5.15 Change-Id: Ia2e31fa1a8ddd8b047891c2035dc89509f889b0b Signed-off-by: ppadasal --- raydium/raydium_driver.c | 106 ++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 63 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index b21a790e38..c85e746159 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -38,6 +38,7 @@ #include #include #include +#include #if defined(CONFIG_FB) #include @@ -695,7 +696,7 @@ void raydium_irq_control(bool enable) } /* Clear interrupts first */ - if (g_raydium_ts->blank != DRM_PANEL_BLANK_POWERDOWN) { + if (g_raydium_ts->blank != DRM_PANEL_EVENT_BLANK_LP) { if (g_u8_i2c_mode == PDA2_MODE) { mutex_lock(&g_raydium_ts->lock); if (raydium_i2c_pda2_set_page(g_raydium_ts->client, @@ -1202,8 +1203,8 @@ static void raydium_work_handler(struct work_struct *work) } } - if (g_raydium_ts->blank == DRM_PANEL_BLANK_LP || - g_raydium_ts->blank == DRM_PANEL_BLANK_POWERDOWN || g_raydium_ts->fb_state == FB_OFF) { + if (g_raydium_ts->blank == DRM_PANEL_EVENT_BLANK_LP|| + g_raydium_ts->blank == DRM_PANEL_EVENT_BLANK_LP || g_raydium_ts->fb_state == FB_OFF) { LOGD(LOG_DEBUG, "[touch] elseif u8_tp_status:%x\n", u8_tp_status[POS_GES_STATUS]); /*need check small area*/ /*if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP */ @@ -1220,7 +1221,7 @@ static void raydium_work_handler(struct work_struct *work) } } /*when display on can use palm to suspend*/ - else if (g_raydium_ts->blank == DRM_PANEL_BLANK_UNBLANK) { + else if (g_raydium_ts->blank == DRM_PANEL_EVENT_UNBLANK) { if (u8_tp_status[POS_GES_STATUS] == RAD_PALM_ENABLE) { if (g_raydium_ts->is_palm == 0) { /* release all touches*/ @@ -1571,54 +1572,36 @@ static int raydium_ts_resume(struct device *dev) #if defined(CONFIG_DRM) -/******************************************************************************* - * FUNCTION: drm_notifier_callback - * - * SUMMARY: Call back function for DRM notifier to allow to call - * resume/suspend attention list. - * - * RETURN: - * 0 = success - * - * PARAMETERS: - * self - pointer to notifier_block structure - * event - event type of fb notifier - * data - pointer to fb_event structure - ******************************************************************************/ -static int drm_notifier_callback(struct notifier_block *self, - unsigned long event, void *data) +static void drm_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data) { - struct raydium_ts_data *g_raydium_ts = - container_of(self, struct raydium_ts_data, fb_notif); - struct drm_panel_notifier *evdata = data; - int *blank; + + struct raydium_ts_data *g_raydium_ts = client_data; + if(!notification) + { + LOGD(LOG_INFO, "%s: Invalid notification\n", __func__); + return ; + } LOGD(LOG_INFO, "%s: DRM notifier called!\n", __func__); - - if (!evdata) - goto exit; - - if (!(event == DRM_PANEL_EARLY_EVENT_BLANK || - event == DRM_PANEL_EVENT_BLANK)) { - LOGD(LOG_INFO, "%s: Event(%lu) do not need process\n", - __func__, event); + if (!(notification->notif_type == DRM_PANEL_EVENT_BLANK)){ + LOGD(LOG_INFO, "%s: Event(%d do not need process\n", + __func__, notification->notif_type); goto exit; } - blank = evdata->data; - g_raydium_ts->blank = (*blank); - LOGD(LOG_INFO, "%s: DRM event:%lu,blank:%d fb_state %d ", - __func__, event, *blank, g_raydium_ts->fb_state); - LOGD(LOG_INFO, "%s: DRM Power - %s - FB state %d ", - __func__, (*blank == DRM_PANEL_BLANK_UNBLANK)?"UP":"DOWN", g_raydium_ts->fb_state); + LOGD(LOG_INFO, "%s: DRM event:%d fb_state %d ", + __func__, notification->notif_type, g_raydium_ts->fb_state); + LOGD(LOG_INFO, "%s: DRM Power - %d- FB state %d ", + __func__, (notification->notif_type == DRM_PANEL_EVENT_UNBLANK)?"UP":"DOWN", g_raydium_ts->fb_state); - if (*blank == DRM_PANEL_BLANK_UNBLANK) { + if (notification->notif_type == DRM_PANEL_EVENT_UNBLANK) { LOGD(LOG_INFO, "%s: UNBLANK!\n", __func__); - if (event == DRM_PANEL_EARLY_EVENT_BLANK) { - LOGD(LOG_INFO, "%s: resume: event = %lu, not care\n", - __func__, event); - } else if (event == DRM_PANEL_EVENT_BLANK) { + if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { + LOGD(LOG_INFO, "%s: resume: event = %d not care\n", + __func__, notification->notif_type); + } else if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { if (g_raydium_ts->fb_state != FB_ON) { LOGD(LOG_INFO, "%s: Resume notifier called!\n", __func__); @@ -1634,10 +1617,10 @@ static int drm_notifier_callback(struct notifier_block *self, LOGD(LOG_INFO, "%s: Resume notified!\n", __func__); } } - } else if (*blank == DRM_PANEL_BLANK_LP || *blank == DRM_PANEL_BLANK_POWERDOWN - || *blank == DRM_PANEL_BLANK_FPS_CHANGE) { + } else if (notification->notif_type == DRM_PANEL_EVENT_BLANK_LP || + notification->notif_type == DRM_PANEL_EVENT_FPS_CHANGE) { LOGD(LOG_INFO, "%s: LOWPOWER!\n", __func__); - if (event == DRM_PANEL_EARLY_EVENT_BLANK) { + if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { if (g_raydium_ts->fb_state != FB_OFF) { LOGD(LOG_INFO, "%s: Suspend notifier called!\n", __func__); @@ -1652,16 +1635,16 @@ static int drm_notifier_callback(struct notifier_block *self, g_raydium_ts->fb_state = FB_OFF; LOGD(LOG_INFO, "%s: Suspend notified!\n", __func__); } - } else if (event == DRM_PANEL_EVENT_BLANK) { - LOGD(LOG_INFO, "%s: suspend: event = %lu, not care\n", - __func__, event); + } else if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { + LOGD(LOG_INFO, "%s: suspend: event = %d not care\n", + __func__, notification->notif_type); } } else { LOGD(LOG_INFO, "%s: DRM BLANK(%d) do not need process\n", - __func__, *blank); + __func__, notification->notif_type); } exit: - return 0; + return ; } /******************************************************************************* @@ -1674,16 +1657,13 @@ exit: *******************************************************************************/ static void raydium_setup_drm_notifier(struct raydium_ts_data *g_raydium_ts) { - g_raydium_ts->fb_state = FB_ON; - g_raydium_ts->fb_notif.notifier_call = drm_notifier_callback; - LOGD(LOG_INFO, "[touch]%s: Setting up drm notifier\n", __func__); - + void *cookie = NULL; if (!active_panel) - LOGD(LOG_ERR, "[touch]%s: Active panel not registered!\n", __func__); + LOGD(LOG_ERR, "[touch]%s: Active panel not registered!\n", __func__); - if (active_panel && drm_panel_notifier_register(active_panel, - &g_raydium_ts->fb_notif) < 0) - LOGD(LOG_ERR, "[touch]%s: Register notifier failed!\n", __func__); + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, + active_panel,&drm_notifier_callback, g_raydium_ts); } #endif /*end of CONFIG_DRM*/ @@ -2400,8 +2380,8 @@ void raydium_ts_shutdown(struct i2c_client *client) #elif defined(CONFIG_HAS_EARLYSUSPEND) unregister_early_suspend(&g_raydium_ts->early_suspend); #elif defined(CONFIG_DRM) - if (active_panel) - drm_panel_notifier_unregister(active_panel, &g_raydium_ts->fb_notifier); +if (active_panel) + panel_event_notifier_unregister(&g_raydium_ts->fb_notif); #endif/*end of CONFIG_FB*/ input_unregister_device(g_raydium_ts->input_dev); input_free_device(g_raydium_ts->input_dev); @@ -2436,8 +2416,8 @@ static int raydium_ts_remove(struct i2c_client *client) #elif defined(CONFIG_HAS_EARLYSUSPEND) unregister_early_suspend(&g_raydium_ts->early_suspend); #elif defined(CONFIG_DRM) - if (active_panel) - drm_panel_notifier_unregister(active_panel, &g_raydium_ts->fb_notifier); +if (active_panel) + panel_event_notifier_unregister(&g_raydium_ts->fb_notif); #endif/*end of CONFIG_FB*/ input_unregister_device(g_raydium_ts->input_dev); input_free_device(g_raydium_ts->input_dev); From 10ca119f57e9bc546f9f6c248d4fdc4740ff90e5 Mon Sep 17 00:00:00 2001 From: Vara Reddy Date: Mon, 12 Dec 2022 02:54:33 -0800 Subject: [PATCH 104/170] touch: fix compilation errors due to kernel upgrade The value returned by an driver's remove function is ignored in 6.1 kernel. Modify the function's while keeping backward compatibility. Change-Id: I211e7ef822d9febe434b3ab45cd0c711617d85ea Signed-off-by: jianzhou Signed-off-by: Vara Reddy Signed-off-by: Kirill Shpin --- atmel_mxt/atmel_mxt_ts.c | 18 ++++++++++++++++++ goodix_berlin_driver/goodix_brl_i2c.c | 8 ++++++++ nt36xxx/nt36xxx.c | 9 ++++++++- synaptics_tcm/synaptics_tcm_i2c.c | 10 ++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/atmel_mxt/atmel_mxt_ts.c b/atmel_mxt/atmel_mxt_ts.c index d2f9162a4b..916e2ac3e5 100644 --- a/atmel_mxt/atmel_mxt_ts.c +++ b/atmel_mxt/atmel_mxt_ts.c @@ -37,6 +37,7 @@ #include #include #include +#include #ifdef CONFIG_DRM #include @@ -3881,6 +3882,22 @@ err_disable_regulator: return error; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)) +static void mxt_remove(struct i2c_client *client) +{ + struct mxt_data *data = i2c_get_clientdata(client); + +#ifdef CONFIG_DRM + if (active_panel && data->notifier_cookie) + panel_event_notifier_unregister(data->notifier_cookie); +#endif + disable_irq(data->irq); + mxt_regulator_disable(data); + sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); + mxt_free_input_device(data); + mxt_free_object_table(data); +} +#else static int mxt_remove(struct i2c_client *client) { struct mxt_data *data = i2c_get_clientdata(client); @@ -3897,6 +3914,7 @@ static int mxt_remove(struct i2c_client *client) return 0; } +#endif static int __maybe_unused mxt_suspend(struct device *dev) { diff --git a/goodix_berlin_driver/goodix_brl_i2c.c b/goodix_berlin_driver/goodix_brl_i2c.c index b9824551a1..d4aa1549c6 100644 --- a/goodix_berlin_driver/goodix_brl_i2c.c +++ b/goodix_berlin_driver/goodix_brl_i2c.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "goodix_ts_core.h" @@ -218,11 +219,18 @@ err_pdev: return ret; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)) +static void goodix_i2c_remove(struct i2c_client *client) +{ + platform_device_unregister(goodix_pdev); +} +#else static int goodix_i2c_remove(struct i2c_client *client) { platform_device_unregister(goodix_pdev); return 0; } +#endif #ifdef CONFIG_OF static const struct of_device_id i2c_matchs[] = { diff --git a/nt36xxx/nt36xxx.c b/nt36xxx/nt36xxx.c index fa3e6a3fd1..6f36ae8740 100644 --- a/nt36xxx/nt36xxx.c +++ b/nt36xxx/nt36xxx.c @@ -28,6 +28,7 @@ #include #include #include +#include #if defined(CONFIG_DRM) #include @@ -1605,7 +1606,11 @@ err_register_fb_notif_failed: * return: * Executive outcomes. 0---succeed. *******************************************************/ -static int32_t nvt_ts_remove(struct i2c_client *client) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)) +static void nvt_ts_remove(struct i2c_client *client) +#else +static int32_t nvt_ts_remove(struct i2c_client *client) +#endif { NVT_LOG("Removing driver...\n"); @@ -1672,7 +1677,9 @@ static int32_t nvt_ts_remove(struct i2c_client *client) ts = NULL; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)) return 0; +#endif } static void nvt_ts_shutdown(struct i2c_client *client) diff --git a/synaptics_tcm/synaptics_tcm_i2c.c b/synaptics_tcm/synaptics_tcm_i2c.c index 483002ea12..5783839058 100644 --- a/synaptics_tcm/synaptics_tcm_i2c.c +++ b/synaptics_tcm/synaptics_tcm_i2c.c @@ -32,6 +32,7 @@ #include #include +#include #include "synaptics_tcm_core.h" #include "linux/moduleparam.h" @@ -466,6 +467,14 @@ static int syna_tcm_i2c_probe(struct i2c_client *i2c, return 0; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)) +static void syna_tcm_i2c_remove(struct i2c_client *i2c) +{ + syna_tcm_i2c_device->dev.platform_data = NULL; + + platform_device_unregister(syna_tcm_i2c_device); +} +#else static int syna_tcm_i2c_remove(struct i2c_client *i2c) { syna_tcm_i2c_device->dev.platform_data = NULL; @@ -474,6 +483,7 @@ static int syna_tcm_i2c_remove(struct i2c_client *i2c) return 0; } +#endif static const struct i2c_device_id syna_tcm_id_table[] = { {I2C_MODULE_NAME, 0}, From 3d03d5839ed0e3779cbdb2933becb7e1b00a4dda Mon Sep 17 00:00:00 2001 From: Yu Wu Date: Wed, 21 Dec 2022 17:10:02 +0800 Subject: [PATCH 105/170] touch: goodix: Sleep 100ms after reset operation It is found there might be some i2c NACK error after power on and reset operation, from goodix datasheet it is required to wait 100ms after reset operation before I2C communication, so put a 100ms sleep after reset. Change-Id: I2601085f85c83a57bc895915506eaf0c29442678 Signed-off-by: Yu Wu --- goodix_berlin_driver/goodix_brl_hw.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/goodix_berlin_driver/goodix_brl_hw.c b/goodix_berlin_driver/goodix_brl_hw.c index f7b2afda7e..1aebdc0e03 100644 --- a/goodix_berlin_driver/goodix_brl_hw.c +++ b/goodix_berlin_driver/goodix_brl_hw.c @@ -231,9 +231,12 @@ static int brl_power_on(struct goodix_ts_core *cd, bool on) goto power_off; } } + + gpio_direction_output(cd->board_data.reset_gpio, 0); usleep_range(15000, 15100); - gpio_direction_output(reset_gpio, 1); - usleep_range(4000, 4100); + gpio_direction_output(cd->board_data.reset_gpio, 1); + msleep(GOODIX_NORMAL_RESET_DELAY_MS); + ret = brl_dev_confirm(cd); if (ret < 0) goto power_off; @@ -241,7 +244,6 @@ static int brl_power_on(struct goodix_ts_core *cd, bool on) if (ret < 0) goto power_off; - msleep(GOODIX_NORMAL_RESET_DELAY_MS); return 0; } From 3574f26e7eee52e9861ec90d1d3b20b5e74e5543 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Tue, 3 Jan 2023 11:00:56 +0530 Subject: [PATCH 106/170] touch: Add panel event notifier support Add panel event notifier support for raydium driver. Change-Id: I4ebe4ebf8522bb0ecc637aefec69f35a0d426c9e Signed-off-by: Surya Teja Kudiri --- raydium/raydium_driver.c | 205 +++++++++++++++++++++++++++++++-------- raydium/raydium_driver.h | 8 +- 2 files changed, 173 insertions(+), 40 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index c85e746159..c40489bfc6 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -38,16 +38,16 @@ #include #include #include -#include - +#include "raydium_driver.h" #if defined(CONFIG_FB) #include #include #elif defined(CONFIG_HAS_EARLYSUSPEND) #include #endif /*end of CONFIG_FB*/ - -#include "raydium_driver.h" +#if defined(CONFIG_PANEL_NOTIFIER) +#include +#endif struct raydium_slot_status { unsigned char pt_id; /*Occupied point ID*/ @@ -65,7 +65,7 @@ static int raydium_enable_regulator(struct raydium_ts_data *cd, bool en); const struct attribute_group raydium_attr_group; #endif /*end of CONFIG_RM_SYSFS_DEBUG*/ -#if defined(CONFIG_DRM) +#if defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER) static struct drm_panel *active_panel; #endif @@ -696,7 +696,11 @@ void raydium_irq_control(bool enable) } /* Clear interrupts first */ - if (g_raydium_ts->blank != DRM_PANEL_EVENT_BLANK_LP) { +#if defined(CONFIG_PANEL_NOTIFIER) + if (g_raydium_ts->blank != DRM_PANEL_EVENT_BLANK) { +#else + if (g_raydium_ts->blank != DRM_PANEL_BLANK_POWERDOWN) { +#endif if (g_u8_i2c_mode == PDA2_MODE) { mutex_lock(&g_raydium_ts->lock); if (raydium_i2c_pda2_set_page(g_raydium_ts->client, @@ -1202,9 +1206,13 @@ static void raydium_work_handler(struct work_struct *work) return; } } - - if (g_raydium_ts->blank == DRM_PANEL_EVENT_BLANK_LP|| - g_raydium_ts->blank == DRM_PANEL_EVENT_BLANK_LP || g_raydium_ts->fb_state == FB_OFF) { +#if defined(CONFIG_PANEL_NOTIFIER) + if (g_raydium_ts->blank == DRM_PANEL_EVENT_BLANK_LP || + g_raydium_ts->blank == DRM_PANEL_EVENT_BLANK || g_raydium_ts->fb_state == FB_OFF) { +#else + if (g_raydium_ts->blank == DRM_PANEL_BLANK_LP || + g_raydium_ts->blank == DRM_PANEL_BLANK_POWERDOWN || g_raydium_ts->fb_state == FB_OFF) { +#endif LOGD(LOG_DEBUG, "[touch] elseif u8_tp_status:%x\n", u8_tp_status[POS_GES_STATUS]); /*need check small area*/ /*if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP */ @@ -1221,7 +1229,11 @@ static void raydium_work_handler(struct work_struct *work) } } /*when display on can use palm to suspend*/ +#if defined(CONFIG_PANEL_NOTIFIER) else if (g_raydium_ts->blank == DRM_PANEL_EVENT_UNBLANK) { +#else + else if (g_raydium_ts->blank == DRM_PANEL_BLANK_UNBLANK) { +#endif if (u8_tp_status[POS_GES_STATUS] == RAD_PALM_ENABLE) { if (g_raydium_ts->is_palm == 0) { /* release all touches*/ @@ -1570,9 +1582,8 @@ static int raydium_ts_resume(struct device *dev) } #endif /*end of CONFIG_FB*/ - -#if defined(CONFIG_DRM) -static void drm_notifier_callback(enum panel_event_notifier_tag tag, +#if defined(CONFIG_PANEL_NOTIFIER) +static void panel_event_notifier_callback(enum panel_event_notifier_tag tag, struct panel_event_notification *notification, void *client_data) { @@ -1584,11 +1595,6 @@ static void drm_notifier_callback(enum panel_event_notifier_tag tag, } LOGD(LOG_INFO, "%s: DRM notifier called!\n", __func__); - if (!(notification->notif_type == DRM_PANEL_EVENT_BLANK)){ - LOGD(LOG_INFO, "%s: Event(%d do not need process\n", - __func__, notification->notif_type); - goto exit; - } LOGD(LOG_INFO, "%s: DRM event:%d fb_state %d ", __func__, notification->notif_type, g_raydium_ts->fb_state); @@ -1598,10 +1604,122 @@ static void drm_notifier_callback(enum panel_event_notifier_tag tag, if (notification->notif_type == DRM_PANEL_EVENT_UNBLANK) { LOGD(LOG_INFO, "%s: UNBLANK!\n", __func__); - if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { + if (notification->notif_data.early_trigger) { LOGD(LOG_INFO, "%s: resume: event = %d not care\n", __func__, notification->notif_type); - } else if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { + } else { + LOGD(LOG_INFO, "%s: Resume notifier called!\n", + __func__); +#ifdef GESTURE_EN + /* clear palm status */ + g_raydium_ts->is_palm = 0; +#endif + +#if defined(CONFIG_PM) + raydium_ts_resume(&g_raydium_ts->client->dev); +#endif + g_raydium_ts->fb_state = FB_ON; + LOGD(LOG_INFO, "%s: Resume notified!\n", __func__); + } + } else if (notification->notif_type == DRM_PANEL_EVENT_BLANK_LP || + notification->notif_type == DRM_PANEL_EVENT_BLANK) { + if (notification->notif_type == DRM_PANEL_EVENT_BLANK_LP) { + LOGD(LOG_INFO, "%s: LOWPOWER!\n", __func__); + } else { + LOGD(LOG_INFO, "%s: BLANK!\n", __func__); + } + if (notification->notif_data.early_trigger) { + LOGD(LOG_INFO, "%s: Suspend notifier called!\n", + __func__); +#ifdef GESTURE_EN + /* clear palm status */ + g_raydium_ts->is_palm = 0; +#endif + +#if defined(CONFIG_PM) + raydium_ts_suspend(&g_raydium_ts->client->dev); +#endif + g_raydium_ts->fb_state = FB_OFF; + LOGD(LOG_INFO, "%s: Suspend notified!\n", __func__); + } else { + LOGD(LOG_INFO, "%s: suspend: event = %d not care\n", + __func__, notification->notif_type); + } + } else { + LOGD(LOG_INFO, "%s: Undefined notif type (%d) do not need process\n", + __func__, notification->notif_type); + } + return ; +} + +/******************************************************************************* + * FUNCTION: raydium_setup_drm_notifier + * + * SUMMARY: Set up call back function into drm notifier. + * + * PARAMETERS: + * g_raydium_ts - pointer to core data + *******************************************************************************/ +static void raydium_setup_panel_notifier(struct raydium_ts_data *g_raydium_ts) +{ + void *cookie = NULL; + if (!active_panel) + LOGD(LOG_ERR, "[touch]%s: Active panel not registered!\n", __func__); + + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, + active_panel,&panel_event_notifier_callback, g_raydium_ts); +} + +#elif defined(CONFIG_DRM) +/******************************************************************************* + * FUNCTION: drm_notifier_callback + * + * SUMMARY: Call back function for DRM notifier to allow to call + * resume/suspend attention list. + * + * RETURN: + * 0 = success + * + * PARAMETERS: + * self - pointer to notifier_block structure + * event - event type of fb notifier + * data - pointer to fb_event structure + ******************************************************************************/ +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct raydium_ts_data *g_raydium_ts = + container_of(self, struct raydium_ts_data, fb_notif); + struct drm_panel_notifier *evdata = data; + int *blank; + + LOGD(LOG_INFO, "%s: DRM notifier called!\n", __func__); + + if (!evdata) + goto exit; + + if (!(event == DRM_PANEL_EARLY_EVENT_BLANK || + event == DRM_PANEL_EVENT_BLANK)) { + LOGD(LOG_INFO, "%s: Event(%lu) do not need process\n", + __func__, event); + goto exit; + } + + blank = evdata->data; + g_raydium_ts->blank = (*blank); + LOGD(LOG_INFO, "%s: DRM event:%lu,blank:%d fb_state %d ", + __func__, event, *blank, g_raydium_ts->fb_state); + LOGD(LOG_INFO, "%s: DRM Power - %s - FB state %d ", + __func__, (*blank == DRM_PANEL_BLANK_UNBLANK)?"UP":"DOWN", g_raydium_ts->fb_state); + + if (*blank == DRM_PANEL_BLANK_UNBLANK) { + LOGD(LOG_INFO, "%s: UNBLANK!\n", __func__); + + if (event == DRM_PANEL_EARLY_EVENT_BLANK) { + LOGD(LOG_INFO, "%s: resume: event = %lu, not care\n", + __func__, event); + } else if (event == DRM_PANEL_EVENT_BLANK) { if (g_raydium_ts->fb_state != FB_ON) { LOGD(LOG_INFO, "%s: Resume notifier called!\n", __func__); @@ -1617,10 +1735,10 @@ static void drm_notifier_callback(enum panel_event_notifier_tag tag, LOGD(LOG_INFO, "%s: Resume notified!\n", __func__); } } - } else if (notification->notif_type == DRM_PANEL_EVENT_BLANK_LP || - notification->notif_type == DRM_PANEL_EVENT_FPS_CHANGE) { + } else if (*blank == DRM_PANEL_BLANK_LP || *blank == DRM_PANEL_BLANK_POWERDOWN + || *blank == DRM_PANEL_BLANK_FPS_CHANGE) { LOGD(LOG_INFO, "%s: LOWPOWER!\n", __func__); - if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { + if (event == DRM_PANEL_EARLY_EVENT_BLANK) { if (g_raydium_ts->fb_state != FB_OFF) { LOGD(LOG_INFO, "%s: Suspend notifier called!\n", __func__); @@ -1635,16 +1753,16 @@ static void drm_notifier_callback(enum panel_event_notifier_tag tag, g_raydium_ts->fb_state = FB_OFF; LOGD(LOG_INFO, "%s: Suspend notified!\n", __func__); } - } else if (notification->notif_type == DRM_PANEL_EVENT_BLANK) { - LOGD(LOG_INFO, "%s: suspend: event = %d not care\n", - __func__, notification->notif_type); + } else if (event == DRM_PANEL_EVENT_BLANK) { + LOGD(LOG_INFO, "%s: suspend: event = %lu, not care\n", + __func__, event); } } else { LOGD(LOG_INFO, "%s: DRM BLANK(%d) do not need process\n", - __func__, notification->notif_type); + __func__, *blank); } exit: - return ; + return 0; } /******************************************************************************* @@ -1657,13 +1775,16 @@ exit: *******************************************************************************/ static void raydium_setup_drm_notifier(struct raydium_ts_data *g_raydium_ts) { - void *cookie = NULL; - if (!active_panel) - LOGD(LOG_ERR, "[touch]%s: Active panel not registered!\n", __func__); + g_raydium_ts->fb_state = FB_ON; + g_raydium_ts->fb_notif.notifier_call = drm_notifier_callback; + LOGD(LOG_INFO, "[touch]%s: Setting up drm notifier\n", __func__); - cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, - PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, - active_panel,&drm_notifier_callback, g_raydium_ts); + if (!active_panel) + LOGD(LOG_ERR, "[touch]%s: Active panel not registered!\n", __func__); + + if (active_panel && drm_panel_notifier_register(active_panel, + &g_raydium_ts->fb_notif) < 0) + LOGD(LOG_ERR, "[touch]%s: Register notifier failed!\n", __func__); } #endif /*end of CONFIG_DRM*/ @@ -2240,7 +2361,7 @@ static int raydium_ts_probe(struct i2c_client *client, ret = -EPROBE_DEFER; goto exit_check_i2c; } -#ifdef CONFIG_DRM +#if defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER) /* Setup active dsi panel */ active_panel = pdata->active_panel; #endif @@ -2291,8 +2412,10 @@ static int raydium_ts_probe(struct i2c_client *client, LOGD(LOG_DEBUG, "[touch]pdata irq : %d\n", g_raydium_ts->irq_gpio); LOGD(LOG_DEBUG, "[touch]client irq : %d, pdata flags : %d\n", client->irq, pdata->irqflags); - -#if defined(CONFIG_DRM) +#if defined(CONFIG_PANEL_NOTIFIER) + LOGD(LOG_DEBUG, "%s: Probe: Setup panel event notifier\n", __func__); + raydium_setup_panel_notifier(g_raydium_ts); +#elif defined(CONFIG_DRM) LOGD(LOG_DEBUG, "%s: Probe: Setup drm notifier\n", __func__); raydium_setup_drm_notifier(g_raydium_ts); #endif/*end of CONFIG_DRM*/ @@ -2379,9 +2502,12 @@ void raydium_ts_shutdown(struct i2c_client *client) raydium_unregister_notifier(); #elif defined(CONFIG_HAS_EARLYSUSPEND) unregister_early_suspend(&g_raydium_ts->early_suspend); -#elif defined(CONFIG_DRM) +#elif defined(CONFIG_PANEL_NOTIFIER) if (active_panel) panel_event_notifier_unregister(&g_raydium_ts->fb_notif); +#elif defined(CONFIG_DRM) +if (active_panel) + drm_panel_notifier_unregister(active_panel, &g_raydium_ts->fb_notif); #endif/*end of CONFIG_FB*/ input_unregister_device(g_raydium_ts->input_dev); input_free_device(g_raydium_ts->input_dev); @@ -2415,9 +2541,12 @@ static int raydium_ts_remove(struct i2c_client *client) raydium_unregister_notifier(); #elif defined(CONFIG_HAS_EARLYSUSPEND) unregister_early_suspend(&g_raydium_ts->early_suspend); +#elif defined(CONFIG_PANEL_NOTIFIER) +if (active_panel) + panel_event_notifier_unregister(&g_raydium_ts->fb_notif); #elif defined(CONFIG_DRM) if (active_panel) - panel_event_notifier_unregister(&g_raydium_ts->fb_notif); + drm_panel_notifier_unregister(active_panel, &g_raydium_ts->fb_notif); #endif/*end of CONFIG_FB*/ input_unregister_device(g_raydium_ts->input_dev); input_free_device(g_raydium_ts->input_dev); diff --git a/raydium/raydium_driver.h b/raydium/raydium_driver.h index adc3d78616..f3428a3924 100644 --- a/raydium/raydium_driver.h +++ b/raydium/raydium_driver.h @@ -262,12 +262,16 @@ #define CONFIG_DRM #endif +#if IS_ENABLED(CONFIG_QCOM_PANEL_EVENT_NOTIFIER) +#define CONFIG_PANEL_NOTIFIER +#endif + #include #include #include #ifdef CONFIG_HAS_EARLYSUSPEND #include -#elif defined(CONFIG_DRM) +#elif defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER) #include #endif @@ -303,7 +307,7 @@ struct raydium_ts_data { bool irq_enabled; bool irq_wake; -#if defined(CONFIG_FB) || defined(CONFIG_DRM) +#if defined(CONFIG_FB) || defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER) struct notifier_block fb_notif; int blank; enum raydium_fb_state fb_state; From d3292bde416fe79662c9a7fc075788c0646613b8 Mon Sep 17 00:00:00 2001 From: Vara Reddy Date: Wed, 4 Jan 2023 15:10:56 -0800 Subject: [PATCH 107/170] touch: remove raydium touch driver for Pineapple target Change removes compilation of raydium touch driver which is not needed for Pineapple. Change-Id: Ie3088ab8426b855f20c265cde41f458a4105aabe Signed-off-by: Vara Reddy --- Android.mk | 22 +++++++++++----------- Kbuild | 54 +++++++++++++++++++++++++++--------------------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/Android.mk b/Android.mk index a815b145ba..74b952ec70 100644 --- a/Android.mk +++ b/Android.mk @@ -66,17 +66,6 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### - ########################################################### - include $(CLEAR_VARS) - LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) - LOCAL_MODULE := raydium_ts.ko - LOCAL_MODULE_KBUILD_NAME := raydium_ts.ko - LOCAL_MODULE_TAGS := optional - #LOCAL_MODULE_DEBUG_ENABLE := true - LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) - include $(DLKM_DIR)/Build_external_kernelmodule.mk - ########################################################### - ########################################################### include $(CLEAR_VARS) LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) @@ -121,6 +110,17 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := raydium_ts.ko + LOCAL_MODULE_KBUILD_NAME := raydium_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### endif # pineapple endif # DLKM check endif diff --git a/Kbuild b/Kbuild index 699437be8b..47afe05c5a 100644 --- a/Kbuild +++ b/Kbuild @@ -136,33 +136,6 @@ ifeq ($(CONFIG_TOUCHSCREEN_DUMMY), y) obj-$(CONFIG_MSM_TOUCH) += dummy_ts.o endif -ifeq ($(CONFIG_TOUCHSCREEN_RM_TS), y) - LINUX_INC += -include $(TOUCH_ROOT)/raydium/Config.h - LINUX_INC += -include $(TOUCH_ROOT)/raydium/drv_interface.h - LINUX_INC += -include $(TOUCH_ROOT)/raydium/rad_fw_image_30.h - LINUX_INC += -include $(TOUCH_ROOT)/raydium/raydium_driver.h - LINUX_INC += -include $(TOUCH_ROOT)/raydium/raydium_selftest.h - LINUX_INC += -include $(TOUCH_ROOT)/raydium/tpselftest_30.h - LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/f303_ic_control.h - LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/f303_ic_reg.h - LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/f303_ic_test.h - LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/ic_drv_global.h - LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/ic_drv_interface.h - - raydium_ts-y := \ - ./raydium/drv_interface.o \ - ./raydium/raydium_driver.o \ - ./raydium/raydium_fw_update.o \ - ./raydium/raydium_selftest.o \ - ./raydium/raydium_sysfs.o \ - ./raydium/chip_raydium/f303_ic_control.o \ - ./raydium/chip_raydium/f303_ic_test.o \ - ./raydium/chip_raydium/ic_drv_global.o \ - ./raydium/chip_raydium/ic_drv_interface.o - - obj-$(CONFIG_MSM_TOUCH) += raydium_ts.o -endif - ifeq ($(CONFIG_TOUCHSCREEN_SYNAPTICS_TCM), y) synaptics_tcm_ts-y := \ ./synaptics_tcm/synaptics_tcm_core.o \ @@ -208,6 +181,33 @@ ifneq ($(CONFIG_ARCH_PINEAPPLE), y) obj-$(CONFIG_MSM_TOUCH) += pt_device_access.o endif + + ifeq ($(CONFIG_TOUCHSCREEN_RM_TS), y) + LINUX_INC += -include $(TOUCH_ROOT)/raydium/Config.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/drv_interface.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/rad_fw_image_30.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/raydium_driver.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/raydium_selftest.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/tpselftest_30.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/f303_ic_control.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/f303_ic_reg.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/f303_ic_test.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/ic_drv_global.h + LINUX_INC += -include $(TOUCH_ROOT)/raydium/chip_raydium/ic_drv_interface.h + + raydium_ts-y := \ + ./raydium/drv_interface.o \ + ./raydium/raydium_driver.o \ + ./raydium/raydium_fw_update.o \ + ./raydium/raydium_selftest.o \ + ./raydium/raydium_sysfs.o \ + ./raydium/chip_raydium/f303_ic_control.o \ + ./raydium/chip_raydium/f303_ic_test.o \ + ./raydium/chip_raydium/ic_drv_global.o \ + ./raydium/chip_raydium/ic_drv_interface.o + + obj-$(CONFIG_MSM_TOUCH) += raydium_ts.o + endif endif # pineapple CDEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\" From 4ca379016d886f33a1a22ca84e75e5521726803e Mon Sep 17 00:00:00 2001 From: Raviteja Tamatam Date: Mon, 30 Jan 2023 13:19:06 -0800 Subject: [PATCH 108/170] touch: goodix: fix qts sequence in secure vm case qts register is skipped when QTI_VM is defined. Fix the sequence to allow qts register in secure vm cases. Change-Id: Id9476de998d9598bab125795ad918a066ad7b15d Signed-off-by: Raviteja Tamatam --- goodix_berlin_driver/goodix_ts_core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/goodix_berlin_driver/goodix_ts_core.c b/goodix_berlin_driver/goodix_ts_core.c index a3ffc0508d..5783f0b4f9 100644 --- a/goodix_berlin_driver/goodix_ts_core.c +++ b/goodix_berlin_driver/goodix_ts_core.c @@ -1,7 +1,7 @@ /* * Goodix Touchscreen Driver * Copyright (C) 2020 - 2021 Goodix, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. 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 @@ -2563,6 +2563,10 @@ static int goodix_ts_probe(struct platform_device *pdev) goto err_out; } +#ifdef CONFIG_ARCH_QTI_VM +skip_to_power_gpio_setup: +#endif + qts_en = of_property_read_bool(node, "goodix,qts_en"); if (qts_en) { mutex_init(&core_data->tui_transition_lock); @@ -2576,10 +2580,6 @@ static int goodix_ts_probe(struct platform_device *pdev) core_data->qts_en = qts_en; } -#ifdef CONFIG_ARCH_QTI_VM -skip_to_power_gpio_setup: -#endif - /* generic notifier callback */ core_data->ts_notifier.notifier_call = goodix_generic_noti_callback; goodix_ts_register_notifier(&core_data->ts_notifier); From 2521a158df6253ace108d88cdb8cef71a234376f Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Fri, 10 Feb 2023 11:33:29 +0530 Subject: [PATCH 109/170] touch: raydium: Unregister of Panel_notifier Implementation of Panel notifier callbacks and unregistration of callback during touch probing. Change-Id: I1706d01b99302125e37dd04c94597cf37e004cd0 Signed-off-by: Surya Teja Kudiri --- raydium/raydium_driver.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index c40489bfc6..d20bacb007 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -1765,6 +1765,12 @@ exit: return 0; } +static void raydium_setup_drm_unregister_notifier(void) +{ + if (active_panel && drm_panel_notifier_unregister(active_panel, + &g_raydium_ts->fb_notif) < 0) + LOGD(LOG_ERR, "[touch]%s: DRM UnRegister notifier failed!\n", __func__); +} /******************************************************************************* * FUNCTION: raydium_setup_drm_notifier * @@ -2452,7 +2458,12 @@ static int raydium_ts_probe(struct i2c_client *client, exit_irq_request_failed: #if defined(CONFIG_FB) raydium_unregister_notifier(); -#endif/*end of CONFIG_FB*/ +#elif defined(CONFIG_PANEL_NOTIFIER) + if (active_panel) + panel_event_notifier_unregister(&g_raydium_ts->fb_notif); +#elif defined(CONFIG_DRM) + raydium_setup_drm_unregister_notifier(); +#endif cancel_work_sync(&g_raydium_ts->work); input_unregister_device(input_dev); From a69bc36df5b7e5f16e2143b134b4b5e36189d597 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Fri, 10 Feb 2023 12:38:44 +0530 Subject: [PATCH 110/170] touch: raydium: Handling NULL reference Handing NULL reference of input_dev during probe failure. Change-Id: I1c41773d0fa4892940e6faae044c211d21095901 Signed-off-by: Surya Teja Kudiri --- raydium/raydium_driver.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index d20bacb007..c039aceb6b 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -2467,9 +2467,12 @@ exit_irq_request_failed: cancel_work_sync(&g_raydium_ts->work); input_unregister_device(input_dev); + g_raydium_ts->input_dev = NULL; exit_input_register_device_failed: - input_free_device(input_dev); + if (g_raydium_ts->input_dev) + input_free_device(input_dev); + g_raydium_ts->input_dev = NULL; exit_input_dev_alloc_failed: exit_check_i2c: From 0ebf44a4f92a14872a219e3130f7bbdec64d862e Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Fri, 10 Feb 2023 12:48:59 +0530 Subject: [PATCH 111/170] touch: raydium: Handle NULL reference Handling NULL reference of input_dev during shutdown failure. Change-Id: If303f4a4cb238272aa696617b57f9bbdd8959b55 Signed-off-by: Surya Teja Kudiri --- raydium/raydium_driver.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index c039aceb6b..04b5253eb5 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -2524,7 +2524,9 @@ if (active_panel) drm_panel_notifier_unregister(active_panel, &g_raydium_ts->fb_notif); #endif/*end of CONFIG_FB*/ input_unregister_device(g_raydium_ts->input_dev); - input_free_device(g_raydium_ts->input_dev); + if (g_raydium_ts->input_dev) + input_free_device(g_raydium_ts->input_dev); + g_raydium_ts->input_dev = NULL; gpio_free(g_raydium_ts->rst_gpio); #ifdef CONFIG_RM_SYSFS_DEBUG From 1966defdcfcb91eb6e8335b8030419590e3007eb Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Fri, 10 Feb 2023 13:50:51 +0530 Subject: [PATCH 112/170] touch: raydium: NULL pointer panic from TOUCH driver During Reboot,Cancel the work and destroy_workqueue and deallocate the memory for g_raydium_ts in shutdown and remove callbacks. Change-Id: I8ce1fa9814e3d85fde33d3241cec2f00755b9f06 Signed-off-by: Surya Teja Kudiri --- raydium/raydium_driver.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 04b5253eb5..a7320e6b52 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -2512,6 +2512,10 @@ exit_check_functionality_failed: void raydium_ts_shutdown(struct i2c_client *client) { + if (g_raydium_ts->workqueue) { + destroy_workqueue(g_raydium_ts->workqueue); + g_raydium_ts->workqueue = NULL; + } #if defined(CONFIG_FB) raydium_unregister_notifier(); #elif defined(CONFIG_HAS_EARLYSUSPEND) @@ -2541,18 +2545,22 @@ if (active_panel) if (gpio_is_valid(g_raydium_ts->irq_gpio)) gpio_free(g_raydium_ts->irq_gpio); - cancel_work_sync(&g_raydium_ts->work); - destroy_workqueue(g_raydium_ts->workqueue); - raydium_enable_regulator(g_raydium_ts, false); raydium_get_regulator(g_raydium_ts, false); + kfree(g_raydium_ts); + i2c_set_clientdata(client, NULL); } static int raydium_ts_remove(struct i2c_client *client) { + if (g_raydium_ts->workqueue) { + destroy_workqueue(g_raydium_ts->workqueue); + g_raydium_ts->workqueue = NULL; + } + #if defined(CONFIG_FB) raydium_unregister_notifier(); #elif defined(CONFIG_HAS_EARLYSUSPEND) @@ -2580,12 +2588,11 @@ if (active_panel) if (gpio_is_valid(g_raydium_ts->irq_gpio)) gpio_free(g_raydium_ts->irq_gpio); - cancel_work_sync(&g_raydium_ts->work); - destroy_workqueue(g_raydium_ts->workqueue); - raydium_enable_regulator(g_raydium_ts, false); raydium_get_regulator(g_raydium_ts, false); + kfree(g_raydium_ts); + i2c_set_clientdata(client, NULL); return 0; } From 552e89606c699c1e9e801ed5844aef550c6d5c57 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Fri, 10 Feb 2023 14:00:03 +0530 Subject: [PATCH 113/170] touch: raydium: NULL pointer from TOUCH driver During reboot,disable irq in shutdown and remove callbacks. Change-Id: Ifb2af617ae0ddffd9598c50320930319c6280e9b Signed-off-by: Surya Teja Kudiri --- raydium/raydium_driver.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index a7320e6b52..3ac39b2458 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -2537,6 +2537,7 @@ if (active_panel) raydium_release_sysfs(client); #endif /*end of CONFIG_RM_SYSFS_DEBUG*/ + disable_irq(g_raydium_ts->irq); free_irq(client->irq, g_raydium_ts); if (gpio_is_valid(g_raydium_ts->rst_gpio)) @@ -2580,6 +2581,7 @@ if (active_panel) raydium_release_sysfs(client); #endif /*end of CONFIG_RM_SYSFS_DEBUG*/ + disable_irq(g_raydium_ts->irq); free_irq(client->irq, g_raydium_ts); if (gpio_is_valid(g_raydium_ts->rst_gpio)) From d4a3f6dc49a70fd0a8f18743f786e61901907d79 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Thu, 9 Feb 2023 15:33:50 +0530 Subject: [PATCH 114/170] touch: focal_tech touch driver upgrade upgrading focal tech touch driver for kernel 5.15 on kona target. Change-Id: I21a1216e617d470f64f087f6c5b2b5ac619b2932 Signed-off-by: Surya Teja Kudiri --- Android.mk | 225 +++++++++++++++++-------------- Kbuild | 21 +-- config/gki_konatouch.conf | 4 + config/gki_konatouchconf.h | 7 + focaltech_touch/focaltech_core.c | 11 +- focaltech_touch/focaltech_core.h | 2 + touch_driver_board.mk | 47 ++++--- touch_driver_product.mk | 39 +++--- 8 files changed, 201 insertions(+), 155 deletions(-) create mode 100644 config/gki_konatouch.conf create mode 100644 config/gki_konatouchconf.h diff --git a/Android.mk b/Android.mk index 74b952ec70..3a481c71a1 100644 --- a/Android.mk +++ b/Android.mk @@ -2,125 +2,144 @@ TOUCH_DLKM_ENABLE := true ifeq ($(TARGET_KERNEL_DLKM_DISABLE), true) - ifeq ($(TARGET_KERNEL_DLKM_TOUCH_OVERRIDE), false) - TOUCH_DLKM_ENABLE := false - endif + ifeq ($(TARGET_KERNEL_DLKM_TOUCH_OVERRIDE), false) + TOUCH_DLKM_ENABLE := false + endif endif ifeq ($(TOUCH_DLKM_ENABLE), true) - TOUCH_SELECT := CONFIG_MSM_TOUCH=m + TOUCH_SELECT := CONFIG_MSM_TOUCH=m - LOCAL_PATH := $(call my-dir) - include $(CLEAR_VARS) + LOCAL_PATH := $(call my-dir) + include $(CLEAR_VARS) - # This makefile is only for DLKM - ifneq ($(findstring vendor,$(LOCAL_PATH)),) + # This makefile is only for DLKM + ifneq ($(findstring vendor,$(LOCAL_PATH)),) - ifneq ($(findstring opensource,$(LOCAL_PATH)),) - TOUCH_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/touch-drivers - endif # opensource + ifneq ($(findstring opensource,$(LOCAL_PATH)),) + TOUCH_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/touch-drivers + endif # opensource - DLKM_DIR := $(TOP)/device/qcom/common/dlkm + DLKM_DIR := $(TOP)/device/qcom/common/dlkm - LOCAL_ADDITIONAL_DEPENDENCIES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_ADDITIONAL_DEPENDENCIES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) - # Build - ########################################################### - # This is set once per LOCAL_PATH, not per (kernel) module - KBUILD_OPTIONS := TOUCH_ROOT=$(TOUCH_BLD_DIR) + # Build + ########################################################### + # This is set once per LOCAL_PATH, not per (kernel) module + KBUILD_OPTIONS := TOUCH_ROOT=$(TOUCH_BLD_DIR) - KBUILD_OPTIONS += MODNAME=touch_dlkm - KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) - KBUILD_OPTIONS += $(TOUCH_SELECT) + KBUILD_OPTIONS += MODNAME=touch_dlkm + KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM) + KBUILD_OPTIONS += $(TOUCH_SELECT) - ########################################################### - include $(CLEAR_VARS) - LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) - LOCAL_MODULE := nt36xxx-i2c.ko - LOCAL_MODULE_KBUILD_NAME := nt36xxx-i2c.ko - LOCAL_MODULE_TAGS := optional - #LOCAL_MODULE_DEBUG_ENABLE := true - LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) - include $(DLKM_DIR)/Build_external_kernelmodule.mk - ########################################################### + ########################################################### - ########################################################### - include $(CLEAR_VARS) - LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) - LOCAL_MODULE := goodix_ts.ko - LOCAL_MODULE_KBUILD_NAME := goodix_ts.ko - LOCAL_MODULE_TAGS := optional - #LOCAL_MODULE_DEBUG_ENABLE := true - LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) - include $(DLKM_DIR)/Build_external_kernelmodule.mk - ########################################################### +ifeq ($(TARGET_BOARD_PLATFORM), monaco) - ########################################################### - include $(CLEAR_VARS) - LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) - LOCAL_MODULE := atmel_mxt_ts.ko - LOCAL_MODULE_KBUILD_NAME := atmel_mxt_ts.ko - LOCAL_MODULE_TAGS := optional - #LOCAL_MODULE_DEBUG_ENABLE := true - LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) - include $(DLKM_DIR)/Build_external_kernelmodule.mk - ########################################################### + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := pt_ts.ko + LOCAL_MODULE_KBUILD_NAME := pt_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### - ########################################################### - include $(CLEAR_VARS) - LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) - LOCAL_MODULE := synaptics_tcm_ts.ko - LOCAL_MODULE_KBUILD_NAME := synaptics_tcm_ts.ko - LOCAL_MODULE_TAGS := optional - #LOCAL_MODULE_DEBUG_ENABLE := true - LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) - include $(DLKM_DIR)/Build_external_kernelmodule.mk - ########################################################### + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := pt_i2c.ko + LOCAL_MODULE_KBUILD_NAME := pt_i2c.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### - ifneq ($(TARGET_BOARD_PLATFORM), pineapple) - ########################################################### - include $(CLEAR_VARS) - LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) - LOCAL_MODULE := pt_ts.ko - LOCAL_MODULE_KBUILD_NAME := pt_ts.ko - LOCAL_MODULE_TAGS := optional - #LOCAL_MODULE_DEBUG_ENABLE := true - LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) - include $(DLKM_DIR)/Build_external_kernelmodule.mk - ########################################################### + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := pt_device_access.ko + LOCAL_MODULE_KBUILD_NAME := pt_device_access.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### - ########################################################### - include $(CLEAR_VARS) - LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) - LOCAL_MODULE := pt_i2c.ko - LOCAL_MODULE_KBUILD_NAME := pt_i2c.ko - LOCAL_MODULE_TAGS := optional - #LOCAL_MODULE_DEBUG_ENABLE := true - LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) - include $(DLKM_DIR)/Build_external_kernelmodule.mk - ########################################################### + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := raydium_ts.ko + LOCAL_MODULE_KBUILD_NAME := raydium_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### - ########################################################### - include $(CLEAR_VARS) - LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) - LOCAL_MODULE := pt_device_access.ko - LOCAL_MODULE_KBUILD_NAME := pt_device_access.ko - LOCAL_MODULE_TAGS := optional - #LOCAL_MODULE_DEBUG_ENABLE := true - LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) - include $(DLKM_DIR)/Build_external_kernelmodule.mk - ########################################################### +else ifeq ($(TARGET_BOARD_PLATFORM), kona) - ########################################################### - include $(CLEAR_VARS) - LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) - LOCAL_MODULE := raydium_ts.ko - LOCAL_MODULE_KBUILD_NAME := raydium_ts.ko - LOCAL_MODULE_TAGS := optional - #LOCAL_MODULE_DEBUG_ENABLE := true - LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) - include $(DLKM_DIR)/Build_external_kernelmodule.mk - ########################################################### - endif # pineapple - endif # DLKM check + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := focaltech_fts.ko + LOCAL_MODULE_KBUILD_NAME := focaltech_fts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + +else + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := nt36xxx-i2c.ko + LOCAL_MODULE_KBUILD_NAME := nt36xxx-i2c.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := goodix_ts.ko + LOCAL_MODULE_KBUILD_NAME := goodix_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := atmel_mxt_ts.ko + LOCAL_MODULE_KBUILD_NAME := atmel_mxt_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := synaptics_tcm_ts.ko + LOCAL_MODULE_KBUILD_NAME := synaptics_tcm_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + +endif #kona + endif # DLKM check endif diff --git a/Kbuild b/Kbuild index 47afe05c5a..39d5e26825 100644 --- a/Kbuild +++ b/Kbuild @@ -6,25 +6,30 @@ ifeq ($(CONFIG_ARCH_WAIPIO), y) LINUX_INC += -include $(TOUCH_ROOT)/config/gki_waipiotouchconf.h endif -#ifeq ($(CONFIG_ARCH_KALAMA), y) +ifeq ($(CONFIG_ARCH_KALAMA), y) include $(TOUCH_ROOT)/config/gki_kalamatouch.conf LINUX_INC += -include $(TOUCH_ROOT)/config/gki_kalamatouchconf.h -#endif +endif -#ifeq ($(CONFIG_ARCH_KHAJE), y) +ifeq ($(CONFIG_ARCH_KHAJE), y) include $(TOUCH_ROOT)/config/gki_khajetouch.conf LINUX_INC += -include $(TOUCH_ROOT)/config/gki_khajetouchconf.h -#endif +endif -#ifeq ($(CONFIG_ARCH_PINEAPPLE), y) +ifeq ($(CONFIG_ARCH_PINEAPPLE), y) include $(TOUCH_ROOT)/config/gki_pineappletouch.conf LINUX_INC += -include $(TOUCH_ROOT)/config/gki_pineappletouchconf.h -#endif +endif -#ifeq ($(CONFIG_ARCH_MONACO), y) +ifeq ($(CONFIG_ARCH_MONACO), y) include $(TOUCH_ROOT)/config/gki_monacotouch.conf LINUX_INC += -include $(TOUCH_ROOT)/config/gki_monacotouchconf.h -#endif +endif + +ifeq ($(CONFIG_ARCH_KONA), y) + include $(TOUCH_ROOT)/config/gki_konatouch.conf + LINUX_INC += -include $(TOUCH_ROOT)/config/gki_konatouchconf.h +endif LINUX_INC += -Iinclude/linux \ -Iinclude/linux/drm \ diff --git a/config/gki_konatouch.conf b/config/gki_konatouch.conf new file mode 100644 index 0000000000..1921d5823e --- /dev/null +++ b/config/gki_konatouch.conf @@ -0,0 +1,4 @@ +export CONFIG_MSM_TOUCH=m +export CONFIG_TOUCH_FOCALTECH=y +export CONFIG_TOUCHSCREEN_FTS_DIRECTORY="focaltech_touch" +export CONFIG_FTS_TRUSTED_TOUCH=n diff --git a/config/gki_konatouchconf.h b/config/gki_konatouchconf.h new file mode 100644 index 0000000000..954901a9fa --- /dev/null +++ b/config/gki_konatouchconf.h @@ -0,0 +1,7 @@ +/* +* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. +* SPDX-License-Identifier: GPL-2.0-only +*/ + +#define CONFIG_TOUCH_FOCALTECH 1 +#define CONFIG_TOUCHSCREEN_FTS_DIRECTORY "focaltech_touch" diff --git a/focaltech_touch/focaltech_core.c b/focaltech_touch/focaltech_core.c index 9abd6cecaf..40b3c04659 100644 --- a/focaltech_touch/focaltech_core.c +++ b/focaltech_touch/focaltech_core.c @@ -998,15 +998,14 @@ static int fts_ts_vm_mem_lend(struct fts_ts_data *fts_data) acl_desc = fts_ts_vm_get_acl(GH_TRUSTED_VM); if (IS_ERR(acl_desc)) { pr_err("Failed to get acl of IO memories for Trusted touch\n"); - PTR_ERR(acl_desc); - return -EINVAL; + rc = PTR_ERR(acl_desc); + return rc; } sgl_desc = fts_ts_vm_get_sgl(fts_data->vm_info); if (IS_ERR(sgl_desc)) { pr_err("Failed to get sgl of IO memories for Trusted touch\n"); - PTR_ERR(sgl_desc); - rc = -EINVAL; + rc = PTR_ERR(sgl_desc); goto sgl_error; } @@ -2866,7 +2865,11 @@ static int fts_ts_probe_entry(struct fts_ts_data *ts_data) if (ts_data->ts_workqueue) INIT_WORK(&ts_data->resume_work, fts_resume_work); +#ifdef CONFIG_FTS_TRUSTED_TOUCH if (!strcmp(fts_data->touch_environment, "pvm")) +#else + if (active_panel) +#endif fts_ts_register_for_panel_events(ts_data->dev->of_node, ts_data); #elif defined(CONFIG_FB) if (ts_data->ts_workqueue) { diff --git a/focaltech_touch/focaltech_core.h b/focaltech_touch/focaltech_core.h index 9be61d55fd..c63d4cff9b 100644 --- a/focaltech_touch/focaltech_core.h +++ b/focaltech_touch/focaltech_core.h @@ -61,8 +61,10 @@ #include #include #include +#if defined(CONFIG_FTS_TRUSTED_TOUCH) #include #include +#endif #include "focaltech_common.h" /***************************************************************************** diff --git a/touch_driver_board.mk b/touch_driver_board.mk index 26712deb38..852952db5b 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -1,28 +1,31 @@ TOUCH_DLKM_ENABLE := true ifeq ($(TARGET_KERNEL_DLKM_DISABLE), true) - ifeq ($(TARGET_KERNEL_DLKM_TOUCH_OVERRIDE), false) - TOUCH_DLKM_ENABLE := false - endif + ifeq ($(TARGET_KERNEL_DLKM_TOUCH_OVERRIDE), false) + TOUCH_DLKM_ENABLE := false + endif endif ifeq ($(TOUCH_DLKM_ENABLE), true) - ifneq ($(TARGET_BOARD_AUTO),true) - ifeq ($(call is-board-platform-in-list,$(TARGET_BOARD_PLATFORM)),true) - ifeq ($(TARGET_BOARD_PLATFORM), pineapple) - BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ - $(KERNEL_MODULES_OUT)/goodix_ts.ko \ - $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ - $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko - else # pineapple - BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ - $(KERNEL_MODULES_OUT)/goodix_ts.ko \ - $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ - $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ - $(KERNEL_MODULES_OUT)/pt_ts.ko \ - $(KERNEL_MODULES_OUT)/pt_i2c.ko \ - $(KERNEL_MODULES_OUT)/pt_device_access.ko \ - $(KERNEL_MODULES_OUT)/raydium_ts.ko - endif # pineapple - endif - endif + ifneq ($(TARGET_BOARD_AUTO),true) + ifeq ($(call is-board-platform-in-list,$(TARGET_BOARD_PLATFORM)),true) + ifeq ($(TARGET_BOARD_PLATFORM), monaco) + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/pt_ts.ko \ + $(KERNEL_MODULES_OUT)/pt_i2c.ko \ + $(KERNEL_MODULES_OUT)/pt_device_access.ko \ + $(KERNEL_MODULES_OUT)/raydium_ts.ko + else ifeq ($(TARGET_BOARD_PLATFORM), kona) + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko + else ifeq ($(TARGET_BOARD_PLATFORM), pineapple) + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ + $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko + else + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ + $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko + endif + endif + endif endif diff --git a/touch_driver_product.mk b/touch_driver_product.mk index 6f163dce7c..903c3db0d0 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -1,24 +1,27 @@ TOUCH_DLKM_ENABLE := true ifeq ($(TARGET_KERNEL_DLKM_DISABLE), true) - ifeq ($(TARGET_KERNEL_DLKM_TOUCH_OVERRIDE), false) - TOUCH_DLKM_ENABLE := false - endif + ifeq ($(TARGET_KERNEL_DLKM_TOUCH_OVERRIDE), false) + TOUCH_DLKM_ENABLE := false + endif endif ifeq ($(TOUCH_DLKM_ENABLE), true) - ifeq ($(TARGET_BOARD_PLATFORM), pineapple) - PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ - $(KERNEL_MODULES_OUT)/goodix_ts.ko \ - $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ - $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko - else # pineapple - PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ - $(KERNEL_MODULES_OUT)/goodix_ts.ko \ - $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ - $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ - $(KERNEL_MODULES_OUT)/pt_ts.ko \ - $(KERNEL_MODULES_OUT)/pt_i2c.ko \ - $(KERNEL_MODULES_OUT)/pt_device_access.ko \ - $(KERNEL_MODULES_OUT)/raydium_ts.ko - endif # pineapple + ifeq ($(TARGET_BOARD_PLATFORM), monaco) + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/pt_ts.ko \ + $(KERNEL_MODULES_OUT)/pt_i2c.ko \ + $(KERNEL_MODULES_OUT)/pt_device_access.ko \ + $(KERNEL_MODULES_OUT)/raydium_ts.ko + else ifeq ($(TARGET_BOARD_PLATFORM), kona) + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko + else ifeq ($(TARGET_BOARD_PLATFORM), pineapple) + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ + $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko + else + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ + $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko + endif endif From 8b1e6c6e0ba96aea6fe871b75e2f710d9b7cd9ea Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Mon, 20 Feb 2023 11:23:15 +0530 Subject: [PATCH 115/170] touch: derefernce NULL pointer during shutdown dereference NULL pointers during shutdown and remove calls. Change-Id: Ifae6edbc87d12a3f77cff6d2fd4bb9e4d24591ae Signed-off-by: Surya Teja Kudiri --- raydium/raydium_driver.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 3ac39b2458..41fda2e286 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -1300,8 +1300,10 @@ static irqreturn_t raydium_ts_interrupt(int irq, void *dev_id) } else { if (!work_pending(&g_raydium_ts->work)) { /* Clear interrupts*/ - result = queue_work(g_raydium_ts->workqueue, - &g_raydium_ts->work); + if (g_raydium_ts->workqueue) { + result = queue_work(g_raydium_ts->workqueue, + &g_raydium_ts->work); + } if (!result) { /*queue_work fail*/ @@ -2512,6 +2514,9 @@ exit_check_functionality_failed: void raydium_ts_shutdown(struct i2c_client *client) { + LOGD(LOG_INFO, "[touch] %s: start\n", __func__); + + cancel_work_sync(&g_raydium_ts->work); if (g_raydium_ts->workqueue) { destroy_workqueue(g_raydium_ts->workqueue); g_raydium_ts->workqueue = NULL; @@ -2546,22 +2551,26 @@ if (active_panel) if (gpio_is_valid(g_raydium_ts->irq_gpio)) gpio_free(g_raydium_ts->irq_gpio); + raydium_enable_regulator(g_raydium_ts, false); raydium_get_regulator(g_raydium_ts, false); kfree(g_raydium_ts); i2c_set_clientdata(client, NULL); + LOGD(LOG_INFO, "[touch] %s: done\n", __func__); } static int raydium_ts_remove(struct i2c_client *client) { - if (g_raydium_ts->workqueue) { - destroy_workqueue(g_raydium_ts->workqueue); - g_raydium_ts->workqueue = NULL; - } + LOGD(LOG_INFO, "[touch] %s: start\n", __func__); + cancel_work_sync(&g_raydium_ts->work); + if (g_raydium_ts->workqueue) { + destroy_workqueue(g_raydium_ts->workqueue); + g_raydium_ts->workqueue = NULL; + } #if defined(CONFIG_FB) raydium_unregister_notifier(); #elif defined(CONFIG_HAS_EARLYSUSPEND) @@ -2590,12 +2599,14 @@ if (active_panel) if (gpio_is_valid(g_raydium_ts->irq_gpio)) gpio_free(g_raydium_ts->irq_gpio); + raydium_enable_regulator(g_raydium_ts, false); raydium_get_regulator(g_raydium_ts, false); kfree(g_raydium_ts); i2c_set_clientdata(client, NULL); + LOGD(LOG_INFO, "[touch] %s: done\n", __func__); return 0; } From 3d8efb7b4cc4911591d577537c1b93bc724eaab1 Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Mon, 13 Feb 2023 13:42:14 +0530 Subject: [PATCH 116/170] touch: pt: Touch_offload using debugfs Enablement of Debugfs manual touch_offload for PT touch sensor. Change-Id: I64ce6e92469fb24793375ed7044797b0b8875529 Signed-off-by: Srikanth Katteboina --- pt/pt_core.c | 316 ++++++++++++++++++++++++++++++++++++++++++++++++++- pt/pt_regs.h | 1 + 2 files changed, 312 insertions(+), 5 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index 03ac8fc970..db1dcb2f62 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -54,6 +54,9 @@ #define PT_STATUS_STR_LEN (50) +#define PT_DATA_SIZE (2 * 256) + + #if defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER) static struct drm_panel *active_panel; #endif @@ -62,12 +65,24 @@ MODULE_FIRMWARE(PT_FW_FILE_NAME); #define ENABLE_I2C_REG_ONLY +enum core_states { + STATE_NONE, + STATE_RESUME, + STATE_SUSPEND +}; #ifdef ENABLE_I2C_REG_ONLY static int pt_enable_i2c_regulator(struct pt_core_data *cd, bool en); #endif + +static int pt_device_exit(struct i2c_client *client); +int pt_device_entry(struct device *dev, + u16 irq, size_t xfer_buf_size); + static const char *pt_driver_core_name = PT_CORE_NAME; static const char *pt_driver_core_version = PT_DRIVER_VERSION; static const char *pt_driver_core_date = PT_DRIVER_DATE; +enum core_states pt_core_state = STATE_NONE; + struct pt_hid_field { int report_count; @@ -10777,6 +10792,12 @@ static int pt_core_suspend(struct device *dev) if (cd->drv_debug_suspend || (cd->cpdata->flags & PT_CORE_FLAG_SKIP_SYS_SLEEP)) return 0; + if (pt_core_state == STATE_SUSPEND) + { + pt_debug(cd->dev, DL_INFO, "%s Already in Suspend state\n", __func__); + return 0; + } + pt_debug(cd->dev, DL_INFO, "%s Suspend start\n", __func__); cancel_work_sync(&cd->resume_work); cancel_work_sync(&cd->suspend_work); @@ -10785,7 +10806,7 @@ static int pt_core_suspend(struct device *dev) cd->wait_until_wake = 0; mutex_unlock(&cd->system_lock); - if (pm_suspend_via_firmware()) { + if (pm_suspend_via_firmware() || cd->touch_offload) { rc = pt_core_suspend_(cd->dev); cd->quick_boot = true; } else { @@ -10851,7 +10872,6 @@ exit: if (rc) { dev_err(dev, "%s: Failed to wake up: rc=%d\n", __func__, rc); - pt_enable_regulator(cd, false); return -EAGAIN; } @@ -10929,7 +10949,7 @@ static void pt_resume_offload_work(struct work_struct *work) { int rc = 0; - int retry_count = 10; + int retry_count = 1000; struct pt_core_data *pt_data = container_of(work, struct pt_core_data, resume_offload_work); @@ -10942,6 +10962,11 @@ static void pt_resume_offload_work(struct work_struct *work) "%s: Error on wake\n", __func__); } while (retry_count && rc < 0); + if (rc < 0){ + pt_debug(pt_data->dev, DL_ERROR, "%s: Error on wake\n", __func__); + return; + } + #ifdef TOUCH_TO_WAKE_POWER_FEATURE_WORK_AROUND rc = pt_core_easywake_on(pt_data); if (rc < 0) { @@ -10981,8 +11006,8 @@ static int pt_core_resume(struct device *dev) return 0; - if (pm_suspend_via_firmware()) { - rc = pt_core_restore(cd->dev); + if (pm_suspend_via_firmware() || cd->touch_offload) { + rc = pt_core_restore(cd->dev); } else { pt_debug(cd->dev, DL_INFO, "%s start\n", __func__); rc = pt_enable_i2c_regulator(cd, true); @@ -15019,6 +15044,126 @@ static ssize_t pt_pip2_gpio_read_show(struct device *dev, rc); } +/******************************************************************************* + * FUNCTION: pt_device_exit + * + * SUMMARY: Remove functon for the I2C module + * + * PARAMETERS: + * *client - pointer to i2c client structure + ******************************************************************************/ +static int pt_device_exit(struct i2c_client *client) +{ + + struct pt_core_data *cd = i2c_get_clientdata(client); + struct device *dev = cd->dev; + + pt_debug(dev, DL_INFO,"%s: Start pt_device_exit\n", __func__); + + if (active_panel) + panel_event_notifier_unregister(&cd->fb_notifier); + pt_core_state = STATE_SUSPEND; + + pm_runtime_suspend(dev); + pm_runtime_disable(dev); + + pt_stop_wd_timer(cd); + call_atten_cb(cd, PT_ATTEN_CANCEL_LOADER, 0); + cancel_work_sync(&cd->ttdl_restart_work); + cancel_work_sync(&cd->enum_work); + cancel_work_sync(&cd->resume_offload_work); + cancel_work_sync(&cd->suspend_offload_work); + cancel_work_sync(&cd->resume_work); + cancel_work_sync(&cd->suspend_work); + + pt_stop_wd_timer(cd); + + device_init_wakeup(dev, 0); + + disable_irq_nosync(cd->irq); + if (cd->cpdata->setup_irq) + cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_FREE, dev); + + if (cd->cpdata->init) + cd->cpdata->init(cd->cpdata, PT_MT_POWER_OFF, dev); + + if (cd->cpdata->setup_power) + cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev); + + pt_debug(dev, DL_INFO,"%s: End pt_device_exit \n", __func__); + return 0; +} + +/******************************************************************************* + * FUNCTION: pt_touch_offload_store + * + * SUMMARY: The store method for the touch_offload sysfs node that allows the TTDL + * to be enabled/disabled. + * + * RETURN: Size of passed in buffer + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to buffer that hold the command parameters + * size - size of buf + ******************************************************************************/ +static ssize_t pt_touch_offload_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct i2c_client *client = to_i2c_client(dev); + u32 input_data[2]; + int length; + int rc = 0; + + /* Maximum input of one value */ + length = _pt_ic_parse_input(dev, buf, size, input_data, ARRAY_SIZE(input_data)); + if (length != 1) { + pt_debug(dev, DL_ERROR, "%s: Invalid number of arguments\n", __func__); + rc = -EINVAL; + goto exit; + } + + switch (input_data[0]) { + case 0: + pt_debug(dev, DL_ERROR, "%s: TTDL: Core Touch Offload OFF\n", __func__); + cd->touch_offload = true; + rc = pt_device_exit(client); + if (rc) + pt_debug(dev, DL_ERROR, "%s: Power off error detected rc=%d\n", + __func__, rc); + else { + cd->touch_offload = true; + pt_debug(dev, DL_ERROR, "%s: Debugfs PT DEVICE EXIT flag set:\n", + __func__); + } + break; + + case 1: + pt_debug(dev, DL_ERROR, "%s: TTDL: Core Touch Offload ON\n", __func__); + rc = pt_device_entry(&client->dev, client->irq, PT_DATA_SIZE); + if (rc) + pt_debug(dev, DL_ERROR, "%s: Power on error detected rc=%d\n", + __func__, rc); + else { + cd->touch_offload = false; + pt_debug(dev, DL_ERROR, "%s: Debugfs PT DEVICE ENTRY flag set:\n", + __func__); + } + break; + + default: + rc = -EINVAL; + pt_debug(dev, DL_ERROR, "%s: Invalid value\n", __func__); + } + +exit: + if (rc) + return rc; + return size; +} + /******************************************************************************* * FUNCTION: pt_pip2_version_show * @@ -17411,6 +17556,8 @@ static struct device_attribute attributes[] = { __ATTR(panel_id, 0444, pt_panel_id_show, NULL), __ATTR(get_param, 0644, pt_get_param_show, pt_get_param_store), + __ATTR(pt_touch_offload, 0644, + NULL, pt_touch_offload_store), #ifdef EASYWAKE_TSG6 __ATTR(easy_wakeup_gesture, 0644, pt_easy_wakeup_gesture_show, pt_easy_wakeup_gesture_store), @@ -17651,6 +17798,7 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, cd->sleep_state = SS_SLEEP_NONE; cd->quick_boot = false; cd->drv_debug_suspend = false; + cd->touch_offload = false; if (cd->cpdata->config_dut_generation == CONFIG_DUT_PIP2_CAPABLE) { cd->set_dut_generation = true; @@ -18025,6 +18173,7 @@ skip_enum: cd->core_probe_complete = 1; mutex_unlock(&cd->system_lock); + pt_core_state = STATE_RESUME; pt_debug(dev, DL_ERROR, "%s: TTDL Core Probe Completed Successfully\n", __func__); return 0; @@ -18069,6 +18218,163 @@ error_no_pdata: pr_err("%s failed.\n", __func__); return rc; } + +/******************************************************************************* + * FUNCTION: pt_device_entry + * + * SUMMARY: Wrapper function of pt_core_suspend_() to help avoid TP from being + * woke up or put to sleep based on Kernel power state even when the display + * is off based on the check of TTDL core platform flag. + * + * RETURN: + * 0 = success + * !0 = failure + * + * PARAMETERS: + * *dev - pointer to core device + ******************************************************************************/ +int pt_device_entry(struct device *dev, + u16 irq, size_t xfer_buf_size) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + struct pt_platform_data *pdata = dev_get_platdata(dev); + struct i2c_client *client = to_i2c_client(dev); + int rc = 0; + + pt_debug(dev, DL_INFO, "%s: Start pt_device_entry\n", __func__); + + cd->dev = dev; + cd->pdata = pdata; + cd->cpdata = pdata->core_pdata; + + if (!rc && cd->ts_pinctrl) { + /* + * Pinctrl handle is optional. If pinctrl handle is found + * let pins to be configured in active state. If not + * found continue further without error. + */ + rc = pinctrl_select_state(cd->ts_pinctrl, cd->pinctrl_state_active); + if (rc < 0) + dev_err(&client->dev, "failed to select pin to active state\n"); + } + + /* Set platform easywake value */ + cd->easy_wakeup_gesture = cd->cpdata->easy_wakeup_gesture; + + /* + * When the IRQ GPIO is not direclty accessible and no function is + * defined to get the IRQ status, the IRQ passed in must be assigned + * directly as the gpio_to_irq will not work. e.g. CHROMEOS + */ + + if (!cd->cpdata->irq_stat) { + cd->irq = irq; + pt_debug(cd->dev, DL_ERROR, "%s:No irq_stat, Set cd->irq = %d\n", __func__, cd->irq); + } + + /* Call platform init function before setting up the GPIO's */ + if (cd->cpdata->init) { + pt_debug(cd->dev, DL_INFO, "%s: Init HW\n", __func__); + rc = cd->cpdata->init(cd->cpdata, PT_MT_POWER_ON, cd->dev); + } else { + pt_debug(cd->dev, DL_ERROR, "%s: No HW INIT function\n", __func__); + rc = 0; + } + if (rc < 0) { + pt_debug(cd->dev, DL_ERROR, "%s: HW Init fail r=%d\n", __func__, rc); + } + + /* Power on any needed regulator(s) */ + if (cd->cpdata->setup_power) { + pt_debug(cd->dev, DL_INFO, "%s: Device power on!\n", __func__); + rc = cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_ON, cd->dev); + } else { + pt_debug(cd->dev, DL_ERROR, "%s: No setup power function\n", __func__); + rc = 0; + } + if (rc < 0) + pt_debug(cd->dev, DL_ERROR, "%s: Setup power on fail r=%d\n", __func__, rc); + + if (cd->cpdata->detect) { + pt_debug(cd->dev, DL_INFO, "%s: Detect HW\n", __func__); + rc = cd->cpdata->detect(cd->cpdata, cd->dev, pt_platform_detect_read); + if (!rc) { + cd->hw_detected = true; + pt_debug(cd->dev, DL_INFO, "%s: HW detected\n", __func__); + } else { + cd->hw_detected = false; + pt_debug(cd->dev, DL_INFO, "%s: No HW detected\n", __func__); + rc = -ENODEV; + goto pt_error_detect; + } + } else { + pt_debug(dev, DL_ERROR, "%s: PARADE No HW detect function pointer\n", __func__); + /* + * "hw_reset" is not needed in the "if" statement, + * because "hw_reset" is already included in "hw_detect" + * function. + */ + rc = pt_hw_hard_reset(cd); + if (rc) + pt_debug(cd->dev, DL_ERROR, "%s: FAILED to execute HARD reset\n", __func__); + } + + if (cd->cpdata->setup_irq) { + pt_debug(cd->dev, DL_INFO, "%s: setup IRQ\n", __func__); + rc = cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_REG, cd->dev); + if (rc) { + pt_debug(dev, DL_ERROR, "%s: Error, couldn't setup IRQ\n", __func__); + goto pt_error_setup_irq; + } + } else { + pt_debug(dev, DL_ERROR, "%s: IRQ function pointer not setup\n", __func__); + goto pt_error_setup_irq; + } + + rc = device_init_wakeup(dev, 1); + if (rc < 0) + pt_debug(dev, DL_ERROR, "%s: Error, device_init_wakeup rc:%d\n", __func__, rc); + + if (!enable_irq_wake(cd->irq)) { + cd->irq_wake = 1; + pt_debug(cd->dev, DL_WARN, "%s Device MAY wakeup\n", __func__); + } + + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + /* Without sleep DUT is not ready and will NAK the first write */ + msleep(150); + + pm_runtime_put_sync(dev); + +#if defined(CONFIG_PANEL_NOTIFIER) + /* Setup active dsi panel */ + active_panel = cd->cpdata->active_panel; + + + pt_debug(dev, DL_ERROR, "%s: Probe: Setup Panel Event notifier\n", __func__); + pt_setup_panel_event_notifier(cd); +#endif + + mutex_lock(&cd->system_lock); + cd->core_probe_complete = 1; + mutex_unlock(&cd->system_lock); + + pt_debug(dev, DL_INFO, "%s: ####TTDL Core Device Probe Completed Successfully\n", __func__); + pt_core_state = STATE_RESUME; + return 0; + +pt_error_setup_irq: + device_init_wakeup(dev, 0); +pt_error_detect: + if (cd->cpdata->init) + cd->cpdata->init(cd->cpdata, PT_MT_POWER_OFF, dev); + if (cd->cpdata->setup_power) + cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev); + return rc; +} EXPORT_SYMBOL_GPL(pt_probe); /******************************************************************************* diff --git a/pt/pt_regs.h b/pt/pt_regs.h index c807a6604f..2e7c18b4c5 100644 --- a/pt/pt_regs.h +++ b/pt/pt_regs.h @@ -1592,6 +1592,7 @@ struct pt_core_data { #endif bool quick_boot; bool drv_debug_suspend; + bool touch_offload; }; struct gd_sensor { From 62215928fd1d910b6dcf67a52f9d19cb05ae5046 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Fri, 24 Feb 2023 10:23:57 +0530 Subject: [PATCH 117/170] touch : Stop panel notifier callbacks stop panel notifier callbacks after probe failure. Change-Id: I2a00c6917e7671e7731e80d95530fe8bb41c9907 Signed-off-by: Surya Teja Kudiri --- raydium/raydium_driver.c | 8 ++++---- raydium/raydium_driver.h | 7 +++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 41fda2e286..25608698c3 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -1671,6 +1671,7 @@ static void raydium_setup_panel_notifier(struct raydium_ts_data *g_raydium_ts) cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, active_panel,&panel_event_notifier_callback, g_raydium_ts); + g_raydium_ts->entry = cookie; } #elif defined(CONFIG_DRM) @@ -2461,8 +2462,7 @@ exit_irq_request_failed: #if defined(CONFIG_FB) raydium_unregister_notifier(); #elif defined(CONFIG_PANEL_NOTIFIER) - if (active_panel) - panel_event_notifier_unregister(&g_raydium_ts->fb_notif); + panel_event_notifier_unregister(g_raydium_ts->entry); #elif defined(CONFIG_DRM) raydium_setup_drm_unregister_notifier(); #endif @@ -2527,7 +2527,7 @@ void raydium_ts_shutdown(struct i2c_client *client) unregister_early_suspend(&g_raydium_ts->early_suspend); #elif defined(CONFIG_PANEL_NOTIFIER) if (active_panel) - panel_event_notifier_unregister(&g_raydium_ts->fb_notif); + panel_event_notifier_unregister(g_raydium_ts->entry); #elif defined(CONFIG_DRM) if (active_panel) drm_panel_notifier_unregister(active_panel, &g_raydium_ts->fb_notif); @@ -2577,7 +2577,7 @@ static int raydium_ts_remove(struct i2c_client *client) unregister_early_suspend(&g_raydium_ts->early_suspend); #elif defined(CONFIG_PANEL_NOTIFIER) if (active_panel) - panel_event_notifier_unregister(&g_raydium_ts->fb_notif); + panel_event_notifier_unregister(g_raydium_ts->entry); #elif defined(CONFIG_DRM) if (active_panel) drm_panel_notifier_unregister(active_panel, &g_raydium_ts->fb_notif); diff --git a/raydium/raydium_driver.h b/raydium/raydium_driver.h index f3428a3924..9a9e98a3fa 100644 --- a/raydium/raydium_driver.h +++ b/raydium/raydium_driver.h @@ -306,8 +306,11 @@ struct raydium_ts_data { struct irq_desc *irq_desc; bool irq_enabled; bool irq_wake; - -#if defined(CONFIG_FB) || defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER) +#if defined(CONFIG_PANEL_NOTIFIER) + struct panel_event_notifier_entry *entry; + int blank; + enum raydium_fb_state fb_state; +#elif defined(CONFIG_FB) || defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER) struct notifier_block fb_notif; int blank; enum raydium_fb_state fb_state; From e572c26f33f722c9110ed30f66cf0557053d014d Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Thu, 16 Mar 2023 14:55:08 +0530 Subject: [PATCH 118/170] touch : raydium: resolve KW issues resolved KW issues in raydium touch driver Change-Id: I1e922389ade468aada9e5b3eda263f3afe1ff6dc Signed-off-by: Surya Teja Kudiri --- raydium/raydium_driver.c | 4 ++-- raydium/raydium_selftest.c | 42 +++++++++++++++++++------------------- raydium/raydium_sysfs.c | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 25608698c3..302e03f92d 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -1188,8 +1188,8 @@ reset_error: static void raydium_work_handler(struct work_struct *work) { int i32_ret = 0; - unsigned char u8_tp_status[MAX_TCH_STATUS_PACKET_SIZE]; - unsigned char u8_buf[MAX_REPORT_PACKET_SIZE]; + unsigned char u8_tp_status[MAX_TCH_STATUS_PACKET_SIZE] = {0}; + unsigned char u8_buf[MAX_REPORT_PACKET_SIZE] = {0}; #ifdef GESTURE_EN unsigned char u8_i; diff --git a/raydium/raydium_selftest.c b/raydium/raydium_selftest.c index b91e07a32a..c04ee6a43a 100644 --- a/raydium/raydium_selftest.c +++ b/raydium/raydium_selftest.c @@ -253,106 +253,106 @@ int self_test_save_test_raw_data_to_file(int i32_ng_type) memset(write_string, 0, strlen(write_string)); if (i32_ng_type & WEARABLE_FT_TEST_RESULT_SYSFS_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "System NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_I2C_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "I2C NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_INT_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "INT NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_RESET_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "RESET NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_PRAM_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "PRAM NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_NORMAL_FW_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "NORMAL_FW_NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_OPEN_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "OPEN NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_SHORT_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "SHORT NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_BURN_CC_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "BURN CC NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_GET_DATA_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "GET DATA NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_FLASH_ID_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "FLASH ID NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_NORMAL_FW_VER_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "NORMAL FW VER NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_TEST_FW_VER_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "TEST FW VER NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_TEST_INIT_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "TEST INIT NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_LOAD_TESTFW_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "LOAD TESTFW NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_BURN_FW_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "BURN FW NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_SINGLE_CC_OPEN_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "Open NG (Single Pin CC) "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_SINGLE_CC_SHORT_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "Short NG (Single Pin CC) "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_UB_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "Uniformity Baseline NG "); } if (i32_ng_type & WEARABLE_FT_TEST_RESULT_UC_NG) { - snprintf(write_string + strlen(write_string), + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "Uniformity CC NG "); } - snprintf(write_string + strlen(write_string), RM_SELF_TEST_MAX_STR_LENGTH, "\n"); + snprintf(write_string, RM_SELF_TEST_MAX_STR_LENGTH, "\n"); self_test_save_to_file(RM_SELF_TEST_LOGFILE, write_string, strlen(write_string)); } diff --git a/raydium/raydium_sysfs.c b/raydium/raydium_sysfs.c index 6317724379..e61675457a 100644 --- a/raydium/raydium_sysfs.c +++ b/raydium/raydium_sysfs.c @@ -1080,7 +1080,7 @@ static ssize_t raydium_receive_fw_store(struct device *dev, free_token = token; free_temp_buf = temp_buf; - snprintf(temp_buf, PAGE_SIZE, "%s", p_i8_buf); + snprintf(temp_buf, 32, "%s", p_i8_buf); token = strsep(&temp_buf, delim); i32_ret = kstrtou8(token, 16, &u8_cmd); if (i32_ret < 0) { From 040e6ca7325fe76ca9055fe0f6fd5d090c45e246 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Thu, 16 Mar 2023 12:58:41 +0530 Subject: [PATCH 119/170] touch: pt: Stop panel notifier callbacks Stop panel notifier callbacks upon probe failure. Change-Id: I8c01d32b8b967cd630e226f45a0c882e3a88e603 Signed-off-by: Surya Teja Kudiri --- pt/pt_core.c | 5 +++-- pt/pt_regs.h | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index db1dcb2f62..1d5932df43 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -12961,6 +12961,7 @@ static void pt_setup_panel_event_notifier(struct pt_core_data *cd) pt_debug(cd->dev, DL_ERROR, "%s: Register notifier failed!\n", __func__); } + cd->entry = cookie; } #else @@ -15061,7 +15062,7 @@ static int pt_device_exit(struct i2c_client *client) pt_debug(dev, DL_INFO,"%s: Start pt_device_exit\n", __func__); if (active_panel) - panel_event_notifier_unregister(&cd->fb_notifier); + panel_event_notifier_unregister(cd->entry); pt_core_state = STATE_SUSPEND; pm_runtime_suspend(dev); @@ -18435,7 +18436,7 @@ int pt_release(struct pt_core_data *cd) unregister_early_suspend(&cd->es); #elif defined(CONFIG_PANEL_NOTIFIER) if (active_panel) - panel_event_notifier_unregister(&cd->fb_notifier); + panel_event_notifier_unregister(cd->entry); #elif defined(CONFIG_DRM) if (active_panel) drm_panel_notifier_unregister(active_panel, &cd->fb_notifier); diff --git a/pt/pt_regs.h b/pt/pt_regs.h index 2e7c18b4c5..7e53bc5261 100644 --- a/pt/pt_regs.h +++ b/pt/pt_regs.h @@ -1525,6 +1525,9 @@ struct pt_core_data { int raw_cmd_status; #ifdef CONFIG_HAS_EARLYSUSPEND struct early_suspend es; +#elif defined(CONFIG_PANEL_NOTIFIER) + struct panel_event_notifier_entry *entry; + enum pt_fb_state fb_state; #elif defined(CONFIG_FB) || defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER) struct notifier_block fb_notifier; enum pt_fb_state fb_state; From ecce7c030cd58ae8c80abfe18e7a2d14a4388b87 Mon Sep 17 00:00:00 2001 From: Prakruthi Deepak Heragu Date: Wed, 1 Mar 2023 11:12:50 -0800 Subject: [PATCH 120/170] touch: qts: Rename Gunyah RM APIs As we are merging upstream patches, resolve conflicts of namespaces in downstream modules. Change-Id: I09f4930534b5242335c4884822c5110ca6e8ba8a Signed-off-by: Prakruthi Deepak Heragu Signed-off-by: Raviteja Tamatam --- qts/qts_core.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/qts/qts_core.c b/qts/qts_core.c index b98c104eb3..89ecc84be3 100644 --- a/qts/qts_core.c +++ b/qts/qts_core.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. */ #define pr_fmt(fmt) "%s: " fmt, __func__ @@ -40,7 +40,11 @@ static struct gh_acl_desc *qts_vm_get_acl(enum gh_vm_names vm_name) struct gh_acl_desc *acl_desc; gh_vmid_t vmid; +#if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) + ghd_rm_get_vmid(vm_name, &vmid); +#else gh_rm_get_vmid(vm_name, &vmid); +#endif acl_desc = kzalloc(offsetof(struct gh_acl_desc, acl_entries[1]), GFP_KERNEL); @@ -739,7 +743,12 @@ static void qts_trusted_touch_abort_pvm(struct qts_data *qts_data) case PVM_IOMEM_LENT: case PVM_IOMEM_LENT_NOTIFIED: case PVM_IOMEM_RELEASE_NOTIFIED: +#if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) + rc = ghd_rm_mem_reclaim(qts_data->vm_info->vm_mem_handle, 0); +#else rc = gh_rm_mem_reclaim(qts_data->vm_info->vm_mem_handle, 0); +#endif + if (rc) { pr_err("failed to reclaim iomem on pvm rc:%d\n", rc); qts_trusted_touch_set_vm_state(qts_data, PVM_IOMEM_RELEASE_NOTIFIED); @@ -872,7 +881,12 @@ static void qts_trusted_touch_pvm_vm_mode_disable(struct qts_data *qts_data) if (qts_data->vendor_ops.pre_la_tui_disable) qts_data->vendor_ops.pre_la_tui_disable(qts_data->vendor_data); +#if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) + rc = ghd_rm_mem_reclaim(qts_data->vm_info->vm_mem_handle, 0); +#else rc = gh_rm_mem_reclaim(qts_data->vm_info->vm_mem_handle, 0); +#endif + if (rc) { pr_err("Trusted touch VM mem reclaim failed rc:%d\n", rc); goto error; @@ -990,8 +1004,14 @@ static int qts_vm_mem_lend(struct qts_data *qts_data) goto sgl_error; } +#if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) + rc = ghd_rm_mem_lend(GH_RM_MEM_TYPE_IO, 0, TRUSTED_TOUCH_MEM_LABEL, + acl_desc, sgl_desc, NULL, &mem_handle); +#else rc = gh_rm_mem_lend(GH_RM_MEM_TYPE_IO, 0, TRUSTED_TOUCH_MEM_LABEL, acl_desc, sgl_desc, NULL, &mem_handle); +#endif + if (rc) { pr_err("Failed to lend IO memories for Trusted touch rc:%d\n", rc); goto error; @@ -1001,7 +1021,11 @@ static int qts_vm_mem_lend(struct qts_data *qts_data) qts_trusted_touch_set_vm_state(qts_data, PVM_IOMEM_LENT); +#if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) + ghd_rm_get_vmid(GH_TRUSTED_VM, &trusted_vmid); +#else gh_rm_get_vmid(GH_TRUSTED_VM, &trusted_vmid); +#endif vmid_desc = qts_vm_get_vmid(trusted_vmid); From 04709b588a5dbaacac9e588d7671c868973c9467 Mon Sep 17 00:00:00 2001 From: Rohith Iyer Date: Mon, 27 Mar 2023 14:49:04 -0700 Subject: [PATCH 121/170] touch: goodix: Set L12B regulator current load for suspend and resume. Accounts for touch panel current load in L12B regulator, for enable and disable states. Change-Id: Ie3668b878ccc5fe3a325e93042d3241ac6787ed1 Signed-off-by: Rohith Iyer --- goodix_berlin_driver/goodix_brl_hw.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/goodix_berlin_driver/goodix_brl_hw.c b/goodix_berlin_driver/goodix_brl_hw.c index 40068c87b2..0b9a60f8b5 100644 --- a/goodix_berlin_driver/goodix_brl_hw.c +++ b/goodix_berlin_driver/goodix_brl_hw.c @@ -204,6 +204,8 @@ static int brl_reset_after(struct goodix_ts_core *cd) return 0; } +#define REG_SUSPEND_CURRENT 20 +#define REG_RESUME_CURRENT 14000 static int brl_power_on(struct goodix_ts_core *cd, bool on) { int ret = 0; @@ -215,6 +217,13 @@ static int brl_power_on(struct goodix_ts_core *cd, bool on) if (iovdd_gpio > 0) { gpio_direction_output(iovdd_gpio, 1); } else if (cd->iovdd) { + if (regulator_count_voltages(cd->iovdd) > 0) { + ret = regulator_set_load(cd->iovdd, REG_RESUME_CURRENT); + if (ret) { + ts_err("Setting regulator load failed:%d", ret); + goto power_off; + } + } ret = regulator_enable(cd->iovdd); if (ret < 0) { ts_err("Failed to enable iovdd:%d", ret); @@ -251,8 +260,11 @@ power_off: gpio_direction_output(reset_gpio, 0); if (iovdd_gpio > 0) gpio_direction_output(iovdd_gpio, 0); - else if (cd->iovdd) + else if (cd->iovdd) { regulator_disable(cd->iovdd); + if (regulator_count_voltages(cd->iovdd) > 0) + regulator_set_load(cd->iovdd, REG_SUSPEND_CURRENT); + } if (avdd_gpio > 0) gpio_direction_output(avdd_gpio, 0); else if (cd->avdd) From 69fec10e97e9a4baa21a1022754505e6a740bf7b Mon Sep 17 00:00:00 2001 From: Simranjeet Thind Date: Wed, 22 Mar 2023 16:09:59 -0700 Subject: [PATCH 122/170] touch: pineapple: Replacing disable_irq with disable_irq_nosync disable_irq ensures existing instances of the IRQ handler have completed before returning which may create a deadlock if the handler doesn't find the resource. Change-Id: I7010d2a415f97de5ece8119b855c169ffba665b7 Signed-off-by: Simranjeet Thind --- goodix_berlin_driver/goodix_brl_hw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/goodix_berlin_driver/goodix_brl_hw.c b/goodix_berlin_driver/goodix_brl_hw.c index 0b9a60f8b5..9f66269819 100644 --- a/goodix_berlin_driver/goodix_brl_hw.c +++ b/goodix_berlin_driver/goodix_brl_hw.c @@ -348,11 +348,11 @@ static int brl_irq_enbale(struct goodix_ts_core *cd, bool enable) } if (!enable && atomic_cmpxchg(&cd->irq_enabled, 1, 0)) { - disable_irq(cd->irq); + disable_irq_nosync(cd->irq); ts_debug("Irq disabled"); return 0; } - ts_info("warnning: irq deepth inbalance!"); + ts_info("warning: irq depth imbalance!"); return 0; } From 56fcf3d4d4d40bfa1de989b41161b2ec55b8859e Mon Sep 17 00:00:00 2001 From: Simranjeet Thind Date: Fri, 31 Mar 2023 15:27:43 -0700 Subject: [PATCH 123/170] touch: pineapple: Removing synaptics driver for pineapple. Resolving Compilation errors by removing synaptics_tcm_ts.ko for pineapple platform. Change-Id: I8c5e64fbfce60453fa38e9aba30c5977e4606d33 Signed-off-by: Simranjeet Thind --- Android.mk | 35 +++++++++++++++++++++++++++++++++++ touch_driver_board.mk | 3 +-- touch_driver_product.mk | 3 +-- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/Android.mk b/Android.mk index 3a481c71a1..68691aa561 100644 --- a/Android.mk +++ b/Android.mk @@ -94,6 +94,41 @@ else ifeq ($(TARGET_BOARD_PLATFORM), kona) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### +else ifeq ($(TARGET_BOARD_PLATFORM), pineapple) + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := nt36xxx-i2c.ko + LOCAL_MODULE_KBUILD_NAME := nt36xxx-i2c.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := goodix_ts.ko + LOCAL_MODULE_KBUILD_NAME := goodix_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := atmel_mxt_ts.ko + LOCAL_MODULE_KBUILD_NAME := atmel_mxt_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + else ########################################################### diff --git a/touch_driver_board.mk b/touch_driver_board.mk index 852952db5b..ff675bfc92 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -18,8 +18,7 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) else ifeq ($(TARGET_BOARD_PLATFORM), pineapple) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ - $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ - $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko else BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ diff --git a/touch_driver_product.mk b/touch_driver_product.mk index 903c3db0d0..f67ce46145 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -16,8 +16,7 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) else ifeq ($(TARGET_BOARD_PLATFORM), pineapple) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ - $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko \ - $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko else PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ From e536a52345eb5849e290c4773803a0d5080a5ec0 Mon Sep 17 00:00:00 2001 From: Simranjeet Thind Date: Fri, 14 Apr 2023 16:10:34 -0700 Subject: [PATCH 124/170] touch: pineapple: Add support for Bazel to build touch modules Add support for touch modules to be built with Bazel for pineapple. Change-Id: Idf7527318a36ea33e60ba95aadb03a107f2e7205 Signed-off-by: Simranjeet Thind --- BUILD.bazel | 48 +++++++++++++++++++++++ target.bzl | 23 +++++++++++ touch_modules.bzl | 60 ++++++++++++++++++++++++++++ touch_modules_build.bzl | 86 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 217 insertions(+) create mode 100644 BUILD.bazel create mode 100644 target.bzl create mode 100644 touch_modules.bzl create mode 100644 touch_modules_build.bzl diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 0000000000..0da056db5b --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,48 @@ +load("//build/kernel/kleaf:kernel.bzl", "ddk_headers") + +package( + default_visibility = [ + "//visibility:public"], +) + +ddk_headers( + name = "goodix_ts_headers", + hdrs = glob([ + "goodix_berlin_driver/*.h", + "qts/*.h" + ] + ) +) + +ddk_headers( + name = "nt36xxx_headers", + hdrs = glob([ + "nt36xxx/*.h" + ] + ) +) + +ddk_headers( + name = "atmel_mxt_headers", + hdrs = glob([ + "qts/*.h" + ] + ) +) + +ddk_headers( + name = "config_headers", + hdrs = glob([ + "config/*.h" + ] + ), + includes = ["config"] +) + +ddk_headers( + name = "touch_drivers_headers", + hdrs = [":goodix_ts_headers", ":nt36xxx_headers", ":atmel_mxt_headers", ":config_headers"] +) + +load(":target.bzl", "define_pineapple") +define_pineapple() diff --git a/target.bzl b/target.bzl new file mode 100644 index 0000000000..0af8e2963d --- /dev/null +++ b/target.bzl @@ -0,0 +1,23 @@ +load(":touch_modules.bzl", "touch_driver_modules") +load(":touch_modules_build.bzl", "define_consolidate_gki_modules") + +def define_pineapple(): + define_consolidate_gki_modules( + target = "pineapple", + registry = touch_driver_modules, + modules = [ + "nt36xxx-i2c", + "atmel_mxt_ts", + "dummy_ts", + "goodix_ts" + ], + config_options = [ + "TOUCH_DLKM_ENABLE", + "CONFIG_ARCH_PINEAPPLE", + "CONFIG_MSM_TOUCH", + "CONFIG_TOUCHSCREEN_GOODIX_BRL", + "CONFIG_TOUCHSCREEN_NT36XXX_I2C", + "CONFIG_TOUCHSCREEN_ATMEL_MXT", + "CONFIG_TOUCHSCREEN_DUMMY" + ], +) diff --git a/touch_modules.bzl b/touch_modules.bzl new file mode 100644 index 0000000000..eaf05fac0a --- /dev/null +++ b/touch_modules.bzl @@ -0,0 +1,60 @@ +# Importing to touch module entry api from touch_modules_build.bzl to define module entried for touch drivers +load(":touch_modules_build.bzl", "touch_module_entry") + +# Importing the touch driver headers defined in BUILD.bazel +touch_driver_modules = touch_module_entry([":touch_drivers_headers"]) + +#Including the headers in the modules to be declared +module_entry = touch_driver_modules.register + +#--------------- TOUCH-DRIVERS MODULES ------------------ + +#define ddk_module() for goodix_ts +module_entry( + name = "goodix_ts", + config_option = "CONFIG_TOUCHSCREEN_GOODIX_BRL", + srcs = [ + "goodix_berlin_driver/goodix_brl_fwupdate.c", + "goodix_berlin_driver/goodix_brl_hw.c", + "goodix_berlin_driver/goodix_brl_i2c.c", + "goodix_berlin_driver/goodix_brl_spi.c", + "goodix_berlin_driver/goodix_cfg_bin.c", + "goodix_berlin_driver/goodix_ts_core.c", + "goodix_berlin_driver/goodix_ts_gesture.c", + "goodix_berlin_driver/goodix_ts_inspect.c", + "goodix_berlin_driver/goodix_ts_tools.c", + "goodix_berlin_driver/goodix_ts_utils.c", + "qts/qts_core.c" + ] +) + +#define ddk_module() for nt36xxx +module_entry( + name = "nt36xxx-i2c", + config_option = "CONFIG_TOUCHSCREEN_NT36XXX_I2C", + srcs = [ + "nt36xxx/nt36xxx_ext_proc.c", + "nt36xxx/nt36xxx_fw_update.c", + "nt36xxx/nt36xxx_mp_ctrlram.c", + "nt36xxx/nt36xxx.c" + ] +) + +#define ddk_module() for atmel_mxt_ts +module_entry( + name = "atmel_mxt_ts", + config_option = "CONFIG_TOUCHSCREEN_ATMEL_MXT", + srcs = [ + "atmel_mxt/atmel_mxt_ts.c", + "qts/qts_core.c" + ] +) + +#define ddk_module() for dummy_ts +module_entry( + name = "dummy_ts", + config_option = "CONFIG_TOUCHSCREEN_DUMMY", + srcs = [ + "dummy_touch/dummy_touch.c" + ] +) diff --git a/touch_modules_build.bzl b/touch_modules_build.bzl new file mode 100644 index 0000000000..02ee79769d --- /dev/null +++ b/touch_modules_build.bzl @@ -0,0 +1,86 @@ +load("//build/kernel/kleaf:kernel.bzl", "ddk_module","ddk_submodule") +load("//build/bazel_common_rules/dist:dist.bzl", "copy_to_dist_dir") + +def _register_module_to_map(module_map, name, path, config_option, srcs): + module = struct( + name = name, + path = path, + srcs = srcs, + config_option = config_option + ) + + module_map[name] = module + +def _get_config_choices(map, options): + choices = [] + for option in map: + choices.extend(map[option].get(option in options,[])) + return choices + +def _get_kernel_build_options(modules, config_options): + all_options = {option: True for option in config_options} + all_options = all_options | {module.config_option: True for module in modules if module.config_option} + return all_options + +def _get_kernel_build_module_srcs(module, options, formatter): + srcs = module.srcs + print("-",module.name,",",module.config_option,",srcs =",srcs) + module_path = "{}/".format(module.path) if module.path else "" + return ["{}{}".format(module_path, formatter(src)) for src in srcs] + +def touch_module_entry(hdrs = []): + module_map = {} + + def register(name, path = None, config_option = [], srcs = [], config_srcs = None, deps = None): + _register_module_to_map(module_map, name, path, config_option, srcs) + return struct( + register = register, + get = module_map.get, + hdrs = hdrs + ) + +def define_target_variant_modules(target, variant, registry, modules, config_options = []): + kernel_build = "{}_{}".format(target, variant) + kernel_build_label = "//msm-kernel:{}".format(kernel_build) + modules = [registry.get(module_name) for module_name in modules] + options = _get_kernel_build_options(modules, config_options) + build_print = lambda message : print("{}: {}".format(kernel_build, message)) + formatter = lambda s : s.replace("%b", kernel_build).replace("%t", target) + headers = ["//msm-kernel:all_headers"] + registry.hdrs + all_module_rules = [] + + for module in modules: + rule_name = "{}_{}".format(kernel_build, module.name) + module_srcs = _get_kernel_build_module_srcs(module, options, formatter) + + if not module_srcs: + continue + + ddk_submodule( + name = rule_name, + srcs = module_srcs, + out = "{}.ko".format(module.name), + deps = headers, + local_defines = options.keys(), + ) + all_module_rules.append(rule_name) + + ddk_module( + name = "{}_touch_drivers".format(kernel_build), + kernel_build = kernel_build_label, + deps = all_module_rules, + ) + copy_to_dist_dir( + name = "{}_touch_drivers_dist".format(kernel_build), + data = [":{}_touch_drivers".format(kernel_build)], + dist_dir = "out/target/product/{}/dlkm/lib/modules".format(target), + flat = True, + wipe_dist_dir = False, + allow_duplicate_filenames = False, + mode_overrides = {"**/*": "644"}, + log = "info", + ) + +def define_consolidate_gki_modules(target, registry, modules, config_options = []): + define_target_variant_modules(target, "gki", registry, modules, config_options) + define_target_variant_modules(target, "consolidate", registry, modules, config_options) From b8201e85e3de8e991579871472d6afd12a93d5c7 Mon Sep 17 00:00:00 2001 From: Ritesh Kumar Date: Fri, 19 May 2023 13:44:21 +0530 Subject: [PATCH 125/170] touch: kalama: Removing synaptics driver for kalama. Resolving Compilation errors by removing synaptics_tcm_ts.ko for kalama platform. Change-Id: I493f367d2c74317f08e7fdd25d57ed4a3f96bc93 Signed-off-by: Ritesh Kumar --- Android.mk | 35 +++++++++++++++++++++++++++++++++++ touch_driver_board.mk | 4 ++++ touch_driver_product.mk | 4 ++++ 3 files changed, 43 insertions(+) diff --git a/Android.mk b/Android.mk index 68691aa561..f0bc808a9e 100644 --- a/Android.mk +++ b/Android.mk @@ -129,6 +129,41 @@ else ifeq ($(TARGET_BOARD_PLATFORM), pineapple) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### +else ifeq ($(TARGET_BOARD_PLATFORM), kalama) + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := nt36xxx-i2c.ko + LOCAL_MODULE_KBUILD_NAME := nt36xxx-i2c.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := goodix_ts.ko + LOCAL_MODULE_KBUILD_NAME := goodix_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := atmel_mxt_ts.ko + LOCAL_MODULE_KBUILD_NAME := atmel_mxt_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + else ########################################################### diff --git a/touch_driver_board.mk b/touch_driver_board.mk index ff675bfc92..1f45eec467 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -19,6 +19,10 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko + else ifeq ($(TARGET_BOARD_PLATFORM), kalama) + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko else BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ diff --git a/touch_driver_product.mk b/touch_driver_product.mk index f67ce46145..305eae9cf7 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -17,6 +17,10 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko + else ifeq ($(TARGET_BOARD_PLATFORM), kalama) + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko else PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ From 18444e7144c3cd1d268bcdd82d12df692941be62 Mon Sep 17 00:00:00 2001 From: Ritesh Kumar Date: Fri, 19 May 2023 13:55:27 +0530 Subject: [PATCH 126/170] touch: blair: enable touch drivers for blair Add touch config files and enable for blair platform. Change-Id: I16e3fcdc3a5e8faab0804db24f10563e873bd594 Signed-off-by: Jyothi bommidi Signed-off-by: Ritesh Kumar --- Android.mk | 35 ++++++++++++++++++++++++++++++++ Kbuild | 5 +++++ config/gki_blairtouch.conf | 9 ++++++++ config/gki_blairtouchconf.h | 12 +++++++++++ focaltech_touch/focaltech_core.c | 25 +++++++++++++++++++++-- touch_driver_board.mk | 4 ++++ touch_driver_product.mk | 4 ++++ 7 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 config/gki_blairtouch.conf create mode 100644 config/gki_blairtouchconf.h diff --git a/Android.mk b/Android.mk index f0bc808a9e..70a8d605eb 100644 --- a/Android.mk +++ b/Android.mk @@ -164,6 +164,41 @@ else ifeq ($(TARGET_BOARD_PLATFORM), kalama) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### +else ifeq ($(TARGET_BOARD_PLATFORM), blair) + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := focaltech_fts.ko + LOCAL_MODULE_KBUILD_NAME := focaltech_fts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := nt36xxx-i2c.ko + LOCAL_MODULE_KBUILD_NAME := nt36xxx-i2c.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := synaptics_tcm_ts.ko + LOCAL_MODULE_KBUILD_NAME := synaptics_tcm_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + else ########################################################### diff --git a/Kbuild b/Kbuild index 39d5e26825..26dafc3b0b 100644 --- a/Kbuild +++ b/Kbuild @@ -31,6 +31,11 @@ ifeq ($(CONFIG_ARCH_KONA), y) LINUX_INC += -include $(TOUCH_ROOT)/config/gki_konatouchconf.h endif +ifeq ($(CONFIG_ARCH_BLAIR), y) + include $(TOUCH_ROOT)/config/gki_blairtouch.conf + LINUX_INC += -include $(TOUCH_ROOT)/config/gki_blairtouchconf.h +endif + LINUX_INC += -Iinclude/linux \ -Iinclude/linux/drm \ -Iinclude/linux/gunyah \ diff --git a/config/gki_blairtouch.conf b/config/gki_blairtouch.conf new file mode 100644 index 0000000000..251e9f1a98 --- /dev/null +++ b/config/gki_blairtouch.conf @@ -0,0 +1,9 @@ +export CONFIG_MSM_TOUCH=m +export CONFIG_TOUCH_FOCALTECH=y +export CONFIG_TOUCHSCREEN_NT36XXX_I2C=y +export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM=y +export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C=y +export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE=y +export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH=y +export CONFIG_TOUCHSCREEN_FTS_DIRECTORY="focaltech_touch" +export CONFIG_FTS_TRUSTED_TOUCH=n diff --git a/config/gki_blairtouchconf.h b/config/gki_blairtouchconf.h new file mode 100644 index 0000000000..63892cb9c1 --- /dev/null +++ b/config/gki_blairtouchconf.h @@ -0,0 +1,12 @@ +/* +* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. +* SPDX-License-Identifier: GPL-2.0-only +*/ + +#define CONFIG_TOUCH_FOCALTECH 1 +#define CONFIG_TOUCHSCREEN_NT36XXX_I2C 1 +#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM 1 +#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C 1 +#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE 1 +#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH 1 +#define CONFIG_TOUCHSCREEN_FTS_DIRECTORY "focaltech_touch" diff --git a/focaltech_touch/focaltech_core.c b/focaltech_touch/focaltech_core.c index 40b3c04659..6d3d6a827b 100644 --- a/focaltech_touch/focaltech_core.c +++ b/focaltech_touch/focaltech_core.c @@ -3193,9 +3193,19 @@ static int fts_ts_i2c_probe(struct i2c_client *client, const struct i2c_device_i return 0; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)) +static void fts_ts_i2c_remove(struct i2c_client *client) +#else static int fts_ts_i2c_remove(struct i2c_client *client) +#endif { - return fts_ts_remove_entry(i2c_get_clientdata(client)); + int rc = 0; + + rc = fts_ts_remove_entry(i2c_get_clientdata(client)); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)) + return rc; +#endif } static const struct i2c_device_id fts_ts_i2c_id[] = { @@ -3292,9 +3302,20 @@ static int fts_ts_spi_probe(struct spi_device *spi) return 0; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)) +static void fts_ts_spi_remove(struct spi_device *spi) +#else static int fts_ts_spi_remove(struct spi_device *spi) +#endif { - return fts_ts_remove_entry(spi_get_drvdata(spi)); + int rc = 0; + + rc = fts_ts_remove_entry(spi_get_drvdata(spi)); + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)) + return rc; +#endif } static const struct spi_device_id fts_ts_spi_id[] = { diff --git a/touch_driver_board.mk b/touch_driver_board.mk index 1f45eec467..a7ccb4322c 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -23,6 +23,10 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko + else ifeq ($(TARGET_BOARD_PLATFORM), blair) + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko \ + $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko else BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ diff --git a/touch_driver_product.mk b/touch_driver_product.mk index 305eae9cf7..714e878dc3 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -21,6 +21,10 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ $(KERNEL_MODULES_OUT)/atmel_mxt_ts.ko + else ifeq ($(TARGET_BOARD_PLATFORM), blair) + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko \ + $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko else PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ From 338c03659cea464f8b9279cfae8a59b17b4c31ab Mon Sep 17 00:00:00 2001 From: Anand Tarakh Date: Wed, 10 May 2023 17:28:53 +0530 Subject: [PATCH 127/170] touch: crow: enable touch drivers for crow Add touch config files and enable for crow platform. Change-Id: Ia4b3ee2c81d396fee2b9ddd1b5278d46597c9d33 Signed-off-by: Anand Tarakh --- Android.mk | 13 +++++++++++++ Kbuild | 5 +++++ config/gki_crowtouch.conf | 2 ++ config/gki_crowtouchconf.h | 6 ++++++ touch_driver_board.mk | 2 ++ touch_driver_product.mk | 2 ++ 6 files changed, 30 insertions(+) create mode 100644 config/gki_crowtouch.conf create mode 100644 config/gki_crowtouchconf.h diff --git a/Android.mk b/Android.mk index 70a8d605eb..ab0db5385d 100644 --- a/Android.mk +++ b/Android.mk @@ -199,6 +199,19 @@ else ifeq ($(TARGET_BOARD_PLATFORM), blair) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### +else ifeq ($(TARGET_BOARD_PLATFORM), crow) + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := goodix_ts.ko + LOCAL_MODULE_KBUILD_NAME := goodix_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + else ########################################################### diff --git a/Kbuild b/Kbuild index 26dafc3b0b..417a98bb07 100644 --- a/Kbuild +++ b/Kbuild @@ -36,6 +36,11 @@ ifeq ($(CONFIG_ARCH_BLAIR), y) LINUX_INC += -include $(TOUCH_ROOT)/config/gki_blairtouchconf.h endif +ifeq ($(CONFIG_ARCH_CROW), y) + include $(TOUCH_ROOT)/config/gki_crowtouch.conf + LINUX_INC += -include $(TOUCH_ROOT)/config/gki_crowtouchconf.h +endif + LINUX_INC += -Iinclude/linux \ -Iinclude/linux/drm \ -Iinclude/linux/gunyah \ diff --git a/config/gki_crowtouch.conf b/config/gki_crowtouch.conf new file mode 100644 index 0000000000..cfad2ad177 --- /dev/null +++ b/config/gki_crowtouch.conf @@ -0,0 +1,2 @@ +export CONFIG_MSM_TOUCH=m +export CONFIG_TOUCHSCREEN_GOODIX_BRL=y diff --git a/config/gki_crowtouchconf.h b/config/gki_crowtouchconf.h new file mode 100644 index 0000000000..044dc92a10 --- /dev/null +++ b/config/gki_crowtouchconf.h @@ -0,0 +1,6 @@ +/* +* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. +* SPDX-License-Identifier: GPL-2.0-only +*/ + +#define CONFIG_TOUCHSCREEN_GOODIX_BRL 1 diff --git a/touch_driver_board.mk b/touch_driver_board.mk index a7ccb4322c..bd62d161b4 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -27,6 +27,8 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko \ $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko + else ifeq ($(TARGET_BOARD_PLATFORM), crow) + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/goodix_ts.ko else BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ diff --git a/touch_driver_product.mk b/touch_driver_product.mk index 714e878dc3..363a864b2f 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -25,6 +25,8 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko \ $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko + else ifeq ($(TARGET_BOARD_PLATFORM), crow) + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/goodix_ts.ko else PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ From 18a6eaa6c7948e3f7d7a27475f1ebef9f792a41c Mon Sep 17 00:00:00 2001 From: Simranjeet Thind Date: Mon, 15 May 2023 16:53:55 -0700 Subject: [PATCH 128/170] touch: goodix: Change L12B regulator current load to 30mA on resume. Increasing the load current for touch IC to 30mA from 14mA to avoid the regulator from going into LPM, as some touch panels are drawing more power. Change-Id: I0f2551e91ce0d33904f4874173e3af00c2ecbc58 Signed-off-by: Simranjeet Thind --- goodix_berlin_driver/goodix_brl_hw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/goodix_berlin_driver/goodix_brl_hw.c b/goodix_berlin_driver/goodix_brl_hw.c index 9f66269819..1bb7356583 100644 --- a/goodix_berlin_driver/goodix_brl_hw.c +++ b/goodix_berlin_driver/goodix_brl_hw.c @@ -1,7 +1,7 @@ /* * Goodix Touchscreen Driver * Copyright (C) 2020 - 2021 Goodix, Inc. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022 - 2023 Qualcomm Innovation Center, Inc. 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 @@ -205,7 +205,7 @@ static int brl_reset_after(struct goodix_ts_core *cd) } #define REG_SUSPEND_CURRENT 20 -#define REG_RESUME_CURRENT 14000 +#define REG_RESUME_CURRENT 30000 static int brl_power_on(struct goodix_ts_core *cd, bool on) { int ret = 0; From b0624f68e4077e1c49412c813f436cadeeefae6b Mon Sep 17 00:00:00 2001 From: Simranjeet Thind Date: Mon, 1 May 2023 12:21:59 -0700 Subject: [PATCH 129/170] touch: pineapple: Enabling bazel build for touch modules. Allowing bazel build to generate touch modules for pineapple. Change-Id: Id291374269f0ecdac3d0b87a078bca7d6c46c576 Signed-off-by: Simranjeet Thind --- Android.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Android.mk b/Android.mk index ab0db5385d..b8ec54a746 100644 --- a/Android.mk +++ b/Android.mk @@ -11,6 +11,9 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) TOUCH_SELECT := CONFIG_MSM_TOUCH=m LOCAL_PATH := $(call my-dir) + ifeq ($(TARGET_BOARD_PLATFORM), pineapple) + LOCAL_MODULE_DDK_BUILD := true + endif include $(CLEAR_VARS) # This makefile is only for DLKM From d665495c2c8fa5dca01f1020899a7e48f9da07be Mon Sep 17 00:00:00 2001 From: Raviteja Tamatam Date: Fri, 2 Jun 2023 11:07:57 -0700 Subject: [PATCH 130/170] touch: pineapple: qts support for atmel_mxt_ts not enabled QTS is not used by atmel_mxt_ts and its compilation needs to be avoided. QTS as seperate ko is needed to enable this support. Change-Id: I6473355cb93a29ee3fd9282d7f42534feb89c202 Signed-off-by: Raviteja Tamatam --- BUILD.bazel | 10 +--------- touch_modules.bzl | 1 - 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/BUILD.bazel b/BUILD.bazel index 0da056db5b..d1f5ff70e9 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -22,14 +22,6 @@ ddk_headers( ) ) -ddk_headers( - name = "atmel_mxt_headers", - hdrs = glob([ - "qts/*.h" - ] - ) -) - ddk_headers( name = "config_headers", hdrs = glob([ @@ -41,7 +33,7 @@ ddk_headers( ddk_headers( name = "touch_drivers_headers", - hdrs = [":goodix_ts_headers", ":nt36xxx_headers", ":atmel_mxt_headers", ":config_headers"] + hdrs = [":goodix_ts_headers", ":nt36xxx_headers", ":config_headers"] ) load(":target.bzl", "define_pineapple") diff --git a/touch_modules.bzl b/touch_modules.bzl index eaf05fac0a..2d23a201dd 100644 --- a/touch_modules.bzl +++ b/touch_modules.bzl @@ -46,7 +46,6 @@ module_entry( config_option = "CONFIG_TOUCHSCREEN_ATMEL_MXT", srcs = [ "atmel_mxt/atmel_mxt_ts.c", - "qts/qts_core.c" ] ) From 77e650b9f7ac543a71927b533df3e277f749aa8b Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Wed, 17 May 2023 16:05:48 +0530 Subject: [PATCH 131/170] touch: raydium: Fix reboot issue Fix reboot issue by resolving memory leaks. Change-Id: I150c6eca0fcd955a701ba3cbe7e29166ec27e8b6 Signed-off-by: Surya Teja Kudiri --- raydium/raydium_driver.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 302e03f92d..73c30d2372 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -2533,8 +2533,6 @@ if (active_panel) drm_panel_notifier_unregister(active_panel, &g_raydium_ts->fb_notif); #endif/*end of CONFIG_FB*/ input_unregister_device(g_raydium_ts->input_dev); - if (g_raydium_ts->input_dev) - input_free_device(g_raydium_ts->input_dev); g_raydium_ts->input_dev = NULL; gpio_free(g_raydium_ts->rst_gpio); @@ -2583,7 +2581,7 @@ if (active_panel) drm_panel_notifier_unregister(active_panel, &g_raydium_ts->fb_notif); #endif/*end of CONFIG_FB*/ input_unregister_device(g_raydium_ts->input_dev); - input_free_device(g_raydium_ts->input_dev); + g_raydium_ts->input_dev = NULL; gpio_free(g_raydium_ts->rst_gpio); #ifdef CONFIG_RM_SYSFS_DEBUG From 064ab41b8ce9dc7facbf6af4641ec018a47130aa Mon Sep 17 00:00:00 2001 From: Yu Wu Date: Mon, 12 Jun 2023 15:28:39 +0800 Subject: [PATCH 132/170] touch: fix KW issue KW reported 'val' is used uninitialized, fix it. Change-Id: I8a563737bb365e8f88716c8aeab64f05c11fa1ab Signed-off-by: Yu Wu --- atmel_mxt/atmel_mxt_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atmel_mxt/atmel_mxt_ts.c b/atmel_mxt/atmel_mxt_ts.c index 916e2ac3e5..7ad2743864 100644 --- a/atmel_mxt/atmel_mxt_ts.c +++ b/atmel_mxt/atmel_mxt_ts.c @@ -1377,7 +1377,7 @@ static int mxt_check_retrigen(struct mxt_data *data) { struct i2c_client *client = data->client; int error; - int val; + int val = 0; struct irq_data *irqd; data->use_retrigen_workaround = false; From 03a6d6486e86d5fff6a3027d549d61c13c194296 Mon Sep 17 00:00:00 2001 From: Ritesh Kumar Date: Thu, 22 Jun 2023 16:31:39 +0530 Subject: [PATCH 133/170] touch: bengal: enable touch driver compilation Enable synaptics_tcm and novatek touch driver compilation for bengal target. Change-Id: Iea3ed8fb3f4ddcdf833a36b0f9730c878d63f412 Signed-off-by: Ritesh Kumar --- Android.mk | 24 ++++++++++++++++++++++++ config/gki_khajetouch.conf | 1 + config/gki_khajetouchconf.h | 1 + touch_driver_board.mk | 3 +++ touch_driver_product.mk | 3 +++ 5 files changed, 32 insertions(+) diff --git a/Android.mk b/Android.mk index b8ec54a746..e5385e827b 100644 --- a/Android.mk +++ b/Android.mk @@ -215,6 +215,30 @@ else ifeq ($(TARGET_BOARD_PLATFORM), crow) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### +else ifeq ($(TARGET_BOARD_PLATFORM), bengal) + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := synaptics_tcm_ts.ko + LOCAL_MODULE_KBUILD_NAME := synaptics_tcm_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := nt36xxx-i2c.ko + LOCAL_MODULE_KBUILD_NAME := nt36xxx-i2c.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + else ########################################################### diff --git a/config/gki_khajetouch.conf b/config/gki_khajetouch.conf index 366a02322a..29f3770654 100644 --- a/config/gki_khajetouch.conf +++ b/config/gki_khajetouch.conf @@ -2,4 +2,5 @@ export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM=y export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C=y export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE=y export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH=y +export CONFIG_TOUCHSCREEN_NT36XXX_I2C=y export CONFIG_MSM_TOUCH=m diff --git a/config/gki_khajetouchconf.h b/config/gki_khajetouchconf.h index f79af6a910..f0a37b4c25 100644 --- a/config/gki_khajetouchconf.h +++ b/config/gki_khajetouchconf.h @@ -7,4 +7,5 @@ #define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C 1 #define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE 1 #define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH 1 +#define CONFIG_TOUCHSCREEN_NT36XXX_I2C 1 diff --git a/touch_driver_board.mk b/touch_driver_board.mk index bd62d161b4..f243bff810 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -29,6 +29,9 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko else ifeq ($(TARGET_BOARD_PLATFORM), crow) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/goodix_ts.ko + else ifeq ($(TARGET_BOARD_PLATFORM), bengal) + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ + $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko else BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ diff --git a/touch_driver_product.mk b/touch_driver_product.mk index 363a864b2f..dfccfd0cfe 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -27,6 +27,9 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko else ifeq ($(TARGET_BOARD_PLATFORM), crow) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/goodix_ts.ko + else ifeq ($(TARGET_BOARD_PLATFORM), bengal) + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ + $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko else PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ From 720eb08518a4c8cafcd534bd81b4cf8aa330b7d9 Mon Sep 17 00:00:00 2001 From: Jyothi bommidi Date: Wed, 28 Jun 2023 12:41:43 +0530 Subject: [PATCH 134/170] touch: blair: enable goodix touch driver for blair Add goodix touch config files and enable for blair platform. Change-Id: I4ba4407459f469dc551083d75aa610d13ef39f51 Signed-off-by: Jyothi bommidi --- Android.mk | 11 +++++++++++ config/gki_blairtouch.conf | 1 + config/gki_blairtouchconf.h | 1 + touch_driver_board.mk | 3 ++- touch_driver_product.mk | 3 ++- 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Android.mk b/Android.mk index e5385e827b..a29dcc0603 100644 --- a/Android.mk +++ b/Android.mk @@ -202,6 +202,17 @@ else ifeq ($(TARGET_BOARD_PLATFORM), blair) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := goodix_ts.ko + LOCAL_MODULE_KBUILD_NAME := goodix_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + else ifeq ($(TARGET_BOARD_PLATFORM), crow) ########################################################### diff --git a/config/gki_blairtouch.conf b/config/gki_blairtouch.conf index 251e9f1a98..546b7d8fff 100644 --- a/config/gki_blairtouch.conf +++ b/config/gki_blairtouch.conf @@ -5,5 +5,6 @@ export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM=y export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C=y export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE=y export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH=y +export CONFIG_TOUCHSCREEN_GOODIX_BRL=y export CONFIG_TOUCHSCREEN_FTS_DIRECTORY="focaltech_touch" export CONFIG_FTS_TRUSTED_TOUCH=n diff --git a/config/gki_blairtouchconf.h b/config/gki_blairtouchconf.h index 63892cb9c1..27469e99cd 100644 --- a/config/gki_blairtouchconf.h +++ b/config/gki_blairtouchconf.h @@ -9,4 +9,5 @@ #define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C 1 #define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE 1 #define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH 1 +#define CONFIG_TOUCHSCREEN_GOODIX_BRL 1 #define CONFIG_TOUCHSCREEN_FTS_DIRECTORY "focaltech_touch" diff --git a/touch_driver_board.mk b/touch_driver_board.mk index f243bff810..deacae8b3e 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -26,7 +26,8 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) else ifeq ($(TARGET_BOARD_PLATFORM), blair) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko \ $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ - $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko + $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko else ifeq ($(TARGET_BOARD_PLATFORM), crow) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/goodix_ts.ko else ifeq ($(TARGET_BOARD_PLATFORM), bengal) diff --git a/touch_driver_product.mk b/touch_driver_product.mk index dfccfd0cfe..f27a982cc7 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -24,7 +24,8 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) else ifeq ($(TARGET_BOARD_PLATFORM), blair) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko \ $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ - $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko + $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko else ifeq ($(TARGET_BOARD_PLATFORM), crow) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/goodix_ts.ko else ifeq ($(TARGET_BOARD_PLATFORM), bengal) From c4e8550d3bceaebbe4a94def18efe6e38d9d80e7 Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Tue, 4 Jul 2023 12:40:12 +0530 Subject: [PATCH 135/170] touch: trinket: enable touch driver Enable synaptics_tcm touch driver for trinket target. Change-Id: I7c6233b4f345c52a01ebebb92da2cb7d546ef24f Signed-off-by: Surya Teja Kudiri --- Android.mk | 13 +++++++++++++ Kbuild | 5 +++++ config/gki_trinkettouch.conf | 5 +++++ config/gki_trinkettouchconf.h | 9 +++++++++ touch_driver_board.mk | 2 ++ touch_driver_product.mk | 2 ++ 6 files changed, 36 insertions(+) create mode 100644 config/gki_trinkettouch.conf create mode 100644 config/gki_trinkettouchconf.h diff --git a/Android.mk b/Android.mk index a29dcc0603..67d53b2aa4 100644 --- a/Android.mk +++ b/Android.mk @@ -250,6 +250,19 @@ else ifeq ($(TARGET_BOARD_PLATFORM), bengal) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### +else ifeq ($(TARGET_BOARD_PLATFORM), trinket) + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := synaptics_tcm_ts.ko + LOCAL_MODULE_KBUILD_NAME := synaptics_tcm_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + else ########################################################### diff --git a/Kbuild b/Kbuild index 417a98bb07..392d00f3df 100644 --- a/Kbuild +++ b/Kbuild @@ -41,6 +41,11 @@ ifeq ($(CONFIG_ARCH_CROW), y) LINUX_INC += -include $(TOUCH_ROOT)/config/gki_crowtouchconf.h endif +ifeq ($(CONFIG_ARCH_TRINKET), y) + include $(TOUCH_ROOT)/config/gki_trinkettouch.conf + LINUX_INC += -include $(TOUCH_ROOT)/config/gki_trinkettouchconf.h +endif + LINUX_INC += -Iinclude/linux \ -Iinclude/linux/drm \ -Iinclude/linux/gunyah \ diff --git a/config/gki_trinkettouch.conf b/config/gki_trinkettouch.conf new file mode 100644 index 0000000000..9b5b5f85c6 --- /dev/null +++ b/config/gki_trinkettouch.conf @@ -0,0 +1,5 @@ +export CONFIG_MSM_TOUCH=m +export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM=y +export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C=y +export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE=y +export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH=y diff --git a/config/gki_trinkettouchconf.h b/config/gki_trinkettouchconf.h new file mode 100644 index 0000000000..981944037d --- /dev/null +++ b/config/gki_trinkettouchconf.h @@ -0,0 +1,9 @@ +/* +* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. +* SPDX-License-Identifier: GPL-2.0-only +*/ + +#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM 1 +#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C 1 +#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE 1 +#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH 1 diff --git a/touch_driver_board.mk b/touch_driver_board.mk index deacae8b3e..39fcfa70f1 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -33,6 +33,8 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) else ifeq ($(TARGET_BOARD_PLATFORM), bengal) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko + else ifeq ($(TARGET_BOARD_PLATFORM), trinket) + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko else BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ diff --git a/touch_driver_product.mk b/touch_driver_product.mk index f27a982cc7..d6c69f96ac 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -31,6 +31,8 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) else ifeq ($(TARGET_BOARD_PLATFORM), bengal) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko + else ifeq ($(TARGET_BOARD_PLATFORM), trinket) + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko else PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ From e6702160cfbf7444f27bb31b946ae0aefb4ed01e Mon Sep 17 00:00:00 2001 From: Surya Teja Kudiri Date: Sun, 9 Jul 2023 22:24:50 +0530 Subject: [PATCH 136/170] touch : raydium : fix reboot issue use corresponding free api in shutdown and remove. Change-Id: Id009c764e4007e8bd2e010f8f06e61653df88f21 Signed-off-by: Surya Teja Kudiri --- raydium/raydium_driver.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 73c30d2372..6d8ef874d5 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -2553,7 +2553,8 @@ if (active_panel) raydium_enable_regulator(g_raydium_ts, false); raydium_get_regulator(g_raydium_ts, false); - kfree(g_raydium_ts); + devm_kfree(&client->dev, g_raydium_ts); + g_raydium_ts = NULL; i2c_set_clientdata(client, NULL); LOGD(LOG_INFO, "[touch] %s: done\n", __func__); @@ -2601,7 +2602,8 @@ if (active_panel) raydium_enable_regulator(g_raydium_ts, false); raydium_get_regulator(g_raydium_ts, false); - kfree(g_raydium_ts); + devm_kfree(&client->dev, g_raydium_ts); + g_raydium_ts = NULL; i2c_set_clientdata(client, NULL); LOGD(LOG_INFO, "[touch] %s: done\n", __func__); From b45246852164dcb59bc5da0b9e6a149fd4db011f Mon Sep 17 00:00:00 2001 From: Ritesh Kumar Date: Wed, 19 Jul 2023 11:46:52 +0530 Subject: [PATCH 137/170] touch: synaptics_tcm: fix race condition between touch isr and resume During touch resume for the first time, syna_tcm_identify times out when expected syna_tcm_isr does not come. ISR does not come due to previous pending ISR. Previous ISR is waiting for mutex lock mod_pool.mutex which is acquired in syna_tcm_resume function leading to deadlock till timeout. To fix this, early return from syna_tcm_isr if it is unable to acquire mod_pool.mutex. Change-Id: I18be302edfb19270014610451dda97331d78049f Signed-off-by: Ritesh Kumar --- synaptics_tcm/synaptics_tcm_core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/synaptics_tcm/synaptics_tcm_core.c b/synaptics_tcm/synaptics_tcm_core.c index 4428b164bb..9f89f1ba5c 100644 --- a/synaptics_tcm/synaptics_tcm_core.c +++ b/synaptics_tcm/synaptics_tcm_core.c @@ -629,7 +629,12 @@ static void syna_tcm_dispatch_report(struct syna_tcm_hcd *tcm_hcd) tcm_hcd->report.id = tcm_hcd->status_report_code; - mutex_lock(&mod_pool.mutex); + if (!mutex_trylock(&mod_pool.mutex)) { + LOGI(tcm_hcd->pdev->dev.parent, "unable to acquire mod_pool.mutex\n"); + UNLOCK_BUFFER(tcm_hcd->report.buffer); + UNLOCK_BUFFER(tcm_hcd->in); + return; + } if (!list_empty(&mod_pool.list)) { list_for_each_entry(mod_handler, &mod_pool.list, link) { From 9125b26673e3cccc7a3c777ef72de6600e80ae92 Mon Sep 17 00:00:00 2001 From: Jyothi bommidi Date: Mon, 31 Jul 2023 14:12:55 +0530 Subject: [PATCH 138/170] touch: blair: Add support for Bazel to build touch modules Add support for touch modules to be built with Bazel for blair. Change-Id: Ib5c5767a714e6b8534c8d520652226ab3e034018 Signed-off-by: Jyothi bommidi --- Android.mk | 5 +++++ BUILD.bazel | 22 +++++++++++++++++++--- target.bzl | 39 +++++++++++++++++++++++++++++++++++---- touch_modules.bzl | 28 ++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 7 deletions(-) diff --git a/Android.mk b/Android.mk index 67d53b2aa4..fc87646275 100644 --- a/Android.mk +++ b/Android.mk @@ -14,6 +14,11 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) ifeq ($(TARGET_BOARD_PLATFORM), pineapple) LOCAL_MODULE_DDK_BUILD := true endif + + ifeq ($(TARGET_BOARD_PLATFORM), blair) + LOCAL_MODULE_DDK_BUILD := true + endif + include $(CLEAR_VARS) # This makefile is only for DLKM diff --git a/BUILD.bazel b/BUILD.bazel index d1f5ff70e9..539220be41 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -22,6 +22,22 @@ ddk_headers( ) ) +ddk_headers( + name = "focaltech_headers", + hdrs = glob([ + "focaltech_touch/*.h" + ] + ) +) + +ddk_headers( + name = "synaptics_tcm_headers", + hdrs = glob([ + "synaptics_tcm/*.h" + ] + ) +) + ddk_headers( name = "config_headers", hdrs = glob([ @@ -33,8 +49,8 @@ ddk_headers( ddk_headers( name = "touch_drivers_headers", - hdrs = [":goodix_ts_headers", ":nt36xxx_headers", ":config_headers"] + hdrs = [":goodix_ts_headers", ":nt36xxx_headers", ":focaltech_headers", ":synaptics_tcm_headers", ":config_headers"] ) -load(":target.bzl", "define_pineapple") -define_pineapple() +load(":target.bzl", "define_touch_target") +define_touch_target() diff --git a/target.bzl b/target.bzl index 0af8e2963d..3de4cd3ac4 100644 --- a/target.bzl +++ b/target.bzl @@ -1,9 +1,11 @@ load(":touch_modules.bzl", "touch_driver_modules") -load(":touch_modules_build.bzl", "define_consolidate_gki_modules") +load(":touch_modules_build.bzl", "define_target_variant_modules") +load("//msm-kernel:target_variants.bzl", "get_all_la_variants", "get_all_le_variants", "get_all_lxc_variants") -def define_pineapple(): - define_consolidate_gki_modules( - target = "pineapple", +def define_pineapple(t,v): + define_target_variant_modules( + target = t, + variant = v, registry = touch_driver_modules, modules = [ "nt36xxx-i2c", @@ -21,3 +23,32 @@ def define_pineapple(): "CONFIG_TOUCHSCREEN_DUMMY" ], ) + +def define_blair(t,v): + define_target_variant_modules( + target = t, + variant = v, + registry = touch_driver_modules, + modules = [ + "nt36xxx-i2c", + "goodix_ts", + "focaltech_fts", + "synaptics_tcm_ts" + ], + config_options = [ + "TOUCH_DLKM_ENABLE", + "CONFIG_ARCH_BLAIR", + "CONFIG_MSM_TOUCH", + "CONFIG_TOUCHSCREEN_NT36XXX_I2C", + "CONFIG_TOUCHSCREEN_GOODIX_BRL", + "CONFIG_TOUCH_FOCALTECH", + "CONFIG_TOUCHSCREEN_SYNAPTICS_TCM" + ], +) + +def define_touch_target(): + for (t, v) in get_all_la_variants() + get_all_le_variants() + get_all_lxc_variants(): + if t == "blair": + define_blair(t, v) + else: + define_pineapple(t, v) diff --git a/touch_modules.bzl b/touch_modules.bzl index 2d23a201dd..1db7646bbf 100644 --- a/touch_modules.bzl +++ b/touch_modules.bzl @@ -40,6 +40,34 @@ module_entry( ] ) +#define ddk_module() for focaltech_fts +module_entry( + name = "focaltech_fts", + config_option = "CONFIG_TOUCH_FOCALTECH", + srcs = [ + "focaltech_touch/focaltech_core.c", + "focaltech_touch/focaltech_esdcheck.c", + "focaltech_touch/focaltech_ex_fun.c", + "focaltech_touch/focaltech_ex_mode.c", + "focaltech_touch/focaltech_flash/focaltech_upgrade_ft3518.c", + "focaltech_touch/focaltech_flash.c", + "focaltech_touch/focaltech_gesture.c", + "focaltech_touch/focaltech_i2c.c", + "focaltech_touch/focaltech_point_report_check.c" + ] +) + +#define ddk_module() for synaptics_tcm_ts +module_entry( + name = "synaptics_tcm_ts", + config_option = "CONFIG_TOUCHSCREEN_SYNAPTICS_TCM", + srcs = [ + "synaptics_tcm/synaptics_tcm_core.c", + "synaptics_tcm/synaptics_tcm_i2c.c", + "synaptics_tcm/synaptics_tcm_touch.c" + ] +) + #define ddk_module() for atmel_mxt_ts module_entry( name = "atmel_mxt_ts", From ec94fd9408825ebf8840b91bc878f58d0ae8a581 Mon Sep 17 00:00:00 2001 From: Anand Tarakh Date: Mon, 21 Aug 2023 18:20:44 +0530 Subject: [PATCH 139/170] touch: synaptics_tcm: disable touch irq during suspend Disable touch irq during suspend. Change-Id: Ic2c4eac2dece9c4290b04b7936f99b2d3dcbb131 Signed-off-by: Anand Tarakh --- synaptics_tcm/synaptics_tcm_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/synaptics_tcm/synaptics_tcm_core.c b/synaptics_tcm/synaptics_tcm_core.c index 9f89f1ba5c..0b4fba2dae 100644 --- a/synaptics_tcm/synaptics_tcm_core.c +++ b/synaptics_tcm/synaptics_tcm_core.c @@ -3036,6 +3036,10 @@ static int syna_tcm_suspend(struct device *dev) } } +#ifndef WAKEUP_GESTURE + tcm_hcd->enable_irq(tcm_hcd, false, true); +#endif + retval = syna_tcm_enable_regulator(tcm_hcd, false); if (retval < 0) { LOGE(tcm_hcd->pdev->dev.parent, From 340864a1699dd6719c9df7eaeffd6a8fd78b5c87 Mon Sep 17 00:00:00 2001 From: Ritesh Kumar Date: Mon, 11 Sep 2023 11:09:46 +0530 Subject: [PATCH 140/170] touch: synaptics_tcm: skip creating sysfs nodes Sysfs nodes are defined as device type. If they are accessed through kobj type, it leads to CFI failure. This change skip creating sysfs node as its not required for touch functionality. Ideally all the sysfs node should be refactored with kobj attribute. Change-Id: I9ca69799231b0df7b10b637d669b64f5bba76918 Signed-off-by: Ritesh Kumar --- synaptics_tcm/synaptics_tcm_core.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/synaptics_tcm/synaptics_tcm_core.c b/synaptics_tcm/synaptics_tcm_core.c index 0b4fba2dae..1f4a058ac6 100644 --- a/synaptics_tcm/synaptics_tcm_core.c +++ b/synaptics_tcm/synaptics_tcm_core.c @@ -94,6 +94,8 @@ #define HOST_DOWNLOAD_TIMEOUT_MS 1000 +/* #define CREATE_SYSFS */ + #define DYNAMIC_CONFIG_SYSFS_DIR_NAME "dynamic_config" #define dynamic_config_sysfs(c_name, id) \ @@ -3350,7 +3352,9 @@ err_pinctrl_get: static int syna_tcm_probe(struct platform_device *pdev) { int retval; +#ifdef CREATE_SYSFS int idx; +#endif struct syna_tcm_hcd *tcm_hcd; const struct syna_tcm_board_data *bdata; const struct syna_tcm_hw_interface *hw_if; @@ -3489,6 +3493,7 @@ static int syna_tcm_probe(struct platform_device *pdev) } } +#ifdef CREATE_SYSFS sysfs_dir = kobject_create_and_add(PLATFORM_DRIVER_NAME, &pdev->dev.kobj); if (!sysfs_dir) { @@ -3529,6 +3534,7 @@ static int syna_tcm_probe(struct platform_device *pdev) goto err_sysfs_create_dynamic_config_file; } } +#endif #ifdef CONFIG_DRM if (active_panel) { @@ -3592,6 +3598,7 @@ err_create_run_kthread: fb_unregister_client(&tcm_hcd->fb_notifier); #endif +#ifdef CREATE_SYSFS err_sysfs_create_dynamic_config_file: for (idx--; idx >= 0; idx--) { sysfs_remove_file(tcm_hcd->dynamnic_config_sysfs_dir, @@ -3619,6 +3626,7 @@ err_sysfs_create_dir: if (bdata->reset_gpio >= 0) syna_tcm_set_gpio(tcm_hcd, bdata->reset_gpio, false, 0, 0); +#endif err_config_gpio: syna_tcm_enable_regulator(tcm_hcd, false); From bdc9f8a9b71610321f0f3956f4ebfc6b617b300f Mon Sep 17 00:00:00 2001 From: Rohith Iyer Date: Fri, 16 Jun 2023 09:30:41 -0700 Subject: [PATCH 141/170] touch: goodix: Replace DEVICE_ATTR() with __ATTR() For the two nodes goodix_sysfs_update_en_store and goodix_sysfs_result_show: Since sysfs expects kobj type, replace DEVICE_ATTR which creates a device, with __ATTR which creates a kobj. Change-Id: If571d1c8e18ee4c94f60e7930b9e8ed886e6402e Signed-off-by: Rohith Iyer --- goodix_berlin_driver/goodix_brl_fwupdate.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/goodix_berlin_driver/goodix_brl_fwupdate.c b/goodix_berlin_driver/goodix_brl_fwupdate.c index fc2db13e6f..f319ab752c 100644 --- a/goodix_berlin_driver/goodix_brl_fwupdate.c +++ b/goodix_berlin_driver/goodix_brl_fwupdate.c @@ -997,7 +997,7 @@ err_fw_prepare: * '6'[110] update in unblocking mode with fwdata from request */ static ssize_t goodix_sysfs_update_en_store( - struct device *dev, struct device_attribute *attr, + struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { int ret = 0; @@ -1066,7 +1066,7 @@ static ssize_t goodix_sysfs_fwimage_store(struct file *file, /* return fw_update result */ static ssize_t goodix_sysfs_result_show( - struct device *dev, struct device_attribute *attr, + struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; @@ -1101,12 +1101,14 @@ static ssize_t goodix_sysfs_result_show( return r; } -static DEVICE_ATTR(update_en, 0220, NULL, goodix_sysfs_update_en_store); -static DEVICE_ATTR(result, 0664, goodix_sysfs_result_show, NULL); +static struct kobj_attribute goodix_sysfs_update = + __ATTR(update_en, 0220, NULL, goodix_sysfs_update_en_store); +static struct kobj_attribute goodix_sysfs_result = + __ATTR(result, 0664, goodix_sysfs_result_show, NULL); static struct attribute *goodix_fwu_attrs[] = { - &dev_attr_update_en.attr, - &dev_attr_result.attr + &goodix_sysfs_update.attr, + &goodix_sysfs_result.attr }; static int goodix_fw_sysfs_init(struct goodix_ts_core *core_data, From dafcd09657e19a603bdcfaec510f0b8cb7a7fc20 Mon Sep 17 00:00:00 2001 From: Ritesh Kumar Date: Wed, 27 Sep 2023 00:29:47 +0530 Subject: [PATCH 142/170] touch: enable touch drivers for pitti Add touch driver compilation support for pitti target. Change-Id: I2323cdf83a8f582c1e31fb0e7c42e33e05ad4a2e Signed-off-by: Ritesh Kumar --- Android.mk | 28 ++++++++++++++++++++++++++++ target.bzl | 20 ++++++++++++++++++++ touch_driver_board.mk | 3 +++ touch_driver_product.mk | 3 +++ 4 files changed, 54 insertions(+) diff --git a/Android.mk b/Android.mk index fc87646275..64782937c5 100644 --- a/Android.mk +++ b/Android.mk @@ -19,6 +19,10 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) LOCAL_MODULE_DDK_BUILD := true endif + ifeq ($(TARGET_BOARD_PLATFORM), pitti) + LOCAL_MODULE_DDK_BUILD := true + endif + include $(CLEAR_VARS) # This makefile is only for DLKM @@ -268,6 +272,30 @@ else ifeq ($(TARGET_BOARD_PLATFORM), trinket) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### +else ifeq ($(TARGET_BOARD_PLATFORM), pitti) + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := focaltech_fts.ko + LOCAL_MODULE_KBUILD_NAME := focaltech_fts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := goodix_ts.ko + LOCAL_MODULE_KBUILD_NAME := goodix_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + else ########################################################### diff --git a/target.bzl b/target.bzl index 3de4cd3ac4..b06b5c3644 100644 --- a/target.bzl +++ b/target.bzl @@ -46,9 +46,29 @@ def define_blair(t,v): ], ) +def define_pitti(t,v): + define_target_variant_modules( + target = t, + variant = v, + registry = touch_driver_modules, + modules = [ + "focaltech_fts", + "goodix_ts" + ], + config_options = [ + "TOUCH_DLKM_ENABLE", + "CONFIG_ARCH_PITTI", + "CONFIG_MSM_TOUCH", + "CONFIG_TOUCH_FOCALTECH", + "CONFIG_TOUCHSCREEN_GOODIX_BRL" + ], +) + def define_touch_target(): for (t, v) in get_all_la_variants() + get_all_le_variants() + get_all_lxc_variants(): if t == "blair": define_blair(t, v) + elif t == "pitti": + define_pitti(t, v) else: define_pineapple(t, v) diff --git a/touch_driver_board.mk b/touch_driver_board.mk index 39fcfa70f1..b50ff2b123 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -35,6 +35,9 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko else ifeq ($(TARGET_BOARD_PLATFORM), trinket) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko + else ifeq ($(TARGET_BOARD_PLATFORM), pitti) + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko else BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ diff --git a/touch_driver_product.mk b/touch_driver_product.mk index d6c69f96ac..a0ed8c4949 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -33,6 +33,9 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko else ifeq ($(TARGET_BOARD_PLATFORM), trinket) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko + else ifeq ($(TARGET_BOARD_PLATFORM), pitti) + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko \ + $(KERNEL_MODULES_OUT)/goodix_ts.ko else PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ From 4c103f2d576fd2177573ee039744a5e636fdc38b Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Fri, 6 Oct 2023 15:26:19 +0530 Subject: [PATCH 143/170] touch-drivers: Update hardcoded paths Change the hardcoded path to store into a variable that can be changed later according to the usage. Change-Id: Ie404e26f7d5de986f589c21b988e07331d850093 Signed-off-by: Srikanth Katteboina --- Android.mk | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Android.mk b/Android.mk index 64782937c5..1b9431e31c 100644 --- a/Android.mk +++ b/Android.mk @@ -9,7 +9,9 @@ endif ifeq ($(TOUCH_DLKM_ENABLE), true) TOUCH_SELECT := CONFIG_MSM_TOUCH=m - + BOARD_OPENSOURCE_DIR ?= vendor/qcom/opensource + BOARD_COMMON_DIR ?= device/qcom/common + LOCAL_PATH := $(call my-dir) ifeq ($(TARGET_BOARD_PLATFORM), pineapple) LOCAL_MODULE_DDK_BUILD := true @@ -29,10 +31,10 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) ifneq ($(findstring vendor,$(LOCAL_PATH)),) ifneq ($(findstring opensource,$(LOCAL_PATH)),) - TOUCH_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/touch-drivers + TOUCH_BLD_DIR := $(shell pwd)/$(BOARD_OPENSOURCE_DIR)/touch-drivers endif # opensource - DLKM_DIR := $(TOP)/device/qcom/common/dlkm + DLKM_DIR := $(TOP)/$(BOARD_COMMON_DIR)/dlkm LOCAL_ADDITIONAL_DEPENDENCIES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) From 11b8630d8877f0e926be6971c8592760bdead45d Mon Sep 17 00:00:00 2001 From: Rohith Iyer Date: Fri, 16 Jun 2023 09:30:41 -0700 Subject: [PATCH 144/170] touch: goodix: Replace DEVICE_ATTR() with __ATTR() For the two nodes goodix_sysfs_update_en_store and goodix_sysfs_result_show: Since sysfs expects kobj type, replace DEVICE_ATTR which creates a device, with __ATTR which creates a kobj. Change-Id: If571d1c8e18ee4c94f60e7930b9e8ed886e6402e Signed-off-by: Rohith Iyer --- goodix_berlin_driver/goodix_brl_fwupdate.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/goodix_berlin_driver/goodix_brl_fwupdate.c b/goodix_berlin_driver/goodix_brl_fwupdate.c index fc2db13e6f..f319ab752c 100644 --- a/goodix_berlin_driver/goodix_brl_fwupdate.c +++ b/goodix_berlin_driver/goodix_brl_fwupdate.c @@ -997,7 +997,7 @@ err_fw_prepare: * '6'[110] update in unblocking mode with fwdata from request */ static ssize_t goodix_sysfs_update_en_store( - struct device *dev, struct device_attribute *attr, + struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { int ret = 0; @@ -1066,7 +1066,7 @@ static ssize_t goodix_sysfs_fwimage_store(struct file *file, /* return fw_update result */ static ssize_t goodix_sysfs_result_show( - struct device *dev, struct device_attribute *attr, + struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; @@ -1101,12 +1101,14 @@ static ssize_t goodix_sysfs_result_show( return r; } -static DEVICE_ATTR(update_en, 0220, NULL, goodix_sysfs_update_en_store); -static DEVICE_ATTR(result, 0664, goodix_sysfs_result_show, NULL); +static struct kobj_attribute goodix_sysfs_update = + __ATTR(update_en, 0220, NULL, goodix_sysfs_update_en_store); +static struct kobj_attribute goodix_sysfs_result = + __ATTR(result, 0664, goodix_sysfs_result_show, NULL); static struct attribute *goodix_fwu_attrs[] = { - &dev_attr_update_en.attr, - &dev_attr_result.attr + &goodix_sysfs_update.attr, + &goodix_sysfs_result.attr }; static int goodix_fw_sysfs_init(struct goodix_ts_core *core_data, From a0abc9d2dc2a95c6228525a43adaec7b699fdd6f Mon Sep 17 00:00:00 2001 From: Sayantan Majumder Date: Thu, 31 Aug 2023 22:12:25 +0530 Subject: [PATCH 145/170] touch: LW: Add support for Bazel to build touch modules Add support for touch modules to be built with Bazel for LW IDP. Change-Id: Iae6825c816938b810daa400095265222eda2772a Signed-off-by: Srikanth Katteboina Signed-off-by: Sayantan Majumder --- Android.mk | 4 ++++ BUILD.bazel | 10 +++++++++- target.bzl | 27 +++++++++++++++++++++++++++ touch_modules.bzl | 42 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 64782937c5..3f45863eb5 100644 --- a/Android.mk +++ b/Android.mk @@ -23,6 +23,10 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) LOCAL_MODULE_DDK_BUILD := true endif + ifeq ($(TARGET_BOARD_PLATFORM), monaco) + LOCAL_MODULE_DDK_BUILD := true + endif + include $(CLEAR_VARS) # This makefile is only for DLKM diff --git a/BUILD.bazel b/BUILD.bazel index 539220be41..28a7c03f39 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -47,9 +47,17 @@ ddk_headers( includes = ["config"] ) +ddk_headers( + name = "pt_headers", + hdrs = glob([ + "pt/*.h", + ] + ) +) + ddk_headers( name = "touch_drivers_headers", - hdrs = [":goodix_ts_headers", ":nt36xxx_headers", ":focaltech_headers", ":synaptics_tcm_headers", ":config_headers"] + hdrs = [":goodix_ts_headers", ":nt36xxx_headers", ":focaltech_headers", ":synaptics_tcm_headers", ":pt_headers", ":config_headers"] ) load(":target.bzl", "define_touch_target") diff --git a/target.bzl b/target.bzl index b06b5c3644..6dc0e2009f 100644 --- a/target.bzl +++ b/target.bzl @@ -64,11 +64,38 @@ def define_pitti(t,v): ], ) +def define_monaco(t,v): + define_target_variant_modules( + target = t, + variant = v, + registry = touch_driver_modules, + modules = [ + "pt_ts", + "pt_i2c", + "pt_device_access", + "pt_debug", + ], + config_options = [ + "TOUCH_DLKM_ENABLE", + "CONFIG_ARCH_MONACO", + "CONFIG_MSM_TOUCH", + "CONFIG_TOUCHSCREEN_PARADE", + "CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT", + "CONFIG_TOUCHSCREEN_PARADE_I2C", + "CONFIG_TOUCHSCREEN_PARADE_DEVICE_ACCESS", + "CONFIG_TOUCHSCREEN_PARADE_BUTTON", + "CONFIG_TOUCHSCREEN_PARADE_PROXIMITY", + "CONFIG_TOUCHSCREEN_PARADE_DEBUG_MDL", + ], +) + def define_touch_target(): for (t, v) in get_all_la_variants() + get_all_le_variants() + get_all_lxc_variants(): if t == "blair": define_blair(t, v) elif t == "pitti": define_pitti(t, v) + elif t == "monaco": + define_monaco(t, v) else: define_pineapple(t, v) diff --git a/touch_modules.bzl b/touch_modules.bzl index 1db7646bbf..b26b0ec9f9 100644 --- a/touch_modules.bzl +++ b/touch_modules.bzl @@ -85,3 +85,45 @@ module_entry( "dummy_touch/dummy_touch.c" ] ) + +#define ddk_module() for pt_ts +module_entry( + name = "pt_ts", + config_option = "CONFIG_TOUCHSCREEN_PARADE", + srcs = [ + "pt/pt_core.c", + "pt/pt_devtree.c", + "pt/pt_mt_common.c", + "pt/pt_platform.c", + "pt/pt_btn.c", + "pt/pt_mtb.c", + "pt/pt_proximity.c" + ] +) + +#define ddk_module() for pt_i2c +module_entry( + name = "pt_i2c", + config_option = "CONFIG_TOUCHSCREEN_PARADE_I2C", + srcs = [ + "pt/pt_i2c.c" + ] +) + +#define ddk_module() for pt_device_access +module_entry( + name = "pt_device_access", + config_option = "CONFIG_TOUCHSCREEN_PARADE_DEVICE_ACCESS", + srcs = [ + "pt/pt_device_access.c" + ] +) + +#define ddk_module() for pt_debug +module_entry( + name = "pt_debug", + config_option = "CONFIG_TOUCHSCREEN_PARADE_DEBUG_MDL", + srcs = [ + "pt/pt_debug.c" + ] +) From 2896379d1d67dee0603603c58d9dfad44ef36c30 Mon Sep 17 00:00:00 2001 From: Sayantan Majumder Date: Thu, 31 Aug 2023 23:06:19 +0530 Subject: [PATCH 146/170] touch: LW: Add support for Bazel to build touch modules Adding support for touch modules to be built with Bazel for LW WDP. Change-Id: I2f0b400e959e17329ae8a3a35cad03ebd0d9cad9 Signed-off-by: Srikanth Katteboina Signed-off-by: Sayantan Majumder --- BUILD.bazel | 13 +++++++++++-- target.bzl | 2 ++ touch_modules.bzl | 17 +++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/BUILD.bazel b/BUILD.bazel index 28a7c03f39..add00fdf4f 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -50,14 +50,23 @@ ddk_headers( ddk_headers( name = "pt_headers", hdrs = glob([ - "pt/*.h", + "pt/*.h" + ] + ) +) + +ddk_headers( + name = "raydium_headers", + hdrs = glob([ + "raydium/*.h", + "raydium/chip_raydium/*.h" ] ) ) ddk_headers( name = "touch_drivers_headers", - hdrs = [":goodix_ts_headers", ":nt36xxx_headers", ":focaltech_headers", ":synaptics_tcm_headers", ":pt_headers", ":config_headers"] + hdrs = [":goodix_ts_headers", ":nt36xxx_headers", ":focaltech_headers", ":synaptics_tcm_headers", ":pt_headers", ":raydium_headers", ":config_headers"] ) load(":target.bzl", "define_touch_target") diff --git a/target.bzl b/target.bzl index 6dc0e2009f..9cc9810f1b 100644 --- a/target.bzl +++ b/target.bzl @@ -74,6 +74,7 @@ def define_monaco(t,v): "pt_i2c", "pt_device_access", "pt_debug", + "raydium_ts", ], config_options = [ "TOUCH_DLKM_ENABLE", @@ -86,6 +87,7 @@ def define_monaco(t,v): "CONFIG_TOUCHSCREEN_PARADE_BUTTON", "CONFIG_TOUCHSCREEN_PARADE_PROXIMITY", "CONFIG_TOUCHSCREEN_PARADE_DEBUG_MDL", + "CONFIG_TOUCHSCREEN_RM_TS", ], ) diff --git a/touch_modules.bzl b/touch_modules.bzl index b26b0ec9f9..e250a41681 100644 --- a/touch_modules.bzl +++ b/touch_modules.bzl @@ -127,3 +127,20 @@ module_entry( "pt/pt_debug.c" ] ) + +#define ddk_module() for raydium_ts +module_entry( + name = "raydium_ts", + config_option = "CONFIG_TOUCHSCREEN_RM_TS", + srcs = [ + "raydium/drv_interface.c", + "raydium/raydium_driver.c", + "raydium/raydium_fw_update.c", + "raydium/raydium_selftest.c", + "raydium/raydium_sysfs.c", + "raydium/chip_raydium/f303_ic_control.c", + "raydium/chip_raydium/f303_ic_test.c", + "raydium/chip_raydium/ic_drv_global.c", + "raydium/chip_raydium/ic_drv_interface.c" + ] +) From 6207267c95f530eb718b5568f8fe883b3bf5b75f Mon Sep 17 00:00:00 2001 From: Rui Chen Date: Fri, 17 Nov 2023 10:11:28 +0800 Subject: [PATCH 147/170] touch: goodix: vote avdd power grid to the right voltage On palawan target, TP avdd was loaded on L22B which limited the voltage from 2700mv to 3400mv and initialized to 2700mv. From the touch side, the typical voltage is 2.8v~3.2v, and our existing FW was tuned based on AVDD 3.2V. Change-Id: I4efe51b26cf8de41d0a03bf29b69e3fd07ddabab Signed-off-by: Rui Chen --- goodix_berlin_driver/goodix_brl_hw.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/goodix_berlin_driver/goodix_brl_hw.c b/goodix_berlin_driver/goodix_brl_hw.c index 1bb7356583..c02ba6178b 100644 --- a/goodix_berlin_driver/goodix_brl_hw.c +++ b/goodix_berlin_driver/goodix_brl_hw.c @@ -206,6 +206,9 @@ static int brl_reset_after(struct goodix_ts_core *cd) #define REG_SUSPEND_CURRENT 20 #define REG_RESUME_CURRENT 30000 +#define REG_RESUME_MIN_VOLTAGE 3200000 +#define REG_RESUME_MAX_VOLTAGE 3200000 + static int brl_power_on(struct goodix_ts_core *cd, bool on) { int ret = 0; @@ -234,6 +237,19 @@ static int brl_power_on(struct goodix_ts_core *cd, bool on) if (avdd_gpio > 0) { gpio_direction_output(avdd_gpio, 1); } else if (cd->avdd) { + if (regulator_count_voltages(cd->avdd) > 0) { + ret = regulator_set_load(cd->avdd, REG_RESUME_CURRENT); + if (ret) { + ts_err("vdd regulator set_load failed ret=%d", ret); + return ret; + } + ret = regulator_set_voltage(cd->avdd, REG_RESUME_MIN_VOLTAGE, + REG_RESUME_MAX_VOLTAGE); + if (ret) { + ts_err("vdd regulator set_vtg failed ret=%d", ret); + return ret; + } + } ret = regulator_enable(cd->avdd); if (ret < 0) { ts_err("Failed to enable avdd:%d", ret); From a405414d959b46d416beff9af5c8869bce5e06b8 Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Fri, 25 Aug 2023 17:15:15 +0530 Subject: [PATCH 148/170] touch: pt: Touch_offload using Glink Enabled Glink interface for Parade Touch Sensor. Change-Id: Ie5a63797eec8ed329484d751fbb843f5876a4229 Signed-off-by: Srikanth Katteboina --- pt/pt_core.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index 1d5932df43..33fa6c6dea 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include "pt_regs.h" #if defined(CONFIG_PANEL_NOTIFIER) #include @@ -74,16 +76,20 @@ enum core_states { static int pt_enable_i2c_regulator(struct pt_core_data *cd, bool en); #endif +#define PT_AMBIENT_MODE + +#ifdef PT_AMBIENT_MODE static int pt_device_exit(struct i2c_client *client); int pt_device_entry(struct device *dev, u16 irq, size_t xfer_buf_size); +#endif static const char *pt_driver_core_name = PT_CORE_NAME; static const char *pt_driver_core_version = PT_DRIVER_VERSION; static const char *pt_driver_core_date = PT_DRIVER_DATE; enum core_states pt_core_state = STATE_NONE; - +uint32_t pt_slate_resp_ack; struct pt_hid_field { int report_count; int report_size; @@ -10816,6 +10822,7 @@ static int pt_core_suspend(struct device *dev) "%s: Error on disabling i2c regulator\n", __func__); } pt_debug(cd->dev, DL_INFO, "%s Suspend exit - rc = %d\n", __func__, rc); + return rc; } @@ -15053,14 +15060,22 @@ static ssize_t pt_pip2_gpio_read_show(struct device *dev, * PARAMETERS: * *client - pointer to i2c client structure ******************************************************************************/ +#ifdef PT_AMBIENT_MODE static int pt_device_exit(struct i2c_client *client) { struct pt_core_data *cd = i2c_get_clientdata(client); struct device *dev = cd->dev; + void *glink_pt_send_msg; + int glink_touch_enter = TOUCH_ENTER; + pt_debug(dev, DL_INFO,"%s: Start pt_device_exit\n", __func__); + glink_pt_send_msg = &glink_touch_enter; + pt_debug(dev, DL_INFO, "[touch]glink_pt_send_msg = %0x\n", glink_pt_send_msg); + glink_touch_tx_msg(glink_pt_send_msg, TOUCH_MSG_SIZE); + if (active_panel) panel_event_notifier_unregister(cd->entry); pt_core_state = STATE_SUSPEND; @@ -15078,10 +15093,9 @@ static int pt_device_exit(struct i2c_client *client) cancel_work_sync(&cd->suspend_work); pt_stop_wd_timer(cd); - device_init_wakeup(dev, 0); - disable_irq_nosync(cd->irq); + if (cd->cpdata->setup_irq) cd->cpdata->setup_irq(cd->cpdata, PT_MT_IRQ_FREE, dev); @@ -15094,6 +15108,7 @@ static int pt_device_exit(struct i2c_client *client) pt_debug(dev, DL_INFO,"%s: End pt_device_exit \n", __func__); return 0; } +#endif /******************************************************************************* * FUNCTION: pt_touch_offload_store @@ -17713,6 +17728,32 @@ err_pinctrl_get: return retval; } +void touch_notify_glink_pt_channel_state(bool state) +{ + pr_info("%s:[touch] touch_notify_glink\n", __func__); +} + +void glink_touch_pt_rx_msg(void *data, int len) +{ + int rc = 0; + pr_info("%s: TOUCH_RX_MSG Start:\n", __func__); + + if (len > TOUCH_GLINK_INTENT_SIZE) { + pr_err("Invalid TOUCH glink intent size\n"); + return; + } + /* check SLATE response */ + pt_slate_resp_ack = *(uint32_t *)&data[8]; + if (pt_slate_resp_ack == 0x01) { + pr_err("Bad SLATE response\n"); + rc = -EINVAL; + goto err_ret; + } + pr_info("%s: TOUCH_RX_MSG End:\n", __func__); +err_ret: +return; +} + /******************************************************************************* ******************************************************************************* * FUNCTION: pt_probe @@ -17937,6 +17978,8 @@ int pt_probe(const struct pt_bus_ops *ops, struct device *dev, */ cd->bus_ops = ops; + glink_touch_channel_init(&touch_notify_glink_pt_channel_state, &glink_touch_pt_rx_msg); + /* * When the IRQ GPIO is not direclty accessible and no function is * defined to get the IRQ status, the IRQ passed in must be assigned @@ -18234,6 +18277,7 @@ error_no_pdata: * PARAMETERS: * *dev - pointer to core device ******************************************************************************/ +#ifdef PT_AMBIENT_MODE int pt_device_entry(struct device *dev, u16 irq, size_t xfer_buf_size) { @@ -18241,6 +18285,8 @@ int pt_device_entry(struct device *dev, struct pt_platform_data *pdata = dev_get_platdata(dev); struct i2c_client *client = to_i2c_client(dev); int rc = 0; + void *glink_pt_send_msg; + int glink_touch_exit = TOUCH_EXIT; pt_debug(dev, DL_INFO, "%s: Start pt_device_entry\n", __func__); @@ -18248,6 +18294,12 @@ int pt_device_entry(struct device *dev, cd->pdata = pdata; cd->cpdata = pdata->core_pdata; + glink_pt_send_msg = &glink_touch_exit; + pt_debug(dev, DL_INFO, "[touch]glink_pt_send_msg = %d\n", glink_pt_send_msg); + glink_touch_tx_msg(glink_pt_send_msg, TOUCH_MSG_SIZE); + + msleep(150); + if (!rc && cd->ts_pinctrl) { /* * Pinctrl handle is optional. If pinctrl handle is found @@ -18376,6 +18428,8 @@ pt_error_detect: cd->cpdata->setup_power(cd->cpdata, PT_MT_POWER_OFF, dev); return rc; } +#endif + EXPORT_SYMBOL_GPL(pt_probe); /******************************************************************************* From 998604e0780fc539d452d41e70800626b6165924 Mon Sep 17 00:00:00 2001 From: Jyothi bommidi Date: Tue, 5 Dec 2023 11:25:22 +0530 Subject: [PATCH 149/170] touch: volcano: enable goodix touch compilation for volcano target Enable goodix touch compilation for volcano target. Change-Id: Ia5392ab3af579b1d1160dc1588bc91a2825e73b9 Signed-off-by: Jyothi bommidi --- Android.mk | 17 +++++++++++++++++ target.bzl | 18 ++++++++++++++++++ touch_driver_board.mk | 2 ++ touch_driver_product.mk | 2 ++ 4 files changed, 39 insertions(+) diff --git a/Android.mk b/Android.mk index 44101945c6..bd41ca7273 100644 --- a/Android.mk +++ b/Android.mk @@ -29,6 +29,10 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) LOCAL_MODULE_DDK_BUILD := true endif + ifeq ($(TARGET_BOARD_PLATFORM), volcano) + LOCAL_MODULE_DDK_BUILD := true + endif + include $(CLEAR_VARS) # This makefile is only for DLKM @@ -302,6 +306,19 @@ else ifeq ($(TARGET_BOARD_PLATFORM), pitti) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### +else ifeq ($(TARGET_BOARD_PLATFORM), volcano) + + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := goodix_ts.ko + LOCAL_MODULE_KBUILD_NAME := goodix_ts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + else ########################################################### diff --git a/target.bzl b/target.bzl index 9cc9810f1b..4e3db6a6c5 100644 --- a/target.bzl +++ b/target.bzl @@ -91,6 +91,22 @@ def define_monaco(t,v): ], ) +def define_volcano(t,v): + define_target_variant_modules( + target = t, + variant = v, + registry = touch_driver_modules, + modules = [ + "goodix_ts" + ], + config_options = [ + "TOUCH_DLKM_ENABLE", + "CONFIG_ARCH_VOLCANO", + "CONFIG_MSM_TOUCH", + "CONFIG_TOUCHSCREEN_GOODIX_BRL" + ], +) + def define_touch_target(): for (t, v) in get_all_la_variants() + get_all_le_variants() + get_all_lxc_variants(): if t == "blair": @@ -99,5 +115,7 @@ def define_touch_target(): define_pitti(t, v) elif t == "monaco": define_monaco(t, v) + elif t == "volcano": + define_volcano(t, v) else: define_pineapple(t, v) diff --git a/touch_driver_board.mk b/touch_driver_board.mk index b50ff2b123..6e11fe5a85 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -38,6 +38,8 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) else ifeq ($(TARGET_BOARD_PLATFORM), pitti) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko + else ifeq ($(TARGET_BOARD_PLATFORM), volcano) + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/goodix_ts.ko else BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ diff --git a/touch_driver_product.mk b/touch_driver_product.mk index a0ed8c4949..4d0876df58 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -36,6 +36,8 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) else ifeq ($(TARGET_BOARD_PLATFORM), pitti) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko + else ifeq ($(TARGET_BOARD_PLATFORM), volcano) + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/goodix_ts.ko else PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ From 5f7922ac8d08a277ea8f729f32517b0157571743 Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Wed, 7 Jun 2023 15:32:01 +0530 Subject: [PATCH 150/170] touch: raydium: firmware upgrade to 0.1 Firmware version upgraded to 0.1. Change-Id: I09c4daa579d7e0b4b83eba3ae758ff990b9f7d2b Signed-off-by: Srikanth Katteboina --- raydium/rad_fw_image_30.h | 15488 ++++++++++++++++++------------------ raydium/raydium_driver.h | 2 +- 2 files changed, 7762 insertions(+), 7728 deletions(-) diff --git a/raydium/rad_fw_image_30.h b/raydium/rad_fw_image_30.h index 60f9ff3197..fc549b4aeb 100644 --- a/raydium/rad_fw_image_30.h +++ b/raydium/rad_fw_image_30.h @@ -1,7 +1,12 @@ /* rad_fw_image_30.h + * + * QTI elects to receive under BSD license only. * * Raydium TouchScreen driver. * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * Copyright (c) 2021 Raydium tech Ltd. * * This program is free software; you can redistribute it and/or modify @@ -12,7746 +17,7775 @@ * 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. + * GNU General Public License for more details.i2c_driver/rad_fw_image_30.h * + * BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. or Linaro Ltd. nor the names of + * its contributors may be used to endorse or promote products + * derived from this software without specific prior written + * permission. + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* RAYDIUM FW VERSION : 0x02020001 */ + #define RAD_30 0x3202 const unsigned char u8_rad_boot_30[] = { -0x18, 0x01, 0x00, 0x20, 0x95, 0x01, 0x00, 0x00, -0x99, 0x01, 0x00, 0x00, 0x9B, 0x01, 0x00, 0x00, -0x9D, 0x01, 0x00, 0x00, 0x9F, 0x01, 0x00, 0x00, -0xA1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xA3, 0x01, 0x00, 0x00, -0xA5, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xA7, 0x01, 0x00, 0x00, 0x81, 0x0F, 0x00, 0x00, -0xE1, 0x0C, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00, -0xA1, 0x0F, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x00, -0xA1, 0x0D, 0x00, 0x00, 0x41, 0x0A, 0x00, 0x00, -0xAB, 0x01, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00, -0xAB, 0x01, 0x00, 0x00, 0x41, 0x0B, 0x00, 0x00, -0xE1, 0x0A, 0x00, 0x00, 0xA1, 0x0A, 0x00, 0x00, -0xC1, 0x0A, 0x00, 0x00, 0x81, 0x0A, 0x00, 0x00, -0xF1, 0x0F, 0x00, 0x00, 0xAB, 0x01, 0x00, 0x00, -0x03, 0xF3, 0x00, 0x0B, 0x03, 0x48, 0x85, 0x46, -0x00, 0xF0, 0x92, 0xF8, 0x00, 0x48, 0x00, 0x47, -0xCB, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x20, -0x80, 0xF3, 0x08, 0x88, 0x70, 0x47, 0x00, 0x00, -0x10, 0xB5, 0x25, 0x48, 0x81, 0x69, 0x01, 0x22, -0x92, 0x03, 0x11, 0x43, 0x81, 0x61, 0x41, 0x69, -0x10, 0x22, 0x11, 0x43, 0x41, 0x61, 0xD4, 0x01, -0x20, 0x68, 0xFF, 0xF7, 0xED, 0xFF, 0x60, 0x68, -0x1E, 0x49, 0x48, 0x60, 0x08, 0x60, 0x80, 0x47, -0x10, 0xBD, 0x01, 0x26, 0xB6, 0x07, 0x30, 0x68, -0x1B, 0x49, 0x08, 0x43, 0x30, 0x60, 0x18, 0x49, -0x48, 0x69, 0x20, 0x22, 0x10, 0x43, 0x48, 0x61, -0x00, 0xF0, 0x34, 0xF8, 0x00, 0xF0, 0xA5, 0xF8, -0x16, 0x4F, 0x17, 0x4C, 0x01, 0x25, 0x60, 0x68, -0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x16, 0xD0, -0x02, 0x28, 0xF8, 0xD1, 0xFF, 0xF7, 0xD0, 0xFF, -0xF5, 0xE7, 0x00, 0xF0, 0x5F, 0xF9, 0x03, 0x28, -0x07, 0xD0, 0x02, 0x28, 0xEF, 0xD1, 0x65, 0x60, -0x7D, 0x20, 0xC0, 0x00, 0x00, 0xF0, 0x84, 0xF8, -0xE9, 0xE7, 0xB8, 0x68, 0x00, 0x28, 0xE6, 0xD1, -0x02, 0x20, 0x60, 0x60, 0xE3, 0xE7, 0x00, 0xF0, -0x91, 0xFA, 0x01, 0x28, 0xDF, 0xD1, 0x00, 0x20, -0x60, 0x60, 0x75, 0x60, 0xDB, 0xE7, 0x00, 0x00, -0x00, 0x09, 0x00, 0x50, 0x0C, 0x00, 0x00, 0x20, -0x40, 0x01, 0x00, 0xC8, 0x10, 0x02, 0x00, 0x20, -0x00, 0x02, 0x00, 0x20, 0x0D, 0x48, 0x0C, 0x49, -0x01, 0x61, 0x0D, 0x4A, 0x03, 0x21, 0xD1, 0x61, -0x01, 0x21, 0x89, 0x07, 0x0A, 0x68, 0x0B, 0x4B, -0x1A, 0x43, 0x0A, 0x60, 0x0A, 0x4A, 0x02, 0x63, -0x02, 0x6A, 0x52, 0x00, 0x52, 0x08, 0x02, 0x62, -0xC2, 0x6A, 0x04, 0x23, 0x1A, 0x43, 0xC2, 0x62, -0x10, 0x20, 0x48, 0x60, 0x70, 0x47, 0x00, 0x00, -0x00, 0x00, 0x06, 0x07, 0x00, 0x06, 0x00, 0x50, -0x00, 0x0E, 0x00, 0x50, 0x10, 0x01, 0x00, 0x80, -0x0A, 0x66, 0x00, 0x00, 0x05, 0x48, 0x00, 0x47, -0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, -0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, -0xFE, 0xE7, 0xFE, 0xE7, 0x85, 0x00, 0x00, 0x00, -0x06, 0x4C, 0x01, 0x25, 0x06, 0x4E, 0x05, 0xE0, -0xE3, 0x68, 0x07, 0xCC, 0x2B, 0x43, 0x0C, 0x3C, -0x98, 0x47, 0x10, 0x34, 0xB4, 0x42, 0xF7, 0xD3, -0xFF, 0xF7, 0x60, 0xFF, 0xDC, 0x06, 0x00, 0x00, -0xFC, 0x06, 0x00, 0x00, 0x30, 0xB4, 0x74, 0x46, -0x64, 0x1E, 0x25, 0x78, 0x64, 0x1C, 0xAB, 0x42, -0x00, 0xD2, 0x1D, 0x46, 0x63, 0x5D, 0x5B, 0x00, -0xE3, 0x18, 0x30, 0xBC, 0x18, 0x47, 0x02, 0xE0, -0x08, 0xC8, 0x12, 0x1F, 0x08, 0xC1, 0x00, 0x2A, -0xFA, 0xD1, 0x70, 0x47, 0x70, 0x47, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x49, 0x00, 0x20, -0x88, 0x60, 0xC8, 0x60, 0x48, 0x60, 0x3F, 0x22, -0x0A, 0x60, 0x08, 0x61, 0xFB, 0x49, 0x08, 0x60, -0xFB, 0x49, 0x08, 0x60, 0x48, 0x60, 0x70, 0x47, -0xE1, 0x21, 0x09, 0x01, 0x48, 0x43, 0x00, 0x21, -0x00, 0xE0, 0x49, 0x1C, 0x81, 0x42, 0xFC, 0xD3, -0x70, 0x47, 0x10, 0xB5, 0xF3, 0x49, 0x00, 0x20, -0x48, 0x60, 0x88, 0x60, 0xC8, 0x60, 0xF2, 0x4B, -0x98, 0x60, 0xEF, 0x4C, 0xA0, 0x69, 0x01, 0x21, -0x89, 0x03, 0x88, 0x43, 0xA0, 0x61, 0xFF, 0xF7, -0xD9, 0xFF, 0xA0, 0x69, 0x00, 0x05, 0x05, 0xD5, -0x82, 0x20, 0x99, 0x68, 0x00, 0x29, 0x01, 0xD1, -0x58, 0x60, 0xFA, 0xE7, 0x10, 0xBD, 0x70, 0xB5, -0x05, 0x46, 0x00, 0x20, 0x02, 0x46, 0x01, 0x26, -0x09, 0xE0, 0x34, 0x46, 0x94, 0x40, 0x33, 0x46, -0x2C, 0x42, 0x03, 0xD0, 0x8C, 0x1A, 0x64, 0x1E, -0xA3, 0x40, 0x18, 0x43, 0x52, 0x1C, 0x8A, 0x42, -0xF3, 0xD3, 0x70, 0xBD, 0xF0, 0xB5, 0x00, 0x24, -0xE4, 0x43, 0x0E, 0x46, 0x07, 0x46, 0x00, 0x25, -0x14, 0xE0, 0x78, 0x5D, 0x08, 0x21, 0xFF, 0xF7, -0xE2, 0xFF, 0xC1, 0xB2, 0xD9, 0x4A, 0x00, 0x20, -0x23, 0x46, 0x4B, 0x40, 0xDB, 0x07, 0x02, 0xD0, -0x64, 0x08, 0x54, 0x40, 0x00, 0xE0, 0x64, 0x08, -0x40, 0x1C, 0x49, 0x08, 0xC0, 0xB2, 0x08, 0x28, -0xF2, 0xD3, 0x6D, 0x1C, 0xB5, 0x42, 0xE8, 0xD3, -0x20, 0x21, 0x20, 0x46, 0xFF, 0xF7, 0xCB, 0xFF, -0xF0, 0xBD, 0x1F, 0x20, 0x80, 0x01, 0xCE, 0x49, -0xC0, 0x6B, 0x08, 0x60, 0xC8, 0x49, 0x80, 0x31, -0x0A, 0x68, 0x82, 0x42, 0x15, 0xD1, 0xCB, 0x48, -0xCB, 0x4A, 0x40, 0x6B, 0x10, 0x60, 0x4A, 0x68, -0x82, 0x42, 0x0E, 0xD1, 0xC9, 0x48, 0xCA, 0x4A, -0xC0, 0x6B, 0x10, 0x60, 0x8A, 0x68, 0x82, 0x42, -0x07, 0xD1, 0xC6, 0x48, 0xC7, 0x4A, 0x80, 0x30, -0xC0, 0x6B, 0x10, 0x60, 0xC9, 0x68, 0x81, 0x42, -0x01, 0xD0, 0x00, 0x20, 0x70, 0x47, 0x01, 0x20, -0x70, 0x47, 0x70, 0xB5, 0x0D, 0x46, 0x03, 0x46, -0xC1, 0x4A, 0xB7, 0x4E, 0x02, 0xE0, 0x01, 0x20, -0xFF, 0xF7, 0x7A, 0xFF, 0x74, 0x69, 0x1C, 0x40, -0xAC, 0x42, 0x04, 0xD0, 0x10, 0x46, 0x52, 0x1E, -0x92, 0xB2, 0x00, 0x28, 0xF3, 0xD1, 0x00, 0x2A, -0x01, 0xD0, 0x01, 0x20, 0x70, 0xBD, 0x00, 0x20, -0x70, 0xBD, 0xF0, 0xB5, 0x00, 0x23, 0x01, 0x24, -0xB6, 0x4D, 0xE4, 0x02, 0x19, 0x46, 0x1A, 0x46, -0x01, 0x28, 0x0E, 0xD0, 0x04, 0x28, 0x05, 0xD0, -0x10, 0x28, 0x0E, 0xD1, 0x03, 0x02, 0xB2, 0x4A, -0x21, 0x46, 0x0A, 0xE0, 0x09, 0x23, 0x61, 0x22, -0xA8, 0x49, 0x1B, 0x03, 0xD2, 0x00, 0x38, 0x31, -0x03, 0xE0, 0x80, 0x22, 0xFF, 0x21, 0x23, 0x46, -0xC9, 0x01, 0x9F, 0x4C, 0x20, 0x68, 0x1D, 0x26, -0xB0, 0x43, 0x20, 0x60, 0x23, 0x61, 0x40, 0x27, -0x11, 0xE0, 0x60, 0x69, 0x38, 0x43, 0x60, 0x61, -0x28, 0x46, 0x63, 0x69, 0x5B, 0x06, 0x04, 0xD5, -0x03, 0x46, 0x40, 0x1E, 0x80, 0xB2, 0x00, 0x2B, -0xF7, 0xD1, 0xE0, 0x6B, 0x01, 0xC1, 0x20, 0x69, -0x00, 0x1D, 0x20, 0x61, 0x12, 0x1F, 0x00, 0x2A, -0xEB, 0xD1, 0x20, 0x68, 0x30, 0x43, 0x20, 0x60, -0x01, 0x20, 0xF0, 0xBD, 0xF0, 0xB5, 0x8F, 0x4B, -0x98, 0x68, 0x01, 0x25, 0x8C, 0x4C, 0x00, 0x28, -0x09, 0xD0, 0x80, 0x27, 0x8C, 0x4E, 0x01, 0x28, -0x0F, 0xD0, 0x00, 0x21, 0x02, 0x28, 0x1E, 0xD0, -0x03, 0x28, 0x1F, 0xD1, 0x24, 0xE0, 0xA0, 0x69, -0x80, 0x04, 0x1B, 0xD4, 0xA0, 0x69, 0x00, 0x05, -0x01, 0xD5, 0x9D, 0x60, 0x16, 0xE0, 0x03, 0x20, -0x0F, 0xE0, 0x1D, 0x60, 0xA0, 0x20, 0x30, 0x60, -0x81, 0x20, 0x70, 0x60, 0xFF, 0xF7, 0x65, 0xFF, -0x00, 0x28, 0x00, 0xD1, 0x77, 0x60, 0xA0, 0x69, -0x01, 0x21, 0xC9, 0x02, 0x88, 0x43, 0xA0, 0x61, -0x02, 0x20, 0x98, 0x60, 0x02, 0xE0, 0x18, 0x68, -0x00, 0x28, 0x01, 0xD0, 0x00, 0x20, 0xF0, 0xBD, -0x99, 0x60, 0xFF, 0xF7, 0xEB, 0xFE, 0x27, 0xE0, -0x99, 0x60, 0xA0, 0x69, 0xC0, 0x06, 0x02, 0xD5, -0xA0, 0x69, 0x80, 0x05, 0x15, 0xD5, 0xFF, 0xF7, -0x48, 0xFF, 0x00, 0x28, 0x1C, 0xD1, 0x10, 0x20, -0xFF, 0xF7, 0x7F, 0xFF, 0x04, 0x20, 0xFF, 0xF7, -0x7C, 0xFF, 0x01, 0x20, 0xFF, 0xF7, 0x79, 0xFF, -0xA0, 0x69, 0x80, 0x05, 0x12, 0xD4, 0x21, 0x20, -0x00, 0x01, 0xA0, 0x61, 0x80, 0x06, 0x45, 0x60, -0x0C, 0xE0, 0xA0, 0x69, 0x40, 0x05, 0xA0, 0x69, -0x04, 0xD5, 0x01, 0x21, 0x89, 0x02, 0x88, 0x43, -0xA0, 0x61, 0x01, 0xE0, 0x40, 0x06, 0x01, 0xD5, -0x03, 0x20, 0xF0, 0xBD, 0x77, 0x60, 0x02, 0x20, -0xF0, 0xBD, 0xF0, 0xB5, 0x69, 0x4F, 0x00, 0x26, -0x5B, 0x4C, 0x3E, 0x70, 0x21, 0x68, 0x62, 0x68, -0x91, 0x43, 0x21, 0x60, 0x04, 0x28, 0x2C, 0xD0, -0x10, 0x28, 0x27, 0xD0, 0x20, 0x28, 0x2C, 0xD1, -0x5A, 0x48, 0xC1, 0x21, 0x89, 0x00, 0x38, 0x30, -0xFF, 0xF7, 0xEC, 0xFE, 0x59, 0x4D, 0xE9, 0x6B, -0x88, 0x42, 0x18, 0xD0, 0x20, 0x68, 0x04, 0x21, -0x88, 0x43, 0x20, 0x60, 0x00, 0x20, 0x41, 0x1E, -0x1F, 0x22, 0x92, 0x02, 0x83, 0x00, 0x9B, 0x18, -0x40, 0x1C, 0xC0, 0xB2, 0x99, 0x67, 0xC1, 0x28, -0xF8, 0xD3, 0x4E, 0x48, 0xC1, 0x21, 0x89, 0x00, -0x38, 0x30, 0xAE, 0x63, 0xFF, 0xF7, 0xD2, 0xFE, -0xE8, 0x63, 0x01, 0x20, 0x38, 0x70, 0x20, 0x68, -0x10, 0x21, 0x04, 0xE0, 0x20, 0x68, 0x08, 0x21, -0x01, 0xE0, 0x20, 0x68, 0x02, 0x21, 0x88, 0x43, -0x20, 0x60, 0xF0, 0xBD, 0xF0, 0xB5, 0x3E, 0x4C, -0x0F, 0x46, 0x05, 0x46, 0xC6, 0x19, 0x20, 0x61, -0x0E, 0xE0, 0x60, 0x69, 0x02, 0x21, 0x08, 0x43, -0x60, 0x61, 0x08, 0x21, 0x08, 0x46, 0xFF, 0xF7, -0xFC, 0xFE, 0x00, 0x28, 0x16, 0xD0, 0x21, 0x69, -0x01, 0x20, 0x00, 0x03, 0x08, 0x18, 0x20, 0x61, -0x20, 0x69, 0xB0, 0x42, 0xED, 0xD3, 0x25, 0x61, -0x38, 0x0A, 0x00, 0x02, 0xE0, 0x60, 0x60, 0x69, -0x04, 0x21, 0x08, 0x43, 0x60, 0x61, 0x08, 0x21, -0x08, 0x46, 0xFF, 0xF7, 0xE6, 0xFE, 0x00, 0x28, -0x00, 0xD0, 0x01, 0x20, 0xF0, 0xBD, 0x2A, 0x4A, -0x52, 0x68, 0x04, 0x2A, 0x0D, 0xD0, 0x10, 0x2A, -0x06, 0xD0, 0x20, 0x2A, 0x0F, 0xD1, 0x00, 0x22, -0x02, 0x60, 0x11, 0x20, 0xC0, 0x01, 0x09, 0xE0, -0x01, 0x22, 0x12, 0x03, 0x02, 0x60, 0x2E, 0x48, -0x04, 0xE0, 0x09, 0x22, 0x12, 0x03, 0x02, 0x60, -0x61, 0x20, 0xC0, 0x00, 0x08, 0x60, 0x70, 0x47, -0xF0, 0xB5, 0x2B, 0x4C, 0x00, 0x25, 0x20, 0x68, -0x01, 0x23, 0x1D, 0x4E, 0x1B, 0x4F, 0x00, 0x28, -0x04, 0xD0, 0x01, 0x28, 0x0D, 0xD0, 0x02, 0x28, -0x14, 0xD1, 0x15, 0xE0, 0x24, 0x49, 0x08, 0x31, -0x08, 0x1F, 0xFF, 0xF7, 0xD4, 0xFF, 0x75, 0x60, -0xA8, 0x20, 0x30, 0x60, 0x23, 0x60, 0x3B, 0x60, -0x08, 0xE0, 0x38, 0x68, 0x00, 0x28, 0x05, 0xD1, -0x3B, 0x60, 0x70, 0x68, 0xA8, 0x28, 0x01, 0xD1, -0x02, 0x20, 0x20, 0x60, 0x00, 0x20, 0xF0, 0xBD, -0xA1, 0x68, 0x60, 0x68, 0x49, 0x1E, 0xFF, 0xF7, -0x95, 0xFF, 0x00, 0x28, 0xF7, 0xD0, 0x15, 0x48, -0x07, 0x4E, 0x00, 0x78, 0x01, 0x28, 0x08, 0xD1, -0x0A, 0x48, 0x38, 0x30, 0xB0, 0x60, 0x13, 0x49, -0x30, 0x01, 0xFF, 0xF7, 0x87, 0xFF, 0x00, 0x28, -0xE9, 0xD0, 0x75, 0x63, 0xB5, 0x63, 0x1F, 0xE0, -0x00, 0x09, 0x00, 0x50, 0x00, 0x02, 0x00, 0x20, -0x10, 0x02, 0x00, 0x20, 0x20, 0x83, 0xB8, 0xED, -0x98, 0x01, 0x00, 0x20, 0x40, 0x7C, 0x00, 0x00, -0x9C, 0x01, 0x00, 0x20, 0x40, 0x7F, 0x00, 0x00, -0xA0, 0x01, 0x00, 0x20, 0xA4, 0x01, 0x00, 0x20, -0xB8, 0x0B, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, -0x78, 0x74, 0x00, 0x00, 0x14, 0x00, 0x00, 0x20, -0x00, 0x00, 0x00, 0x20, 0x07, 0x03, 0x00, 0x00, -0x01, 0x20, 0xC0, 0x02, 0xB0, 0x61, 0x01, 0x20, -0x25, 0x60, 0xF0, 0xBD, 0xF0, 0xB5, 0x1C, 0x4C, -0x00, 0x27, 0xE0, 0x68, 0x01, 0x25, 0x1B, 0x4E, -0x03, 0x00, 0xFF, 0xF7, 0xBB, 0xFD, 0x07, 0x05, -0x2E, 0x0F, 0x2E, 0x2E, 0x1B, 0x28, 0x2E, 0x00, -0xB0, 0x68, 0x01, 0x28, 0x25, 0xD1, 0xA1, 0x20, -0x30, 0x60, 0xFF, 0x20, 0x70, 0x60, 0x25, 0x60, -0x02, 0x20, 0x1D, 0xE0, 0x20, 0x68, 0x00, 0x28, -0x1B, 0xD1, 0x25, 0x60, 0x10, 0x48, 0x40, 0x68, -0xFF, 0xF7, 0xFF, 0xFE, 0xA3, 0x20, 0x30, 0x60, -0x05, 0x20, 0x11, 0xE0, 0xFF, 0xF7, 0x78, 0xFF, -0x00, 0x28, 0x0E, 0xD0, 0xB0, 0x68, 0x00, 0x28, -0x01, 0xD0, 0x25, 0x60, 0x01, 0xE0, 0x00, 0x20, -0x20, 0x60, 0x06, 0x20, 0x04, 0xE0, 0x20, 0x68, -0x00, 0x28, 0x02, 0xD1, 0x25, 0x60, 0x01, 0x27, -0xE0, 0x60, 0x38, 0x46, 0xF0, 0xBD, 0x00, 0x00, -0x00, 0x02, 0x00, 0x20, 0x10, 0x02, 0x00, 0x20, -0x00, 0x09, 0x00, 0x50, 0x00, 0x20, 0x01, 0xE0, -0x01, 0xC1, 0x12, 0x1F, 0x00, 0x2A, 0xFB, 0xD1, -0x70, 0x47, 0x00, 0x00, 0xFC, 0x06, 0x00, 0x00, -0x00, 0x00, 0x00, 0x20, 0x18, 0x00, 0x00, 0x00, -0xEE, 0x01, 0x00, 0x00, 0x14, 0x07, 0x00, 0x00, -0x18, 0x00, 0x00, 0x20, 0x04, 0x02, 0x00, 0x00, -0xCC, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xD5, 0x4D, 0xDB, 0x52, + 0x18, 0x01, 0x00, 0x20, 0x95, 0x01, 0x00, 0x00, + 0x99, 0x01, 0x00, 0x00, 0x9B, 0x01, 0x00, 0x00, + 0x9D, 0x01, 0x00, 0x00, 0x9F, 0x01, 0x00, 0x00, + 0xA1, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xA3, 0x01, 0x00, 0x00, + 0xA5, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xA7, 0x01, 0x00, 0x00, 0x81, 0x0F, 0x00, 0x00, + 0xE1, 0x0C, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00, + 0xA1, 0x0F, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x00, + 0xA1, 0x0D, 0x00, 0x00, 0x41, 0x0A, 0x00, 0x00, + 0xAB, 0x01, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00, + 0xAB, 0x01, 0x00, 0x00, 0x41, 0x0B, 0x00, 0x00, + 0xE1, 0x0A, 0x00, 0x00, 0xA1, 0x0A, 0x00, 0x00, + 0xC1, 0x0A, 0x00, 0x00, 0x81, 0x0A, 0x00, 0x00, + 0xF1, 0x0F, 0x00, 0x00, 0xAB, 0x01, 0x00, 0x00, + 0x03, 0xF3, 0x00, 0x0B, 0x03, 0x48, 0x85, 0x46, + 0x00, 0xF0, 0x92, 0xF8, 0x00, 0x48, 0x00, 0x47, + 0xCB, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x20, + 0x80, 0xF3, 0x08, 0x88, 0x70, 0x47, 0x00, 0x00, + 0x10, 0xB5, 0x25, 0x48, 0x81, 0x69, 0x01, 0x22, + 0x92, 0x03, 0x11, 0x43, 0x81, 0x61, 0x41, 0x69, + 0x10, 0x22, 0x11, 0x43, 0x41, 0x61, 0xD4, 0x01, + 0x20, 0x68, 0xFF, 0xF7, 0xED, 0xFF, 0x60, 0x68, + 0x1E, 0x49, 0x48, 0x60, 0x08, 0x60, 0x80, 0x47, + 0x10, 0xBD, 0x01, 0x26, 0xB6, 0x07, 0x30, 0x68, + 0x1B, 0x49, 0x08, 0x43, 0x30, 0x60, 0x18, 0x49, + 0x48, 0x69, 0x20, 0x22, 0x10, 0x43, 0x48, 0x61, + 0x00, 0xF0, 0x34, 0xF8, 0x00, 0xF0, 0xA5, 0xF8, + 0x16, 0x4F, 0x17, 0x4C, 0x01, 0x25, 0x60, 0x68, + 0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x16, 0xD0, + 0x02, 0x28, 0xF8, 0xD1, 0xFF, 0xF7, 0xD0, 0xFF, + 0xF5, 0xE7, 0x00, 0xF0, 0x5F, 0xF9, 0x03, 0x28, + 0x07, 0xD0, 0x02, 0x28, 0xEF, 0xD1, 0x65, 0x60, + 0x7D, 0x20, 0xC0, 0x00, 0x00, 0xF0, 0x84, 0xF8, + 0xE9, 0xE7, 0xB8, 0x68, 0x00, 0x28, 0xE6, 0xD1, + 0x02, 0x20, 0x60, 0x60, 0xE3, 0xE7, 0x00, 0xF0, + 0x91, 0xFA, 0x01, 0x28, 0xDF, 0xD1, 0x00, 0x20, + 0x60, 0x60, 0x75, 0x60, 0xDB, 0xE7, 0x00, 0x00, + 0x00, 0x09, 0x00, 0x50, 0x0C, 0x00, 0x00, 0x20, + 0x40, 0x01, 0x00, 0xC8, 0x10, 0x02, 0x00, 0x20, + 0x00, 0x02, 0x00, 0x20, 0x0D, 0x48, 0x0C, 0x49, + 0x01, 0x61, 0x0D, 0x4A, 0x03, 0x21, 0xD1, 0x61, + 0x01, 0x21, 0x89, 0x07, 0x0A, 0x68, 0x0B, 0x4B, + 0x1A, 0x43, 0x0A, 0x60, 0x0A, 0x4A, 0x02, 0x63, + 0x02, 0x6A, 0x52, 0x00, 0x52, 0x08, 0x02, 0x62, + 0xC2, 0x6A, 0x04, 0x23, 0x1A, 0x43, 0xC2, 0x62, + 0x10, 0x20, 0x48, 0x60, 0x70, 0x47, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x07, 0x00, 0x06, 0x00, 0x50, + 0x00, 0x0E, 0x00, 0x50, 0x10, 0x01, 0x00, 0x80, + 0x0A, 0x66, 0x00, 0x00, 0x05, 0x48, 0x00, 0x47, + 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, + 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, + 0xFE, 0xE7, 0xFE, 0xE7, 0x85, 0x00, 0x00, 0x00, + 0x06, 0x4C, 0x01, 0x25, 0x06, 0x4E, 0x05, 0xE0, + 0xE3, 0x68, 0x07, 0xCC, 0x2B, 0x43, 0x0C, 0x3C, + 0x98, 0x47, 0x10, 0x34, 0xB4, 0x42, 0xF7, 0xD3, + 0xFF, 0xF7, 0x60, 0xFF, 0xDC, 0x06, 0x00, 0x00, + 0xFC, 0x06, 0x00, 0x00, 0x30, 0xB4, 0x74, 0x46, + 0x64, 0x1E, 0x25, 0x78, 0x64, 0x1C, 0xAB, 0x42, + 0x00, 0xD2, 0x1D, 0x46, 0x63, 0x5D, 0x5B, 0x00, + 0xE3, 0x18, 0x30, 0xBC, 0x18, 0x47, 0x02, 0xE0, + 0x08, 0xC8, 0x12, 0x1F, 0x08, 0xC1, 0x00, 0x2A, + 0xFA, 0xD1, 0x70, 0x47, 0x70, 0x47, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x49, 0x00, 0x20, + 0x88, 0x60, 0xC8, 0x60, 0x48, 0x60, 0x3F, 0x22, + 0x0A, 0x60, 0x08, 0x61, 0xFB, 0x49, 0x08, 0x60, + 0xFB, 0x49, 0x08, 0x60, 0x48, 0x60, 0x70, 0x47, + 0xE1, 0x21, 0x09, 0x01, 0x48, 0x43, 0x00, 0x21, + 0x00, 0xE0, 0x49, 0x1C, 0x81, 0x42, 0xFC, 0xD3, + 0x70, 0x47, 0x10, 0xB5, 0xF3, 0x49, 0x00, 0x20, + 0x48, 0x60, 0x88, 0x60, 0xC8, 0x60, 0xF2, 0x4B, + 0x98, 0x60, 0xEF, 0x4C, 0xA0, 0x69, 0x01, 0x21, + 0x89, 0x03, 0x88, 0x43, 0xA0, 0x61, 0xFF, 0xF7, + 0xD9, 0xFF, 0xA0, 0x69, 0x00, 0x05, 0x05, 0xD5, + 0x82, 0x20, 0x99, 0x68, 0x00, 0x29, 0x01, 0xD1, + 0x58, 0x60, 0xFA, 0xE7, 0x10, 0xBD, 0x70, 0xB5, + 0x05, 0x46, 0x00, 0x20, 0x02, 0x46, 0x01, 0x26, + 0x09, 0xE0, 0x34, 0x46, 0x94, 0x40, 0x33, 0x46, + 0x2C, 0x42, 0x03, 0xD0, 0x8C, 0x1A, 0x64, 0x1E, + 0xA3, 0x40, 0x18, 0x43, 0x52, 0x1C, 0x8A, 0x42, + 0xF3, 0xD3, 0x70, 0xBD, 0xF0, 0xB5, 0x00, 0x24, + 0xE4, 0x43, 0x0E, 0x46, 0x07, 0x46, 0x00, 0x25, + 0x14, 0xE0, 0x78, 0x5D, 0x08, 0x21, 0xFF, 0xF7, + 0xE2, 0xFF, 0xC1, 0xB2, 0xD9, 0x4A, 0x00, 0x20, + 0x23, 0x46, 0x4B, 0x40, 0xDB, 0x07, 0x02, 0xD0, + 0x64, 0x08, 0x54, 0x40, 0x00, 0xE0, 0x64, 0x08, + 0x40, 0x1C, 0x49, 0x08, 0xC0, 0xB2, 0x08, 0x28, + 0xF2, 0xD3, 0x6D, 0x1C, 0xB5, 0x42, 0xE8, 0xD3, + 0x20, 0x21, 0x20, 0x46, 0xFF, 0xF7, 0xCB, 0xFF, + 0xF0, 0xBD, 0x1F, 0x20, 0x80, 0x01, 0xCE, 0x49, + 0xC0, 0x6B, 0x08, 0x60, 0xC8, 0x49, 0x80, 0x31, + 0x0A, 0x68, 0x82, 0x42, 0x15, 0xD1, 0xCB, 0x48, + 0xCB, 0x4A, 0x40, 0x6B, 0x10, 0x60, 0x4A, 0x68, + 0x82, 0x42, 0x0E, 0xD1, 0xC9, 0x48, 0xCA, 0x4A, + 0xC0, 0x6B, 0x10, 0x60, 0x8A, 0x68, 0x82, 0x42, + 0x07, 0xD1, 0xC6, 0x48, 0xC7, 0x4A, 0x80, 0x30, + 0xC0, 0x6B, 0x10, 0x60, 0xC9, 0x68, 0x81, 0x42, + 0x01, 0xD0, 0x00, 0x20, 0x70, 0x47, 0x01, 0x20, + 0x70, 0x47, 0x70, 0xB5, 0x0D, 0x46, 0x03, 0x46, + 0xC1, 0x4A, 0xB7, 0x4E, 0x02, 0xE0, 0x01, 0x20, + 0xFF, 0xF7, 0x7A, 0xFF, 0x74, 0x69, 0x1C, 0x40, + 0xAC, 0x42, 0x04, 0xD0, 0x10, 0x46, 0x52, 0x1E, + 0x92, 0xB2, 0x00, 0x28, 0xF3, 0xD1, 0x00, 0x2A, + 0x01, 0xD0, 0x01, 0x20, 0x70, 0xBD, 0x00, 0x20, + 0x70, 0xBD, 0xF0, 0xB5, 0x00, 0x23, 0x01, 0x24, + 0xB6, 0x4D, 0xE4, 0x02, 0x19, 0x46, 0x1A, 0x46, + 0x01, 0x28, 0x0E, 0xD0, 0x04, 0x28, 0x05, 0xD0, + 0x10, 0x28, 0x0E, 0xD1, 0x03, 0x02, 0xB2, 0x4A, + 0x21, 0x46, 0x0A, 0xE0, 0x09, 0x23, 0x61, 0x22, + 0xA8, 0x49, 0x1B, 0x03, 0xD2, 0x00, 0x38, 0x31, + 0x03, 0xE0, 0x80, 0x22, 0xFF, 0x21, 0x23, 0x46, + 0xC9, 0x01, 0x9F, 0x4C, 0x20, 0x68, 0x1D, 0x26, + 0xB0, 0x43, 0x20, 0x60, 0x23, 0x61, 0x40, 0x27, + 0x11, 0xE0, 0x60, 0x69, 0x38, 0x43, 0x60, 0x61, + 0x28, 0x46, 0x63, 0x69, 0x5B, 0x06, 0x04, 0xD5, + 0x03, 0x46, 0x40, 0x1E, 0x80, 0xB2, 0x00, 0x2B, + 0xF7, 0xD1, 0xE0, 0x6B, 0x01, 0xC1, 0x20, 0x69, + 0x00, 0x1D, 0x20, 0x61, 0x12, 0x1F, 0x00, 0x2A, + 0xEB, 0xD1, 0x20, 0x68, 0x30, 0x43, 0x20, 0x60, + 0x01, 0x20, 0xF0, 0xBD, 0xF0, 0xB5, 0x8F, 0x4B, + 0x98, 0x68, 0x01, 0x25, 0x8C, 0x4C, 0x00, 0x28, + 0x09, 0xD0, 0x80, 0x27, 0x8C, 0x4E, 0x01, 0x28, + 0x0F, 0xD0, 0x00, 0x21, 0x02, 0x28, 0x1E, 0xD0, + 0x03, 0x28, 0x1F, 0xD1, 0x24, 0xE0, 0xA0, 0x69, + 0x80, 0x04, 0x1B, 0xD4, 0xA0, 0x69, 0x00, 0x05, + 0x01, 0xD5, 0x9D, 0x60, 0x16, 0xE0, 0x03, 0x20, + 0x0F, 0xE0, 0x1D, 0x60, 0xA0, 0x20, 0x30, 0x60, + 0x81, 0x20, 0x70, 0x60, 0xFF, 0xF7, 0x65, 0xFF, + 0x00, 0x28, 0x00, 0xD1, 0x77, 0x60, 0xA0, 0x69, + 0x01, 0x21, 0xC9, 0x02, 0x88, 0x43, 0xA0, 0x61, + 0x02, 0x20, 0x98, 0x60, 0x02, 0xE0, 0x18, 0x68, + 0x00, 0x28, 0x01, 0xD0, 0x00, 0x20, 0xF0, 0xBD, + 0x99, 0x60, 0xFF, 0xF7, 0xEB, 0xFE, 0x27, 0xE0, + 0x99, 0x60, 0xA0, 0x69, 0xC0, 0x06, 0x02, 0xD5, + 0xA0, 0x69, 0x80, 0x05, 0x15, 0xD5, 0xFF, 0xF7, + 0x48, 0xFF, 0x00, 0x28, 0x1C, 0xD1, 0x10, 0x20, + 0xFF, 0xF7, 0x7F, 0xFF, 0x04, 0x20, 0xFF, 0xF7, + 0x7C, 0xFF, 0x01, 0x20, 0xFF, 0xF7, 0x79, 0xFF, + 0xA0, 0x69, 0x80, 0x05, 0x12, 0xD4, 0x21, 0x20, + 0x00, 0x01, 0xA0, 0x61, 0x80, 0x06, 0x45, 0x60, + 0x0C, 0xE0, 0xA0, 0x69, 0x40, 0x05, 0xA0, 0x69, + 0x04, 0xD5, 0x01, 0x21, 0x89, 0x02, 0x88, 0x43, + 0xA0, 0x61, 0x01, 0xE0, 0x40, 0x06, 0x01, 0xD5, + 0x03, 0x20, 0xF0, 0xBD, 0x77, 0x60, 0x02, 0x20, + 0xF0, 0xBD, 0xF0, 0xB5, 0x69, 0x4F, 0x00, 0x26, + 0x5B, 0x4C, 0x3E, 0x70, 0x21, 0x68, 0x62, 0x68, + 0x91, 0x43, 0x21, 0x60, 0x04, 0x28, 0x2C, 0xD0, + 0x10, 0x28, 0x27, 0xD0, 0x20, 0x28, 0x2C, 0xD1, + 0x5A, 0x48, 0xC1, 0x21, 0x89, 0x00, 0x38, 0x30, + 0xFF, 0xF7, 0xEC, 0xFE, 0x59, 0x4D, 0xE9, 0x6B, + 0x88, 0x42, 0x18, 0xD0, 0x20, 0x68, 0x04, 0x21, + 0x88, 0x43, 0x20, 0x60, 0x00, 0x20, 0x41, 0x1E, + 0x1F, 0x22, 0x92, 0x02, 0x83, 0x00, 0x9B, 0x18, + 0x40, 0x1C, 0xC0, 0xB2, 0x99, 0x67, 0xC1, 0x28, + 0xF8, 0xD3, 0x4E, 0x48, 0xC1, 0x21, 0x89, 0x00, + 0x38, 0x30, 0xAE, 0x63, 0xFF, 0xF7, 0xD2, 0xFE, + 0xE8, 0x63, 0x01, 0x20, 0x38, 0x70, 0x20, 0x68, + 0x10, 0x21, 0x04, 0xE0, 0x20, 0x68, 0x08, 0x21, + 0x01, 0xE0, 0x20, 0x68, 0x02, 0x21, 0x88, 0x43, + 0x20, 0x60, 0xF0, 0xBD, 0xF0, 0xB5, 0x3E, 0x4C, + 0x0F, 0x46, 0x05, 0x46, 0xC6, 0x19, 0x20, 0x61, + 0x0E, 0xE0, 0x60, 0x69, 0x02, 0x21, 0x08, 0x43, + 0x60, 0x61, 0x08, 0x21, 0x08, 0x46, 0xFF, 0xF7, + 0xFC, 0xFE, 0x00, 0x28, 0x16, 0xD0, 0x21, 0x69, + 0x01, 0x20, 0x00, 0x03, 0x08, 0x18, 0x20, 0x61, + 0x20, 0x69, 0xB0, 0x42, 0xED, 0xD3, 0x25, 0x61, + 0x38, 0x0A, 0x00, 0x02, 0xE0, 0x60, 0x60, 0x69, + 0x04, 0x21, 0x08, 0x43, 0x60, 0x61, 0x08, 0x21, + 0x08, 0x46, 0xFF, 0xF7, 0xE6, 0xFE, 0x00, 0x28, + 0x00, 0xD0, 0x01, 0x20, 0xF0, 0xBD, 0x2A, 0x4A, + 0x52, 0x68, 0x04, 0x2A, 0x0D, 0xD0, 0x10, 0x2A, + 0x06, 0xD0, 0x20, 0x2A, 0x0F, 0xD1, 0x00, 0x22, + 0x02, 0x60, 0x11, 0x20, 0xC0, 0x01, 0x09, 0xE0, + 0x01, 0x22, 0x12, 0x03, 0x02, 0x60, 0x2E, 0x48, + 0x04, 0xE0, 0x09, 0x22, 0x12, 0x03, 0x02, 0x60, + 0x61, 0x20, 0xC0, 0x00, 0x08, 0x60, 0x70, 0x47, + 0xF0, 0xB5, 0x2B, 0x4C, 0x00, 0x25, 0x20, 0x68, + 0x01, 0x23, 0x1D, 0x4E, 0x1B, 0x4F, 0x00, 0x28, + 0x04, 0xD0, 0x01, 0x28, 0x0D, 0xD0, 0x02, 0x28, + 0x14, 0xD1, 0x15, 0xE0, 0x24, 0x49, 0x08, 0x31, + 0x08, 0x1F, 0xFF, 0xF7, 0xD4, 0xFF, 0x75, 0x60, + 0xA8, 0x20, 0x30, 0x60, 0x23, 0x60, 0x3B, 0x60, + 0x08, 0xE0, 0x38, 0x68, 0x00, 0x28, 0x05, 0xD1, + 0x3B, 0x60, 0x70, 0x68, 0xA8, 0x28, 0x01, 0xD1, + 0x02, 0x20, 0x20, 0x60, 0x00, 0x20, 0xF0, 0xBD, + 0xA1, 0x68, 0x60, 0x68, 0x49, 0x1E, 0xFF, 0xF7, + 0x95, 0xFF, 0x00, 0x28, 0xF7, 0xD0, 0x15, 0x48, + 0x07, 0x4E, 0x00, 0x78, 0x01, 0x28, 0x08, 0xD1, + 0x0A, 0x48, 0x38, 0x30, 0xB0, 0x60, 0x13, 0x49, + 0x30, 0x01, 0xFF, 0xF7, 0x87, 0xFF, 0x00, 0x28, + 0xE9, 0xD0, 0x75, 0x63, 0xB5, 0x63, 0x1F, 0xE0, + 0x00, 0x09, 0x00, 0x50, 0x00, 0x02, 0x00, 0x20, + 0x10, 0x02, 0x00, 0x20, 0x20, 0x83, 0xB8, 0xED, + 0x98, 0x01, 0x00, 0x20, 0x40, 0x7C, 0x00, 0x00, + 0x9C, 0x01, 0x00, 0x20, 0x40, 0x7F, 0x00, 0x00, + 0xA0, 0x01, 0x00, 0x20, 0xA4, 0x01, 0x00, 0x20, + 0xB8, 0x0B, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, + 0x78, 0x74, 0x00, 0x00, 0x14, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x20, 0x07, 0x03, 0x00, 0x00, + 0x01, 0x20, 0xC0, 0x02, 0xB0, 0x61, 0x01, 0x20, + 0x25, 0x60, 0xF0, 0xBD, 0xF0, 0xB5, 0x1C, 0x4C, + 0x00, 0x27, 0xE0, 0x68, 0x01, 0x25, 0x1B, 0x4E, + 0x03, 0x00, 0xFF, 0xF7, 0xBB, 0xFD, 0x07, 0x05, + 0x2E, 0x0F, 0x2E, 0x2E, 0x1B, 0x28, 0x2E, 0x00, + 0xB0, 0x68, 0x01, 0x28, 0x25, 0xD1, 0xA1, 0x20, + 0x30, 0x60, 0xFF, 0x20, 0x70, 0x60, 0x25, 0x60, + 0x02, 0x20, 0x1D, 0xE0, 0x20, 0x68, 0x00, 0x28, + 0x1B, 0xD1, 0x25, 0x60, 0x10, 0x48, 0x40, 0x68, + 0xFF, 0xF7, 0xFF, 0xFE, 0xA3, 0x20, 0x30, 0x60, + 0x05, 0x20, 0x11, 0xE0, 0xFF, 0xF7, 0x78, 0xFF, + 0x00, 0x28, 0x0E, 0xD0, 0xB0, 0x68, 0x00, 0x28, + 0x01, 0xD0, 0x25, 0x60, 0x01, 0xE0, 0x00, 0x20, + 0x20, 0x60, 0x06, 0x20, 0x04, 0xE0, 0x20, 0x68, + 0x00, 0x28, 0x02, 0xD1, 0x25, 0x60, 0x01, 0x27, + 0xE0, 0x60, 0x38, 0x46, 0xF0, 0xBD, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x20, 0x10, 0x02, 0x00, 0x20, + 0x00, 0x09, 0x00, 0x50, 0x00, 0x20, 0x01, 0xE0, + 0x01, 0xC1, 0x12, 0x1F, 0x00, 0x2A, 0xFB, 0xD1, + 0x70, 0x47, 0x00, 0x00, 0xFC, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x18, 0x00, 0x00, 0x00, + 0xEE, 0x01, 0x00, 0x00, 0x14, 0x07, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x20, 0x04, 0x02, 0x00, 0x00, + 0xCC, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xD5, 0x4D, 0xDB, 0x52, }; const unsigned char u8_rad_init_30[] = { -0x10, 0x06, 0x00, 0x50, 0x00, 0x00, 0x06, 0x01, -0x1C, 0x0E, 0x00, 0x50, 0x03, 0x00, 0x00, 0x00, -0x28, 0x06, 0x00, 0x50, 0x06, 0x00, 0x00, 0x00, -0x20, 0x06, 0x00, 0x50, 0x00, 0x00, 0x00, 0x40, -0x10, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, -0x14, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, -0x18, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, -0x1C, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, -0x20, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, -0x24, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, -0x28, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, -0x2C, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, -0x30, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, -0x34, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, -0x74, 0x09, 0x00, 0x50, 0xFF, 0x00, 0xAA, 0x55, -0x3C, 0x07, 0x00, 0x20, 0x17, 0x26, 0x28, 0x2A, + 0x10, 0x06, 0x00, 0x50, 0x00, 0x00, 0x06, 0x01, + 0x1C, 0x0E, 0x00, 0x50, 0x03, 0x00, 0x00, 0x00, + 0x28, 0x06, 0x00, 0x50, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x06, 0x00, 0x50, 0x00, 0x00, 0x00, 0x40, + 0x10, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, + 0x14, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, + 0x18, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, + 0x1C, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, + 0x20, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, + 0x24, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, + 0x28, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, + 0x2C, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, + 0x30, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, + 0x34, 0x07, 0x00, 0x20, 0xFF, 0x00, 0xAA, 0x55, + 0x74, 0x09, 0x00, 0x50, 0xFF, 0x00, 0xAA, 0x55, + 0x3C, 0x07, 0x00, 0x20, 0x17, 0x26, 0x28, 0x2A, }; const unsigned char u8_rad_fw_30[] = { -0xD0, 0x0D, 0x00, 0x20, 0xB1, 0x08, 0x00, 0x00, -0xB9, 0x08, 0x00, 0x00, 0x95, 0x08, 0x00, 0x00, -0xBD, 0x08, 0x00, 0x00, 0xBF, 0x08, 0x00, 0x00, -0xC1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xC3, 0x08, 0x00, 0x00, -0xC5, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xC7, 0x08, 0x00, 0x00, 0x81, 0x0F, 0x00, 0x00, -0xE1, 0x0C, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00, -0xCB, 0x08, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x00, -0xA1, 0x0D, 0x00, 0x00, 0x41, 0x0A, 0x00, 0x00, -0xCB, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, -0xCB, 0x08, 0x00, 0x00, 0x41, 0x0B, 0x00, 0x00, -0xE1, 0x0A, 0x00, 0x00, 0xA1, 0x0A, 0x00, 0x00, -0xC1, 0x0A, 0x00, 0x00, 0x81, 0x0A, 0x00, 0x00, -0xCB, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, -0x03, 0x48, 0x85, 0x46, 0x00, 0xF0, 0x80, 0xF8, -0x00, 0x48, 0x00, 0x47, 0x71, 0x4C, 0x00, 0x00, -0xD0, 0x0D, 0x00, 0x20, 0x04, 0x20, 0x71, 0x46, -0x08, 0x42, 0x02, 0xD0, 0xEF, 0xF3, 0x09, 0x80, -0x01, 0xE0, 0xEF, 0xF3, 0x08, 0x80, 0x71, 0x46, -0x00, 0x4A, 0x10, 0x47, 0x19, 0x4B, 0x00, 0x00, -0x06, 0x48, 0x80, 0x47, 0x06, 0x48, 0x00, 0x47, -0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, -0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, -0xFE, 0xE7, 0xFE, 0xE7, 0x15, 0x0A, 0x00, 0x00, -0x81, 0x08, 0x00, 0x00, 0x30, 0xB5, 0x0B, 0x46, -0x01, 0x46, 0x00, 0x20, 0x20, 0x22, 0x01, 0x24, -0x09, 0xE0, 0x0D, 0x46, 0xD5, 0x40, 0x9D, 0x42, -0x05, 0xD3, 0x1D, 0x46, 0x95, 0x40, 0x49, 0x1B, -0x25, 0x46, 0x95, 0x40, 0x40, 0x19, 0x15, 0x46, -0x52, 0x1E, 0x00, 0x2D, 0xF1, 0xDC, 0x30, 0xBD, -0x70, 0xB5, 0x00, 0x24, 0x25, 0x46, 0x00, 0x28, -0x01, 0xDA, 0x01, 0x24, 0x40, 0x42, 0x00, 0x29, -0x01, 0xDA, 0x01, 0x25, 0x49, 0x42, 0xFF, 0xF7, -0xDD, 0xFF, 0xAC, 0x42, 0x00, 0xD0, 0x40, 0x42, -0x00, 0x2C, 0x00, 0xD0, 0x49, 0x42, 0x70, 0xBD, -0x03, 0x46, 0x0B, 0x43, 0x9B, 0x07, 0x03, 0xD0, -0x09, 0xE0, 0x08, 0xC9, 0x12, 0x1F, 0x08, 0xC0, -0x04, 0x2A, 0xFA, 0xD2, 0x03, 0xE0, 0x0B, 0x78, -0x49, 0x1C, 0x03, 0x70, 0x40, 0x1C, 0x52, 0x1E, -0xF9, 0xD2, 0x70, 0x47, 0xD2, 0xB2, 0x01, 0xE0, -0x02, 0x70, 0x40, 0x1C, 0x49, 0x1E, 0xFB, 0xD2, -0x70, 0x47, 0x00, 0x22, 0xF6, 0xE7, 0x10, 0xB5, -0x04, 0x46, 0x08, 0x46, 0x11, 0x46, 0x02, 0x46, -0x20, 0x46, 0xFF, 0xF7, 0xEF, 0xFF, 0x20, 0x46, -0x10, 0xBD, 0x00, 0x1D, 0x03, 0x21, 0x40, 0x1E, -0x03, 0x78, 0x12, 0x02, 0x1A, 0x43, 0x49, 0x1E, -0xF9, 0xD5, 0x10, 0x46, 0x70, 0x47, 0x00, 0x00, -0x06, 0x4C, 0x01, 0x25, 0x06, 0x4E, 0x05, 0xE0, -0x20, 0x46, 0xE3, 0x68, 0x07, 0xC8, 0x2B, 0x43, -0x98, 0x47, 0x10, 0x34, 0xB4, 0x42, 0xF7, 0xD3, -0xFF, 0xF7, 0x72, 0xFF, 0xA0, 0x64, 0x00, 0x00, -0xC0, 0x64, 0x00, 0x00, 0xC1, 0x06, 0xC9, 0x0E, -0x01, 0x20, 0x88, 0x40, 0x01, 0x49, 0x08, 0x60, -0x70, 0x47, 0x00, 0x00, 0x00, 0xE1, 0x00, 0xE0, -0x0B, 0x49, 0x10, 0xB5, 0x88, 0x42, 0x01, 0xD9, -0x01, 0x20, 0x10, 0xBD, 0x01, 0x02, 0x09, 0x0A, -0x08, 0x48, 0x49, 0x1E, 0x41, 0x61, 0x08, 0x49, -0x07, 0x23, 0xCA, 0x69, 0x12, 0x02, 0x12, 0x0A, -0x04, 0x04, 0x22, 0x43, 0xCA, 0x61, 0x00, 0x21, -0x81, 0x61, 0x03, 0x61, 0x08, 0x46, 0x10, 0xBD, -0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xE0, 0x00, 0xE0, -0x04, 0xED, 0x00, 0xE0, 0x70, 0x47, 0x00, 0x00, -0x03, 0x49, 0x02, 0x20, 0x08, 0x60, 0x02, 0x49, -0x80, 0x39, 0x08, 0x60, 0x70, 0x47, 0x00, 0x00, -0x80, 0xE1, 0x00, 0xE0, 0x62, 0xB6, 0x02, 0x48, -0x00, 0x21, 0x01, 0x60, 0x70, 0x47, 0x00, 0x00, -0x0C, 0x00, 0x00, 0x40, 0x30, 0xB4, 0x74, 0x46, -0x64, 0x1E, 0x25, 0x78, 0x64, 0x1C, 0xAB, 0x42, -0x00, 0xD2, 0x1D, 0x46, 0x63, 0x5D, 0x5B, 0x00, -0xE3, 0x18, 0x30, 0xBC, 0x18, 0x47, 0x00, 0x00, -0x05, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, -0x01, 0x20, 0x80, 0x07, 0x01, 0x6A, 0x03, 0x22, -0xD2, 0x03, 0x11, 0x43, 0x01, 0x62, 0x70, 0x47, -0x68, 0x02, 0x00, 0x20, 0x02, 0xE0, 0x08, 0xC8, -0x12, 0x1F, 0x08, 0xC1, 0x00, 0x2A, 0xFA, 0xD1, -0x70, 0x47, 0x00, 0x20, 0x01, 0xE0, 0x01, 0xC1, -0x12, 0x1F, 0x00, 0x2A, 0xFB, 0xD1, 0x70, 0x47, -0x01, 0x21, 0x89, 0x07, 0x48, 0x60, 0x70, 0x47, -0x01, 0x20, 0x80, 0x07, 0x41, 0x6A, 0x82, 0x13, -0x11, 0x43, 0x41, 0x62, 0x41, 0x6A, 0xC2, 0x13, -0x11, 0x43, 0x41, 0x62, 0x70, 0x47, 0x10, 0xB5, -0xFF, 0xF7, 0xEE, 0xFF, 0x10, 0xBD, 0x00, 0x00, -0x03, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, -0x02, 0x49, 0x01, 0x20, 0xC8, 0x60, 0x70, 0x47, -0x18, 0x03, 0x00, 0x20, 0x00, 0x03, 0x00, 0x50, -0x10, 0xB5, 0x02, 0xF0, 0xDF, 0xFC, 0x10, 0xBD, -0x70, 0x47, 0x00, 0x00, 0x04, 0x49, 0x06, 0x22, -0x00, 0x28, 0x08, 0x68, 0x01, 0xD0, 0x10, 0x43, -0x00, 0xE0, 0x90, 0x43, 0x08, 0x60, 0x70, 0x47, -0x00, 0x09, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, -0x70, 0x47, 0x00, 0x00, 0x30, 0xB5, 0x07, 0x49, -0x07, 0x4B, 0x0A, 0x68, 0x00, 0x20, 0x41, 0x00, -0xCD, 0x18, 0x54, 0x5A, 0x2D, 0x88, 0x40, 0x1C, -0x64, 0x1B, 0xC0, 0xB2, 0x54, 0x52, 0x30, 0x28, -0xF5, 0xD3, 0x30, 0xBD, 0x00, 0x03, 0x00, 0x20, -0x00, 0x00, 0x01, 0x20, 0x10, 0xB5, 0x08, 0x4A, -0x00, 0x21, 0x12, 0x68, 0x60, 0x32, 0x12, 0x78, -0x52, 0x1C, 0xD4, 0xB2, 0x4A, 0x00, 0x83, 0x5E, -0x49, 0x1C, 0x63, 0x43, 0x1B, 0x11, 0xC9, 0xB2, -0x83, 0x52, 0x30, 0x29, 0xF6, 0xD3, 0x10, 0xBD, -0xAC, 0x04, 0x00, 0x20, 0x10, 0xB5, 0x02, 0xF0, -0x91, 0xFD, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00, -0x30, 0xB5, 0x1E, 0x4B, 0x58, 0x68, 0x99, 0x68, -0x00, 0x28, 0x01, 0xDA, 0xDA, 0x04, 0x5A, 0x60, -0x1B, 0x4A, 0x15, 0x68, 0x01, 0x24, 0x05, 0x40, -0x00, 0x2D, 0x02, 0xD1, 0x50, 0x68, 0x08, 0x42, -0x0F, 0xD0, 0x18, 0x48, 0x04, 0x70, 0x02, 0xF0, -0x03, 0xFA, 0x14, 0x72, 0x16, 0x48, 0x00, 0x78, -0x00, 0x28, 0x06, 0xD0, 0x18, 0x68, 0x01, 0x21, -0x00, 0x09, 0x00, 0x01, 0x89, 0x02, 0x08, 0x43, -0x18, 0x60, 0x58, 0x68, 0x80, 0x02, 0x02, 0xD5, -0x01, 0x20, 0x40, 0x05, 0x58, 0x60, 0x58, 0x68, -0x00, 0x04, 0x03, 0xD5, 0x01, 0x20, 0xC0, 0x03, -0x58, 0x60, 0x94, 0x72, 0x58, 0x68, 0x80, 0x00, -0x03, 0xD5, 0x01, 0x20, 0x40, 0x07, 0x58, 0x60, -0xD4, 0x72, 0x58, 0x68, 0x40, 0x00, 0x03, 0xD5, -0x01, 0x20, 0x80, 0x07, 0x58, 0x60, 0x94, 0x72, -0x30, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, -0x50, 0x02, 0x00, 0x20, 0x9B, 0x01, 0x00, 0x20, -0x99, 0x01, 0x00, 0x20, 0x00, 0xB5, 0x03, 0x46, -0x00, 0x20, 0xFF, 0xF7, 0x77, 0xFF, 0x04, 0x49, -0x01, 0x20, 0x09, 0x68, 0x0A, 0x88, 0x9A, 0x43, -0x0A, 0x80, 0xFF, 0xF7, 0x6F, 0xFF, 0x00, 0xBD, -0x08, 0x00, 0x00, 0x20, 0x03, 0x49, 0x0A, 0x68, -0x10, 0x18, 0x0A, 0x68, 0x90, 0x42, 0xFC, 0xD1, -0x70, 0x47, 0x00, 0x00, 0x18, 0x03, 0x00, 0x20, -0x70, 0xB5, 0x2B, 0x4B, 0x05, 0x20, 0x19, 0x7A, -0x40, 0x04, 0x02, 0x29, 0x4C, 0xD1, 0x29, 0x4C, -0x21, 0x78, 0x00, 0x29, 0x45, 0xD1, 0x28, 0x49, -0x0A, 0x78, 0x00, 0x2A, 0x44, 0xD0, 0x00, 0x22, -0x0A, 0x70, 0x27, 0x49, 0x25, 0x4A, 0x09, 0x78, -0x26, 0x4D, 0x05, 0xE0, 0x16, 0x78, 0x81, 0x2E, -0x0A, 0xD0, 0x00, 0x29, 0x08, 0xD1, 0x40, 0x1E, -0x2E, 0x78, 0x00, 0x2E, 0x04, 0xD0, 0x00, 0x28, -0x02, 0xD0, 0x1E, 0x7A, 0x02, 0x2E, 0xF1, 0xD0, -0x18, 0x7A, 0x02, 0x28, 0x26, 0xD1, 0x10, 0x78, -0x81, 0x28, 0x23, 0xD0, 0x00, 0x29, 0x21, 0xD1, -0x00, 0xF0, 0x76, 0xF9, 0x1A, 0x48, 0x40, 0x68, -0x40, 0x01, 0x40, 0x0D, 0xBC, 0x28, 0x01, 0xD9, -0xBC, 0x38, 0x80, 0xB2, 0x17, 0x49, 0x49, 0x68, -0x09, 0x06, 0x0D, 0xD4, 0x16, 0x49, 0x09, 0x88, -0x88, 0x42, 0x03, 0xD3, 0x15, 0x49, 0x09, 0x88, -0x88, 0x42, 0x05, 0xD9, 0x01, 0x20, 0x80, 0x07, -0x81, 0x68, 0x01, 0x22, 0x11, 0x43, 0x81, 0x60, -0x00, 0x20, 0x00, 0xBF, 0x40, 0x1C, 0xC0, 0xB2, -0x14, 0x28, 0xFA, 0xD3, 0x20, 0x78, 0x00, 0x28, -0x02, 0xD0, 0x20, 0x78, 0x40, 0x1E, 0x20, 0x70, -0x01, 0xF0, 0x88, 0xFD, 0x70, 0xBD, 0x00, 0x00, -0x50, 0x02, 0x00, 0x20, 0xEC, 0x02, 0x00, 0x20, -0x9B, 0x01, 0x00, 0x20, 0x60, 0x07, 0x00, 0x20, -0x4E, 0x07, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, -0x00, 0x11, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50, -0x62, 0x02, 0x00, 0x20, 0x64, 0x02, 0x00, 0x20, -0x10, 0xB5, 0xFF, 0xF7, 0x87, 0xFF, 0x10, 0xBD, -0x70, 0xB5, 0x01, 0x25, 0xAD, 0x07, 0xE8, 0x68, -0x40, 0x08, 0x40, 0x00, 0xE8, 0x60, 0x20, 0x48, -0x41, 0x68, 0x01, 0x26, 0x49, 0x07, 0x00, 0x29, -0x33, 0xDA, 0x42, 0x68, 0x04, 0x21, 0x0A, 0x43, -0x42, 0x60, 0x1C, 0x48, 0x00, 0x68, 0x1C, 0x4A, -0x40, 0x05, 0x40, 0x0F, 0x01, 0x28, 0x07, 0xD0, -0x00, 0x23, 0x1A, 0x4C, 0x13, 0x70, 0x20, 0x73, -0x20, 0x7B, 0x06, 0x28, 0x03, 0xD0, 0x03, 0xE0, -0x03, 0x20, 0x10, 0x70, 0x1D, 0xE0, 0x21, 0x73, -0x20, 0x7B, 0x02, 0x28, 0x1D, 0xD0, 0x20, 0x7B, -0x06, 0x28, 0x05, 0xD8, 0x20, 0x7B, 0x04, 0x28, -0x02, 0xD3, 0x01, 0x20, 0x01, 0xF0, 0x4A, 0xFD, -0x20, 0x7B, 0x0F, 0x49, 0x09, 0x78, 0x88, 0x42, -0x0B, 0xD0, 0x0E, 0x4C, 0xA0, 0x79, 0x00, 0x28, -0x01, 0xD0, 0x01, 0xF0, 0x9D, 0xFD, 0x0C, 0x48, -0x01, 0x78, 0x31, 0x43, 0x01, 0x70, 0x81, 0x20, -0x20, 0x70, 0xE8, 0x68, 0x30, 0x43, 0xE8, 0x60, -0x70, 0xBD, 0x00, 0x20, 0xE6, 0xE7, 0x00, 0x00, -0x40, 0x00, 0x00, 0x50, 0x00, 0x11, 0x00, 0x50, -0x61, 0x02, 0x00, 0x20, 0x50, 0x02, 0x00, 0x20, -0xE5, 0x02, 0x00, 0x20, 0x60, 0x07, 0x00, 0x20, -0x98, 0x01, 0x00, 0x20, 0x01, 0x20, 0x80, 0x07, -0x41, 0x68, 0x82, 0x14, 0x11, 0x43, 0x41, 0x60, -0x70, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x10, 0xB5, 0x14, 0x48, 0x80, 0x69, 0x40, 0x04, -0x23, 0xD5, 0x13, 0x4C, 0xE0, 0x68, 0xA2, 0x68, -0x02, 0x40, 0xE0, 0x68, 0xA1, 0x68, 0x88, 0x43, -0xE0, 0x60, 0x10, 0x07, 0x03, 0xD5, 0x08, 0x20, -0xE0, 0x60, 0x04, 0xF0, 0xFF, 0xFE, 0x50, 0x07, -0x0E, 0xD5, 0x04, 0x20, 0xE0, 0x60, 0x04, 0xF0, -0xF9, 0xFE, 0x03, 0xF0, 0x53, 0xF9, 0x00, 0x28, -0x06, 0xD1, 0x08, 0x48, 0x00, 0x78, 0x00, 0x28, -0x02, 0xD0, 0x01, 0x20, 0xFF, 0xF7, 0x68, 0xFE, -0xFF, 0x20, 0xF3, 0x30, 0xE0, 0x60, 0x01, 0x20, -0xE0, 0x60, 0x10, 0xBD, 0x00, 0x09, 0x00, 0x50, -0x00, 0x05, 0x00, 0x50, 0x90, 0x02, 0x00, 0x20, -0x01, 0x28, 0x05, 0xD0, 0x02, 0x28, 0x05, 0xD0, -0x04, 0x28, 0x06, 0xD0, 0x00, 0x20, 0x70, 0x47, -0x03, 0x48, 0x70, 0x47, 0x02, 0x48, 0xC0, 0x30, -0x70, 0x47, 0x02, 0x48, 0x70, 0x47, 0x00, 0x00, -0x78, 0x7C, 0x00, 0x00, 0xF8, 0x7D, 0x00, 0x00, -0x00, 0xB5, 0x08, 0x49, 0x0A, 0x28, 0x05, 0xD0, -0x07, 0x48, 0x00, 0x0C, 0x48, 0x63, 0x07, 0x48, -0x08, 0x63, 0x00, 0xBD, 0x06, 0x48, 0x00, 0x68, -0x08, 0x62, 0x0D, 0x20, 0x01, 0xF0, 0xB4, 0xF8, -0x00, 0xBD, 0x00, 0x00, 0x00, 0x06, 0x00, 0x50, -0xBC, 0x02, 0x00, 0x20, 0xCC, 0x02, 0x00, 0x20, -0xC0, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, -0xF8, 0xB5, 0x34, 0x48, 0x80, 0x69, 0x40, 0x04, -0x62, 0xD5, 0x33, 0x4D, 0xA8, 0x6A, 0xE9, 0x68, -0xAC, 0x68, 0x0C, 0x40, 0xE9, 0x68, 0xAA, 0x68, -0x91, 0x43, 0xE9, 0x60, 0x2F, 0x4E, 0xC0, 0x07, -0x15, 0xD0, 0x68, 0x69, 0x89, 0x27, 0xC0, 0xB2, -0xEF, 0x60, 0xAA, 0x6A, 0x01, 0x21, 0x0A, 0x43, -0xAA, 0x62, 0x0B, 0x28, 0x03, 0xD2, 0x31, 0x70, -0xFF, 0xF7, 0xC6, 0xFF, 0x03, 0xE0, 0x00, 0x21, -0x31, 0x70, 0x01, 0xF0, 0x85, 0xF8, 0xBC, 0x43, -0x01, 0x20, 0xFF, 0xF7, 0x05, 0xFE, 0x24, 0x49, -0xA0, 0x04, 0x04, 0xD5, 0x28, 0x6A, 0x88, 0x43, -0x28, 0x62, 0x48, 0x14, 0xE8, 0x60, 0xE0, 0x04, -0x16, 0xD5, 0x28, 0x6A, 0x88, 0x43, 0x28, 0x62, -0x01, 0x20, 0x00, 0x03, 0xE8, 0x60, 0xE8, 0x6B, -0x1C, 0x4F, 0x00, 0x04, 0x79, 0x78, 0x00, 0x0E, -0x02, 0x29, 0x02, 0xD0, 0x00, 0x28, 0x02, 0xD0, -0x06, 0xE0, 0x03, 0x28, 0x04, 0xD1, 0x01, 0x20, -0xFF, 0xF7, 0xE6, 0xFD, 0x00, 0x20, 0x38, 0x70, -0xA0, 0x05, 0x02, 0xD5, 0x01, 0x20, 0x40, 0x02, -0xE8, 0x60, 0x20, 0x07, 0x09, 0xD5, 0x30, 0x78, -0x01, 0x28, 0x02, 0xD1, 0x0A, 0x20, 0x01, 0xF0, -0x53, 0xF8, 0x64, 0x08, 0x64, 0x00, 0x09, 0x20, -0xE8, 0x60, 0x60, 0x07, 0x07, 0xD5, 0x30, 0x78, -0x01, 0x28, 0x02, 0xD1, 0x0A, 0x20, 0x01, 0xF0, -0x47, 0xF8, 0x05, 0x20, 0xE8, 0x60, 0xFF, 0x20, -0xF3, 0x30, 0xE8, 0x60, 0x01, 0x20, 0xE8, 0x60, -0xF8, 0xBD, 0x00, 0x00, 0x00, 0x09, 0x00, 0x50, -0x00, 0x06, 0x00, 0x50, 0x80, 0x02, 0x00, 0x20, -0x00, 0x00, 0x00, 0x40, 0x90, 0x02, 0x00, 0x20, -0x64, 0x20, 0x05, 0x49, 0x02, 0xE0, 0x00, 0xBF, -0x40, 0x1E, 0xC0, 0xB2, 0x4A, 0x68, 0x12, 0x06, -0x01, 0xD5, 0x00, 0x28, 0xF7, 0xD1, 0x70, 0x47, -0x00, 0x06, 0x00, 0x50, 0x10, 0xB5, 0x01, 0x22, -0x92, 0x07, 0x13, 0x68, 0x03, 0x4C, 0x23, 0x43, -0x13, 0x60, 0x89, 0x04, 0x09, 0x0C, 0x01, 0xF0, -0xF9, 0xFC, 0x10, 0xBD, 0x10, 0x01, 0x42, 0x88, -0x02, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, -0x70, 0x47, 0x00, 0x00, 0x5C, 0x07, 0x00, 0x20, -0x10, 0xB5, 0xFF, 0xF7, 0xFB, 0xFE, 0x10, 0xBD, -0x10, 0xB5, 0x00, 0xF0, 0x17, 0xFB, 0x10, 0xBD, -0x05, 0x20, 0x00, 0x07, 0x82, 0x69, 0x0C, 0x49, -0x00, 0x2A, 0x07, 0xDA, 0x82, 0x69, 0xC3, 0x00, -0x92, 0x00, 0x92, 0x08, 0xD2, 0x18, 0x82, 0x61, -0x01, 0x22, 0x4A, 0x72, 0x82, 0x69, 0x52, 0x00, -0x08, 0xD5, 0x82, 0x69, 0x01, 0x23, 0x92, 0x00, -0x92, 0x08, 0x9B, 0x07, 0xD2, 0x18, 0x82, 0x61, -0x00, 0x20, 0x48, 0x72, 0x70, 0x47, 0x00, 0x00, -0x60, 0x07, 0x00, 0x20, 0xF0, 0xB5, 0x00, 0x20, -0x84, 0x46, 0x8F, 0x48, 0x87, 0xB0, 0x00, 0x68, -0x8E, 0x49, 0xB0, 0x30, 0x00, 0x90, 0x00, 0x20, -0x08, 0x5E, 0x8D, 0x49, 0x01, 0x90, 0x00, 0x20, -0x08, 0x5E, 0x02, 0x90, 0x07, 0xE1, 0x34, 0x21, -0x48, 0x43, 0x41, 0x19, 0xC8, 0x7A, 0xFF, 0x28, -0x30, 0xD1, 0x00, 0x22, 0x00, 0x20, 0x07, 0xE0, -0x34, 0x24, 0x44, 0x43, 0x64, 0x19, 0xE4, 0x7A, -0x94, 0x42, 0x03, 0xD0, 0x40, 0x1C, 0xC0, 0xB2, -0x83, 0x42, 0xF5, 0xD8, 0x83, 0x42, 0x01, 0xD1, -0xCA, 0x72, 0x03, 0xE0, 0x52, 0x1C, 0xD2, 0xB2, -0x02, 0x2A, 0xEB, 0xD3, 0x0C, 0x24, 0x0C, 0x5F, -0x30, 0x20, 0x7C, 0x4D, 0x42, 0x43, 0xA3, 0x01, -0x50, 0x19, 0x83, 0x60, 0xC3, 0x60, 0xAC, 0x50, -0x0E, 0x22, 0x44, 0x60, 0x8A, 0x5E, 0x93, 0x01, -0x83, 0x61, 0x02, 0x61, 0xC3, 0x61, 0x42, 0x61, -0x10, 0x22, 0x8A, 0x5E, 0x93, 0x01, 0x83, 0x62, -0x02, 0x62, 0xC3, 0x62, 0x42, 0x62, 0xC8, 0x7A, -0xFF, 0x28, 0x7D, 0xD0, 0x70, 0x4A, 0xC6, 0xB2, -0x12, 0x78, 0x08, 0x6A, 0x00, 0x2A, 0x11, 0xD1, -0x00, 0x9A, 0xD3, 0x79, 0x94, 0x79, 0x1A, 0x02, -0x22, 0x43, 0x82, 0x42, 0x01, 0xD2, 0x02, 0x22, -0x09, 0xE0, 0x00, 0x9A, 0x53, 0x7A, 0x14, 0x7A, -0x1A, 0x02, 0x22, 0x43, 0x82, 0x42, 0x01, 0xD2, -0x03, 0x22, 0x00, 0xE0, 0x04, 0x22, 0x65, 0x4D, -0x28, 0x78, 0x82, 0x42, 0x28, 0xD0, 0x82, 0x42, -0x02, 0xD3, 0x13, 0x1A, 0x02, 0x2B, 0x23, 0xDA, -0x61, 0x4C, 0x23, 0x78, 0x00, 0x2B, 0x14, 0xD0, -0x60, 0x4F, 0x3B, 0x78, 0x9A, 0x42, 0x02, 0xD0, -0x00, 0x23, 0x23, 0x70, 0x3A, 0x70, 0x22, 0x78, -0x5C, 0x4B, 0x52, 0x1C, 0xD2, 0xB2, 0x22, 0x70, -0x1B, 0x78, 0x98, 0x42, 0x08, 0xD9, 0x01, 0x2A, -0x06, 0xD9, 0x00, 0x20, 0x20, 0x70, 0x2B, 0x70, -0x0A, 0xE0, 0x56, 0x4B, 0x1A, 0x70, 0xEE, 0xE7, -0x98, 0x42, 0x05, 0xD2, 0x02, 0x2A, 0x03, 0xD9, -0x00, 0x22, 0x22, 0x70, 0x40, 0x1C, 0x28, 0x70, -0x28, 0x78, 0x02, 0x28, 0x6A, 0xD0, 0x03, 0x28, -0x6C, 0xD0, 0x29, 0x22, 0x64, 0x23, 0x05, 0x24, -0x30, 0x20, 0x46, 0x43, 0x47, 0x48, 0x30, 0x18, -0xC5, 0x68, 0x03, 0x95, 0x87, 0x68, 0x5D, 0x43, -0x57, 0x43, 0xED, 0x1B, 0x20, 0x35, 0xAD, 0x11, -0xAE, 0x46, 0x0C, 0x25, 0x4D, 0x5F, 0x41, 0x4F, -0x04, 0x95, 0xBF, 0x59, 0xEF, 0x19, 0x45, 0x68, -0x6D, 0x00, 0x7F, 0x19, 0x67, 0x43, 0xBF, 0x1C, -0xBF, 0x10, 0x75, 0x46, 0x7D, 0x19, 0xAE, 0x46, -0xC5, 0x69, 0x87, 0x69, 0x5D, 0x43, 0x57, 0x43, -0xED, 0x1B, 0x20, 0x35, 0xAD, 0x11, 0x06, 0x95, -0x0E, 0x25, 0x4D, 0x5F, 0x05, 0x95, 0x07, 0x69, -0xEF, 0x19, 0x45, 0x69, 0x6D, 0x00, 0x7D, 0x19, -0x65, 0x43, 0xAD, 0x1C, 0xAF, 0x10, 0x00, 0xE0, -0x4D, 0xE0, 0x06, 0x9D, 0x7F, 0x19, 0xC5, 0x6A, -0x5D, 0x43, 0x83, 0x6A, 0x53, 0x43, 0xEA, 0x1A, -0x20, 0x32, 0x95, 0x11, 0x10, 0x22, 0x8A, 0x5E, -0x06, 0x92, 0x03, 0x6A, 0xD2, 0x18, 0x43, 0x6A, -0x5B, 0x00, 0xD2, 0x18, 0x62, 0x43, 0x92, 0x1C, -0x92, 0x10, 0x53, 0x19, 0x03, 0x9D, 0x85, 0x60, -0x75, 0x46, 0xC5, 0x60, 0xC2, 0x69, 0xC7, 0x61, -0x82, 0x61, 0xC2, 0x6A, 0xC3, 0x62, 0x82, 0x62, -0x20, 0x4C, 0x42, 0x68, 0xA2, 0x51, 0x04, 0x9D, -0x45, 0x60, 0x42, 0x69, 0x02, 0x61, 0x05, 0x9D, -0x45, 0x61, 0x42, 0x6A, 0x02, 0x62, 0x06, 0x9A, -0x42, 0x62, 0x75, 0x46, 0x01, 0x9C, 0xA8, 0x11, -0xBA, 0x11, 0x9B, 0x11, 0xA0, 0x42, 0x09, 0xDB, -0x60, 0x1E, 0x0A, 0xE0, 0x1C, 0x22, 0x4D, 0x23, -0x0F, 0x24, 0x95, 0xE7, 0x22, 0x22, 0x58, 0x23, -0x0A, 0x24, 0x91, 0xE7, 0x00, 0x28, 0x00, 0xDA, -0x00, 0x20, 0x02, 0x9C, 0xA2, 0x42, 0x01, 0xDB, -0x62, 0x1E, 0x02, 0xE0, 0x00, 0x2A, 0x00, 0xDA, -0x00, 0x22, 0x00, 0x2B, 0x00, 0xDA, 0x00, 0x23, -0x88, 0x81, 0xCA, 0x81, 0x0B, 0x82, 0x60, 0x46, -0x40, 0x1C, 0xC0, 0xB2, 0x84, 0x46, 0x0C, 0x4D, -0x60, 0x46, 0x2B, 0x78, 0x63, 0x45, 0x00, 0xD9, -0xF1, 0xE6, 0x07, 0xB0, 0xF0, 0xBD, 0x00, 0x00, -0xAC, 0x04, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, -0x46, 0x00, 0x00, 0x20, 0x38, 0x01, 0x00, 0x20, -0x34, 0x00, 0x00, 0x20, 0xCF, 0x00, 0x00, 0x20, -0xCE, 0x00, 0x00, 0x20, 0xD0, 0x00, 0x00, 0x20, -0x7C, 0x07, 0x00, 0x20, 0xF0, 0xB5, 0x93, 0xB0, -0x00, 0x20, 0x11, 0x90, 0x03, 0xF0, 0x94, 0xF9, -0x0F, 0x90, 0xA5, 0x48, 0x00, 0x25, 0x05, 0x70, -0xA4, 0x48, 0x41, 0x5F, 0x0F, 0x98, 0x81, 0x42, -0x7E, 0xDD, 0xA3, 0x48, 0x30, 0x21, 0x00, 0x68, -0x06, 0x7E, 0x40, 0x7E, 0x12, 0x90, 0x02, 0xA8, -0xFF, 0xF7, 0x73, 0xFB, 0x9F, 0x48, 0x00, 0x78, -0x06, 0x28, 0x72, 0xD2, 0x01, 0x20, 0x10, 0x90, -0x08, 0xA8, 0x0E, 0x90, 0x34, 0x20, 0x29, 0x46, -0x41, 0x43, 0x9B, 0x48, 0x00, 0x27, 0x0C, 0x18, -0x27, 0x77, 0x10, 0x98, 0x00, 0x28, 0x04, 0xD0, -0x94, 0x48, 0x01, 0x88, 0x97, 0x48, 0x01, 0x80, -0x09, 0xE0, 0x01, 0xA9, 0x68, 0x46, 0x0F, 0x9A, -0x04, 0xF0, 0x18, 0xFC, 0x00, 0x28, 0x58, 0xD0, -0x10, 0x98, 0x00, 0x28, 0x0E, 0xD0, 0x92, 0x48, -0x31, 0x46, 0x00, 0x88, 0x10, 0x90, 0xFF, 0xF7, -0x09, 0xFB, 0x69, 0x46, 0x08, 0x70, 0x10, 0x99, -0x70, 0x43, 0x08, 0x1A, 0x69, 0x46, 0x08, 0x71, -0x00, 0x20, 0x10, 0x90, 0x0F, 0x98, 0x02, 0xAA, -0x83, 0xB2, 0x01, 0xA9, 0x68, 0x46, 0x03, 0xF0, -0x91, 0xF9, 0x88, 0x49, 0x88, 0x42, 0x7D, 0xD0, -0x84, 0x4A, 0x00, 0x21, 0x51, 0x5E, 0xE1, 0x82, -0x20, 0x82, 0x0A, 0x22, 0x50, 0x43, 0xFF, 0xF7, -0x03, 0xFB, 0xC0, 0xB2, 0x7C, 0x49, 0xA0, 0x72, -0x09, 0x68, 0xA0, 0x31, 0x49, 0x78, 0x88, 0x42, -0x01, 0xD9, 0x01, 0x21, 0x11, 0x91, 0x0A, 0x28, -0x68, 0xD3, 0xFF, 0x20, 0x20, 0x71, 0x00, 0x20, -0x20, 0x62, 0x68, 0x46, 0x01, 0x79, 0x02, 0xAA, -0x01, 0x20, 0x03, 0xF0, 0xED, 0xF8, 0x78, 0x49, -0xA0, 0x81, 0x0A, 0x78, 0x00, 0x2A, 0x0B, 0xD0, -0x76, 0x4A, 0x00, 0x23, 0xD3, 0x5E, 0x76, 0x4A, -0x12, 0x78, 0x9B, 0x1A, 0x98, 0x42, 0x03, 0xDA, -0x90, 0x42, 0x01, 0xDD, 0x00, 0x22, 0x0A, 0x70, -0x6A, 0x46, 0x12, 0x79, 0x00, 0x2A, 0x13, 0xD0, -0x73, 0x1E, 0x9A, 0x42, 0x29, 0xD1, 0x6E, 0x4A, -0x12, 0x78, 0x53, 0x00, 0xD2, 0x18, 0x01, 0xE0, -0x93, 0xE0, 0x9C, 0xE0, 0x93, 0x08, 0x69, 0x4A, -0x00, 0x27, 0xD7, 0x5F, 0xFA, 0x1A, 0x90, 0x42, -0x12, 0xDD, 0x01, 0x20, 0x08, 0x70, 0x12, 0xE0, -0x65, 0x4A, 0x12, 0x78, 0x53, 0x00, 0xD2, 0x18, -0x92, 0x08, 0x90, 0x42, 0x02, 0xDA, 0x01, 0x20, -0x08, 0x70, 0x02, 0xE0, 0x08, 0x78, 0x00, 0x28, -0x0B, 0xD0, 0x02, 0xAA, 0x01, 0x21, 0x04, 0xE0, -0x08, 0x78, 0x00, 0x28, 0x05, 0xD0, 0x02, 0xAA, -0x00, 0x21, 0x01, 0x20, 0x03, 0xF0, 0x14, 0xF8, -0xA0, 0x81, 0x68, 0x46, 0x01, 0x78, 0x00, 0x20, -0x0E, 0x9A, 0x03, 0xF0, 0xA5, 0xF8, 0x57, 0x49, -0xE0, 0x81, 0x0A, 0x78, 0x00, 0x2A, 0x0B, 0xD0, -0x55, 0x4A, 0x00, 0x23, 0xD3, 0x5E, 0x55, 0x4A, -0x12, 0x78, 0x9B, 0x1A, 0x98, 0x42, 0x03, 0xDA, -0x90, 0x42, 0x01, 0xDD, 0x00, 0x22, 0x0A, 0x70, -0x6A, 0x46, 0x12, 0x78, 0x00, 0x2A, 0x13, 0xD0, -0x12, 0x9B, 0x00, 0xE0, 0x4D, 0xE0, 0x5B, 0x1E, -0x9A, 0x42, 0x31, 0xD1, 0x4B, 0x4A, 0x00, 0x27, -0x12, 0x78, 0x53, 0x00, 0xD2, 0x18, 0x93, 0x08, -0x47, 0x4A, 0xD7, 0x5F, 0xFA, 0x1A, 0x90, 0x42, -0x16, 0xDD, 0x01, 0x20, 0x08, 0x70, 0x16, 0xE0, -0x44, 0x4A, 0x12, 0x78, 0x53, 0x00, 0xD2, 0x18, -0x92, 0x08, 0x90, 0x42, 0x02, 0xDA, 0x01, 0x20, -0x08, 0x70, 0x02, 0xE0, 0x08, 0x78, 0x00, 0x28, -0x16, 0xD0, 0x01, 0x21, 0x00, 0x20, 0x0E, 0x9A, -0x02, 0xF0, 0xD2, 0xFF, 0xE0, 0x81, 0x0F, 0xE0, -0x08, 0x78, 0x00, 0x28, 0x05, 0xD0, 0x00, 0x21, -0x08, 0x46, 0x0E, 0x9A, 0x02, 0xF0, 0xC8, 0xFF, -0xE0, 0x81, 0x37, 0x48, 0x00, 0x78, 0x00, 0x28, -0x02, 0xD1, 0x36, 0x49, 0x01, 0x20, 0x08, 0x70, -0x07, 0x98, 0x20, 0x83, 0x0D, 0x98, 0x60, 0x83, -0x68, 0x46, 0x00, 0x79, 0x00, 0x28, 0x0A, 0xD0, -0x71, 0x1E, 0x88, 0x42, 0x07, 0xD0, 0x68, 0x46, -0x00, 0x78, 0x00, 0x28, 0x03, 0xD0, 0x12, 0x99, -0x49, 0x1E, 0x88, 0x42, 0x01, 0xD1, 0x01, 0x20, -0x00, 0xE0, 0x00, 0x20, 0x6D, 0x1C, 0x20, 0x77, -0xED, 0xB2, 0x03, 0x2D, 0x00, 0xD2, 0xFD, 0xE6, -0x09, 0xE0, 0x1B, 0x49, 0x00, 0x20, 0x08, 0x70, -0x25, 0x48, 0x40, 0x88, 0x0A, 0x28, 0x02, 0xD9, -0x00, 0x20, 0x13, 0xB0, 0xF0, 0xBD, 0x15, 0x48, -0x01, 0x78, 0x01, 0x29, 0x03, 0xD1, 0x11, 0x99, -0x00, 0x29, 0x00, 0xD1, 0x01, 0x70, 0x1C, 0x49, -0x09, 0x78, 0x00, 0x29, 0x03, 0xD1, 0x00, 0x78, -0x03, 0x28, 0x00, 0xD3, 0x00, 0x25, 0x0E, 0x48, -0x00, 0x2D, 0x05, 0x70, 0x0B, 0xD0, 0x19, 0x48, -0x00, 0x78, 0xC0, 0x07, 0x07, 0xD0, 0x06, 0x48, -0x01, 0x78, 0x17, 0x48, 0x00, 0x29, 0x01, 0xD0, -0x01, 0x78, 0x49, 0x1C, 0x01, 0x70, 0x15, 0x48, -0x05, 0x70, 0x01, 0x20, 0xD9, 0xE7, 0x00, 0x00, -0xD1, 0x00, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20, -0xAC, 0x04, 0x00, 0x20, 0x4F, 0x07, 0x00, 0x20, -0x7C, 0x07, 0x00, 0x20, 0x32, 0x00, 0x00, 0x20, -0x3C, 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0xFF, -0xD2, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, -0x38, 0x00, 0x00, 0x20, 0xD3, 0x00, 0x00, 0x20, -0x46, 0x00, 0x00, 0x20, 0x39, 0x00, 0x00, 0x20, -0x1C, 0x08, 0x00, 0x20, 0xA8, 0x04, 0x00, 0x20, -0x60, 0x07, 0x00, 0x20, 0xBD, 0x02, 0x00, 0x20, -0xBE, 0x02, 0x00, 0x20, 0xCB, 0x02, 0x00, 0x20, -0xF0, 0xB5, 0x8E, 0x46, 0x02, 0x21, 0x4B, 0x00, -0xC3, 0x5E, 0x00, 0x2B, 0x03, 0xDD, 0x49, 0x1E, -0x09, 0xB2, 0x00, 0x29, 0xF7, 0xDA, 0x49, 0x1C, -0x00, 0x27, 0x0B, 0xB2, 0x9C, 0x46, 0x3C, 0x46, -0x01, 0x25, 0x0E, 0xE0, 0x5E, 0x00, 0x86, 0x5F, -0x00, 0x2E, 0x02, 0xDC, 0x5B, 0x1E, 0x1B, 0xB2, -0x09, 0xE0, 0x31, 0x46, 0x69, 0x43, 0x6D, 0x1C, -0xCF, 0x19, 0x34, 0x19, 0x5B, 0x1C, 0x2D, 0xB2, -0x1B, 0xB2, 0x05, 0x2B, 0xEE, 0xDB, 0x61, 0x46, -0x59, 0x1A, 0x49, 0x1C, 0x66, 0x08, 0x63, 0x46, -0x00, 0x25, 0x51, 0x61, 0x06, 0xE0, 0x59, 0x00, -0x41, 0x5E, 0x4D, 0x19, 0xB5, 0x42, 0x03, 0xD8, -0x5B, 0x1C, 0x1B, 0xB2, 0x05, 0x2B, 0xF6, 0xDB, -0x5E, 0x00, 0x81, 0x5F, 0x69, 0x1A, 0x11, 0x60, -0x80, 0x5F, 0x50, 0x60, 0x60, 0x1B, 0xD4, 0x60, -0x90, 0x60, 0x60, 0x46, 0x18, 0x1A, 0x60, 0x43, -0x38, 0x1A, 0x10, 0x61, 0x70, 0x46, 0x00, 0x78, -0x9B, 0x1E, 0xC0, 0x18, 0x71, 0x46, 0x08, 0x70, -0x20, 0xB2, 0xF0, 0xBD, 0xFE, 0xB5, 0x00, 0x26, -0xA8, 0x4C, 0x00, 0x96, 0x20, 0x78, 0x00, 0x28, -0x01, 0xD0, 0x03, 0xF0, 0xED, 0xFD, 0x00, 0x20, -0x05, 0x46, 0xA5, 0x4A, 0x0D, 0xE0, 0x34, 0x21, -0x41, 0x43, 0x89, 0x18, 0x0B, 0x79, 0x1B, 0x06, -0x01, 0xD5, 0xCD, 0x71, 0x03, 0xE0, 0xCB, 0x79, -0x5B, 0x1C, 0xCB, 0x71, 0x01, 0x26, 0x40, 0x1C, -0xC0, 0xB2, 0x11, 0x78, 0x81, 0x42, 0xEE, 0xD8, -0x9B, 0x49, 0x20, 0x78, 0x09, 0x78, 0x88, 0x42, -0x01, 0xD1, 0x00, 0x2E, 0x02, 0xD0, 0x04, 0xF0, -0x97, 0xF8, 0x0E, 0xE0, 0xA0, 0x70, 0x00, 0x20, -0x34, 0x21, 0x41, 0x43, 0x09, 0x19, 0x8D, 0x71, -0x40, 0x1C, 0xCD, 0x71, 0xC0, 0xB2, 0x0D, 0x72, -0x03, 0x28, 0xF5, 0xD3, 0x90, 0x48, 0x65, 0x70, -0x45, 0x70, 0x20, 0x78, 0xA1, 0x78, 0x88, 0x42, -0x01, 0xD0, 0x03, 0xF0, 0x7D, 0xFF, 0x20, 0x78, -0x00, 0x28, 0x08, 0xD1, 0x60, 0x78, 0x00, 0x28, -0x05, 0xD1, 0x8A, 0x48, 0x01, 0x78, 0xC9, 0x07, -0x01, 0xD0, 0x80, 0x21, 0x01, 0x70, 0x03, 0xF0, -0x6F, 0xFA, 0x87, 0x48, 0x87, 0x4A, 0x01, 0x88, -0x11, 0x80, 0x00, 0x21, 0x01, 0x80, 0x0B, 0x46, -0x41, 0xE0, 0x34, 0x21, 0x59, 0x43, 0x0A, 0x18, -0x81, 0x49, 0x10, 0x6A, 0x0C, 0x88, 0xA0, 0x42, -0x00, 0xD9, 0x08, 0x80, 0x80, 0x48, 0x96, 0x7A, -0x00, 0x68, 0xA0, 0x30, 0x01, 0x90, 0x40, 0x78, -0x86, 0x42, 0x01, 0xD9, 0x01, 0x20, 0x00, 0x90, -0x10, 0x46, 0x20, 0x30, 0x01, 0x7A, 0xC9, 0x07, -0x27, 0xD0, 0x75, 0x4C, 0x00, 0x21, 0x24, 0x78, -0xA4, 0x46, 0x20, 0xE0, 0x15, 0x79, 0x80, 0x24, -0x25, 0x43, 0x34, 0x27, 0x0C, 0x46, 0x7C, 0x43, -0x6F, 0x4F, 0xE4, 0x19, 0x27, 0x79, 0xBD, 0x42, -0x13, 0xD1, 0x20, 0x34, 0x65, 0x79, 0x45, 0x71, -0xA7, 0x79, 0x87, 0x71, 0xE4, 0x79, 0xC4, 0x71, -0x04, 0x79, 0x01, 0x2C, 0x1C, 0xD0, 0x01, 0x9C, -0x64, 0x78, 0xA6, 0x42, 0x05, 0xD9, 0x05, 0x2D, -0x03, 0xD2, 0x01, 0x24, 0x04, 0x71, 0x6D, 0x1C, -0x45, 0x71, 0x49, 0x1C, 0xC9, 0xB2, 0x8C, 0x45, -0xDC, 0xD8, 0x5B, 0x1C, 0xDB, 0xB2, 0x5F, 0x48, -0x01, 0x78, 0x99, 0x42, 0xB9, 0xD8, 0x00, 0x98, -0x62, 0x49, 0x00, 0x28, 0x08, 0x78, 0x1C, 0xD0, -0x64, 0x28, 0x1E, 0xD2, 0x40, 0x1C, 0x1B, 0xE0, -0x8C, 0x45, 0xEE, 0xD9, 0x51, 0x7A, 0x5E, 0x4A, -0x12, 0x78, 0xD2, 0x08, 0x91, 0x42, 0xE8, 0xD9, -0xC1, 0x79, 0xB1, 0x42, 0x09, 0xD9, 0x81, 0x79, -0x49, 0x1C, 0xC9, 0xB2, 0x81, 0x71, 0x05, 0x29, -0xDF, 0xD9, 0x00, 0x21, 0x81, 0x71, 0x01, 0x71, -0xDB, 0xE7, 0x00, 0x21, 0x81, 0x71, 0xC6, 0x71, -0xD7, 0xE7, 0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, -0x08, 0x70, 0x4A, 0x4D, 0x03, 0x21, 0x28, 0x78, -0x34, 0x22, 0x09, 0x1A, 0x50, 0x43, 0x40, 0x19, -0x51, 0x43, 0x00, 0x1D, 0xFF, 0xF7, 0xFD, 0xF8, -0x00, 0x24, 0x45, 0x4E, 0x0F, 0xE0, 0x30, 0x78, -0x34, 0x21, 0x00, 0x19, 0x48, 0x43, 0x81, 0x19, -0x28, 0x78, 0x34, 0x22, 0x00, 0x19, 0x50, 0x43, -0x40, 0x19, 0x09, 0x1D, 0x00, 0x1D, 0xFF, 0xF7, -0xD3, 0xF8, 0x64, 0x1C, 0xE4, 0xB2, 0x69, 0x78, -0xA1, 0x42, 0xEC, 0xD8, 0x2A, 0x78, 0x00, 0x20, -0x53, 0x18, 0x0E, 0xE0, 0x34, 0x21, 0x41, 0x43, -0x4A, 0x19, 0x06, 0x21, 0x41, 0x43, 0x89, 0x19, -0xA0, 0x31, 0x94, 0x89, 0x0C, 0x80, 0xD4, 0x89, -0x4C, 0x80, 0x12, 0x8A, 0x40, 0x1C, 0x8A, 0x80, -0xC0, 0xB2, 0x83, 0x42, 0xEE, 0xD8, 0x00, 0x20, -0x36, 0x4A, 0x01, 0x21, 0x10, 0x80, 0x0C, 0xE0, -0x34, 0x24, 0x44, 0x43, 0x64, 0x19, 0x24, 0x79, -0x66, 0x06, 0x76, 0x0E, 0x0C, 0x46, 0xB4, 0x40, -0x16, 0x88, 0x34, 0x43, 0x40, 0x1C, 0x14, 0x80, -0xC0, 0xB2, 0x83, 0x42, 0xF0, 0xD8, 0x12, 0x88, -0x00, 0x20, 0x0B, 0x46, 0x83, 0x40, 0x13, 0x42, -0x05, 0xD0, 0x40, 0x1C, 0xC0, 0xB2, 0x02, 0x28, -0xF7, 0xD3, 0x00, 0x26, 0x3A, 0xE0, 0x28, 0x49, -0x08, 0x70, 0xFA, 0xE7, 0x34, 0x20, 0x70, 0x43, -0x44, 0x19, 0x27, 0x46, 0x20, 0x37, 0xB8, 0x7D, -0x02, 0x28, 0x2D, 0xD2, 0x0E, 0x23, 0x0C, 0x22, -0x34, 0x21, 0x32, 0x20, 0xE3, 0x5E, 0xA2, 0x5E, -0x61, 0x5E, 0x20, 0x5E, 0x04, 0xF0, 0x4A, 0xFD, -0x19, 0x49, 0x09, 0x68, 0xB0, 0x31, 0xCA, 0x7B, -0x8B, 0x7B, 0x11, 0x02, 0x19, 0x43, 0x81, 0x42, -0x17, 0xD2, 0x1A, 0x49, 0x03, 0x20, 0x08, 0x70, -0xB8, 0x7D, 0x32, 0x22, 0x0C, 0x23, 0xA2, 0x5E, -0x02, 0x21, 0xE3, 0x5E, 0x09, 0x1A, 0x4A, 0x43, -0x43, 0x43, 0xD2, 0x18, 0x52, 0x10, 0xA2, 0x81, -0x34, 0x22, 0xA2, 0x5E, 0x4A, 0x43, 0x0E, 0x21, -0x61, 0x5E, 0x41, 0x43, 0x50, 0x18, 0x40, 0x10, -0xE0, 0x81, 0xB8, 0x7D, 0x40, 0x1C, 0xB8, 0x75, -0x76, 0x1C, 0xF6, 0xB2, 0x28, 0x78, 0xB0, 0x42, -0xC4, 0xD8, 0xFE, 0xBD, 0x7C, 0x07, 0x00, 0x20, -0x1C, 0x08, 0x00, 0x20, 0xBD, 0x02, 0x00, 0x20, -0x14, 0x00, 0x00, 0x20, 0x16, 0x00, 0x00, 0x20, -0xAC, 0x04, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20, -0xB7, 0x02, 0x00, 0x20, 0x40, 0x00, 0x00, 0x20, -0x3B, 0x00, 0x00, 0x20, 0x10, 0x00, 0x00, 0x20, -0xF1, 0xB5, 0x66, 0x48, 0x8A, 0xB0, 0x00, 0x68, -0x08, 0x90, 0x1B, 0x30, 0x64, 0x4A, 0x09, 0x90, -0x00, 0x20, 0x10, 0x5E, 0xC1, 0x0F, 0x08, 0x18, -0xC0, 0x03, 0x01, 0x0C, 0x08, 0x98, 0x00, 0x91, -0x90, 0x30, 0x41, 0x7B, 0x03, 0x7B, 0x08, 0x02, -0x18, 0x43, 0x00, 0x99, 0x07, 0x90, 0x88, 0x42, -0x00, 0xD9, 0x00, 0x90, 0x5C, 0x49, 0x5B, 0x48, -0x08, 0x80, 0x5C, 0x4B, 0xC1, 0x43, 0x19, 0x80, -0x5B, 0x4B, 0x57, 0x4A, 0x18, 0x80, 0x11, 0x80, -0x00, 0x23, 0x5A, 0x4A, 0x1E, 0x46, 0x13, 0x70, -0x01, 0x20, 0xC0, 0x03, 0x74, 0x00, 0x01, 0xA9, -0x08, 0x53, 0x40, 0x1E, 0x03, 0xA9, 0x08, 0x53, -0x00, 0x20, 0x01, 0x46, 0x35, 0x46, 0x1C, 0xE0, -0x09, 0x9A, 0x52, 0x5D, 0x41, 0x2A, 0x16, 0xD0, -0x0A, 0x9A, 0x6B, 0x00, 0xD2, 0x5E, 0x01, 0xAB, -0x49, 0x1C, 0x1F, 0x5F, 0x80, 0x18, 0xC9, 0xB2, -0x97, 0x42, 0x00, 0xDA, 0x1A, 0x53, 0x03, 0xAB, -0x1F, 0x5F, 0x97, 0x42, 0x00, 0xDD, 0x1A, 0x53, -0x00, 0x9B, 0x9A, 0x42, 0x03, 0xDD, 0x47, 0x4B, -0x1A, 0x78, 0x52, 0x1C, 0x1A, 0x70, 0x2D, 0x1D, -0xED, 0xB2, 0x30, 0x2D, 0xE0, 0xD3, 0x00, 0x29, -0x02, 0xD0, 0xFE, 0xF7, 0xD9, 0xFF, 0x00, 0xE0, -0x00, 0x20, 0x05, 0xA9, 0x08, 0x53, 0x3A, 0x49, -0x01, 0xAD, 0x00, 0x22, 0x28, 0x5F, 0x8A, 0x5E, -0x90, 0x42, 0x00, 0xDD, 0x08, 0x80, 0x3A, 0x49, -0x03, 0xAF, 0x00, 0x22, 0x38, 0x5F, 0x8A, 0x5E, -0x90, 0x42, 0x00, 0xDA, 0x08, 0x80, 0x76, 0x1C, -0xF6, 0xB2, 0x04, 0x2E, 0xB8, 0xD3, 0x35, 0x48, -0x01, 0x26, 0x01, 0x78, 0x34, 0x48, 0x19, 0x29, -0x02, 0xD0, 0x00, 0x29, 0x02, 0xD0, 0x03, 0xE0, -0x06, 0x70, 0x01, 0xE0, 0x00, 0x22, 0x02, 0x70, -0x07, 0x98, 0x2D, 0x4A, 0x44, 0x08, 0x00, 0x20, -0x10, 0x5E, 0x2E, 0x4B, 0x84, 0x42, 0x06, 0xDA, -0x25, 0x4A, 0x00, 0x24, 0x14, 0x5F, 0x07, 0x9A, -0x24, 0x1A, 0x94, 0x42, 0x04, 0xDB, 0x08, 0x9A, -0x80, 0x32, 0x92, 0x7B, 0x8A, 0x42, 0x01, 0xD9, -0x1E, 0x70, 0x01, 0xE0, 0x00, 0x21, 0x19, 0x70, -0x25, 0x49, 0x26, 0x4A, 0x09, 0x78, 0x12, 0x78, -0x91, 0x42, 0x05, 0xD2, 0x24, 0x49, 0x00, 0x22, -0x8A, 0x5E, 0x90, 0x42, 0x00, 0xDA, 0x08, 0x80, -0x00, 0x24, 0x05, 0xAE, 0x60, 0x00, 0x31, 0x5E, -0x00, 0x91, 0x3B, 0x5E, 0x2A, 0x5E, 0x21, 0x46, -0x0A, 0x98, 0x00, 0xF0, 0xEF, 0xF8, 0x64, 0x1C, -0xE4, 0xB2, 0x04, 0x2C, 0xF2, 0xD3, 0x00, 0xF0, -0xA1, 0xFB, 0x0E, 0x48, 0x00, 0x68, 0x90, 0x30, -0x41, 0x7B, 0x02, 0x7B, 0x08, 0x02, 0x10, 0x43, -0x0E, 0x49, 0x00, 0x22, 0x8A, 0x5E, 0x82, 0x42, -0x0D, 0xDA, 0x14, 0x48, 0x41, 0x88, 0x0A, 0x29, -0x09, 0xD9, 0x40, 0x88, 0x0B, 0x28, 0x03, 0xD1, -0x80, 0x21, 0x11, 0x48, 0xFE, 0xF7, 0x95, 0xFF, -0x00, 0x20, 0x0B, 0xB0, 0xF0, 0xBD, 0x01, 0x20, -0xFB, 0xE7, 0x00, 0x00, 0xAC, 0x04, 0x00, 0x20, -0x58, 0x07, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, -0x52, 0x07, 0x00, 0x20, 0x54, 0x07, 0x00, 0x20, -0x56, 0x07, 0x00, 0x20, 0xB6, 0x02, 0x00, 0x20, -0x85, 0x02, 0x00, 0x20, 0x60, 0x02, 0x00, 0x20, -0xEE, 0x02, 0x00, 0x20, 0xB7, 0x02, 0x00, 0x20, -0xFC, 0x02, 0x00, 0x20, 0x60, 0x07, 0x00, 0x20, -0x28, 0x04, 0x00, 0x20, 0xF0, 0xB5, 0x00, 0x20, -0x41, 0x1F, 0x8C, 0x46, 0x1F, 0x49, 0x05, 0x24, -0x09, 0x78, 0xC9, 0x07, 0x39, 0xD1, 0x1E, 0x49, -0x09, 0x68, 0x90, 0x31, 0x4A, 0x7B, 0x0B, 0x7B, -0x11, 0x02, 0x1C, 0x4A, 0x19, 0x43, 0x13, 0x5E, -0x99, 0x42, 0x06, 0xDA, 0x1A, 0x49, 0x09, 0x88, -0x00, 0x29, 0x01, 0xD0, 0x0A, 0x24, 0x00, 0xE0, -0x3C, 0x24, 0x18, 0x4F, 0x18, 0x4D, 0x00, 0x26, -0x18, 0x4A, 0x41, 0x00, 0x53, 0x5E, 0xC9, 0x19, -0x00, 0x22, 0x8A, 0x5E, 0x93, 0x42, 0x05, 0xDD, -0x9A, 0x1A, 0x0A, 0x2A, 0x09, 0xDA, 0x2A, 0x5C, -0x52, 0x1C, 0x03, 0xE0, 0x93, 0x42, 0x03, 0xDA, -0x2A, 0x5C, 0x52, 0x1E, 0x2A, 0x54, 0x00, 0xE0, -0x2E, 0x54, 0x2A, 0x56, 0xA2, 0x42, 0x02, 0xDB, -0x0A, 0x88, 0x52, 0x1C, 0x03, 0xE0, 0x62, 0x45, -0x03, 0xDC, 0x0A, 0x88, 0x52, 0x1E, 0x0A, 0x80, -0x2E, 0x54, 0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, -0xDA, 0xD3, 0xF0, 0xBD, 0xEB, 0x02, 0x00, 0x20, -0xAC, 0x04, 0x00, 0x20, 0x54, 0x07, 0x00, 0x20, -0xF4, 0x02, 0x00, 0x20, 0x00, 0x00, 0x01, 0x20, -0x1C, 0x03, 0x00, 0x20, 0xD4, 0x00, 0x00, 0x20, -0x10, 0xB5, 0x21, 0x48, 0x21, 0x4A, 0x01, 0x68, -0x21, 0x48, 0x91, 0x42, 0x01, 0xD1, 0x21, 0x49, -0x00, 0xE0, 0x21, 0x49, 0xC1, 0x61, 0x21, 0x48, -0x81, 0x68, 0x01, 0x22, 0x52, 0x02, 0x11, 0x43, -0x81, 0x60, 0x1F, 0x4A, 0x10, 0x68, 0x30, 0x21, -0x88, 0x43, 0x10, 0x60, 0x1D, 0x48, 0x00, 0x78, -0x00, 0x28, 0x0E, 0xD0, 0x1C, 0x48, 0x01, 0x68, -0x4A, 0x06, 0x90, 0x21, 0x00, 0x2A, 0x02, 0x68, -0x02, 0xDA, 0x0A, 0x43, 0x02, 0x60, 0x10, 0xBD, -0x8A, 0x43, 0x02, 0x60, 0x17, 0x49, 0x41, 0x61, -0x10, 0xBD, 0x05, 0x21, 0x16, 0x48, 0x09, 0x07, -0x48, 0x62, 0x80, 0x07, 0x41, 0x6A, 0x83, 0x13, -0x19, 0x43, 0x41, 0x62, 0x13, 0x49, 0x0B, 0x68, -0x04, 0x12, 0x23, 0x43, 0x0B, 0x60, 0xD3, 0x68, -0x09, 0x68, 0x0B, 0x43, 0xD3, 0x60, 0x01, 0x68, -0x02, 0x14, 0x11, 0x43, 0x01, 0x60, 0x0E, 0x49, -0x40, 0x14, 0x08, 0x60, 0x10, 0xBD, 0x00, 0x00, -0xE4, 0x06, 0x00, 0x20, 0xA2, 0x00, 0x03, 0xF3, -0x40, 0x00, 0x00, 0x50, 0x0B, 0x0B, 0x00, 0x0B, -0x0B, 0x0B, 0x0B, 0x0B, 0x00, 0x06, 0x00, 0x50, -0x00, 0x10, 0x00, 0x50, 0x99, 0x01, 0x00, 0x20, -0x00, 0x11, 0x00, 0x50, 0x13, 0x00, 0x03, 0x00, -0xF1, 0x2A, 0x02, 0x00, 0x50, 0x02, 0x00, 0x20, -0x00, 0xE1, 0x00, 0xE0, 0xFF, 0xB5, 0x04, 0x46, -0x58, 0x48, 0x1D, 0x46, 0x02, 0x68, 0x83, 0xB0, -0x17, 0x46, 0x1B, 0x32, 0x94, 0x46, 0x3A, 0x46, -0x80, 0x32, 0x53, 0x7A, 0x16, 0x7A, 0x1A, 0x06, -0x12, 0x14, 0x03, 0x23, 0x32, 0x43, 0xDB, 0x03, -0x5A, 0x43, 0x12, 0x14, 0x96, 0x46, 0x50, 0x4A, -0x00, 0x21, 0x16, 0x78, 0x4F, 0x4A, 0x08, 0x46, -0x12, 0x78, 0x02, 0x92, 0x3A, 0x46, 0x90, 0x32, -0x57, 0x7B, 0x12, 0x7B, 0x3F, 0x02, 0x3A, 0x43, -0x00, 0x2E, 0x02, 0xD1, 0x02, 0x9B, 0x04, 0x2B, -0x01, 0xD0, 0x53, 0x08, 0x00, 0xE0, 0x93, 0x08, -0x00, 0x93, 0x47, 0x4B, 0x76, 0x46, 0x1E, 0x80, -0x0C, 0x9B, 0x00, 0x2B, 0x07, 0xDA, 0x45, 0x4E, -0x00, 0x27, 0xF7, 0x5F, 0x00, 0x9E, 0xB7, 0x42, -0x01, 0xDA, 0x53, 0x42, 0x01, 0xE0, 0x53, 0x08, -0x5B, 0x42, 0x0C, 0x9F, 0x1B, 0xB2, 0x05, 0x9E, -0x01, 0x93, 0xF6, 0x1B, 0x3B, 0x46, 0xEF, 0x1B, -0x00, 0x2E, 0x00, 0xDA, 0x76, 0x42, 0x00, 0x2F, -0x00, 0xDA, 0x7F, 0x42, 0xBE, 0x42, 0x01, 0xDA, -0x00, 0x2B, 0x18, 0xDD, 0x00, 0x9B, 0x0C, 0x9E, -0xDF, 0x00, 0xF3, 0x1B, 0xAB, 0x42, 0x01, 0xDD, -0x00, 0x2D, 0x02, 0xDB, 0x02, 0x9B, 0x04, 0x2B, -0x04, 0xD0, 0x05, 0x9B, 0x5B, 0x19, 0xD2, 0x18, -0x52, 0x10, 0x01, 0xE0, 0x00, 0x9A, 0xAA, 0x18, -0x13, 0xB2, 0x72, 0x46, 0x73, 0x45, 0x00, 0xDD, -0x13, 0x46, 0x04, 0x9A, 0x19, 0xE0, 0x05, 0x9B, -0x9A, 0x1A, 0x13, 0xB2, 0x72, 0x46, 0x52, 0x42, -0x93, 0x42, 0x00, 0xDA, 0x13, 0xB2, 0x04, 0x9A, -0x1F, 0xE0, 0x66, 0x46, 0xB6, 0x5C, 0x41, 0x2E, -0x09, 0xD0, 0x56, 0x00, 0xA6, 0x5F, 0x9E, 0x42, -0x05, 0xDA, 0x01, 0x9F, 0xBE, 0x42, 0x02, 0xDD, -0x30, 0x18, 0x49, 0x1C, 0xC9, 0xB2, 0x12, 0x1D, -0xD2, 0xB2, 0x30, 0x2A, 0xED, 0xD3, 0x0E, 0xE0, -0x66, 0x46, 0xB6, 0x5C, 0x41, 0x2E, 0x06, 0xD0, -0x56, 0x00, 0xA6, 0x5F, 0x9E, 0x42, 0x02, 0xDD, -0x30, 0x18, 0x49, 0x1C, 0xC9, 0xB2, 0x12, 0x1D, -0xD2, 0xB2, 0x30, 0x2A, 0xF0, 0xD3, 0x00, 0x29, -0x02, 0xD0, 0xFE, 0xF7, 0x01, 0xFE, 0x01, 0x46, -0x04, 0x98, 0x05, 0xE0, 0x42, 0x00, 0xA3, 0x5A, -0x5B, 0x1A, 0x00, 0x1D, 0xA3, 0x52, 0xC0, 0xB2, -0x30, 0x28, 0xF7, 0xD3, 0x0E, 0x4A, 0x00, 0x23, -0x68, 0x1A, 0xD3, 0x5E, 0x00, 0xB2, 0x98, 0x42, -0x00, 0xDA, 0x10, 0x80, 0x05, 0x98, 0x00, 0x22, -0x40, 0x1A, 0x0A, 0x49, 0x00, 0xB2, 0x8A, 0x5E, -0x90, 0x42, 0x00, 0xDD, 0x08, 0x80, 0x07, 0xB0, -0xF0, 0xBD, 0x00, 0x00, 0xAC, 0x04, 0x00, 0x20, -0x60, 0x02, 0x00, 0x20, 0x8F, 0x02, 0x00, 0x20, -0xF2, 0x02, 0x00, 0x20, 0x58, 0x07, 0x00, 0x20, -0x52, 0x07, 0x00, 0x20, 0x54, 0x07, 0x00, 0x20, -0xF8, 0xB5, 0x20, 0x4A, 0x20, 0x4B, 0x12, 0x68, -0x9A, 0x42, 0x16, 0xD0, 0x1F, 0x4F, 0x00, 0x29, -0x1A, 0xD0, 0x1F, 0x49, 0x3A, 0x46, 0x09, 0x78, -0x13, 0x68, 0x1D, 0x29, 0x0E, 0xD0, 0x00, 0x21, -0x4A, 0x00, 0x9C, 0x5A, 0x49, 0x1C, 0x65, 0x00, -0x64, 0x19, 0x85, 0x5A, 0xC9, 0xB2, 0x64, 0x19, -0x24, 0xB2, 0xA4, 0x10, 0x9C, 0x52, 0x30, 0x29, -0xF2, 0xD3, 0xF8, 0xBD, 0x01, 0x46, 0x60, 0x22, -0x18, 0x46, 0xFE, 0xF7, 0xC5, 0xFD, 0xF8, 0xBD, -0x00, 0x23, 0x59, 0x00, 0x3A, 0x68, 0x44, 0x5E, -0x55, 0x5E, 0x62, 0x1B, 0xD6, 0x17, 0x76, 0x0F, -0xB2, 0x18, 0xD2, 0x10, 0x00, 0xD5, 0x52, 0x42, -0x52, 0x1C, 0x12, 0xB2, 0x07, 0x2A, 0x00, 0xDD, -0x07, 0x22, 0x08, 0x26, 0xB6, 0x1A, 0x36, 0xB2, -0x54, 0x43, 0x75, 0x43, 0x62, 0x19, 0xD2, 0x10, -0x5B, 0x1C, 0xDB, 0xB2, 0x42, 0x52, 0x30, 0x2B, -0xE3, 0xD3, 0xF8, 0xBD, 0xE4, 0x06, 0x00, 0x20, -0xA1, 0x00, 0x03, 0xF3, 0x14, 0x03, 0x00, 0x20, -0x4C, 0x07, 0x00, 0x20, 0xF8, 0xB5, 0x06, 0x46, -0x0D, 0x46, 0x30, 0x23, 0xFF, 0xF7, 0x04, 0xF8, -0x04, 0x00, 0x28, 0xD0, 0x19, 0x4F, 0x00, 0x2A, -0x21, 0xD0, 0x00, 0x20, 0xFE, 0xF7, 0x5E, 0xFE, -0x02, 0xE0, 0x98, 0x00, 0x29, 0x58, 0x21, 0x50, -0x18, 0x46, 0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, -0xF7, 0xD1, 0x38, 0x68, 0x01, 0x88, 0x31, 0x43, -0x01, 0x80, 0x11, 0x49, 0x09, 0x68, 0xCA, 0x79, -0x8B, 0x79, 0x11, 0x02, 0x19, 0x43, 0x41, 0x80, -0xC1, 0x21, 0x89, 0x00, 0x0D, 0x48, 0x00, 0xF0, -0x1D, 0xF8, 0x0D, 0x49, 0xC8, 0x63, 0x01, 0x20, -0xFE, 0xF7, 0x40, 0xFE, 0x0C, 0xE0, 0x38, 0x68, -0x00, 0x88, 0x30, 0x40, 0x03, 0xD1, 0xF8, 0xBD, -0x98, 0x00, 0x21, 0x58, 0x29, 0x50, 0x18, 0x46, -0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, 0xF7, 0xD1, -0x01, 0x20, 0xF8, 0xBD, 0x08, 0x00, 0x00, 0x20, -0xAC, 0x04, 0x00, 0x20, 0x78, 0x7C, 0x00, 0x00, -0x40, 0x7F, 0x00, 0x00, 0x70, 0xB5, 0x41, 0x18, -0x49, 0x1E, 0x64, 0x24, 0x09, 0x04, 0x0B, 0x4D, -0x01, 0x43, 0x69, 0x63, 0xE8, 0x68, 0x81, 0x21, -0x09, 0x06, 0x08, 0x43, 0xE8, 0x60, 0x02, 0xE0, -0x01, 0x20, 0xFE, 0xF7, 0x21, 0xFF, 0xE8, 0x68, -0xC0, 0x01, 0x04, 0xD5, 0x20, 0x46, 0x64, 0x1E, -0xA4, 0xB2, 0x00, 0x28, 0xF4, 0xD1, 0xA8, 0x6B, -0x70, 0xBD, 0x00, 0x00, 0x40, 0x09, 0x00, 0x50, -0x10, 0xB5, 0x1D, 0x4C, 0x01, 0x21, 0x60, 0x8A, -0x89, 0x02, 0x88, 0x42, 0x01, 0xD0, 0x03, 0x20, -0x60, 0x71, 0x19, 0x48, 0x30, 0x21, 0x38, 0x30, -0xFE, 0xF7, 0x47, 0xFD, 0x00, 0x20, 0x60, 0x72, -0xA0, 0x71, 0x60, 0x63, 0x1E, 0x21, 0x21, 0x72, -0x14, 0x49, 0x20, 0x71, 0x08, 0x70, 0x14, 0x49, -0x09, 0x22, 0x09, 0x78, 0x21, 0x82, 0xE0, 0x81, -0x61, 0x8B, 0x12, 0x03, 0x11, 0x43, 0x61, 0x83, -0xE0, 0x72, 0x10, 0x49, 0x20, 0x73, 0x08, 0x70, -0x0F, 0x49, 0x08, 0x70, 0x0F, 0x49, 0x08, 0x70, -0x0F, 0x49, 0x08, 0x70, 0x0F, 0x49, 0x08, 0x70, -0xA0, 0x72, 0xE0, 0x82, 0x20, 0x83, 0x03, 0x20, -0x40, 0x02, 0x60, 0x62, 0x0D, 0x20, 0xC0, 0x01, -0xA0, 0x62, 0x07, 0x20, 0x00, 0x02, 0xE0, 0x62, -0x0F, 0x20, 0xC0, 0x01, 0x20, 0x63, 0x10, 0xBD, -0xE4, 0x02, 0x00, 0x20, 0x8F, 0x02, 0x00, 0x20, -0xB7, 0x02, 0x00, 0x20, 0xBA, 0x02, 0x00, 0x20, -0xA8, 0x04, 0x00, 0x20, 0xBF, 0x02, 0x00, 0x20, -0xCA, 0x02, 0x00, 0x20, 0xCB, 0x02, 0x00, 0x20, -0x11, 0x48, 0xC0, 0x6B, 0x11, 0x49, 0x08, 0x60, -0x11, 0x48, 0x03, 0x21, 0x41, 0x71, 0x11, 0x49, -0xC1, 0x61, 0x10, 0x49, 0x60, 0x31, 0x01, 0x62, -0x01, 0x21, 0x01, 0x70, 0x07, 0x22, 0x42, 0x70, -0x0D, 0x4B, 0x05, 0x22, 0x1A, 0x70, 0x0D, 0x4B, -0x1A, 0x70, 0x0D, 0x4B, 0x55, 0x22, 0xDA, 0x70, -0x04, 0x22, 0x82, 0x82, 0x00, 0x22, 0x09, 0x23, -0xC2, 0x70, 0x1B, 0x03, 0x43, 0x83, 0x81, 0x70, -0x08, 0x48, 0x02, 0x70, 0x70, 0x47, 0x00, 0x00, -0x80, 0x09, 0x00, 0x50, 0xE4, 0x06, 0x00, 0x20, -0xE4, 0x02, 0x00, 0x20, 0x00, 0x00, 0x04, 0x20, -0x87, 0x02, 0x00, 0x20, 0x4D, 0x07, 0x00, 0x20, -0x90, 0x02, 0x00, 0x20, 0xBD, 0x02, 0x00, 0x20, -0x04, 0x22, 0x0F, 0x49, 0x0C, 0x28, 0x10, 0xD0, -0x8B, 0x05, 0x0D, 0x28, 0x08, 0x6A, 0x10, 0xD0, -0x18, 0x43, 0x08, 0x62, 0x88, 0x6A, 0x10, 0x43, -0x88, 0x62, 0x0A, 0x4A, 0x01, 0x20, 0x10, 0x70, -0xC8, 0x68, 0xC8, 0x60, 0x88, 0x6A, 0x88, 0x62, -0x70, 0x47, 0x08, 0x6A, 0x40, 0x00, 0x40, 0x08, -0x00, 0xE0, 0x18, 0x43, 0x08, 0x62, 0x88, 0x6A, -0x90, 0x43, 0x88, 0x62, 0xF0, 0xE7, 0x00, 0x00, -0x00, 0x06, 0x00, 0x50, 0x80, 0x02, 0x00, 0x20, -0x00, 0xB5, 0x1B, 0x48, 0x00, 0x21, 0x0B, 0x00, -0xFE, 0xF7, 0x10, 0xFD, 0x0C, 0x07, 0x0A, 0x0E, -0x11, 0x2A, 0x15, 0x18, 0x1B, 0x1E, 0x21, 0x24, -0x27, 0x2A, 0x16, 0x4A, 0x02, 0x80, 0x22, 0xE0, -0x14, 0x4A, 0x12, 0x1D, 0x42, 0x80, 0x1E, 0xE0, -0x13, 0x4A, 0x82, 0x80, 0x1B, 0xE0, 0x11, 0x4A, -0x10, 0x32, 0xC2, 0x80, 0x17, 0xE0, 0x11, 0x4A, -0x42, 0x81, 0x14, 0xE0, 0x10, 0x4A, 0x82, 0x81, -0x11, 0xE0, 0x10, 0x4A, 0xC2, 0x81, 0x0E, 0xE0, -0x0F, 0x4A, 0x02, 0x82, 0x0B, 0xE0, 0x0F, 0x4A, -0x42, 0x82, 0x08, 0xE0, 0x0E, 0x4A, 0x82, 0x82, -0x05, 0xE0, 0x0E, 0x4A, 0xC2, 0x82, 0x02, 0xE0, -0x0D, 0x4A, 0x4B, 0x00, 0xC2, 0x52, 0x49, 0x1C, -0x89, 0xB2, 0x0C, 0x29, 0xCB, 0xD3, 0x00, 0xBD, -0xCC, 0x02, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, -0x88, 0x02, 0x00, 0x20, 0x34, 0x01, 0x00, 0x20, -0xE0, 0x06, 0x00, 0x20, 0x10, 0x05, 0x00, 0x20, -0xD8, 0x06, 0x00, 0x20, 0xC0, 0x02, 0x00, 0x20, -0xBC, 0x02, 0x00, 0x20, 0x9C, 0x01, 0x00, 0x20, -0xC4, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x27, 0x4A, -0x11, 0x68, 0x26, 0x48, 0x40, 0x30, 0x01, 0x61, -0x51, 0x68, 0x41, 0x61, 0x91, 0x68, 0x81, 0x61, -0xD1, 0x68, 0xC1, 0x61, 0x83, 0x69, 0xF0, 0x21, -0x8B, 0x43, 0x83, 0x61, 0x83, 0x69, 0x0B, 0x43, -0x83, 0x61, 0x1F, 0x49, 0x1F, 0x4B, 0x09, 0x68, -0x99, 0x42, 0x07, 0xD1, 0x43, 0x69, 0x5B, 0x04, -0x04, 0xD5, 0x43, 0x69, 0x01, 0x24, 0x64, 0x03, -0x1B, 0x1B, 0x43, 0x61, 0x17, 0x48, 0x40, 0x38, -0x03, 0x68, 0x01, 0x24, 0x24, 0x06, 0x23, 0x43, -0x03, 0x60, 0x16, 0x48, 0x40, 0x1C, 0x81, 0x42, -0x09, 0xD1, 0x05, 0x20, 0x00, 0x07, 0x41, 0x69, -0x52, 0x68, 0xD2, 0x0A, 0x12, 0x1F, 0x12, 0x07, -0x12, 0x0A, 0x11, 0x43, 0x41, 0x61, 0x30, 0x21, -0x0F, 0x48, 0xFE, 0xF7, 0x2F, 0xFF, 0x0E, 0x48, -0x40, 0x21, 0xC0, 0x30, 0xFE, 0xF7, 0x2A, 0xFF, -0x0B, 0x20, 0xFE, 0xF7, 0x4B, 0xFC, 0x03, 0x20, -0xFE, 0xF7, 0x48, 0xFC, 0x00, 0x20, 0xFE, 0xF7, -0x45, 0xFC, 0x05, 0x20, 0xFE, 0xF7, 0x42, 0xFC, -0x09, 0x20, 0xFE, 0xF7, 0x3F, 0xFC, 0x01, 0x20, -0x10, 0xBD, 0x00, 0x00, 0x40, 0x14, 0x00, 0x50, -0xE4, 0x06, 0x00, 0x20, 0xA1, 0x00, 0x03, 0xF3, -0xE0, 0x62, 0x00, 0x00, 0xF0, 0xB5, 0x93, 0xB0, -0x00, 0x21, 0xFD, 0x48, 0x0A, 0x91, 0x02, 0x78, -0x08, 0x92, 0x40, 0x78, 0x0B, 0x90, 0x08, 0x46, -0x00, 0x2A, 0x04, 0xD1, 0x0B, 0x9A, 0x00, 0x2A, -0x01, 0xD1, 0xF8, 0x4A, 0x10, 0x70, 0xF8, 0x4A, -0x12, 0x68, 0x90, 0x32, 0x53, 0x7B, 0x14, 0x7B, -0x1B, 0x06, 0x1B, 0x14, 0x23, 0x43, 0x03, 0x93, -0x01, 0x93, 0xF4, 0x4B, 0xF4, 0x4C, 0x1B, 0x78, -0x0C, 0x93, 0x24, 0x78, 0x11, 0x94, 0x23, 0x43, -0x0D, 0x93, 0x03, 0xD1, 0xF1, 0x4B, 0x1B, 0x78, -0x04, 0x2B, 0x04, 0xD9, 0x03, 0x9B, 0x5B, 0x10, -0x01, 0x93, 0x02, 0x23, 0x0A, 0x93, 0x53, 0x7B, -0x14, 0x7B, 0x1A, 0x02, 0x22, 0x43, 0x52, 0x42, -0x13, 0xB2, 0x9C, 0x46, 0xEA, 0x4B, 0x00, 0x22, -0x9A, 0x5E, 0x62, 0x45, 0x06, 0xDA, 0x53, 0x23, -0xDB, 0x43, 0x9A, 0x42, 0x01, 0xDA, 0x9C, 0x46, -0x00, 0xE0, 0x94, 0x46, 0xE5, 0x4A, 0x00, 0x23, -0xD3, 0x5E, 0xE5, 0x4C, 0x00, 0x22, 0xA2, 0x5E, -0x12, 0x92, 0x9A, 0x1A, 0x12, 0xB2, 0x00, 0x92, -0xE2, 0x4C, 0x00, 0x22, 0xA2, 0x5E, 0x04, 0x92, -0x00, 0x2B, 0x7D, 0xD0, 0xE0, 0x4A, 0x12, 0x78, -0x00, 0x2A, 0xFA, 0xD0, 0xDF, 0x4A, 0x12, 0x78, -0x00, 0x2A, 0xF6, 0xD0, 0x0C, 0x9A, 0x00, 0x2A, -0x03, 0xD1, 0xDD, 0x4A, 0x12, 0x78, 0x18, 0x2A, -0x6E, 0xD1, 0xDC, 0x4A, 0xCF, 0x4F, 0x12, 0x78, -0x00, 0x9D, 0xD4, 0x08, 0x0E, 0x94, 0x04, 0x24, -0x3C, 0x5F, 0x05, 0x94, 0xD2, 0x4C, 0xD1, 0x4F, -0x24, 0x88, 0x3F, 0x88, 0x01, 0x9E, 0x3C, 0x1B, -0x27, 0xB2, 0x05, 0x9C, 0xE4, 0x1B, 0x10, 0x94, -0x64, 0x42, 0x0F, 0x94, 0x14, 0x09, 0x09, 0x94, -0xD1, 0x4F, 0x00, 0x24, 0x3C, 0x5F, 0x02, 0x94, -0xB5, 0x42, 0x7D, 0xDD, 0xCF, 0x4C, 0x00, 0x25, -0x65, 0x5F, 0x05, 0x95, 0x06, 0x95, 0xBF, 0x4D, -0x6E, 0x78, 0x07, 0x96, 0x00, 0x2E, 0x0D, 0xD0, -0x57, 0x09, 0xB7, 0x42, 0x13, 0xD9, 0x05, 0x9F, -0x00, 0x9E, 0xF6, 0x19, 0x77, 0x10, 0x27, 0x80, -0x06, 0x26, 0xAE, 0x5F, 0xBE, 0x42, 0x15, 0xDA, -0xEF, 0x80, 0x13, 0xE0, 0x00, 0x9E, 0x26, 0x80, -0xEE, 0x80, 0xC3, 0x4C, 0xA8, 0x70, 0x20, 0x70, -0x01, 0x24, 0x6C, 0x70, 0x7C, 0xE0, 0x05, 0x9E, -0x37, 0x01, 0xBF, 0x1B, 0x00, 0x9E, 0xBE, 0x19, -0x01, 0x27, 0x36, 0x03, 0xFF, 0x03, 0xF6, 0x19, -0x36, 0x14, 0x26, 0x80, 0x26, 0x88, 0x06, 0x9C, -0x34, 0x1B, 0x24, 0xB2, 0x06, 0x94, 0x06, 0x24, -0x2C, 0x5F, 0x36, 0x1B, 0x36, 0xB2, 0x05, 0x96, -0x07, 0x9E, 0x96, 0x42, 0x64, 0xD2, 0x0E, 0x9E, -0x97, 0x1B, 0x07, 0x9E, 0xB7, 0x42, 0x10, 0xDA, -0xA6, 0x11, 0x77, 0x42, 0x05, 0x9E, 0xB7, 0x42, -0x0B, 0xDA, 0x2E, 0x78, 0x00, 0x2E, 0x08, 0xD0, -0xAE, 0x78, 0xAD, 0x4F, 0x76, 0x1C, 0xAE, 0x70, -0x3E, 0x78, 0x00, 0x2E, 0x01, 0xD0, 0x76, 0x1E, -0x3E, 0x70, 0x2F, 0x46, 0x09, 0x9E, 0x00, 0xE0, -0xF7, 0xE1, 0x07, 0x9D, 0xAE, 0x42, 0x0E, 0xD2, -0x06, 0x9C, 0x02, 0x2C, 0x05, 0xDD, 0x08, 0x9C, -0x00, 0x2C, 0x02, 0xD0, 0xBC, 0x78, 0x64, 0x1C, -0xBC, 0x70, 0xBD, 0x78, 0x03, 0x2D, 0x11, 0xD9, -0x01, 0x25, 0x3D, 0x70, 0x0E, 0xE0, 0x24, 0x11, -0x65, 0x42, 0x05, 0x9C, 0xA5, 0x42, 0x14, 0xDD, -0x02, 0x9C, 0x24, 0x11, 0x65, 0x42, 0x04, 0x9C, -0xA5, 0x42, 0x0E, 0xDD, 0x98, 0x4D, 0x2C, 0x78, -0x64, 0x1C, 0x2C, 0x70, 0x96, 0x4C, 0x25, 0x78, -0x03, 0x2D, 0x01, 0xD9, 0x01, 0x21, 0x20, 0x70, -0x07, 0x9C, 0x64, 0x1C, 0x7C, 0x70, 0x1F, 0xE0, -0x01, 0xE0, 0x91, 0x4C, 0xF7, 0xE7, 0x81, 0x4F, -0x8E, 0x4C, 0x78, 0x70, 0x20, 0x80, 0x03, 0x9C, -0x64, 0x10, 0xAC, 0x42, 0x11, 0xDD, 0x38, 0x70, -0x05, 0x9D, 0xAC, 0x42, 0x0D, 0xDD, 0x10, 0x9C, -0x00, 0x2C, 0x00, 0xDA, 0x0F, 0x9C, 0x08, 0x25, -0x7D, 0x5F, 0x24, 0xB2, 0x00, 0x2D, 0x03, 0xD0, -0x6E, 0x01, 0x75, 0x1B, 0x2C, 0x19, 0x64, 0x11, -0x3C, 0x81, 0x83, 0x4C, 0x20, 0x70, 0xB8, 0x70, -0x72, 0x4F, 0x08, 0x25, 0x7D, 0x5F, 0xDC, 0x10, -0x6D, 0x1D, 0xAC, 0x42, 0x01, 0xDA, 0x2C, 0xB2, -0x02, 0xE0, 0x1E, 0x2C, 0x00, 0xDD, 0x1E, 0x24, -0x78, 0x4E, 0x0D, 0x9D, 0x36, 0x78, 0xB6, 0x08, -0x05, 0x96, 0x00, 0x2D, 0x20, 0xD0, 0x3D, 0x78, -0x00, 0x2D, 0x1D, 0xD1, 0x01, 0x9E, 0x00, 0x9D, -0xB5, 0x42, 0x19, 0xDD, 0x10, 0x9E, 0x00, 0x2E, -0x00, 0xDA, 0x0F, 0x9E, 0x73, 0x4D, 0xA6, 0x42, -0x2D, 0x78, 0x72, 0x4C, 0x04, 0xDA, 0x6D, 0x1C, -0x25, 0x70, 0x71, 0x4C, 0x20, 0x70, 0x03, 0xE0, -0x00, 0x2D, 0x01, 0xD0, 0x6D, 0x1E, 0x25, 0x70, -0x6C, 0x4E, 0x05, 0x9C, 0x35, 0x78, 0xAC, 0x42, -0x04, 0xD8, 0x01, 0x21, 0x30, 0x70, 0x01, 0xE0, -0x68, 0x4C, 0x20, 0x70, 0x57, 0x4D, 0x00, 0x9C, -0xAC, 0x80, 0x01, 0x9C, 0x67, 0x4F, 0xA3, 0x42, -0x30, 0xDD, 0x13, 0x25, 0x12, 0x9C, 0xED, 0x43, -0xAC, 0x42, 0x2B, 0xDA, 0x64, 0x4E, 0xB5, 0x88, -0x05, 0x9C, 0xA5, 0x42, 0x26, 0xD2, 0x4F, 0x4C, -0x0A, 0x25, 0x65, 0x5F, 0xAB, 0x42, 0x04, 0xDD, -0xB6, 0x88, 0x09, 0x9D, 0xAE, 0x42, 0x00, 0xD2, -0x63, 0x81, 0x0A, 0x25, 0x65, 0x5F, 0x0A, 0x24, -0x65, 0x43, 0x5A, 0x4C, 0x2D, 0x11, 0x24, 0x78, -0x9D, 0x42, 0x0E, 0xDD, 0x0A, 0x9E, 0x1D, 0x46, -0x35, 0x41, 0x6E, 0x42, 0x04, 0x9D, 0xAE, 0x42, -0x07, 0xDD, 0x64, 0x1C, 0xE4, 0xB2, 0x3C, 0x70, -0x05, 0x2C, 0x0A, 0xD9, 0x38, 0x70, 0x01, 0x21, -0x07, 0xE0, 0x00, 0x2C, 0x05, 0xD0, 0x64, 0x1E, -0x3C, 0x70, 0x02, 0xE0, 0x3B, 0x4C, 0x60, 0x81, -0x38, 0x70, 0x0D, 0x9C, 0x00, 0x2C, 0x15, 0xD0, -0x4B, 0x4C, 0xA4, 0x88, 0x94, 0x42, 0x11, 0xD9, -0x47, 0x4C, 0x4A, 0x4D, 0x26, 0x78, 0x2C, 0x78, -0x00, 0x2E, 0x07, 0xD0, 0x64, 0x1C, 0xE4, 0xB2, -0x2C, 0x70, 0x94, 0x42, 0x06, 0xD9, 0x28, 0x70, -0x01, 0x21, 0x03, 0xE0, 0x00, 0x2C, 0x01, 0xD0, -0x64, 0x1E, 0x2C, 0x70, 0x42, 0x4C, 0x24, 0x78, -0x00, 0x94, 0xE4, 0x07, 0x07, 0xD0, 0x41, 0x4C, -0x55, 0x00, 0x26, 0x78, 0xB5, 0x42, 0x02, 0xD2, -0x20, 0x70, 0x01, 0x21, 0x01, 0xE0, 0x00, 0x29, -0x05, 0xD0, 0x0C, 0x9D, 0x27, 0x4C, 0x00, 0x2D, -0x01, 0xD1, 0x01, 0x25, 0x25, 0x70, 0x36, 0x4C, -0xA6, 0x88, 0x39, 0x4C, 0x05, 0x9D, 0x24, 0x78, -0xAE, 0x42, 0x06, 0xD3, 0x21, 0x4D, 0x2D, 0x78, -0x00, 0x2D, 0x02, 0xD1, 0x11, 0x9D, 0x00, 0x2D, -0x79, 0xD0, 0x65, 0x46, 0x6E, 0x00, 0x04, 0x9D, -0xAE, 0x42, 0x0A, 0xDC, 0xAF, 0x25, 0xAD, 0x00, -0xAB, 0x42, 0x06, 0xDC, 0x03, 0x9D, 0x02, 0x9B, -0xAB, 0x42, 0x0D, 0xDD, 0x04, 0x9B, 0x63, 0x45, -0x0A, 0xDA, 0x2C, 0x4D, 0x2B, 0x78, 0x5B, 0x1C, -0x1B, 0x06, 0x1B, 0x0E, 0x2B, 0x70, 0x05, 0xD0, -0x13, 0x4D, 0x01, 0x21, 0x29, 0x70, 0x01, 0xE0, -0x26, 0x4B, 0x18, 0x70, 0x0F, 0x4B, 0x10, 0x4D, -0x1B, 0x78, 0x2D, 0x78, 0x2B, 0x43, 0x5B, 0xD0, -0x03, 0x9D, 0x02, 0x9B, 0xAB, 0x42, 0x01, 0xDC, -0x00, 0x2C, 0x55, 0xD0, 0x04, 0x9B, 0x5D, 0x42, -0x02, 0x9B, 0x9D, 0x42, 0x02, 0xDC, 0x04, 0x9B, -0x63, 0x45, 0x3F, 0xDA, 0x16, 0x4B, 0x1B, 0x7A, -0x01, 0x2B, 0x36, 0xD9, 0x09, 0x9B, 0x35, 0xE0, -0x7C, 0x07, 0x00, 0x20, 0x1C, 0x04, 0x00, 0x20, -0xAC, 0x04, 0x00, 0x20, 0xB9, 0x02, 0x00, 0x20, -0xBB, 0x02, 0x00, 0x20, 0xE5, 0x02, 0x00, 0x20, -0xFA, 0x02, 0x00, 0x20, 0x54, 0x07, 0x00, 0x20, -0x52, 0x07, 0x00, 0x20, 0x56, 0x07, 0x00, 0x20, -0xE6, 0x02, 0x00, 0x20, 0x84, 0x02, 0x00, 0x20, -0xB5, 0x02, 0x00, 0x20, 0xB7, 0x02, 0x00, 0x20, -0x58, 0x07, 0x00, 0x20, 0x6C, 0x02, 0x00, 0x20, -0x70, 0x02, 0x00, 0x20, 0x71, 0x02, 0x00, 0x20, -0x4F, 0x07, 0x00, 0x20, 0x72, 0x02, 0x00, 0x20, -0x60, 0x07, 0x00, 0x20, 0xBA, 0x02, 0x00, 0x20, -0xBD, 0x02, 0x00, 0x20, 0xBE, 0x02, 0x00, 0x20, -0xF0, 0x02, 0x00, 0x20, 0xEF, 0x02, 0x00, 0x20, -0x0D, 0xE0, 0x0E, 0x9B, 0x5D, 0x4D, 0x1B, 0x19, -0x2B, 0x70, 0x01, 0xE0, 0x5B, 0x4B, 0x18, 0x70, -0x5A, 0x4C, 0x05, 0x9B, 0x24, 0x78, 0xA3, 0x42, -0x06, 0xD2, 0x01, 0x21, 0x04, 0xE0, 0x05, 0x9B, -0x56, 0x4D, 0xA3, 0x42, 0x00, 0xD3, 0x28, 0x70, -0x55, 0x4D, 0x56, 0x4F, 0x2E, 0x78, 0x00, 0x2E, -0x08, 0xD0, 0x55, 0x4B, 0x3C, 0x46, 0x24, 0x78, -0x1B, 0x78, 0x01, 0x2E, 0x0D, 0xD0, 0x02, 0x2E, -0x3A, 0xD0, 0x3E, 0xE0, 0x08, 0x9B, 0x00, 0x2B, -0x3B, 0xD0, 0x0B, 0x9B, 0x00, 0x2B, 0x38, 0xD1, -0x01, 0x23, 0x2B, 0x70, 0x08, 0x9B, 0x3B, 0x70, -0x33, 0xE0, 0xA3, 0x42, 0x26, 0xD9, 0x4B, 0x4C, -0x4B, 0x4E, 0x24, 0x78, 0x77, 0x7A, 0x00, 0x2C, -0x10, 0xD0, 0x1E, 0x2F, 0x0C, 0xD9, 0x47, 0x49, -0x08, 0x70, 0x44, 0x49, 0x28, 0x70, 0x08, 0x70, -0x46, 0x49, 0x09, 0x78, 0x00, 0x29, 0x22, 0xD1, -0x45, 0x4B, 0x01, 0x21, 0x19, 0x70, 0x1E, 0xE0, -0x28, 0x70, 0x1A, 0xE0, 0x41, 0x4C, 0x42, 0x4E, -0x24, 0x78, 0x36, 0x78, 0x34, 0x43, 0x14, 0xD0, -0xB4, 0x2F, 0x04, 0xD9, 0x02, 0x24, 0x2C, 0x70, -0x38, 0x4C, 0x23, 0x70, 0x0D, 0xE0, 0x3D, 0x4B, -0x98, 0x80, 0x0A, 0xE0, 0x35, 0x4E, 0xA3, 0x42, -0x07, 0xD2, 0x28, 0x70, 0x30, 0x70, 0x04, 0xE0, -0xA3, 0x42, 0x02, 0xD2, 0x28, 0x70, 0x38, 0x70, -0x01, 0xE0, 0x00, 0x29, 0x31, 0xD0, 0x36, 0x49, -0x37, 0x4D, 0x08, 0x70, 0x88, 0x70, 0x35, 0x49, -0x36, 0x4C, 0x08, 0x70, 0x36, 0x49, 0x2B, 0x78, -0x09, 0x78, 0x49, 0x08, 0x00, 0x2B, 0x1C, 0xD0, -0x93, 0x42, 0x02, 0xD3, 0x33, 0x4D, 0x01, 0x23, -0x2B, 0x70, 0x33, 0x4B, 0x80, 0x25, 0x18, 0x70, -0x22, 0x4B, 0x18, 0x70, 0x31, 0x4B, 0x18, 0x80, -0x00, 0x98, 0x31, 0x4B, 0x28, 0x43, 0x18, 0x70, -0x24, 0x4B, 0x18, 0x78, 0x00, 0x28, 0x0C, 0xD0, -0x21, 0x70, 0x2E, 0x49, 0xFE, 0x28, 0x0A, 0x80, -0x01, 0xD2, 0x40, 0x1C, 0x18, 0x70, 0x13, 0xB0, -0xF0, 0xBD, 0x01, 0x23, 0x2B, 0x70, 0x21, 0x70, -0xE3, 0xE7, 0x1E, 0x49, 0x01, 0x20, 0x08, 0x72, -0xF5, 0xE7, 0x04, 0x99, 0x61, 0x45, 0x15, 0xDA, -0x25, 0x49, 0x0A, 0x78, 0x1B, 0x49, 0x01, 0x2A, -0x09, 0x78, 0x0B, 0xD0, 0x04, 0x2A, 0x02, 0xD0, -0x05, 0x2A, 0x0B, 0xD1, 0x03, 0xE0, 0x49, 0x08, -0x16, 0x4A, 0x49, 0x00, 0x05, 0xE0, 0x15, 0x4A, -0xFD, 0x23, 0x01, 0xE0, 0x13, 0x4A, 0xFB, 0x23, -0x19, 0x40, 0x11, 0x70, 0x17, 0x49, 0x4B, 0x23, -0x0A, 0x88, 0x1B, 0x01, 0x9A, 0x42, 0xD6, 0xD3, -0x0A, 0x4A, 0x10, 0x70, 0x08, 0x80, 0x0E, 0x49, -0x08, 0x70, 0x09, 0x49, 0x08, 0x70, 0x12, 0x49, -0x08, 0x70, 0xCC, 0xE7, 0xF0, 0x02, 0x00, 0x20, -0xBF, 0x02, 0x00, 0x20, 0xCA, 0x02, 0x00, 0x20, -0xCB, 0x02, 0x00, 0x20, 0xA8, 0x04, 0x00, 0x20, -0x7C, 0x07, 0x00, 0x20, 0xB9, 0x02, 0x00, 0x20, -0xBB, 0x02, 0x00, 0x20, 0x60, 0x07, 0x00, 0x20, -0x1C, 0x04, 0x00, 0x20, 0x9A, 0x01, 0x00, 0x20, -0x73, 0x02, 0x00, 0x20, 0x51, 0x07, 0x00, 0x20, -0xB7, 0x02, 0x00, 0x20, 0x8F, 0x02, 0x00, 0x20, -0xEF, 0x02, 0x00, 0x20, 0x6E, 0x02, 0x00, 0x20, -0xBD, 0x02, 0x00, 0x20, 0xF4, 0x02, 0x00, 0x20, -0xE5, 0x02, 0x00, 0x20, 0x0E, 0x4A, 0x00, 0x21, -0x11, 0x60, 0x0E, 0x4A, 0x20, 0x21, 0x11, 0x60, -0x78, 0x28, 0x13, 0xD0, 0x0C, 0x48, 0x01, 0x23, -0x00, 0x05, 0x00, 0x0D, 0x5B, 0x03, 0xC0, 0x18, -0x05, 0x22, 0x12, 0x07, 0x10, 0x62, 0x90, 0x00, -0xC2, 0x68, 0x0A, 0x43, 0xC2, 0x60, 0x05, 0x4A, -0x80, 0x3A, 0x11, 0x60, 0x41, 0x68, 0x19, 0x43, -0x41, 0x60, 0x70, 0x47, 0xFF, 0x20, 0xEA, 0xE7, -0x68, 0x02, 0x00, 0x20, 0x80, 0xE1, 0x00, 0xE0, -0x24, 0x09, 0x00, 0x00, 0x04, 0x49, 0x00, 0x20, -0x08, 0x60, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, -0x42, 0x14, 0x11, 0x43, 0x41, 0x60, 0x70, 0x47, -0x68, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x15, 0x4A, -0x11, 0x78, 0x81, 0x42, 0x25, 0xD0, 0x14, 0x49, -0x0B, 0x68, 0x01, 0x24, 0xA4, 0x02, 0x23, 0x43, -0x0B, 0x60, 0x10, 0x70, 0x05, 0x22, 0x40, 0x24, -0x10, 0x49, 0x12, 0x07, 0x80, 0x23, 0x00, 0x28, -0x09, 0xD0, 0x08, 0x68, 0x40, 0x06, 0x0C, 0xD4, -0x08, 0x68, 0x20, 0x43, 0x08, 0x60, 0xD0, 0x68, -0x98, 0x43, 0xD0, 0x60, 0x05, 0xE0, 0xD0, 0x68, -0x18, 0x43, 0xD0, 0x60, 0x08, 0x68, 0xA0, 0x43, -0x08, 0x60, 0x08, 0x68, 0x18, 0x43, 0x08, 0x60, -0xD0, 0x68, 0x01, 0x21, 0xC9, 0x03, 0x08, 0x43, -0xD0, 0x60, 0x10, 0xBD, 0x04, 0x00, 0x00, 0x20, -0x00, 0x10, 0x00, 0x50, 0x00, 0x11, 0x00, 0x50, -0x10, 0xB5, 0x10, 0x4C, 0xA0, 0x79, 0x00, 0x28, -0x18, 0xD1, 0x01, 0xF0, 0xED, 0xF8, 0x00, 0xF0, -0x21, 0xFF, 0xFF, 0xF7, 0x65, 0xF9, 0x0C, 0x48, -0x01, 0x78, 0x0C, 0x48, 0x00, 0x29, 0x02, 0xD0, -0x09, 0x21, 0x09, 0x02, 0x00, 0xE0, 0x0A, 0x49, -0x01, 0x60, 0x01, 0x20, 0xA0, 0x71, 0xE0, 0x71, -0x08, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x02, 0xD0, -0x00, 0xF0, 0x0E, 0xF8, 0x10, 0xBD, 0x00, 0xF0, -0xF1, 0xFE, 0x10, 0xBD, 0x60, 0x07, 0x00, 0x20, -0x99, 0x01, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, -0x04, 0x08, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20, -0x1B, 0x48, 0x81, 0x79, 0x00, 0x29, 0x32, 0xD0, -0x00, 0x21, 0x81, 0x71, 0xC1, 0x71, 0x19, 0x48, -0x81, 0x68, 0x01, 0x22, 0x52, 0x02, 0x91, 0x43, -0x81, 0x60, 0x17, 0x48, 0x01, 0x68, 0x92, 0x00, -0x91, 0x43, 0x01, 0x60, 0x15, 0x48, 0x01, 0x68, -0x10, 0x22, 0x91, 0x43, 0x01, 0x60, 0x01, 0x68, -0x80, 0x22, 0x11, 0x43, 0x01, 0x60, 0x12, 0x49, -0x41, 0x61, 0x12, 0x48, 0x00, 0x78, 0x00, 0x28, -0x15, 0xD1, 0x05, 0x20, 0x00, 0x07, 0x41, 0x6A, -0x92, 0x02, 0x91, 0x43, 0x41, 0x62, 0x41, 0x6A, -0x09, 0x22, 0x12, 0x05, 0x11, 0x43, 0x41, 0x62, -0x80, 0x00, 0xC2, 0x68, 0x41, 0x14, 0x8A, 0x43, -0xC2, 0x60, 0x02, 0x68, 0x03, 0x14, 0x9A, 0x43, -0x02, 0x60, 0x07, 0x48, 0x01, 0x60, 0x70, 0x47, -0x60, 0x07, 0x00, 0x20, 0x00, 0x06, 0x00, 0x50, -0x00, 0x10, 0x00, 0x50, 0x00, 0x11, 0x00, 0x50, -0xC8, 0x03, 0x60, 0x00, 0x99, 0x01, 0x00, 0x20, -0x80, 0xE1, 0x00, 0xE0, 0x00, 0xB5, 0x08, 0x49, -0x83, 0x20, 0x08, 0x70, 0x07, 0x49, 0x00, 0x20, -0x08, 0x70, 0x07, 0x48, 0x00, 0x68, 0x07, 0x49, -0x40, 0x05, 0x40, 0x0F, 0x08, 0x73, 0x78, 0x20, -0xFF, 0xF7, 0x1C, 0xFF, 0x00, 0xBD, 0x00, 0x00, -0x60, 0x07, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, -0x00, 0x11, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, -0x04, 0x49, 0x29, 0x20, 0xC8, 0x60, 0x04, 0x49, -0x35, 0x20, 0x08, 0x63, 0x04, 0x20, 0x01, 0x07, -0x88, 0x60, 0x70, 0x47, 0x40, 0x00, 0x00, 0x50, -0x80, 0x10, 0x00, 0x50, 0x10, 0xB5, 0x04, 0x46, -0x05, 0x22, 0x01, 0x20, 0x52, 0x04, 0xCB, 0x07, -0x01, 0xD0, 0x00, 0x20, 0x10, 0xBD, 0x07, 0x4B, -0x9C, 0x61, 0x09, 0x02, 0x49, 0x1C, 0x59, 0x61, -0x02, 0xE0, 0x52, 0x1E, 0x00, 0xD1, 0x00, 0x20, -0x59, 0x69, 0xC9, 0x07, 0xF2, 0xD0, 0x00, 0x2A, -0xF7, 0xD1, 0x10, 0xBD, 0x40, 0x14, 0x00, 0x50, -0xF0, 0xB5, 0x91, 0xB0, 0xAD, 0x49, 0x09, 0x91, -0xAC, 0x49, 0xAD, 0x4A, 0x60, 0x31, 0x07, 0x92, -0x0A, 0x91, 0xAC, 0x49, 0xAC, 0x4A, 0x08, 0x91, -0x12, 0x68, 0x0C, 0x92, 0xAB, 0x4A, 0xAC, 0x4E, -0x12, 0x68, 0x0D, 0x92, 0x00, 0x22, 0x17, 0x46, -0x14, 0x46, 0x00, 0x92, 0x07, 0x9A, 0x06, 0x91, -0xA8, 0x4D, 0x05, 0x92, 0x00, 0x21, 0x8A, 0x00, -0x05, 0x9B, 0x49, 0x1C, 0x9E, 0x50, 0x06, 0x9B, -0x89, 0xB2, 0x9D, 0x50, 0x18, 0x29, 0xF6, 0xD3, -0x01, 0x21, 0x89, 0x02, 0x03, 0x91, 0x01, 0x46, -0x04, 0x90, 0x50, 0x31, 0x50, 0x38, 0x09, 0xB2, -0x00, 0xB2, 0x0E, 0x91, 0x0F, 0x90, 0x00, 0xF0, -0xEF, 0xFC, 0x01, 0xF0, 0x0D, 0xF8, 0x00, 0xF0, -0x69, 0xF9, 0x9B, 0x48, 0x00, 0x78, 0xC0, 0x07, -0x20, 0xD1, 0x00, 0x26, 0x00, 0x21, 0x09, 0x9A, -0x88, 0x00, 0x13, 0x58, 0x0C, 0x9A, 0x49, 0x1C, -0x13, 0x50, 0x0A, 0x9A, 0x89, 0xB2, 0x13, 0x58, -0x0D, 0x9A, 0x18, 0x29, 0x13, 0x50, 0x05, 0x9A, -0x13, 0x58, 0x09, 0x9A, 0x13, 0x50, 0x06, 0x9A, -0x13, 0x58, 0x0A, 0x9A, 0x13, 0x50, 0xEA, 0xD3, -0x00, 0xF0, 0x0C, 0xFE, 0x00, 0xF0, 0xEC, 0xFF, -0x00, 0xF0, 0x48, 0xF9, 0x8A, 0x48, 0x00, 0x78, -0xC0, 0x07, 0x02, 0xD0, 0x00, 0x20, 0x11, 0xB0, -0xF0, 0xBD, 0x00, 0x25, 0x3C, 0xE0, 0x00, 0x2D, -0x3A, 0xD0, 0x02, 0x91, 0x01, 0x90, 0x05, 0x2E, -0x7A, 0xD9, 0x30, 0x2F, 0x78, 0xD2, 0x08, 0x98, -0x82, 0x4F, 0x0B, 0x90, 0x78, 0x6B, 0x03, 0x21, -0x09, 0x05, 0x88, 0x43, 0x01, 0x21, 0x49, 0x05, -0x40, 0x18, 0x78, 0x63, 0x00, 0xF0, 0xEA, 0xFD, -0x00, 0xF0, 0xCA, 0xFF, 0x00, 0xF0, 0x26, 0xF9, -0x79, 0x6B, 0x03, 0x20, 0x00, 0x05, 0x81, 0x43, -0x01, 0x20, 0x00, 0x05, 0x08, 0x18, 0x78, 0x63, -0x00, 0x27, 0x00, 0x20, 0x84, 0x46, 0x76, 0x48, -0x01, 0x68, 0x60, 0x46, 0x08, 0x18, 0xC0, 0x7E, -0x41, 0x28, 0x1F, 0xD0, 0x60, 0x46, 0x41, 0x00, -0x0B, 0x98, 0x0A, 0x2E, 0x40, 0x5A, 0x23, 0xD2, -0x70, 0x4A, 0x12, 0x68, 0x53, 0x5E, 0x04, 0x9A, -0x93, 0x42, 0x01, 0xDA, 0x03, 0x9A, 0x90, 0x43, -0x03, 0x9A, 0x52, 0x08, 0x10, 0x43, 0x7D, 0xE0, -0x6B, 0x48, 0x00, 0x68, 0x01, 0x90, 0x62, 0x48, -0x00, 0x68, 0x02, 0x90, 0x00, 0x98, 0x30, 0x28, -0x77, 0xD0, 0x07, 0x98, 0x0B, 0x90, 0x00, 0x20, -0x00, 0x90, 0xD6, 0xE7, 0x00, 0x2D, 0x02, 0xD0, -0x7F, 0x1C, 0xBF, 0xB2, 0x88, 0xE0, 0x00, 0x98, -0x40, 0x1C, 0x80, 0xB2, 0x00, 0x90, 0x83, 0xE0, -0x10, 0x2E, 0x13, 0xD2, 0x5D, 0x4A, 0x01, 0x23, -0x12, 0x68, 0x9B, 0x02, 0x54, 0x5E, 0xE2, 0x1D, -0xFF, 0x32, 0xFA, 0x32, 0x9A, 0x42, 0x09, 0xD9, -0x00, 0x2C, 0x00, 0xDA, 0x64, 0x42, 0x22, 0xB2, -0x40, 0x32, 0xD3, 0x17, 0x5B, 0x0E, 0x9A, 0x18, -0xD4, 0x11, 0x00, 0xE0, 0x01, 0x24, 0x0B, 0x2E, -0x24, 0xD9, 0x01, 0x9A, 0x52, 0x5E, 0x96, 0x46, -0x00, 0x2A, 0x0E, 0xD0, 0x4F, 0x4B, 0x1B, 0x68, -0x5B, 0x5E, 0x5A, 0x43, 0x00, 0x2A, 0x19, 0xDA, -0x02, 0x9A, 0x54, 0x5A, 0x22, 0x1A, 0x01, 0x2A, -0x05, 0xD0, 0x52, 0x1C, 0x03, 0xD0, 0x10, 0xE0, -0x61, 0xE0, 0x00, 0x24, 0x0E, 0xE0, 0x72, 0x46, -0x00, 0x2A, 0x00, 0xDA, 0x52, 0x42, 0x00, 0x2B, -0x00, 0xDA, 0x5B, 0x42, 0x9A, 0x42, 0x04, 0xDA, -0x20, 0x46, 0x01, 0x9A, 0x00, 0x24, 0x54, 0x52, -0x00, 0xE0, 0x01, 0x24, 0x3F, 0x4A, 0x0F, 0x9B, -0x12, 0x68, 0x52, 0x5E, 0x9A, 0x42, 0x0F, 0xDA, -0x02, 0x05, 0x06, 0xD5, 0x00, 0x1B, 0x01, 0x22, -0x80, 0xB2, 0xD2, 0x02, 0x90, 0x42, 0x10, 0xD3, -0x16, 0xE0, 0xA0, 0x42, 0x02, 0xDD, 0x00, 0x1B, -0x80, 0xB2, 0x11, 0xE0, 0x00, 0x20, 0x0F, 0xE0, -0x0E, 0x9B, 0x9A, 0x42, 0x12, 0xDD, 0x02, 0x05, -0x05, 0xD5, 0x00, 0x19, 0x80, 0xB2, 0x33, 0x4A, -0x04, 0xE0, 0x10, 0x46, 0x04, 0xE0, 0x00, 0x19, -0x31, 0x4A, 0x80, 0xB2, 0x90, 0x42, 0xF8, 0xD8, -0x00, 0x2C, 0x04, 0xD0, 0x00, 0x2D, 0x0F, 0xD1, -0x0A, 0xE0, 0x20, 0xE0, 0x00, 0x24, 0x00, 0x2D, -0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, 0x07, 0xE0, -0x00, 0x9A, 0x52, 0x1C, 0x92, 0xB2, 0x00, 0x92, -0x07, 0x9A, 0x05, 0x2E, 0x50, 0x52, 0x04, 0xD8, -0x01, 0x22, 0xD2, 0x02, 0x10, 0x43, 0x08, 0x9A, -0x50, 0x52, 0x00, 0x2C, 0x04, 0xD0, 0x1F, 0x48, -0x01, 0x9A, 0x00, 0x68, 0x40, 0x5A, 0x50, 0x52, -0x60, 0x46, 0x40, 0x1C, 0x80, 0xB2, 0x84, 0x46, -0x30, 0x28, 0x00, 0xD2, 0x43, 0xE7, 0x13, 0x48, -0x1C, 0x4A, 0x01, 0x68, 0x10, 0x68, 0x6D, 0x1C, -0xED, 0xB2, 0x02, 0x2D, 0x00, 0xD2, 0x16, 0xE7, -0x03, 0x98, 0x40, 0x08, 0x03, 0x90, 0x00, 0x98, -0x30, 0x28, 0x02, 0xD1, 0x30, 0x2F, 0x00, 0xD1, -0x40, 0x26, 0x76, 0x1C, 0xB6, 0xB2, 0x40, 0x2E, -0x00, 0xD8, 0xE3, 0xE6, 0x0B, 0x49, 0x48, 0x6B, -0x03, 0x22, 0x12, 0x05, 0x10, 0x43, 0x48, 0x63, -0x01, 0x20, 0xFC, 0xE6, 0x00, 0x20, 0x00, 0x50, -0x28, 0x04, 0x00, 0x20, 0x00, 0x00, 0x01, 0x20, -0x10, 0x03, 0x00, 0x20, 0x14, 0x03, 0x00, 0x20, -0x00, 0x04, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x0C, -0x98, 0x01, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, -0xAC, 0x04, 0x00, 0x20, 0x00, 0x03, 0x00, 0x20, -0x08, 0x03, 0x00, 0x20, 0xFF, 0x0F, 0x00, 0x00, -0xFF, 0x07, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x20, -0x10, 0xB5, 0x0B, 0x49, 0x30, 0x24, 0x00, 0x28, -0x0A, 0x4A, 0x0B, 0x4B, 0x08, 0x68, 0x06, 0xD0, -0x20, 0x43, 0x08, 0x60, 0x09, 0x48, 0x10, 0x60, -0x08, 0x48, 0x60, 0x30, 0x05, 0xE0, 0xA0, 0x43, -0x08, 0x60, 0x07, 0x48, 0x10, 0x60, 0x06, 0x48, -0x60, 0x30, 0x18, 0x60, 0x10, 0xBD, 0x00, 0x00, -0x00, 0x10, 0x00, 0x50, 0x00, 0x03, 0x00, 0x20, -0x04, 0x03, 0x00, 0x20, 0x00, 0x00, 0x04, 0x20, -0x00, 0x10, 0x04, 0x20, 0xF0, 0xB5, 0x11, 0x48, -0x01, 0x68, 0x11, 0x4D, 0x89, 0x06, 0x30, 0x22, -0x60, 0x35, 0x00, 0x29, 0x0E, 0x4B, 0x0F, 0x4C, -0x0F, 0x4E, 0x10, 0x4F, 0x01, 0x68, 0x0A, 0xDA, -0x91, 0x43, 0x01, 0x60, 0x23, 0x60, 0x0E, 0x48, -0x35, 0x60, 0x07, 0x60, 0x0B, 0x48, 0x0D, 0x49, -0x60, 0x30, 0x08, 0x60, 0xF0, 0xBD, 0x11, 0x43, -0x01, 0x60, 0x08, 0x48, 0x27, 0x60, 0x60, 0x30, -0x30, 0x60, 0x07, 0x48, 0x03, 0x60, 0x07, 0x48, -0x05, 0x60, 0xF0, 0xBD, 0x00, 0x10, 0x00, 0x50, -0x00, 0x00, 0x04, 0x20, 0x74, 0x02, 0x00, 0x20, -0x78, 0x02, 0x00, 0x20, 0x00, 0x10, 0x04, 0x20, -0x00, 0x03, 0x00, 0x20, 0x04, 0x03, 0x00, 0x20, -0x00, 0xB5, 0x09, 0x48, 0x00, 0x78, 0x00, 0x28, -0x08, 0xD0, 0x01, 0x28, 0x09, 0xD0, 0x02, 0x28, -0x03, 0xD1, 0x06, 0x49, 0x04, 0x20, 0x00, 0xF0, -0x55, 0xF8, 0x00, 0xBD, 0x04, 0x49, 0x01, 0x20, -0xF9, 0xE7, 0x04, 0x49, 0x02, 0x20, 0xF6, 0xE7, -0x4D, 0x07, 0x00, 0x20, 0xAC, 0x03, 0x00, 0x20, -0x4C, 0x03, 0x00, 0x20, 0xEC, 0x06, 0x00, 0x20, -0xF8, 0xB5, 0x07, 0x46, 0x00, 0xF0, 0x30, 0xFB, -0x1C, 0x4D, 0x60, 0x21, 0x28, 0x46, 0xFD, 0xF7, -0xE8, 0xFD, 0x00, 0x24, 0x00, 0xF0, 0x48, 0xFE, -0xFF, 0xF7, 0xA4, 0xFF, 0xFD, 0xF7, 0xCE, 0xFE, -0x00, 0x2C, 0x0C, 0xD0, 0x16, 0x48, 0x00, 0x22, -0x06, 0x68, 0x50, 0x00, 0x43, 0x19, 0x19, 0x88, -0x30, 0x5A, 0x52, 0x1C, 0x08, 0x18, 0xD2, 0xB2, -0x18, 0x80, 0x30, 0x2A, 0xF5, 0xD3, 0x64, 0x1C, -0xE4, 0xB2, 0x05, 0x2C, 0xE6, 0xD3, 0x00, 0x20, -0x29, 0x46, 0x42, 0x00, 0x52, 0x18, 0x00, 0x23, -0xD3, 0x5E, 0x40, 0x1C, 0x9B, 0x10, 0xC0, 0xB2, -0x13, 0x80, 0x30, 0x28, 0xF5, 0xD3, 0x09, 0x4C, -0x20, 0x78, 0x38, 0x42, 0x07, 0xD1, 0x08, 0x48, -0x60, 0x22, 0x00, 0x68, 0xFD, 0xF7, 0xA0, 0xFD, -0x20, 0x78, 0x38, 0x43, 0x20, 0x70, 0x01, 0x20, -0xF8, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, -0x00, 0x03, 0x00, 0x20, 0x9A, 0x01, 0x00, 0x20, -0x7C, 0x02, 0x00, 0x20, 0xF0, 0xB5, 0x22, 0x48, -0x00, 0x22, 0x00, 0x68, 0x14, 0x46, 0x21, 0x4B, -0x98, 0x42, 0x28, 0xD8, 0x20, 0x48, 0x00, 0x78, -0x04, 0x28, 0x27, 0xD1, 0x1F, 0x4B, 0x20, 0x4D, -0x1E, 0x68, 0x00, 0x20, 0x33, 0x18, 0xDB, 0x7E, -0x41, 0x2B, 0x08, 0xD0, 0x42, 0x00, 0x57, 0x19, -0x00, 0x23, 0xFB, 0x5E, 0x8A, 0x5E, 0x9B, 0x1A, -0x00, 0xD5, 0x5B, 0x42, 0x1A, 0xB2, 0x94, 0x42, -0x00, 0xDA, 0x14, 0x46, 0x40, 0x1C, 0xC0, 0xB2, -0x30, 0x28, 0xEB, 0xD3, 0x60, 0x20, 0x80, 0x5D, -0x90, 0x36, 0x40, 0x1C, 0x72, 0x7B, 0x60, 0x43, -0x33, 0x7B, 0x00, 0x11, 0x12, 0x02, 0x00, 0xB2, -0x1A, 0x43, 0x82, 0x42, 0x03, 0xDA, 0x0F, 0x49, -0x00, 0x20, 0x08, 0x70, 0xF0, 0xBD, 0x00, 0x20, -0x42, 0x00, 0x8B, 0x5E, 0x00, 0x26, 0xDC, 0x00, -0xE3, 0x1A, 0x54, 0x19, 0xA6, 0x5F, 0x40, 0x1C, -0x9B, 0x19, 0xDB, 0x10, 0xC0, 0xB2, 0x8B, 0x52, -0x30, 0x28, 0xF1, 0xD3, 0xF0, 0xBD, 0x00, 0x00, -0x18, 0x03, 0x00, 0x20, 0x40, 0x77, 0x1B, 0x00, -0x8F, 0x02, 0x00, 0x20, 0xAC, 0x04, 0x00, 0x20, -0x00, 0x00, 0x01, 0x20, 0x9A, 0x01, 0x00, 0x20, -0xF8, 0xB5, 0x27, 0x48, 0x27, 0x49, 0x00, 0x78, -0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x08, 0xD0, -0x02, 0x28, 0x20, 0xD0, 0x05, 0x28, 0x36, 0xD1, -0x3F, 0xE0, 0x01, 0x25, 0x2C, 0x46, 0x22, 0x48, -0x02, 0xE0, 0x02, 0x25, 0x21, 0x48, 0x2C, 0x46, -0x08, 0x60, 0x00, 0x22, 0x20, 0x49, 0x28, 0x46, -0xFE, 0xF7, 0x88, 0xFF, 0x03, 0x27, 0x1F, 0x4E, -0x3F, 0x05, 0x00, 0x28, 0x03, 0xD0, 0x1E, 0x48, -0x00, 0x78, 0x00, 0x28, 0x0B, 0xD0, 0x01, 0x22, -0x21, 0x46, 0x28, 0x46, 0x00, 0xF0, 0x48, 0xF8, -0x00, 0x28, 0x1E, 0xD0, 0x17, 0xE0, 0x04, 0x25, -0x2C, 0x46, 0x18, 0x48, 0xE4, 0xE7, 0x70, 0x6B, -0x38, 0x43, 0x70, 0x63, 0x16, 0x48, 0x00, 0xF0, -0xC1, 0xFA, 0x00, 0x28, 0x0B, 0xD1, 0x20, 0x46, -0xFF, 0xF7, 0x2E, 0xFF, 0x00, 0x28, 0x06, 0xD1, -0x01, 0x22, 0x21, 0x46, 0x28, 0x46, 0x00, 0xF0, -0x2F, 0xF8, 0x00, 0x28, 0x04, 0xD0, 0x0F, 0x48, -0x00, 0x78, 0xC0, 0x07, 0x05, 0xD0, 0x00, 0x20, -0xF8, 0xBD, 0x70, 0x6B, 0x38, 0x43, 0x70, 0x63, -0xF9, 0xE7, 0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, -0x87, 0x02, 0x00, 0x20, 0x7C, 0x02, 0x00, 0x20, -0x4C, 0x03, 0x00, 0x20, 0xEC, 0x06, 0x00, 0x20, -0x00, 0x20, 0x00, 0x50, 0x80, 0x10, 0x00, 0x50, -0xE7, 0x02, 0x00, 0x20, 0xAC, 0x03, 0x00, 0x20, -0x00, 0x00, 0x01, 0x20, 0x98, 0x01, 0x00, 0x20, -0x03, 0x48, 0x02, 0x49, 0x41, 0x60, 0x03, 0x49, -0x81, 0x60, 0x70, 0x47, 0x1F, 0x1F, 0x5F, 0x1F, -0x00, 0x10, 0x00, 0x50, 0x1F, 0x1F, 0x1F, 0x1F, -0xF7, 0xB5, 0x07, 0x46, 0xFD, 0xF7, 0xFE, 0xFE, -0x14, 0x48, 0x00, 0x25, 0x05, 0x70, 0x78, 0x07, -0x1D, 0xD0, 0x01, 0x21, 0x02, 0x20, 0x00, 0xF0, -0x8B, 0xFB, 0x11, 0x4C, 0x60, 0x6B, 0x03, 0x26, -0x36, 0x05, 0x01, 0x21, 0xB0, 0x43, 0x09, 0x05, -0x40, 0x18, 0x60, 0x63, 0x00, 0x20, 0xFF, 0xF7, -0xF3, 0xFC, 0x00, 0x28, 0x0D, 0xD0, 0x01, 0x98, -0xFF, 0xF7, 0xDA, 0xFE, 0x01, 0x22, 0x09, 0x49, -0x38, 0x46, 0xFE, 0xF7, 0x0F, 0xFF, 0x08, 0x48, -0x05, 0x70, 0x08, 0x48, 0x05, 0x70, 0x01, 0x20, -0xFE, 0xBD, 0x60, 0x6B, 0x30, 0x43, 0x60, 0x63, -0x00, 0x20, 0xFE, 0xBD, 0x83, 0x02, 0x00, 0x20, -0x80, 0x10, 0x00, 0x50, 0x00, 0x20, 0x00, 0x50, -0xE7, 0x02, 0x00, 0x20, 0xE8, 0x02, 0x00, 0x20, -0xF0, 0xB5, 0x01, 0x24, 0xEE, 0x4B, 0xA4, 0x02, -0x23, 0x20, 0x13, 0x21, 0x29, 0x22, 0x1C, 0x60, -0x40, 0x01, 0x89, 0x01, 0x52, 0x01, 0x1B, 0x1D, -0x07, 0xC3, 0xEA, 0x4B, 0x1D, 0x68, 0x2B, 0x46, -0x40, 0x33, 0xAC, 0x46, 0x5D, 0x7C, 0x1E, 0x7C, -0x2D, 0x04, 0x36, 0x02, 0x35, 0x43, 0x0C, 0x35, -0x26, 0x46, 0x35, 0x60, 0x5D, 0x7C, 0x1E, 0x7C, -0x2D, 0x04, 0x36, 0x02, 0x35, 0x43, 0x0C, 0x35, -0x26, 0x46, 0x75, 0x60, 0x01, 0x25, 0xB5, 0x60, -0xF5, 0x60, 0x1D, 0x7D, 0x6D, 0x1E, 0xED, 0x05, -0xED, 0x09, 0x6D, 0x1C, 0x35, 0x61, 0xDC, 0x4D, -0x75, 0x61, 0x03, 0x25, 0xB5, 0x61, 0x00, 0x25, -0xF5, 0x61, 0x03, 0x25, 0x2D, 0x02, 0x35, 0x62, -0x9E, 0x7E, 0x1D, 0x7E, 0x77, 0x00, 0xF6, 0x19, -0xAE, 0x19, 0x36, 0x04, 0x2E, 0x43, 0x25, 0x46, -0xEE, 0x62, 0x9D, 0x7E, 0x1E, 0x7E, 0x6F, 0x00, -0xEF, 0x19, 0xBF, 0x1C, 0xF7, 0x19, 0x7F, 0x05, -0x7F, 0x09, 0x75, 0x19, 0x2F, 0x43, 0x25, 0x46, -0x2F, 0x63, 0x1E, 0x7E, 0x9D, 0x7E, 0x6F, 0x00, -0xEF, 0x19, 0x3F, 0x1D, 0xF7, 0x19, 0x6D, 0x00, -0x7F, 0x05, 0x75, 0x19, 0x7F, 0x09, 0x2F, 0x43, -0x25, 0x46, 0x6F, 0x63, 0x00, 0x25, 0x26, 0x46, -0xB5, 0x63, 0x9E, 0x7E, 0x1D, 0x7E, 0x77, 0x00, -0xF6, 0x19, 0xBC, 0x36, 0xAE, 0x19, 0x36, 0x04, -0xBC, 0x35, 0x2E, 0x43, 0x25, 0x46, 0xEE, 0x63, -0x9D, 0x7E, 0x1E, 0x7E, 0x6F, 0x00, 0xEF, 0x19, -0xBE, 0x37, 0xF7, 0x19, 0x7F, 0x05, 0x75, 0x19, -0x7F, 0x09, 0xBC, 0x35, 0x2F, 0x43, 0x25, 0x46, -0x2F, 0x64, 0x9D, 0x7E, 0x1E, 0x7E, 0x6F, 0x00, -0xEF, 0x19, 0xC0, 0x37, 0xF7, 0x19, 0x6D, 0x00, -0xBC, 0x35, 0x7F, 0x05, 0x75, 0x19, 0x7F, 0x09, -0x2F, 0x43, 0x25, 0x46, 0x6F, 0x64, 0x00, 0x25, -0x26, 0x46, 0xB5, 0x64, 0x75, 0x62, 0xB1, 0x4D, -0xB5, 0x62, 0x65, 0x46, 0x50, 0x35, 0xEE, 0x7B, -0xAF, 0x7B, 0x35, 0x02, 0x3D, 0x43, 0xED, 0x1C, -0xAE, 0x05, 0xAD, 0x4D, 0xB6, 0x0D, 0x75, 0x19, -0x26, 0x46, 0xF5, 0x64, 0xAB, 0x4D, 0x2E, 0x68, -0xAB, 0x4D, 0xAE, 0x42, 0x01, 0xD1, 0xAB, 0x4D, -0x00, 0xE0, 0xAB, 0x4D, 0x25, 0x65, 0x5D, 0x7C, -0xA2, 0x4E, 0x0C, 0x3D, 0xEF, 0xB2, 0x05, 0x25, -0x6D, 0x02, 0x7D, 0x19, 0x65, 0x65, 0x1D, 0x7D, -0x00, 0x27, 0x2D, 0x02, 0x21, 0x35, 0xA5, 0x65, -0x02, 0x25, 0xE5, 0x65, 0x5C, 0x7C, 0x1B, 0x7C, -0x24, 0x04, 0x1B, 0x02, 0x1C, 0x43, 0x0C, 0x34, -0x04, 0x60, 0x34, 0x68, 0x23, 0x46, 0x40, 0x33, -0xA4, 0x46, 0x5C, 0x7C, 0x1D, 0x7C, 0x24, 0x04, -0x2D, 0x02, 0x2C, 0x43, 0x0C, 0x34, 0x44, 0x60, -0x01, 0x24, 0x84, 0x60, 0xC4, 0x60, 0x5C, 0x7D, -0x64, 0x1E, 0xE4, 0x05, 0xE4, 0x09, 0x64, 0x1C, -0x04, 0x61, 0x8F, 0x4C, 0x44, 0x61, 0x03, 0x24, -0xC7, 0x61, 0x84, 0x61, 0x24, 0x02, 0x04, 0x62, -0xDD, 0x7E, 0x1C, 0x7E, 0x6E, 0x00, 0xAD, 0x19, -0x65, 0x19, 0x2D, 0x04, 0x25, 0x43, 0xC5, 0x62, -0xDC, 0x7E, 0x1D, 0x7E, 0x66, 0x00, 0xA6, 0x19, -0xB6, 0x1C, 0xAE, 0x19, 0x76, 0x05, 0x76, 0x09, -0x2C, 0x19, 0x26, 0x43, 0x06, 0x63, 0xDC, 0x7E, -0x1D, 0x7E, 0x66, 0x00, 0xA6, 0x19, 0x36, 0x1D, -0xAE, 0x19, 0x76, 0x05, 0x64, 0x00, 0x76, 0x09, -0x2C, 0x19, 0x26, 0x43, 0x87, 0x63, 0x46, 0x63, -0xDD, 0x7E, 0x1C, 0x7E, 0x6E, 0x00, 0xAD, 0x19, -0xBC, 0x35, 0x65, 0x19, 0x2D, 0x04, 0xBC, 0x34, -0x25, 0x43, 0xC5, 0x63, 0xDC, 0x7E, 0x1D, 0x7E, -0x66, 0x00, 0xA6, 0x19, 0xBE, 0x36, 0xAE, 0x19, -0x76, 0x05, 0x2C, 0x19, 0x76, 0x09, 0xBC, 0x34, -0x26, 0x43, 0x06, 0x64, 0xDC, 0x7E, 0x1D, 0x7E, -0x66, 0x00, 0xA6, 0x19, 0xC0, 0x36, 0xAE, 0x19, -0x64, 0x00, 0x76, 0x05, 0xBC, 0x34, 0x76, 0x09, -0x2C, 0x19, 0x26, 0x43, 0x87, 0x64, 0x47, 0x62, -0x6A, 0x4C, 0x46, 0x64, 0x84, 0x62, 0x64, 0x46, -0x50, 0x34, 0xE5, 0x7B, 0xA6, 0x7B, 0x2C, 0x02, -0x34, 0x43, 0xE4, 0x1C, 0xA5, 0x05, 0x66, 0x4C, -0xAD, 0x0D, 0x2C, 0x19, 0xC4, 0x64, 0x65, 0x4C, -0x25, 0x68, 0x65, 0x4C, 0xA5, 0x42, 0x01, 0xD1, -0x64, 0x4C, 0x00, 0xE0, 0x64, 0x4C, 0x04, 0x65, -0x5C, 0x7C, 0x5C, 0x4E, 0x0C, 0x3C, 0xE5, 0xB2, -0x05, 0x24, 0x64, 0x02, 0x2D, 0x19, 0x45, 0x65, -0x5D, 0x7D, 0x2D, 0x02, 0x21, 0x35, 0x85, 0x65, -0x02, 0x25, 0xC5, 0x65, 0xD8, 0x7C, 0x9B, 0x7C, -0x00, 0x04, 0x1B, 0x02, 0x18, 0x43, 0x0C, 0x30, -0x08, 0x60, 0x30, 0x68, 0x40, 0x30, 0xC3, 0x7C, -0x85, 0x7C, 0x1B, 0x04, 0x2D, 0x02, 0x2B, 0x43, -0x0C, 0x33, 0x4B, 0x60, 0x83, 0x7D, 0x55, 0x4D, -0x5B, 0x1E, 0xDB, 0x05, 0xDB, 0x0D, 0x8B, 0x60, -0x83, 0x7D, 0x5B, 0x08, 0x5B, 0x1E, 0xDB, 0x05, -0xDB, 0x0D, 0xCB, 0x60, 0x0F, 0x61, 0x4D, 0x61, -0xCF, 0x61, 0x01, 0x25, 0x8D, 0x61, 0x2D, 0x02, -0x0D, 0x62, 0x45, 0x7E, 0xCD, 0x62, 0x45, 0x7E, -0x06, 0x7F, 0x3B, 0x46, 0xAD, 0x19, 0x0D, 0x63, -0x06, 0x7F, 0x45, 0x7E, 0x8B, 0x63, 0xCB, 0x63, -0x0B, 0x64, 0x40, 0x4F, 0x4B, 0x64, 0x76, 0x00, -0x8B, 0x64, 0x3F, 0x37, 0xAD, 0x19, 0x8F, 0x62, -0x4D, 0x63, 0x3D, 0x4D, 0x4B, 0x62, 0xED, 0x1C, -0xCD, 0x64, 0x3C, 0x4D, 0x3C, 0x4E, 0x2D, 0x68, -0xB5, 0x42, 0x01, 0xD1, 0x3B, 0x4D, 0x00, 0xE0, -0x3B, 0x4D, 0x0D, 0x65, 0xC5, 0x7C, 0x0C, 0x3D, -0xED, 0xB2, 0x2D, 0x19, 0x4D, 0x65, 0x85, 0x7D, -0x6D, 0x08, 0x2D, 0x02, 0x21, 0x35, 0x8D, 0x65, -0xCB, 0x65, 0xC1, 0x7C, 0x80, 0x7C, 0x09, 0x04, -0x00, 0x02, 0x01, 0x43, 0x0C, 0x31, 0x2B, 0x48, -0x11, 0x60, 0x00, 0x68, 0x40, 0x30, 0xC1, 0x7C, -0x85, 0x7C, 0x09, 0x04, 0x2D, 0x02, 0x29, 0x43, -0x0C, 0x31, 0x51, 0x60, 0xC1, 0x7D, 0x49, 0x1E, -0xC9, 0x05, 0xC9, 0x0D, 0x91, 0x60, 0xC1, 0x7D, -0x13, 0x61, 0x49, 0x08, 0x49, 0x1E, 0xC9, 0x05, -0xC9, 0x0D, 0xD1, 0x60, 0x27, 0x49, 0x51, 0x61, -0x01, 0x21, 0xD3, 0x61, 0x91, 0x61, 0x09, 0x02, -0x11, 0x62, 0x41, 0x7E, 0xD1, 0x62, 0x41, 0x7E, -0x45, 0x7F, 0x49, 0x19, 0x11, 0x63, 0x45, 0x7F, -0x41, 0x7E, 0x6D, 0x00, 0x49, 0x19, 0x93, 0x63, -0x51, 0x63, 0x41, 0x7E, 0xBC, 0x31, 0xD1, 0x63, -0x41, 0x7E, 0x45, 0x7F, 0x49, 0x19, 0xBC, 0x31, -0x11, 0x64, 0x45, 0x7F, 0x41, 0x7E, 0x6D, 0x00, -0xBC, 0x35, 0x49, 0x19, 0x93, 0x64, 0x51, 0x64, -0x01, 0x21, 0x89, 0x04, 0x97, 0x62, 0x51, 0x62, -0x0F, 0x49, 0xC9, 0x1C, 0xD1, 0x64, 0x0F, 0x49, -0x09, 0x68, 0xB1, 0x42, 0x01, 0xD1, 0x0F, 0x49, -0x00, 0xE0, 0x0F, 0x49, 0x11, 0x65, 0xC1, 0x7C, -0x0C, 0x39, 0xC9, 0xB2, 0x09, 0x19, 0x51, 0x65, -0xC0, 0x7D, 0xD3, 0x65, 0x40, 0x08, 0x00, 0x02, -0x21, 0x30, 0x90, 0x65, 0xF0, 0xBD, 0x00, 0x00, -0x0C, 0x04, 0x00, 0x20, 0xAC, 0x04, 0x00, 0x20, -0x03, 0x00, 0x03, 0x00, 0x00, 0x3F, 0x3F, 0x3F, -0x00, 0x30, 0xBC, 0x00, 0xE4, 0x06, 0x00, 0x20, -0xA1, 0x00, 0x03, 0xF3, 0x00, 0x26, 0x31, 0x00, -0x00, 0x26, 0x31, 0x08, 0x01, 0x00, 0x01, 0x00, -0x10, 0xB5, 0x00, 0xF0, 0x1D, 0xFB, 0x00, 0xF0, -0x51, 0xF9, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, -0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x00, 0x20, -0xFF, 0xF7, 0x4E, 0xFC, 0x09, 0x48, 0x01, 0x78, -0x09, 0x48, 0x00, 0x29, 0x04, 0xD0, 0x01, 0x68, -0xFF, 0x22, 0x01, 0x32, 0x11, 0x43, 0x01, 0x60, -0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x91, 0x43, -0x01, 0x60, 0x04, 0x49, 0x02, 0x20, 0x08, 0x72, -0x10, 0xBD, 0x00, 0x00, 0x99, 0x01, 0x00, 0x20, -0x00, 0x10, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, -0x10, 0xB5, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, -0x04, 0x24, 0x21, 0x43, 0x41, 0x60, 0x0B, 0x48, -0x09, 0x49, 0x41, 0x60, 0x0A, 0x49, 0x81, 0x60, -0xFF, 0xF7, 0xD6, 0xFD, 0x05, 0x20, 0x00, 0x07, -0x81, 0x69, 0x21, 0x43, 0x81, 0x61, 0x80, 0x21, -0x06, 0x48, 0xFD, 0xF7, 0x7E, 0xFA, 0x80, 0x21, -0x05, 0x48, 0xFD, 0xF7, 0x7A, 0xFA, 0x10, 0xBD, -0x1F, 0x1F, 0x5F, 0x1F, 0x00, 0x10, 0x00, 0x50, -0x1F, 0x1F, 0x1F, 0x1F, 0x2C, 0x06, 0x00, 0x20, -0x9C, 0x01, 0x00, 0x20, 0x01, 0x21, 0x89, 0x07, -0x0A, 0x15, 0x00, 0x28, 0x48, 0x69, 0x02, 0xD0, -0x10, 0x43, 0x48, 0x61, 0x70, 0x47, 0x90, 0x43, -0xFB, 0xE7, 0x00, 0x00, 0x10, 0xB5, 0x0F, 0x49, -0x0A, 0x78, 0x0F, 0x49, 0x00, 0x2A, 0x09, 0x78, -0x04, 0xD0, 0x01, 0x2A, 0x07, 0xD0, 0x02, 0x2A, -0x12, 0xD1, 0x09, 0xE0, 0xC9, 0x07, 0x0F, 0xD0, -0x60, 0x22, 0x0A, 0x49, 0x08, 0xE0, 0x89, 0x07, -0x0A, 0xD5, 0x60, 0x22, 0x08, 0x49, 0x03, 0xE0, -0x49, 0x07, 0x05, 0xD5, 0x60, 0x22, 0x07, 0x49, -0xFD, 0xF7, 0x2E, 0xFA, 0x01, 0x20, 0x10, 0xBD, -0x00, 0x20, 0x10, 0xBD, 0x87, 0x02, 0x00, 0x20, -0x9A, 0x01, 0x00, 0x20, 0x4C, 0x03, 0x00, 0x20, -0xEC, 0x06, 0x00, 0x20, 0xAC, 0x03, 0x00, 0x20, -0x10, 0xB5, 0x13, 0x49, 0x13, 0x4B, 0x09, 0x68, -0x13, 0x4A, 0x40, 0x31, 0x00, 0x28, 0x0F, 0xD0, -0x01, 0x28, 0x12, 0xD0, 0x02, 0x28, 0x15, 0xD0, -0x03, 0x28, 0x08, 0xD1, 0x48, 0x7E, 0x84, 0x1E, -0x1C, 0x80, 0x49, 0x7F, 0x4B, 0x00, 0xC9, 0x18, -0x89, 0x1C, 0x40, 0x18, 0x10, 0x80, 0x10, 0xBD, -0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, 0x89, 0x7E, -0xF4, 0xE7, 0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, -0xC9, 0x7E, 0xEF, 0xE7, 0x48, 0x7E, 0x84, 0x1E, -0x1C, 0x80, 0x09, 0x7F, 0xEA, 0xE7, 0x00, 0x00, -0xAC, 0x04, 0x00, 0x20, 0x62, 0x02, 0x00, 0x20, -0x64, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x1F, 0x4A, -0x00, 0x21, 0x11, 0x60, 0x51, 0x60, 0x1E, 0x4A, -0x01, 0x21, 0x11, 0x70, 0x1D, 0x49, 0x1E, 0x4A, -0x09, 0x78, 0x00, 0x29, 0x06, 0xD0, 0x3C, 0x24, -0x0C, 0x23, 0x01, 0x29, 0x0D, 0xD0, 0x02, 0x29, -0x17, 0xD1, 0x1B, 0xE0, 0x78, 0x21, 0x00, 0x28, -0x11, 0x70, 0x01, 0xD0, 0x00, 0x20, 0x00, 0xE0, -0x01, 0x20, 0x00, 0xF0, 0xD7, 0xF9, 0x78, 0x20, -0x09, 0xE0, 0x00, 0x28, 0x02, 0xD0, 0x14, 0x70, -0x02, 0x20, 0x01, 0xE0, 0x13, 0x70, 0x03, 0x20, -0x00, 0xF0, 0xCC, 0xF9, 0x01, 0x20, 0xFF, 0xF7, -0xED, 0xF8, 0x05, 0x20, 0x0D, 0x49, 0x00, 0x02, -0x08, 0x60, 0x10, 0xBD, 0x00, 0x28, 0x02, 0xD0, -0x14, 0x70, 0x02, 0x20, 0x01, 0xE0, 0x13, 0x70, -0x03, 0x20, 0x00, 0xF0, 0xBB, 0xF9, 0x08, 0x48, -0x81, 0x6A, 0x08, 0x4A, 0x11, 0x40, 0x81, 0x62, -0xE8, 0xE7, 0x00, 0x00, 0x50, 0x02, 0x00, 0x20, -0x99, 0x01, 0x00, 0x20, 0x87, 0x02, 0x00, 0x20, -0xB7, 0x02, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, -0xC0, 0x11, 0x00, 0x50, 0xFF, 0xFF, 0x00, 0xF8, -0x10, 0xB5, 0x19, 0x4C, 0x19, 0x49, 0x20, 0x70, -0x01, 0x20, 0x08, 0x70, 0x00, 0xF0, 0x3C, 0xFA, -0x00, 0xF0, 0x16, 0xFA, 0x00, 0xF0, 0x4A, 0xF8, -0x01, 0x20, 0xFD, 0xF7, 0x5D, 0xFA, 0xFF, 0xF7, -0x9F, 0xFB, 0x01, 0x20, 0xFF, 0xF7, 0x9E, 0xFF, -0x00, 0xF0, 0xC2, 0xFF, 0xFE, 0xF7, 0x50, 0xFC, -0x20, 0x78, 0x0F, 0x49, 0x05, 0x28, 0x08, 0x70, -0x12, 0xD0, 0xFF, 0xF7, 0x45, 0xFC, 0x00, 0x28, -0x0F, 0xD0, 0x0C, 0x48, 0x00, 0x7A, 0x00, 0x28, -0x03, 0xD1, 0x00, 0x21, 0x02, 0x20, 0x00, 0xF0, -0x43, 0xF8, 0x00, 0x20, 0x00, 0xF0, 0x18, 0xFA, -0x07, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, -0x00, 0x20, 0x10, 0xBD, 0x01, 0x20, 0x10, 0xBD, -0x87, 0x02, 0x00, 0x20, 0x84, 0x02, 0x00, 0x20, -0x4D, 0x07, 0x00, 0x20, 0x50, 0x02, 0x00, 0x20, -0x98, 0x01, 0x00, 0x20, 0x08, 0x49, 0x02, 0x20, -0x08, 0x72, 0x08, 0x48, 0x01, 0x78, 0x08, 0x48, -0x00, 0x29, 0x01, 0x68, 0x04, 0xD0, 0x01, 0x22, -0x92, 0x02, 0x91, 0x43, 0x01, 0x60, 0x70, 0x47, -0x01, 0x22, 0x11, 0x43, 0xFA, 0xE7, 0x00, 0x00, -0x50, 0x02, 0x00, 0x20, 0x99, 0x01, 0x00, 0x20, -0x00, 0x10, 0x00, 0x50, 0x10, 0xB5, 0x08, 0x48, -0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x11, 0x43, -0x01, 0x60, 0x00, 0x68, 0x05, 0x4C, 0xC0, 0x07, -0x03, 0xD0, 0x02, 0x20, 0x20, 0x72, 0x00, 0xF0, -0xBB, 0xF9, 0x00, 0x20, 0x20, 0x72, 0x10, 0xBD, -0x00, 0x10, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, -0x70, 0xB5, 0x05, 0x00, 0x0E, 0x46, 0x16, 0xD0, -0xFF, 0xF7, 0x8E, 0xFE, 0x00, 0x24, 0x6D, 0x1E, -0x07, 0xE0, 0x00, 0xF0, 0xA9, 0xF9, 0xFF, 0xF7, -0x05, 0xFB, 0xFF, 0xF7, 0xC3, 0xFF, 0x64, 0x1C, -0xE4, 0xB2, 0xAC, 0x42, 0xF5, 0xDB, 0x00, 0xF0, -0x9F, 0xF9, 0xFF, 0xF7, 0xFB, 0xFA, 0x00, 0x2E, -0x02, 0xD0, 0xFF, 0xF7, 0xCF, 0xFF, 0x70, 0xBD, -0xFF, 0xF7, 0xB4, 0xFF, 0x70, 0xBD, 0x00, 0x00, -0xF0, 0xB5, 0x01, 0x21, 0x31, 0x4A, 0x89, 0x07, -0x05, 0x27, 0x0C, 0x13, 0x04, 0x25, 0x3F, 0x07, -0x12, 0x68, 0x2F, 0x4B, 0x00, 0x28, 0x06, 0xD0, -0x01, 0x28, 0x04, 0xD0, 0x02, 0x28, 0x1A, 0xD0, -0x03, 0x28, 0x40, 0xD1, 0x17, 0xE0, 0x08, 0x68, -0xA0, 0x43, 0x08, 0x60, 0x01, 0x20, 0x98, 0x61, -0xD8, 0x61, 0x27, 0x49, 0x00, 0x20, 0x40, 0x39, -0xC8, 0x63, 0x18, 0x60, 0x98, 0x60, 0x25, 0x48, -0x82, 0x42, 0x30, 0xD1, 0x88, 0x6B, 0xA8, 0x43, -0x88, 0x63, 0x78, 0x69, 0x01, 0x21, 0x09, 0x04, -0x88, 0x43, 0x78, 0x61, 0x27, 0xE0, 0x0E, 0x68, -0x26, 0x43, 0x0E, 0x60, 0x62, 0x21, 0x99, 0x61, -0xD9, 0x61, 0x1B, 0x4C, 0x1C, 0x49, 0x40, 0x3C, -0xE1, 0x63, 0x1C, 0x49, 0x09, 0x68, 0x40, 0x31, -0x02, 0x28, 0x48, 0x7E, 0x23, 0xD0, 0x40, 0x1E, -0x40, 0x05, 0x40, 0x0D, 0x18, 0x60, 0x4E, 0x7E, -0x48, 0x7F, 0x41, 0x00, 0x40, 0x18, 0x80, 0x1C, -0x30, 0x18, 0x40, 0x05, 0x40, 0x0D, 0x98, 0x60, -0x10, 0x48, 0x82, 0x42, 0x07, 0xD1, 0xA0, 0x6B, -0x28, 0x43, 0xA0, 0x63, 0x79, 0x69, 0x01, 0x20, -0x00, 0x04, 0x01, 0x43, 0x79, 0x61, 0x19, 0x68, -0x0D, 0x48, 0x41, 0x63, 0x19, 0x68, 0x81, 0x63, -0x99, 0x68, 0x0B, 0x48, 0x40, 0x30, 0x41, 0x60, -0x99, 0x68, 0x81, 0x60, 0xF0, 0xBD, 0x40, 0x1E, -0x40, 0x05, 0x40, 0x0D, 0x18, 0x60, 0x4E, 0x7E, -0x08, 0x7F, 0xDA, 0xE7, 0xE4, 0x06, 0x00, 0x20, -0xC0, 0x11, 0x00, 0x50, 0xA2, 0x00, 0x03, 0xF3, -0x01, 0x00, 0x01, 0x00, 0xAC, 0x04, 0x00, 0x20, -0x80, 0x13, 0x00, 0x50, 0x01, 0x21, 0x89, 0x07, -0x00, 0xB5, 0x8A, 0x14, 0x00, 0x28, 0x08, 0x68, -0x04, 0xD0, 0x10, 0x43, 0x08, 0x60, 0xFD, 0xF7, -0xCD, 0xFA, 0x00, 0xBD, 0x90, 0x43, 0x08, 0x60, -0x00, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x36, 0x03, 0x35, 0x03, 0x2C, 0x03, 0x9C, 0x03, -0xE9, 0x02, 0x28, 0x03, 0x29, 0x03, 0x1C, 0x03, -0x9D, 0x03, 0x2F, 0x03, 0x4A, 0x03, 0x4D, 0x03, -0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, -0x3D, 0x03, 0x3C, 0x03, 0x2C, 0x03, 0x9C, 0x03, -0xD3, 0x02, 0x28, 0x03, 0x29, 0x03, 0xD6, 0x02, -0x9E, 0x03, 0x30, 0x03, 0x37, 0x03, 0x3A, 0x03, -0xFF, 0x03, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x2F, 0x4A, 0x90, 0x6A, 0x2F, 0x49, 0x08, 0x60, -0xD0, 0x6B, 0x48, 0x60, 0x2C, 0x48, 0x40, 0x30, -0x03, 0x69, 0x8B, 0x60, 0x43, 0x6A, 0xCB, 0x60, -0xD3, 0x6A, 0x0B, 0x61, 0x03, 0x68, 0x4B, 0x61, -0x43, 0x69, 0x8B, 0x61, 0x83, 0x6A, 0xCB, 0x61, -0x93, 0x6A, 0x0B, 0x62, 0xD3, 0x6B, 0x4B, 0x62, -0x03, 0x69, 0x8B, 0x62, 0x43, 0x6A, 0xCB, 0x62, -0xD3, 0x6A, 0x0B, 0x63, 0x03, 0x68, 0x4B, 0x63, -0x43, 0x69, 0x8B, 0x63, 0x83, 0x6A, 0xCB, 0x63, -0x93, 0x6A, 0x1E, 0x49, 0x40, 0x31, 0x0B, 0x60, -0xD3, 0x6B, 0x4B, 0x60, 0x03, 0x69, 0x8B, 0x60, -0x43, 0x6A, 0xCB, 0x60, 0xD3, 0x6A, 0x0B, 0x61, -0x03, 0x68, 0x4B, 0x61, 0x43, 0x69, 0x8B, 0x61, -0x83, 0x6A, 0xCB, 0x61, 0x93, 0x6A, 0x0B, 0x62, -0xD3, 0x6B, 0x4B, 0x62, 0x03, 0x69, 0x8B, 0x62, -0x43, 0x6A, 0xCB, 0x62, 0xD2, 0x6A, 0x0A, 0x63, -0x02, 0x68, 0x4A, 0x63, 0x42, 0x69, 0x8A, 0x63, -0x80, 0x6A, 0xC8, 0x63, 0x0C, 0x49, 0xC0, 0x31, -0x88, 0x6A, 0x80, 0x05, 0x82, 0x09, 0x88, 0x6A, -0x80, 0x05, 0x80, 0x0D, 0x02, 0x43, 0x09, 0x48, -0x80, 0x30, 0x02, 0x60, 0x8A, 0x6A, 0x89, 0x6A, -0x92, 0x05, 0x92, 0x09, 0x89, 0x05, 0x89, 0x0D, -0x0A, 0x43, 0x42, 0x60, 0x04, 0x49, 0xC1, 0x60, -0x01, 0x61, 0xFF, 0x21, 0x81, 0x60, 0x70, 0x47, -0x00, 0x11, 0x00, 0x50, 0x00, 0x13, 0x00, 0x50, -0xBC, 0x00, 0xBC, 0x00, 0x70, 0xB5, 0x04, 0x46, -0x81, 0x00, 0x26, 0x48, 0x41, 0x58, 0x26, 0x48, -0x0A, 0x68, 0x42, 0x60, 0x4A, 0x68, 0x82, 0x60, -0x8A, 0x68, 0x02, 0x61, 0xCA, 0x68, 0x42, 0x61, -0x22, 0x4D, 0x08, 0x69, 0x28, 0x62, 0x20, 0x4A, -0x48, 0x69, 0xC0, 0x32, 0x90, 0x61, 0x88, 0x69, -0xD0, 0x61, 0x48, 0x6A, 0x10, 0x62, 0x88, 0x6A, -0x50, 0x62, 0x1B, 0x48, 0xCB, 0x69, 0x40, 0x38, -0xC3, 0x60, 0x0B, 0x6A, 0x03, 0x61, 0xCB, 0x6A, -0x93, 0x62, 0x0B, 0x6B, 0xD3, 0x63, 0x17, 0x4B, -0x4E, 0x6B, 0x80, 0x3B, 0x1E, 0x61, 0x8E, 0x6B, -0x5E, 0x62, 0xCE, 0x6B, 0xD6, 0x62, 0x0A, 0x6C, -0x1A, 0x60, 0x4A, 0x6C, 0x5A, 0x61, 0x8A, 0x6C, -0x9A, 0x62, 0xCA, 0x6C, 0xAA, 0x62, 0x0E, 0x4B, -0x0A, 0x6D, 0x40, 0x33, 0x5A, 0x63, 0x0C, 0x4B, -0x4A, 0x6D, 0x80, 0x33, 0x9A, 0x60, 0x0C, 0x4A, -0x8B, 0x6D, 0x13, 0x60, 0xC9, 0x6D, 0x51, 0x60, -0xC2, 0x68, 0x0A, 0x49, 0x0A, 0x60, 0x00, 0x69, -0x48, 0x60, 0xFF, 0xF7, 0x51, 0xFF, 0x20, 0x46, -0xFF, 0xF7, 0x92, 0xFD, 0x20, 0x46, 0xFF, 0xF7, -0x97, 0xFE, 0x70, 0xBD, 0x0C, 0x04, 0x00, 0x20, -0x40, 0x10, 0x00, 0x50, 0xC0, 0x11, 0x00, 0x50, -0x00, 0x19, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, -0x10, 0xB5, 0x0C, 0x4C, 0x21, 0x78, 0x81, 0x42, -0x13, 0xD0, 0x20, 0x70, 0xFF, 0xF7, 0x4E, 0xFE, -0x20, 0x78, 0xFF, 0xF7, 0xA7, 0xFD, 0xFF, 0xF7, -0x71, 0xF9, 0x07, 0x48, 0x80, 0x79, 0x01, 0x28, -0x05, 0xD1, 0x06, 0x48, 0x01, 0x68, 0x01, 0x22, -0xD2, 0x02, 0x11, 0x43, 0x01, 0x60, 0xFF, 0xF7, -0x25, 0xFE, 0x10, 0xBD, 0x50, 0x07, 0x00, 0x20, -0x60, 0x07, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, -0x70, 0xB5, 0x05, 0x24, 0x64, 0x04, 0xFD, 0xF7, -0xB1, 0xF9, 0x00, 0x22, 0x0B, 0x49, 0x0C, 0x48, -0x0C, 0x4B, 0x05, 0xE0, 0x05, 0x78, 0xED, 0x07, -0x01, 0xD0, 0x0A, 0x72, 0x09, 0xE0, 0x64, 0x1E, -0x0D, 0x7A, 0x02, 0x2D, 0x02, 0xD0, 0x1D, 0x68, -0xED, 0x07, 0x02, 0xD0, 0x00, 0x2C, 0xF1, 0xD1, -0x01, 0xE0, 0x00, 0x2C, 0x00, 0xD1, 0x0A, 0x72, -0x70, 0xBD, 0x00, 0x00, 0x50, 0x02, 0x00, 0x20, -0x98, 0x01, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, -0x00, 0xB5, 0x03, 0x46, 0x00, 0x20, 0xFF, 0xF7, -0xB5, 0xFE, 0x05, 0x21, 0x09, 0x07, 0xC8, 0x69, -0x00, 0x2B, 0x03, 0xD0, 0xC0, 0x0B, 0xC0, 0x03, -0x05, 0x4A, 0x03, 0xE0, 0xC0, 0x0B, 0x04, 0x4A, -0xC0, 0x03, 0xFC, 0x3A, 0x80, 0x18, 0xC8, 0x61, -0x01, 0x20, 0xFF, 0xF7, 0xA3, 0xFE, 0x00, 0xBD, -0x50, 0x71, 0x00, 0x00, 0xF0, 0xB5, 0x70, 0x48, -0x70, 0x4A, 0x01, 0x78, 0x00, 0x20, 0x85, 0xB0, -0x10, 0x5E, 0x01, 0x90, 0x6E, 0x48, 0x00, 0x24, -0x02, 0x78, 0x6E, 0x48, 0x04, 0x25, 0x04, 0x5F, -0x6D, 0x48, 0x0F, 0x26, 0x00, 0x68, 0x90, 0x30, -0x00, 0x29, 0x01, 0xD0, 0x01, 0x29, 0x17, 0xD1, -0x65, 0x4B, 0x02, 0x27, 0x39, 0x43, 0x19, 0x70, -0x68, 0x49, 0x09, 0x78, 0x1D, 0x29, 0x24, 0xD1, -0x41, 0x7B, 0x03, 0x7B, 0x09, 0x02, 0x19, 0x43, -0x01, 0x9B, 0x99, 0x42, 0x1D, 0xDC, 0x13, 0x21, -0xC9, 0x43, 0x8C, 0x42, 0x01, 0xDA, 0x62, 0x49, -0x0A, 0x70, 0x17, 0x21, 0x61, 0x4B, 0x19, 0x70, -0x61, 0x49, 0x4B, 0x88, 0x58, 0x49, 0x09, 0x78, -0x03, 0x91, 0xC9, 0x07, 0xC9, 0x0F, 0x0B, 0x43, -0x79, 0xD0, 0x41, 0x7B, 0x03, 0x7B, 0x08, 0x02, -0x18, 0x43, 0x5C, 0x49, 0x00, 0x27, 0x00, 0x90, -0xCF, 0x5F, 0xB8, 0x42, 0x03, 0xDA, 0x04, 0x20, -0x02, 0xE0, 0x00, 0x21, 0xE6, 0xE7, 0x02, 0x20, -0x02, 0x90, 0x03, 0x98, 0x83, 0x07, 0x56, 0x48, -0x01, 0x78, 0x48, 0x1C, 0xC0, 0xB2, 0x00, 0x2B, -0x04, 0xDA, 0x10, 0x2F, 0x02, 0xDA, 0x02, 0x22, -0x02, 0x92, 0x15, 0xE0, 0x51, 0x4B, 0x1B, 0x78, -0xDB, 0x07, 0x11, 0xD0, 0x4E, 0x49, 0x00, 0x20, -0x08, 0x70, 0x13, 0x20, 0xC0, 0x43, 0x84, 0x42, -0x05, 0xDA, 0x49, 0x48, 0x80, 0x88, 0x05, 0x28, -0x01, 0xD2, 0x4B, 0x48, 0x02, 0x80, 0x30, 0x21, -0x4A, 0x48, 0xFC, 0xF7, 0xF6, 0xFE, 0x03, 0xE0, -0x45, 0x4A, 0x05, 0x29, 0x00, 0xD2, 0x10, 0x70, -0x43, 0x48, 0x01, 0x78, 0x02, 0x98, 0x81, 0x42, -0x3D, 0xD3, 0x45, 0x49, 0x10, 0x2F, 0x36, 0xDA, -0x1D, 0x20, 0xC0, 0x43, 0x84, 0x42, 0x32, 0xDD, -0x08, 0x78, 0xF0, 0x28, 0x01, 0xD2, 0x40, 0x1C, -0x08, 0x70, 0xC1, 0xB2, 0x02, 0x29, 0x12, 0xD9, -0x36, 0x4A, 0x10, 0x78, 0x16, 0x28, 0x03, 0xD2, -0x02, 0x28, 0x01, 0xD9, 0x17, 0x20, 0x10, 0x70, -0x31, 0x4A, 0x00, 0x20, 0x10, 0x70, 0x36, 0x4A, -0x14, 0x29, 0x10, 0x80, 0x03, 0xD9, 0x37, 0x49, -0x08, 0x70, 0x37, 0x49, 0x08, 0x70, 0x03, 0x98, -0x25, 0x4A, 0x04, 0x28, 0x15, 0xD0, 0x2B, 0x49, -0x08, 0x78, 0x18, 0x28, 0x30, 0xD2, 0x40, 0x1C, -0xC0, 0xB2, 0x08, 0x70, 0x10, 0x2F, 0x02, 0xDB, -0x62, 0x42, 0xBA, 0x42, 0x17, 0xDD, 0x26, 0x4A, -0x12, 0x7A, 0x01, 0x2A, 0x09, 0xD9, 0x03, 0x26, -0x02, 0x25, 0x08, 0xE0, 0x03, 0xE0, 0x00, 0x20, -0xE4, 0xE7, 0xFD, 0xF7, 0x3B, 0xFF, 0x05, 0xB0, -0xF0, 0xBD, 0x01, 0x26, 0x35, 0x46, 0x09, 0x22, -0xD2, 0x43, 0x94, 0x42, 0x03, 0xDD, 0x02, 0x28, -0x01, 0xD9, 0x18, 0x20, 0x08, 0x70, 0x23, 0x4C, -0x23, 0x4F, 0x00, 0x20, 0x42, 0x00, 0x11, 0x19, -0x00, 0x23, 0xCB, 0x5E, 0xBA, 0x5E, 0x73, 0x43, -0x9A, 0x18, 0x2A, 0x41, 0x40, 0x1C, 0xC0, 0xB2, -0x0A, 0x80, 0x30, 0x28, 0xF2, 0xD3, 0xE2, 0xE7, -0x03, 0x98, 0xC0, 0x07, 0x0D, 0xD0, 0x01, 0x9B, -0x00, 0x98, 0x98, 0x42, 0x06, 0xDB, 0x19, 0x4B, -0x00, 0x24, 0x1C, 0x5F, 0x40, 0x08, 0x63, 0x42, -0x98, 0x42, 0x02, 0xDA, 0x0C, 0x20, 0x08, 0x70, -0xD1, 0xE7, 0x04, 0x20, 0x10, 0x70, 0xCE, 0xE7, -0x8F, 0x02, 0x00, 0x20, 0x54, 0x07, 0x00, 0x20, -0xB7, 0x02, 0x00, 0x20, 0x56, 0x07, 0x00, 0x20, -0xAC, 0x04, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, -0x51, 0x07, 0x00, 0x20, 0xB5, 0x02, 0x00, 0x20, -0x60, 0x07, 0x00, 0x20, 0x58, 0x07, 0x00, 0x20, -0xED, 0x02, 0x00, 0x20, 0xEB, 0x02, 0x00, 0x20, -0xF4, 0x02, 0x00, 0x20, 0x1C, 0x03, 0x00, 0x20, -0xEA, 0x02, 0x00, 0x20, 0xEF, 0x02, 0x00, 0x20, -0xF0, 0x02, 0x00, 0x20, 0x00, 0x00, 0x01, 0x20, -0xD4, 0x00, 0x00, 0x20, 0x52, 0x07, 0x00, 0x20, -0xF8, 0xB5, 0x43, 0x4F, 0x01, 0x24, 0x38, 0x7B, -0x42, 0x49, 0x0A, 0x78, 0x42, 0x4B, 0x43, 0x4D, -0x43, 0x4E, 0x90, 0x42, 0x05, 0xD1, 0x28, 0x78, -0x81, 0x28, 0x02, 0xD0, 0x30, 0x78, 0xC0, 0x07, -0x57, 0xD0, 0x00, 0x20, 0x30, 0x70, 0x3B, 0x48, -0x00, 0x78, 0x07, 0x28, 0x0B, 0xD3, 0x18, 0x68, -0x40, 0x05, 0x40, 0x0F, 0x38, 0x73, 0x01, 0x20, -0xFC, 0xF7, 0x80, 0xFF, 0x00, 0x20, 0x30, 0x70, -0x38, 0x7B, 0x02, 0x28, 0x18, 0xD0, 0xA8, 0x79, -0x00, 0x28, 0x01, 0xD0, 0xFE, 0xF7, 0xC8, 0xFD, -0x38, 0x7B, 0x30, 0x49, 0x08, 0x70, 0x81, 0x20, -0x28, 0x70, 0x32, 0x48, 0x01, 0x23, 0x02, 0x88, -0x1B, 0x03, 0x1A, 0x43, 0x02, 0x80, 0x08, 0x78, -0x03, 0x00, 0xFC, 0xF7, 0x83, 0xFE, 0x07, 0x21, -0x2B, 0x2B, 0x49, 0x0B, 0x17, 0x0B, 0x49, 0x00, -0x83, 0x20, 0x28, 0x70, 0x05, 0x20, 0xFF, 0xF7, -0x57, 0xFC, 0xF8, 0xBD, 0x01, 0x20, 0xFE, 0xF7, -0x4D, 0xFD, 0x00, 0x20, 0xFF, 0xF7, 0x50, 0xFC, -0x00, 0x28, 0x01, 0xD0, 0x01, 0x20, 0x32, 0xE0, -0x00, 0x24, 0x31, 0xE0, 0x01, 0x20, 0xFE, 0xF7, -0x41, 0xFD, 0x01, 0x20, 0xFF, 0xF7, 0x44, 0xFC, -0x00, 0x28, 0xF5, 0xD0, 0x04, 0x20, 0x26, 0xE0, -0x00, 0x20, 0xFE, 0xF7, 0x37, 0xFD, 0x02, 0x20, -0xFF, 0xF7, 0x3A, 0xFC, 0x00, 0x28, 0xEB, 0xD0, -0x02, 0x20, 0x1C, 0xE0, 0x83, 0x20, 0x28, 0x70, -0x05, 0x20, 0xFF, 0xF7, 0x31, 0xFC, 0x04, 0x46, -0x16, 0xE0, 0x15, 0x4A, 0x10, 0x78, 0x00, 0x28, -0x12, 0xD0, 0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, -0x10, 0x70, 0x0D, 0xD1, 0x18, 0x68, 0x40, 0x05, -0x40, 0x0F, 0x38, 0x73, 0x38, 0x7B, 0x09, 0x78, -0x88, 0x42, 0x05, 0xD0, 0x30, 0x78, 0x01, 0x21, -0x08, 0x43, 0x30, 0x70, 0x81, 0x20, 0x28, 0x70, -0x30, 0x78, 0xC0, 0x07, 0x02, 0xD0, 0x81, 0x20, -0x28, 0x70, 0x00, 0x24, 0x20, 0x46, 0xF8, 0xBD, -0x50, 0x02, 0x00, 0x20, 0xE5, 0x02, 0x00, 0x20, -0x00, 0x11, 0x00, 0x50, 0x60, 0x07, 0x00, 0x20, -0x98, 0x01, 0x00, 0x20, 0xFE, 0x02, 0x00, 0x20, -0x61, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x0E, 0x4D, -0x28, 0x68, 0xFD, 0xF7, 0x5D, 0xFD, 0x04, 0x00, -0x07, 0xD0, 0x80, 0x21, 0x0B, 0x48, 0xFC, 0xF7, -0xB4, 0xFD, 0x0A, 0x49, 0x28, 0x68, 0x00, 0xF0, -0x2D, 0xF9, 0x09, 0x48, 0x00, 0x88, 0x81, 0x07, -0x06, 0xD0, 0x00, 0x07, 0x04, 0xD5, 0x80, 0x21, -0x04, 0x48, 0xFC, 0xF7, 0xA6, 0xFD, 0x00, 0x24, -0xFF, 0xF7, 0x44, 0xFE, 0x20, 0x46, 0x70, 0xBD, -0x00, 0x03, 0x00, 0x20, 0x2C, 0x06, 0x00, 0x20, -0xFE, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x46, -0x01, 0x24, 0xFE, 0xF7, 0x8D, 0xF8, 0x00, 0x2D, -0x09, 0xD0, 0xF0, 0x20, 0xFC, 0xF7, 0xC4, 0xFD, -0xFE, 0xF7, 0x2C, 0xF9, 0xFE, 0xF7, 0xDC, 0xF8, -0x00, 0x20, 0xFE, 0xF7, 0xC7, 0xFC, 0xFE, 0xF7, -0x69, 0xFD, 0xFF, 0xF7, 0xF1, 0xFA, 0x20, 0x46, -0x70, 0xBD, 0x00, 0x00, 0xF8, 0xB5, 0x54, 0x49, -0x08, 0x78, 0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, -0x08, 0x70, 0x52, 0x4B, 0x53, 0x48, 0x1A, 0x78, -0x51, 0x49, 0x00, 0x78, 0x00, 0x24, 0x04, 0x2A, -0x13, 0xD0, 0x00, 0x28, 0x00, 0xD0, 0x0C, 0x80, -0x50, 0x48, 0x4F, 0x4D, 0x02, 0x78, 0x29, 0x78, -0x50, 0x08, 0x00, 0x29, 0x13, 0xD0, 0x91, 0x42, -0x0F, 0xD2, 0x49, 0x1C, 0xC9, 0xB2, 0x29, 0x70, -0x88, 0x42, 0x0C, 0xD8, 0x01, 0x21, 0x19, 0x70, -0x09, 0xE0, 0x49, 0x4A, 0x12, 0x78, 0x10, 0x43, -0xEA, 0xD0, 0x08, 0x88, 0x40, 0x1C, 0x08, 0x80, -0xE6, 0xE7, 0x78, 0x21, 0x29, 0x70, 0x45, 0x4B, -0x45, 0x4A, 0x19, 0x78, 0x12, 0x78, 0x45, 0x4F, -0x45, 0x4D, 0x00, 0x29, 0x0B, 0xD1, 0x5E, 0x78, -0x00, 0x2E, 0x08, 0xD1, 0x9B, 0x78, 0x00, 0x2B, -0x05, 0xD1, 0x00, 0x2A, 0x03, 0xD1, 0x41, 0x4B, -0x1B, 0x78, 0x00, 0x2B, 0x13, 0xD0, 0x6C, 0x80, -0x3F, 0x48, 0x04, 0x70, 0x3F, 0x48, 0x04, 0x80, -0xA8, 0x88, 0x4B, 0x23, 0x1B, 0x01, 0x98, 0x42, -0x05, 0xD2, 0x08, 0x46, 0x10, 0x43, 0x02, 0xD0, -0xA8, 0x88, 0x40, 0x1C, 0xA8, 0x80, 0x00, 0x29, -0x36, 0xD0, 0x3C, 0x70, 0x39, 0xE0, 0xA9, 0x88, -0x00, 0x29, 0x05, 0xD0, 0x29, 0x7A, 0xFF, 0x29, -0x02, 0xD2, 0x29, 0x7A, 0x49, 0x1C, 0x29, 0x72, -0xAC, 0x80, 0x33, 0x49, 0x0C, 0x70, 0x33, 0x49, -0x0C, 0x70, 0x33, 0x49, 0x0C, 0x70, 0x69, 0x88, -0x01, 0x22, 0x12, 0x03, 0x91, 0x42, 0x02, 0xD2, -0x69, 0x88, 0x49, 0x1C, 0x69, 0x80, 0x2F, 0x4A, -0x11, 0x78, 0x00, 0x29, 0x01, 0xD0, 0x49, 0x1E, -0x11, 0x70, 0x27, 0x4A, 0x11, 0x78, 0x88, 0x42, -0x02, 0xD9, 0x49, 0x1C, 0x11, 0x70, 0x0A, 0xE0, -0x14, 0x70, 0x29, 0x4A, 0x23, 0x49, 0x00, 0x23, -0x00, 0x26, 0xD3, 0x5E, 0x8E, 0x5F, 0x9B, 0x19, -0x5B, 0x10, 0x13, 0x80, 0x0C, 0x80, 0x69, 0x88, -0x81, 0x42, 0x01, 0xD9, 0x23, 0x48, 0x04, 0x70, -0x38, 0x78, 0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, -0x38, 0x70, 0x28, 0x78, 0x01, 0x28, 0x0D, 0xD0, -0x02, 0x28, 0x0B, 0xD0, 0x04, 0x28, 0x09, 0xD0, -0x82, 0x28, 0x09, 0xD1, 0x1C, 0x4E, 0x30, 0x78, -0x30, 0x28, 0x05, 0xD1, 0xFE, 0xF7, 0xE0, 0xFC, -0x34, 0x70, 0x01, 0xE0, 0x00, 0xF0, 0xB6, 0xF8, -0x18, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, -0x81, 0x20, 0x28, 0x70, 0xF8, 0xBD, 0x00, 0x00, -0x4C, 0x07, 0x00, 0x20, 0x8F, 0x02, 0x00, 0x20, -0x6E, 0x02, 0x00, 0x20, 0xB9, 0x02, 0x00, 0x20, -0x73, 0x02, 0x00, 0x20, 0xB7, 0x02, 0x00, 0x20, -0xBB, 0x02, 0x00, 0x20, 0x7C, 0x07, 0x00, 0x20, -0x4F, 0x07, 0x00, 0x20, 0x4E, 0x07, 0x00, 0x20, -0x60, 0x07, 0x00, 0x20, 0x85, 0x02, 0x00, 0x20, -0xEE, 0x02, 0x00, 0x20, 0xFC, 0x02, 0x00, 0x20, -0xBF, 0x02, 0x00, 0x20, 0xCA, 0x02, 0x00, 0x20, -0xCB, 0x02, 0x00, 0x20, 0x51, 0x07, 0x00, 0x20, -0xFA, 0x02, 0x00, 0x20, 0xBE, 0x02, 0x00, 0x20, -0x88, 0x02, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, -0x70, 0xB5, 0x10, 0x4D, 0x68, 0x78, 0x00, 0x07, -0x01, 0xD5, 0x00, 0xF0, 0x11, 0xF9, 0x0E, 0x4C, -0x60, 0x22, 0x0E, 0x48, 0x21, 0x68, 0xFC, 0xF7, -0x87, 0xFC, 0xFC, 0xF7, 0x63, 0xFD, 0x68, 0x78, -0x96, 0x21, 0x08, 0x42, 0x01, 0xD0, 0x00, 0xF0, -0x03, 0xF9, 0x09, 0x48, 0x00, 0x78, 0x00, 0x06, -0x06, 0xD4, 0x01, 0x21, 0x20, 0x68, 0xFD, 0xF7, -0x8F, 0xFE, 0x20, 0x68, 0xFC, 0xF7, 0x66, 0xFD, -0x70, 0xBD, 0x00, 0x00, 0x88, 0x02, 0x00, 0x20, -0x00, 0x03, 0x00, 0x20, 0xD4, 0x00, 0x00, 0x20, -0x8E, 0x02, 0x00, 0x20, 0x30, 0xB5, 0x08, 0x4A, -0x14, 0x68, 0x1B, 0x34, 0x00, 0x22, 0xA3, 0x5C, -0x41, 0x2B, 0x03, 0xD0, 0x55, 0x00, 0x45, 0x5B, -0x5B, 0x00, 0xCD, 0x52, 0x52, 0x1C, 0xD2, 0xB2, -0x30, 0x2A, 0xF4, 0xD3, 0x30, 0xBD, 0x00, 0x00, -0xAC, 0x04, 0x00, 0x20, 0x01, 0x20, 0x80, 0x07, -0x40, 0x69, 0x40, 0x05, 0x01, 0xD5, 0x01, 0x20, -0x70, 0x47, 0x00, 0x20, 0x70, 0x47, 0x00, 0x00, -0x10, 0xB5, 0x00, 0x20, 0xFC, 0xF7, 0x9A, 0xFD, -0x05, 0x49, 0x08, 0x20, 0x08, 0x70, 0x05, 0x49, -0x06, 0x20, 0x08, 0x70, 0x01, 0x20, 0x04, 0x49, -0x80, 0x02, 0x08, 0x80, 0x10, 0xBD, 0x00, 0x00, -0xE5, 0x02, 0x00, 0x20, 0x4D, 0x07, 0x00, 0x20, -0xF6, 0x02, 0x00, 0x20, 0x06, 0x49, 0x01, 0x20, -0x08, 0x70, 0x06, 0x49, 0x08, 0x20, 0x08, 0x70, -0x05, 0x49, 0x06, 0x20, 0x08, 0x70, 0x01, 0x20, -0x04, 0x49, 0x80, 0x02, 0x08, 0x80, 0x70, 0x47, -0xE7, 0x02, 0x00, 0x20, 0xE5, 0x02, 0x00, 0x20, -0x4D, 0x07, 0x00, 0x20, 0xF6, 0x02, 0x00, 0x20, -0x10, 0xB5, 0xFF, 0xF7, 0x99, 0xFC, 0x04, 0x48, -0x00, 0x78, 0x00, 0x28, 0x02, 0xD1, 0x01, 0x20, -0xFF, 0xF7, 0xC0, 0xF9, 0x10, 0xBD, 0x00, 0x00, -0x90, 0x02, 0x00, 0x20, 0xF8, 0xB5, 0x3E, 0x4C, -0x3E, 0x4A, 0x20, 0x88, 0x3E, 0x4D, 0xC1, 0x04, -0x0C, 0xD5, 0x51, 0x10, 0x88, 0x43, 0x20, 0x80, -0x29, 0x78, 0x00, 0x29, 0x03, 0xD0, 0x01, 0x29, -0x01, 0xD0, 0x02, 0x29, 0x67, 0xD1, 0x10, 0x43, -0x20, 0x80, 0x64, 0xE0, 0x81, 0x04, 0x03, 0xD5, -0x90, 0x43, 0x20, 0x80, 0xFE, 0xF7, 0x80, 0xFB, -0x20, 0x88, 0x81, 0x07, 0x0C, 0xD0, 0x01, 0x04, -0x59, 0xD5, 0x40, 0x04, 0x40, 0x0C, 0x20, 0x80, -0x81, 0x07, 0x57, 0xD4, 0xC0, 0x07, 0x52, 0xD0, -0x01, 0x20, 0xFF, 0xF7, 0x45, 0xFC, 0x4E, 0xE0, -0x00, 0x04, 0x78, 0x21, 0x2B, 0x4F, 0x2C, 0x4E, -0x00, 0x28, 0x05, 0xDA, 0x38, 0x78, 0x00, 0x28, -0x00, 0xD0, 0x31, 0x80, 0x00, 0x20, 0x20, 0x80, -0x28, 0x48, 0x40, 0x88, 0x00, 0x28, 0x20, 0xD0, -0x27, 0x48, 0x00, 0x78, 0x04, 0x28, 0x1C, 0xD3, -0x26, 0x48, 0x00, 0x78, 0x00, 0x28, 0x18, 0xD1, -0x25, 0x48, 0x00, 0x78, 0x00, 0x28, 0x14, 0xD1, -0x24, 0x4A, 0x10, 0x5E, 0x1D, 0x22, 0xD2, 0x43, -0x90, 0x42, 0x0E, 0xDB, 0x22, 0x48, 0x00, 0x68, -0x90, 0x30, 0x42, 0x7B, 0x03, 0x7B, 0x10, 0x02, -0x18, 0x43, 0x42, 0x00, 0x80, 0x18, 0x1F, 0x4A, -0x00, 0x23, 0xD3, 0x5E, 0x80, 0x08, 0x98, 0x42, -0x01, 0xDA, 0x31, 0x80, 0x04, 0xE0, 0x30, 0x88, -0x00, 0x28, 0x18, 0xD0, 0x78, 0x28, 0x05, 0xD1, -0x38, 0x78, 0x00, 0x28, 0x02, 0xD1, 0x01, 0x20, -0xFF, 0xF7, 0x06, 0xFC, 0x28, 0x78, 0x02, 0x28, -0x04, 0xD1, 0x30, 0x88, 0x78, 0x28, 0x01, 0xD1, -0x3C, 0x20, 0x30, 0x80, 0x30, 0x88, 0x40, 0x1E, -0x80, 0xB2, 0x30, 0x80, 0x39, 0x78, 0x00, 0x29, -0x01, 0xD0, 0x00, 0x28, 0x02, 0xD0, 0xFC, 0xF7, -0x03, 0xFD, 0xF8, 0xBD, 0x00, 0x20, 0xA8, 0xE7, -0xFE, 0x02, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, -0x87, 0x02, 0x00, 0x20, 0x50, 0x07, 0x00, 0x20, -0x5A, 0x07, 0x00, 0x20, 0x60, 0x07, 0x00, 0x20, -0x8F, 0x02, 0x00, 0x20, 0xB9, 0x02, 0x00, 0x20, -0xBB, 0x02, 0x00, 0x20, 0x52, 0x07, 0x00, 0x20, -0xAC, 0x04, 0x00, 0x20, 0x54, 0x07, 0x00, 0x20, -0xF8, 0xB5, 0x5A, 0x4E, 0x30, 0x78, 0x00, 0x28, -0x7E, 0xD1, 0x70, 0x78, 0x44, 0x08, 0x70, 0x88, -0x64, 0x00, 0x00, 0x0A, 0xC0, 0x07, 0x03, 0xD0, -0x70, 0x88, 0x00, 0x0A, 0x00, 0x02, 0x04, 0x43, -0x30, 0x78, 0x00, 0x28, 0x01, 0xD1, 0x70, 0x88, -0x04, 0x43, 0x70, 0x78, 0xC0, 0x07, 0x6B, 0xD1, -0x4F, 0x4A, 0x50, 0x48, 0xD1, 0x69, 0x05, 0x78, -0x42, 0x2C, 0x2B, 0xD0, 0x0E, 0xDC, 0x08, 0x2C, -0x2C, 0xD0, 0x05, 0xDC, 0x02, 0x2C, 0x23, 0xD0, -0x04, 0x2C, 0x5D, 0xD1, 0x4A, 0x4F, 0x16, 0xE0, -0x10, 0x2C, 0x23, 0xD0, 0x20, 0x2C, 0x57, 0xD1, -0x80, 0x22, 0x66, 0xE0, 0x50, 0x2C, 0x04, 0xD0, -0x05, 0xDC, 0x44, 0x2C, 0x18, 0xD0, 0x48, 0x2C, -0x7E, 0xD1, 0x17, 0x6A, 0x05, 0xE0, 0xFF, 0x3C, -0x09, 0x3C, 0x01, 0xD0, 0x08, 0x2C, 0x77, 0xD1, -0x0F, 0x46, 0x00, 0x2F, 0x74, 0xD0, 0x80, 0x21, -0x3E, 0x48, 0xFC, 0xF7, 0x52, 0xFB, 0x28, 0x06, -0x66, 0xD5, 0x60, 0x22, 0x39, 0x46, 0x4D, 0xE0, -0x3B, 0x4F, 0xF4, 0xE7, 0x3B, 0x4F, 0xF2, 0xE7, -0x3B, 0x4F, 0xF0, 0xE7, 0x60, 0x22, 0x3B, 0x48, -0xFC, 0xF7, 0x2A, 0xFB, 0x20, 0x07, 0x0C, 0xD5, -0x38, 0x4F, 0x2F, 0x20, 0x41, 0x00, 0x34, 0x4A, -0x7B, 0x5A, 0x8A, 0x18, 0x12, 0x88, 0x40, 0x1E, -0x9A, 0x1A, 0x40, 0xB2, 0x7A, 0x52, 0x00, 0x28, -0xF4, 0xDA, 0x2C, 0x4F, 0x28, 0x06, 0x08, 0xD4, -0x31, 0x48, 0x32, 0x49, 0x00, 0x68, 0x88, 0x42, -0x03, 0xD0, 0x00, 0x21, 0x2D, 0x48, 0xFD, 0xF7, -0x27, 0xFD, 0x38, 0x78, 0x80, 0x07, 0x02, 0xD5, -0x2A, 0x48, 0xFC, 0xF7, 0xFB, 0xFB, 0x3D, 0x78, -0x28, 0x06, 0x04, 0xD5, 0x60, 0x22, 0x27, 0x49, -0x29, 0x48, 0xFC, 0xF7, 0x01, 0xFB, 0xE8, 0x07, -0x02, 0xD1, 0x24, 0x48, 0xFD, 0xF7, 0xB8, 0xFA, -0x20, 0x07, 0x0E, 0xD5, 0x2F, 0x20, 0x00, 0xE0, -0x2E, 0xE0, 0x1D, 0x4A, 0x1F, 0x4C, 0x41, 0x00, -0x8D, 0x18, 0x63, 0x5A, 0x2D, 0x88, 0x40, 0x1E, -0x5B, 0x19, 0x40, 0xB2, 0x63, 0x52, 0x00, 0x28, -0xF5, 0xDA, 0x38, 0x78, 0x00, 0x06, 0x05, 0xD5, -0x60, 0x22, 0x18, 0x49, 0x13, 0x48, 0xFC, 0xF7, -0xE3, 0xFA, 0x15, 0xE0, 0x19, 0x49, 0x15, 0x4B, -0x10, 0x4C, 0x0A, 0x68, 0x00, 0x20, 0x11, 0x18, -0xC9, 0x7E, 0x41, 0x29, 0x03, 0xD0, 0x45, 0x00, -0x5D, 0x5B, 0x49, 0x00, 0x65, 0x52, 0x40, 0x1C, -0x40, 0xB2, 0x30, 0x28, 0xF3, 0xDB, 0x03, 0xE0, -0x08, 0x49, 0x38, 0x46, 0xFF, 0xF7, 0x62, 0xFE, -0x70, 0x78, 0x01, 0x21, 0x08, 0x43, 0x70, 0x70, -0xF8, 0xBD, 0x00, 0x00, 0x88, 0x02, 0x00, 0x20, -0xE4, 0x02, 0x00, 0x20, 0x8E, 0x02, 0x00, 0x20, -0x00, 0x20, 0x00, 0x50, 0x9C, 0x01, 0x00, 0x20, -0x00, 0x00, 0x01, 0x20, 0x00, 0x00, 0x02, 0x20, -0x00, 0x30, 0x00, 0x50, 0x2C, 0x06, 0x00, 0x20, -0xE4, 0x06, 0x00, 0x20, 0xA1, 0x00, 0x03, 0xF3, -0x4C, 0x00, 0x00, 0x20, 0xAC, 0x04, 0x00, 0x20, -0xF7, 0xB5, 0x45, 0x49, 0x84, 0xB0, 0x00, 0x28, -0x13, 0xD0, 0x44, 0x48, 0x00, 0x88, 0x84, 0xB2, -0x05, 0x98, 0x00, 0x28, 0x03, 0xD0, 0x90, 0x68, -0x01, 0x26, 0x10, 0x60, 0x00, 0xE0, 0x26, 0x46, -0x20, 0x20, 0x03, 0x90, 0x08, 0x68, 0x28, 0x27, -0x80, 0x30, 0x00, 0x7C, 0x02, 0x90, 0x3C, 0x48, -0x13, 0xE0, 0x3C, 0x48, 0x00, 0x88, 0x84, 0xB2, -0x05, 0x98, 0x00, 0x28, 0x03, 0xD0, 0x90, 0x68, -0x01, 0x26, 0x10, 0x60, 0x00, 0xE0, 0x26, 0x46, -0x20, 0x20, 0x03, 0x90, 0x08, 0x68, 0x28, 0x27, -0x80, 0x30, 0x40, 0x7C, 0x40, 0x08, 0x02, 0x90, -0x33, 0x48, 0x05, 0x78, 0x10, 0x68, 0x00, 0x28, -0x01, 0xDC, 0x01, 0x20, 0x10, 0x60, 0x53, 0x68, -0x01, 0x46, 0xC0, 0x18, 0x00, 0x04, 0x40, 0x0C, -0x83, 0x42, 0x01, 0xDB, 0x18, 0x46, 0x00, 0xE0, -0x50, 0x60, 0x41, 0x18, 0x68, 0x43, 0xFC, 0xF7, -0x57, 0xFA, 0x00, 0xB2, 0x00, 0x90, 0x29, 0x48, -0x00, 0x88, 0xC1, 0x00, 0x40, 0x18, 0xC0, 0x08, -0x28, 0x1A, 0x81, 0xB2, 0x00, 0x98, 0x01, 0x91, -0x88, 0x42, 0x12, 0xDD, 0x09, 0x21, 0xE8, 0x08, -0xFC, 0xF7, 0x46, 0xFA, 0x01, 0x06, 0x09, 0x0E, -0x00, 0xD1, 0x01, 0x21, 0x01, 0x9A, 0x00, 0x98, -0x80, 0x1A, 0xFC, 0xF7, 0x3D, 0xFA, 0x20, 0x30, -0xC0, 0xB2, 0x28, 0x28, 0x02, 0xD2, 0x07, 0x46, -0x00, 0xE0, 0x03, 0x9F, 0x00, 0x98, 0x69, 0x00, -0x47, 0x43, 0x08, 0x37, 0x38, 0x11, 0x08, 0x1A, -0x00, 0xB2, 0x00, 0x28, 0x01, 0xDA, 0x00, 0x20, -0x02, 0xE0, 0xA8, 0x42, 0x00, 0xDD, 0x28, 0x46, -0x05, 0x99, 0x00, 0x29, 0x01, 0xD0, 0x30, 0x18, -0x00, 0xE0, 0x30, 0x1A, 0x02, 0x99, 0x65, 0x08, -0x49, 0x00, 0x40, 0x1B, 0x61, 0x1A, 0x60, 0x43, -0x4A, 0x10, 0x80, 0x18, 0xFC, 0xF7, 0x18, 0xFA, -0x28, 0x18, 0x01, 0x28, 0x01, 0xDA, 0x01, 0x20, -0x02, 0xE0, 0xA0, 0x42, 0x00, 0xDD, 0x20, 0x46, -0x40, 0x1E, 0x00, 0xB2, 0x07, 0xB0, 0xF0, 0xBD, -0xAC, 0x04, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, -0x38, 0x00, 0x00, 0x20, 0x46, 0x00, 0x00, 0x20, -0x39, 0x00, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20, -0xF7, 0xB5, 0x1A, 0x49, 0x09, 0x68, 0x80, 0x31, -0x00, 0x28, 0x06, 0xD0, 0x18, 0x48, 0x0E, 0x7C, -0x00, 0x88, 0xCD, 0x7C, 0x84, 0xB2, 0x4F, 0x7D, -0x05, 0xE0, 0x16, 0x48, 0x4E, 0x7C, 0x00, 0x88, -0x0D, 0x7D, 0x8F, 0x7D, 0x84, 0xB2, 0x10, 0x69, -0xD1, 0x68, 0x68, 0x43, 0x4A, 0x10, 0x80, 0x18, -0xFC, 0xF7, 0xE6, 0xF9, 0xC1, 0x19, 0x01, 0x98, -0x40, 0x1E, 0x68, 0x43, 0x08, 0x18, 0x69, 0x08, -0x40, 0x1A, 0x65, 0x08, 0x71, 0x00, 0x40, 0x1B, -0x61, 0x1A, 0x60, 0x43, 0x4A, 0x10, 0x80, 0x18, -0xFC, 0xF7, 0xD6, 0xF9, 0x28, 0x18, 0x01, 0x28, -0x01, 0xDA, 0x01, 0x20, 0x02, 0xE0, 0xA0, 0x42, -0x00, 0xDD, 0x20, 0x46, 0x40, 0x1E, 0x00, 0xB2, -0xFE, 0xBD, 0x00, 0x00, 0xAC, 0x04, 0x00, 0x20, -0x44, 0x00, 0x00, 0x20, 0x46, 0x00, 0x00, 0x20, -0x04, 0x48, 0x00, 0x68, 0x90, 0x30, 0x41, 0x7B, -0x02, 0x7B, 0x08, 0x02, 0x10, 0x43, 0x00, 0xB2, -0x70, 0x47, 0x00, 0x00, 0xAC, 0x04, 0x00, 0x20, -0x70, 0xB5, 0x1A, 0x4D, 0x1E, 0x20, 0x28, 0x70, -0x68, 0x78, 0x29, 0x46, 0x00, 0x24, 0x14, 0x31, -0x02, 0x28, 0x0C, 0xD8, 0x00, 0x28, 0x05, 0xD1, -0xA8, 0x78, 0x00, 0x28, 0x02, 0xD0, 0x08, 0x46, -0x1C, 0x30, 0x04, 0x70, 0x48, 0x88, 0x00, 0x28, -0x01, 0xD1, 0x03, 0x20, 0x68, 0x71, 0xAC, 0x70, -0x2C, 0x81, 0xEC, 0x80, 0xAC, 0x81, 0x6C, 0x81, -0x4C, 0x80, 0x8C, 0x80, 0x0C, 0x72, 0x0C, 0x48, -0x44, 0x70, 0x84, 0x70, 0x04, 0x70, 0x09, 0x48, -0xB4, 0x21, 0xD0, 0x30, 0xFC, 0xF7, 0xBD, 0xF9, -0x06, 0x48, 0xA0, 0x21, 0x30, 0x30, 0xFC, 0xF7, -0xB8, 0xF9, 0xEC, 0x70, 0x78, 0x20, 0xE8, 0x81, -0x01, 0x20, 0x28, 0x71, 0x2C, 0x61, 0x01, 0xF0, -0x49, 0xFC, 0x70, 0xBD, 0x4C, 0x07, 0x00, 0x20, -0x90, 0x02, 0x00, 0x20, 0xFF, 0xB5, 0xA5, 0xB0, -0x18, 0xB2, 0x17, 0x90, 0x00, 0x20, 0x0D, 0x90, -0x25, 0x98, 0x01, 0x78, 0x03, 0x91, 0x26, 0x98, -0x03, 0x78, 0xFE, 0x4A, 0x04, 0x93, 0x10, 0x68, -0x01, 0x46, 0x90, 0x31, 0xCB, 0x7A, 0x89, 0x7A, -0x1B, 0x02, 0x0B, 0x43, 0xA0, 0x21, 0x1C, 0x93, -0x0B, 0x56, 0x00, 0x21, 0x1F, 0x93, 0x18, 0x91, -0x03, 0x7E, 0x07, 0x93, 0x41, 0x7E, 0x1A, 0x91, -0x03, 0x99, 0x59, 0x43, 0x04, 0x9B, 0xC9, 0x18, -0x89, 0xB2, 0x16, 0x91, 0x55, 0x21, 0x0A, 0x91, -0x38, 0x21, 0x13, 0x91, 0x16, 0x99, 0xF0, 0x4B, -0x49, 0x00, 0x22, 0x91, 0x59, 0x5E, 0x1C, 0x9B, -0x99, 0x42, 0x04, 0xDA, 0x0A, 0x23, 0x59, 0x43, -0x49, 0x11, 0x11, 0x91, 0x07, 0xE0, 0xCB, 0x00, -0xC9, 0x18, 0x49, 0x11, 0x11, 0x91, 0x40, 0x21, -0x0A, 0x91, 0x2A, 0x21, 0x13, 0x91, 0xB0, 0x30, -0x41, 0x7A, 0x02, 0x7A, 0x08, 0x02, 0xE5, 0x49, -0x10, 0x43, 0x0A, 0x88, 0xE4, 0x49, 0x90, 0x42, -0x02, 0xD2, 0x08, 0x78, 0x01, 0x28, 0x03, 0xD0, -0xE2, 0x48, 0x00, 0x78, 0x00, 0x28, 0x0B, 0xD0, -0x0A, 0x98, 0x42, 0x00, 0x80, 0x18, 0xC0, 0x05, -0x00, 0x0E, 0x0A, 0x90, 0x13, 0x98, 0x42, 0x00, -0x80, 0x18, 0xC0, 0x05, 0x00, 0x0E, 0x13, 0x90, -0x07, 0x9A, 0x04, 0x98, 0x10, 0x1A, 0x40, 0x1E, -0x23, 0x90, 0x16, 0x98, 0x02, 0x90, 0x00, 0x20, -0x08, 0x90, 0x0C, 0x90, 0x32, 0x21, 0xD6, 0x48, -0xFC, 0xF7, 0x43, 0xF9, 0x0A, 0x21, 0xD5, 0x48, -0xFC, 0xF7, 0x3F, 0xF9, 0x0A, 0x21, 0xD4, 0x48, -0xFC, 0xF7, 0x3B, 0xF9, 0xD2, 0x48, 0x69, 0x46, -0x00, 0x1D, 0x12, 0x90, 0xCF, 0x48, 0x00, 0x1D, -0x09, 0x90, 0x00, 0x20, 0x48, 0x70, 0xCA, 0x48, -0x00, 0x78, 0x01, 0x28, 0x01, 0xD9, 0x02, 0x20, -0x00, 0xE0, 0x0E, 0x20, 0x08, 0x70, 0x88, 0x70, -0x01, 0x20, 0x00, 0x24, 0x0E, 0x90, 0x2C, 0xE0, -0x00, 0x2C, 0x2A, 0xD0, 0x0E, 0x98, 0x00, 0x28, -0x03, 0x98, 0x13, 0xD0, 0x84, 0x42, 0x06, 0xD9, -0x16, 0x98, 0x00, 0x24, 0x02, 0x90, 0x0E, 0x94, -0x68, 0x46, 0x01, 0x70, 0x6D, 0xE1, 0x07, 0x99, -0x02, 0x98, 0x40, 0x1A, 0x80, 0xB2, 0x02, 0x90, -0x02, 0x20, 0x00, 0x1B, 0x41, 0x00, 0xBB, 0x48, -0x08, 0x18, 0x0C, 0xE0, 0x01, 0x19, 0x1A, 0x98, -0x81, 0x42, 0x77, 0xD0, 0x07, 0x99, 0x02, 0x98, -0x40, 0x18, 0x80, 0xB2, 0x02, 0x90, 0xB5, 0x48, -0x61, 0x00, 0x08, 0x18, 0x00, 0x1D, 0x69, 0x46, -0x09, 0x90, 0x08, 0x78, 0x48, 0x70, 0x00, 0x20, -0x08, 0x70, 0x00, 0x20, 0x19, 0x90, 0x15, 0x90, -0x12, 0xE0, 0x00, 0x28, 0x10, 0xD0, 0x04, 0x98, -0x14, 0x90, 0x00, 0x20, 0x0F, 0x90, 0x68, 0x46, -0x40, 0x78, 0x01, 0x25, 0x08, 0x27, 0x18, 0x21, -0x08, 0x40, 0x0B, 0x90, 0x14, 0x98, 0x02, 0x28, -0x01, 0xD9, 0x02, 0x20, 0x14, 0x90, 0x17, 0xE1, -0x23, 0x98, 0x00, 0x25, 0xC0, 0xB2, 0x14, 0x90, -0x01, 0x20, 0x0F, 0x90, 0x68, 0x46, 0x40, 0x78, -0x04, 0x27, 0x40, 0x07, 0x40, 0x0F, 0xEC, 0xE7, -0x0F, 0x98, 0x00, 0x28, 0x01, 0xD0, 0x10, 0x95, -0x01, 0xE0, 0x68, 0x42, 0x10, 0x90, 0x0B, 0x98, -0xB8, 0x43, 0x0B, 0x90, 0x10, 0x99, 0x02, 0x98, -0x40, 0x18, 0x1B, 0x90, 0x40, 0x00, 0x92, 0x49, -0x1D, 0x90, 0x0E, 0x5E, 0x68, 0x46, 0x40, 0x78, -0x1E, 0x90, 0x38, 0x42, 0x01, 0xD1, 0x00, 0x2C, -0x7E, 0xD1, 0x00, 0x2E, 0x7C, 0xDD, 0xF0, 0x07, -0x0B, 0xD1, 0x09, 0x98, 0x09, 0x99, 0x00, 0x88, -0x80, 0x19, 0x08, 0x80, 0x10, 0x98, 0x12, 0x99, -0x40, 0x00, 0x09, 0x5A, 0x12, 0x9A, 0x89, 0x19, -0x11, 0x52, 0x11, 0x98, 0x86, 0x42, 0x6B, 0xDB, -0xF0, 0x07, 0x69, 0xD1, 0x0A, 0x98, 0x70, 0x43, -0x40, 0x11, 0x00, 0xB2, 0x20, 0x90, 0x13, 0x98, -0x70, 0x43, 0x40, 0x11, 0x00, 0xB2, 0x21, 0x90, -0x00, 0x20, 0x01, 0x90, 0x06, 0x90, 0x0E, 0x98, -0x00, 0x28, 0x08, 0xD0, 0x00, 0x2C, 0x10, 0xD0, -0x03, 0x98, 0x00, 0x1B, 0x00, 0x28, 0x0C, 0xDD, -0x01, 0x20, 0x09, 0xE0, 0xFC, 0xE0, 0x00, 0x2C, -0x07, 0xD0, 0x03, 0x98, 0x01, 0x19, 0x1A, 0x98, -0x40, 0x1E, 0x81, 0x42, 0x01, 0xDA, 0x02, 0x20, -0x01, 0x90, 0x0F, 0x98, 0x00, 0x28, 0x0A, 0xD0, -0x00, 0x2D, 0x12, 0xD0, 0x04, 0x98, 0x41, 0x19, -0x07, 0x98, 0x40, 0x1E, 0x81, 0x42, 0x0C, 0xDA, -0x08, 0x21, 0x01, 0x98, 0x07, 0xE0, 0x00, 0x2D, -0x07, 0xD0, 0x04, 0x98, 0x40, 0x1B, 0x00, 0x28, -0x03, 0xDD, 0x01, 0x98, 0x04, 0x21, 0x08, 0x43, -0x01, 0x90, 0x03, 0x98, 0x00, 0x1B, 0x00, 0x28, -0x01, 0xDC, 0x01, 0x20, 0x06, 0xE0, 0x03, 0x98, -0x01, 0x19, 0x1A, 0x98, 0x40, 0x1E, 0x81, 0x42, -0x01, 0xDB, 0x02, 0x20, 0x06, 0x90, 0x04, 0x98, -0x41, 0x19, 0x07, 0x98, 0x40, 0x1E, 0x81, 0x42, -0x02, 0xDB, 0x08, 0x21, 0x06, 0x98, 0x05, 0xE0, -0x04, 0x98, 0x40, 0x1B, 0x00, 0x28, 0x03, 0xDC, -0x06, 0x98, 0x04, 0x21, 0x08, 0x43, 0x06, 0x90, -0x20, 0x46, 0x28, 0x43, 0x4F, 0xD0, 0x00, 0x20, -0x05, 0x90, 0x00, 0x2C, 0x14, 0xD0, 0x00, 0x2D, -0x18, 0xD0, 0x41, 0x00, 0x3C, 0x20, 0xC8, 0x40, -0x01, 0x99, 0x08, 0x40, 0x01, 0x07, 0x00, 0xE0, -0x39, 0xE0, 0x09, 0x0F, 0x06, 0x98, 0x81, 0x43, -0x1F, 0xD0, 0x1B, 0x98, 0x80, 0xB2, 0x00, 0xF0, -0x13, 0xFC, 0xC1, 0x07, 0x19, 0xD1, 0x0C, 0xE0, -0x01, 0x46, 0x04, 0x20, 0xC8, 0x40, 0x81, 0x07, -0x89, 0x0F, 0x03, 0xE0, 0x02, 0x21, 0x81, 0x40, -0x0C, 0x20, 0x01, 0x40, 0x01, 0x98, 0x01, 0x43, -0xE8, 0xE7, 0x18, 0x9A, 0x0D, 0x99, 0x11, 0x43, -0x04, 0xD1, 0x21, 0x99, 0x88, 0x42, 0x01, 0xDD, -0x01, 0x21, 0x18, 0x91, 0x20, 0x99, 0x88, 0x42, -0x05, 0xDC, 0x05, 0x98, 0x40, 0x1C, 0xC0, 0xB2, -0x05, 0x90, 0x03, 0x28, 0xC9, 0xD3, 0x05, 0x98, -0x03, 0x28, 0x14, 0xD0, 0x09, 0x98, 0x09, 0x9A, -0x01, 0x88, 0x70, 0x10, 0x09, 0x1A, 0x11, 0x80, -0x10, 0x99, 0x12, 0x9A, 0x49, 0x00, 0x52, 0x5A, -0x10, 0x1A, 0x12, 0x9A, 0x50, 0x52, 0x0B, 0x98, -0x00, 0x28, 0x39, 0xD0, 0x0F, 0x98, 0x00, 0x28, -0x2E, 0xD0, 0x7F, 0x08, 0x2E, 0xE0, 0x08, 0x99, -0x1B, 0x98, 0x4A, 0x00, 0x2E, 0x49, 0x88, 0x52, -0x08, 0x98, 0x40, 0x1C, 0x80, 0xB2, 0x08, 0x90, -0x0C, 0x98, 0x86, 0x42, 0x00, 0xDD, 0x0C, 0x96, -0x25, 0x48, 0x1D, 0x99, 0x42, 0x5A, 0x01, 0x21, -0x0A, 0x43, 0x1D, 0x99, 0x42, 0x52, 0x69, 0x46, -0x08, 0x78, 0x38, 0x43, 0x08, 0x70, 0x00, 0x2C, -0x0D, 0xD0, 0x0F, 0x98, 0x00, 0x28, 0x1E, 0x98, -0x05, 0xD0, 0x79, 0x08, 0x08, 0x43, 0x69, 0x46, -0x48, 0x70, 0x00, 0x2D, 0x03, 0xD1, 0x79, 0x00, -0x08, 0x43, 0x69, 0x46, 0x48, 0x70, 0x19, 0x98, -0x40, 0x1C, 0xC0, 0xB2, 0x19, 0x90, 0xCD, 0xE7, -0x78, 0x06, 0x07, 0x0E, 0x6D, 0x1C, 0xED, 0xB2, -0x14, 0x98, 0x85, 0x42, 0x00, 0xD8, 0xEF, 0xE6, -0x15, 0x98, 0x40, 0x1C, 0xC0, 0xB2, 0x15, 0x90, -0x02, 0x28, 0x00, 0xD2, 0xC9, 0xE6, 0x00, 0x2C, -0x02, 0xD1, 0x69, 0x46, 0x08, 0x78, 0x88, 0x70, -0x19, 0x98, 0x00, 0x28, 0x01, 0xD0, 0x02, 0x2C, -0x09, 0xD1, 0x0E, 0x98, 0x00, 0x28, 0x1F, 0xD0, -0x16, 0x98, 0x00, 0x24, 0x69, 0x46, 0x0E, 0x94, -0x02, 0x90, 0x88, 0x78, 0x08, 0x70, 0x68, 0x46, -0x81, 0x78, 0x64, 0x1C, 0xE4, 0xB2, 0x02, 0x2C, -0x10, 0xE0, 0x00, 0x00, 0xAC, 0x04, 0x00, 0x20, -0x2C, 0x06, 0x00, 0x20, 0x14, 0x00, 0x00, 0x20, -0x1C, 0x08, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20, -0x1C, 0x02, 0x00, 0x20, 0x26, 0x00, 0x00, 0x20, -0x1C, 0x00, 0x00, 0x20, 0x00, 0xD8, 0x6B, 0xE6, -0x08, 0x98, 0x01, 0x28, 0x1B, 0xD0, 0x1F, 0x99, -0x88, 0x42, 0x18, 0xDA, 0x2C, 0x48, 0x00, 0x68, -0xB0, 0x30, 0x41, 0x7A, 0x02, 0x7A, 0x08, 0x02, -0x2A, 0x49, 0x10, 0x43, 0x09, 0x88, 0x88, 0x42, -0x03, 0xD2, 0x29, 0x48, 0x00, 0x78, 0x01, 0x28, -0x09, 0xD0, 0x0D, 0x98, 0x01, 0x28, 0x06, 0xD0, -0x18, 0x98, 0x00, 0x28, 0x03, 0xD0, 0x0D, 0x98, -0x00, 0x28, 0x05, 0xD0, 0x2E, 0xE0, 0x17, 0x99, -0x0C, 0x98, 0x88, 0x42, 0x2A, 0xDC, 0x38, 0xE0, -0x01, 0x20, 0x0D, 0x90, 0x38, 0x20, 0x0A, 0x90, -0x1E, 0x4A, 0x22, 0x98, 0x1C, 0x99, 0x10, 0x5E, -0x88, 0x42, 0x05, 0xDA, 0x09, 0x21, 0x09, 0x03, -0x48, 0x43, 0x00, 0x14, 0x11, 0x90, 0x06, 0xE0, -0x05, 0x21, 0x49, 0x03, 0x48, 0x43, 0x00, 0x14, -0x11, 0x90, 0x2A, 0x20, 0x0A, 0x90, 0x00, 0x20, -0x15, 0x4B, 0x08, 0xE0, 0x41, 0x00, 0x59, 0x5A, -0x49, 0x00, 0x54, 0x5A, 0x64, 0x08, 0x64, 0x00, -0x40, 0x1C, 0x54, 0x52, 0x80, 0xB2, 0x08, 0x99, -0x88, 0x42, 0xF3, 0xD3, 0x0D, 0x98, 0x02, 0x28, -0x00, 0xD2, 0xF6, 0xE5, 0x27, 0x9A, 0x0D, 0x48, -0x18, 0x32, 0x25, 0x99, 0xFC, 0xF7, 0x2C, 0xFD, -0x0B, 0x48, 0x27, 0x9A, 0x26, 0x99, 0xFC, 0xF7, -0x27, 0xFD, 0x17, 0x9A, 0x0C, 0x99, 0x91, 0x42, -0x00, 0xDC, 0x08, 0x48, 0x29, 0xB0, 0xF0, 0xBD, -0xAC, 0x04, 0x00, 0x20, 0x14, 0x00, 0x00, 0x20, -0x1C, 0x08, 0x00, 0x20, 0x2C, 0x06, 0x00, 0x20, -0x1C, 0x02, 0x00, 0x20, 0x26, 0x00, 0x00, 0x20, -0x1C, 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0xFF, -0x06, 0x4A, 0x07, 0x4B, 0x00, 0x21, 0x00, 0x20, -0x40, 0x1C, 0x90, 0x42, 0xFC, 0xDB, 0x49, 0x1C, -0x99, 0x42, 0xF8, 0xDB, 0x03, 0x48, 0x01, 0x21, -0x01, 0x60, 0x70, 0x47, 0x10, 0x27, 0x00, 0x00, -0xB8, 0x0B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, -0xF0, 0xB5, 0x1A, 0x48, 0x8F, 0xB0, 0x01, 0x78, -0x02, 0x29, 0x01, 0xD9, 0x02, 0x21, 0x01, 0x70, -0x00, 0x25, 0x24, 0xE0, 0x68, 0x1C, 0xC4, 0xB2, -0x0D, 0x90, 0x1A, 0xE0, 0x34, 0x21, 0x69, 0x43, -0x34, 0x22, 0x0E, 0x18, 0x62, 0x43, 0x17, 0x18, -0x31, 0x79, 0x38, 0x79, 0x81, 0x42, 0x0E, 0xD9, -0x31, 0x1D, 0x34, 0x22, 0x68, 0x46, 0xFB, 0xF7, -0xD7, 0xFE, 0x39, 0x1D, 0x30, 0x1D, 0x34, 0x22, -0xFB, 0xF7, 0xD2, 0xFE, 0x38, 0x1D, 0x34, 0x22, -0x69, 0x46, 0xFB, 0xF7, 0xCD, 0xFE, 0x64, 0x1C, -0xE4, 0xB2, 0x06, 0x48, 0x01, 0x78, 0xA1, 0x42, -0xE0, 0xD8, 0x0D, 0x98, 0xC5, 0xB2, 0x03, 0x49, -0x08, 0x78, 0xA8, 0x42, 0xD6, 0xD8, 0x0F, 0xB0, -0xF0, 0xBD, 0x00, 0x00, 0x7C, 0x07, 0x00, 0x20, -0x70, 0xB5, 0x7B, 0x24, 0x21, 0x48, 0x24, 0x02, -0x04, 0x60, 0xF7, 0x20, 0xC0, 0x01, 0x20, 0x4A, -0xC1, 0x7C, 0x11, 0x70, 0x1F, 0x4A, 0x01, 0x7D, -0x11, 0x70, 0x1F, 0x4A, 0x41, 0x7D, 0x11, 0x70, -0x1E, 0x49, 0x80, 0x7D, 0x08, 0x70, 0x60, 0x7C, -0x21, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, -0x1B, 0x49, 0x05, 0x46, 0x08, 0x80, 0xE0, 0x7C, -0xA1, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, -0x18, 0x49, 0x08, 0x80, 0x20, 0x7E, 0x61, 0x7E, -0x48, 0x43, 0x17, 0x49, 0x08, 0x80, 0x17, 0x48, -0xFB, 0xF7, 0xB7, 0xFE, 0x10, 0x21, 0xC8, 0x41, -0x15, 0x49, 0x08, 0x60, 0x04, 0x22, 0x21, 0x1D, -0x14, 0x48, 0xFB, 0xF7, 0x89, 0xFE, 0x11, 0x49, -0xFF, 0x22, 0x1D, 0x32, 0x89, 0x1F, 0x12, 0x48, -0xFB, 0xF7, 0x82, 0xFE, 0x60, 0x7D, 0x22, 0x7D, -0x01, 0x02, 0x11, 0x43, 0x28, 0x46, 0x0A, 0x22, -0x50, 0x43, 0xFB, 0xF7, 0x65, 0xFE, 0x0D, 0x49, -0x08, 0x80, 0x70, 0xBD, 0xAC, 0x04, 0x00, 0x20, -0x36, 0x00, 0x00, 0x20, 0x37, 0x00, 0x00, 0x20, -0x38, 0x00, 0x00, 0x20, 0x39, 0x00, 0x00, 0x20, -0x44, 0x00, 0x00, 0x20, 0x46, 0x00, 0x00, 0x20, -0x3E, 0x00, 0x00, 0x20, 0x10, 0x7B, 0x00, 0x00, -0xD8, 0x06, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20, -0x10, 0x05, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20, -0x64, 0x24, 0x00, 0x20, 0x66, 0x49, 0x08, 0x70, -0xC9, 0xE0, 0x65, 0x48, 0x00, 0x78, 0x03, 0x00, -0xFB, 0xF7, 0xD0, 0xFE, 0x07, 0x05, 0x10, 0x1E, -0x18, 0x37, 0x48, 0x50, 0xC3, 0x00, 0x00, 0xF0, -0xE9, 0xF9, 0x01, 0x28, 0x02, 0xD1, 0x5E, 0x49, -0x08, 0x70, 0x02, 0xE0, 0x03, 0x20, 0x5C, 0x49, -0x08, 0x70, 0xB3, 0xE0, 0x00, 0xF0, 0x02, 0xF9, -0x01, 0x28, 0x02, 0xD1, 0x02, 0x20, 0x58, 0x49, -0x08, 0x70, 0xAB, 0xE0, 0x00, 0xF0, 0xD0, 0xF9, -0x00, 0x20, 0x55, 0x49, 0x08, 0x70, 0xA5, 0xE0, -0xFE, 0xF7, 0xF6, 0xFF, 0x01, 0x28, 0x10, 0xD1, -0xFF, 0xF7, 0xC4, 0xF8, 0x51, 0x48, 0x00, 0x78, -0x80, 0x21, 0x08, 0x42, 0x03, 0xD0, 0x01, 0x20, -0x4D, 0x49, 0x08, 0x70, 0x02, 0xE0, 0x04, 0x20, -0x4B, 0x49, 0x08, 0x70, 0xFC, 0xF7, 0x54, 0xF9, -0x02, 0xE0, 0x01, 0x20, 0x48, 0x49, 0x08, 0x70, -0x8C, 0xE0, 0x00, 0xF0, 0xCF, 0xF9, 0x01, 0x28, -0x0B, 0xD1, 0x46, 0x48, 0x00, 0x78, 0x80, 0x21, -0x08, 0x42, 0x03, 0xD0, 0x01, 0x20, 0x42, 0x49, -0x08, 0x70, 0x02, 0xE0, 0x05, 0x20, 0x40, 0x49, -0x08, 0x70, 0x7B, 0xE0, 0x00, 0xF0, 0x96, 0xF8, -0x01, 0x28, 0x02, 0xD1, 0x06, 0x20, 0x3C, 0x49, -0x08, 0x70, 0x73, 0xE0, 0x3C, 0x48, 0x00, 0x78, -0x00, 0x28, 0x09, 0xD1, 0x01, 0x20, 0xFB, 0xF7, -0xC3, 0xFE, 0x00, 0x20, 0x39, 0x49, 0x08, 0x70, -0x01, 0x20, 0x35, 0x49, 0x08, 0x70, 0x65, 0xE0, -0xFF, 0xF7, 0x9C, 0xF9, 0x01, 0x28, 0x2F, 0xD1, -0x34, 0x48, 0x00, 0x78, 0x00, 0x28, 0x2B, 0xD1, -0x01, 0xF0, 0xA2, 0xFA, 0x32, 0x48, 0xC0, 0x78, -0x01, 0x28, 0x04, 0xD0, 0x31, 0x48, 0x40, 0x78, -0xC0, 0x07, 0xC0, 0x0F, 0x2E, 0xD0, 0x01, 0x25, -0xAD, 0x07, 0xA9, 0x14, 0x28, 0x68, 0x08, 0x40, -0x00, 0x0A, 0x0A, 0x21, 0x08, 0x43, 0x29, 0x49, -0xC8, 0x70, 0x2B, 0x48, 0x04, 0x78, 0x2B, 0x48, -0x00, 0x78, 0xFF, 0x28, 0x05, 0xDA, 0x29, 0x48, -0x00, 0x78, 0x40, 0x1C, 0x27, 0x49, 0x08, 0x70, -0x02, 0xE0, 0x01, 0x20, 0x25, 0x49, 0x08, 0x70, -0x24, 0x48, 0x00, 0x78, 0x1F, 0x49, 0x08, 0x70, -0x00, 0x20, 0xFB, 0xF7, 0x89, 0xFE, 0x0D, 0xE0, -0x00, 0x2C, 0x05, 0xD0, 0x60, 0x1E, 0xC4, 0xB2, -0x01, 0x20, 0xFB, 0xF7, 0x91, 0xFF, 0x05, 0xE0, -0x00, 0x20, 0x18, 0x49, 0x08, 0x70, 0x01, 0x20, -0xFB, 0xF7, 0x7A, 0xFE, 0x1A, 0x48, 0x00, 0x78, -0x00, 0x28, 0x1A, 0xD0, 0x19, 0x48, 0x00, 0x78, -0x00, 0x28, 0x16, 0xD1, 0x18, 0x48, 0x00, 0x78, -0x01, 0x28, 0x12, 0xD1, 0x17, 0x48, 0x00, 0x78, -0x00, 0x28, 0x0E, 0xD1, 0x16, 0x48, 0x00, 0x78, -0x40, 0x1C, 0xC0, 0xB2, 0x14, 0x49, 0x08, 0x70, -0x0D, 0x49, 0x09, 0x78, 0x88, 0x42, 0x04, 0xDD, -0x00, 0x20, 0x11, 0x49, 0x08, 0x70, 0x0E, 0x49, -0x08, 0x70, 0x01, 0x20, 0x02, 0x49, 0x08, 0x70, -0x00, 0xE0, 0x00, 0xBF, 0x00, 0xBF, 0x34, 0xE7, -0x81, 0x02, 0x00, 0x20, 0x60, 0x07, 0x00, 0x20, -0xE4, 0x02, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, -0x7C, 0x07, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20, -0xB7, 0x02, 0x00, 0x20, 0x86, 0x02, 0x00, 0x20, -0x11, 0x00, 0x00, 0x20, 0x50, 0x07, 0x00, 0x20, -0x12, 0x00, 0x00, 0x20, 0x13, 0x00, 0x00, 0x20, -0xCD, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x18, 0x48, -0x00, 0x78, 0x03, 0x00, 0xFB, 0xF7, 0xEA, 0xFD, -0x06, 0x28, 0x04, 0x0A, 0x16, 0x1C, 0x22, 0x28, -0x01, 0xF0, 0x9A, 0xF8, 0x02, 0x20, 0x12, 0x49, -0x08, 0x70, 0x1D, 0xE0, 0x01, 0xF0, 0x88, 0xF8, -0x00, 0x28, 0x03, 0xD0, 0x03, 0x20, 0x0E, 0x49, -0x08, 0x70, 0x02, 0xE0, 0x05, 0x20, 0x0C, 0x49, -0x08, 0x70, 0x11, 0xE0, 0xFC, 0xF7, 0x8C, 0xF8, -0x04, 0x20, 0x09, 0x49, 0x08, 0x70, 0x0B, 0xE0, -0x01, 0xF0, 0xDC, 0xF8, 0x05, 0x20, 0x06, 0x49, -0x08, 0x70, 0x05, 0xE0, 0x01, 0xF0, 0x0A, 0xFA, -0x01, 0x20, 0x03, 0x49, 0x08, 0x70, 0x10, 0xBD, -0x00, 0xBF, 0x00, 0x20, 0xFB, 0xE7, 0x00, 0x00, -0x0C, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x60, 0x4C, -0x20, 0x78, 0x00, 0x28, 0x3C, 0xD0, 0x02, 0x20, -0xFB, 0xF7, 0x0E, 0xFF, 0x60, 0x88, 0x5D, 0x4A, -0x00, 0x25, 0x90, 0x42, 0x02, 0xD1, 0xA5, 0x70, -0x28, 0x0A, 0xE0, 0x70, 0x20, 0x78, 0x5A, 0x4E, -0x5A, 0x49, 0x44, 0x28, 0x75, 0xD0, 0x13, 0xDC, -0x01, 0x22, 0x33, 0x28, 0x34, 0xD0, 0x08, 0xDC, -0x20, 0x28, 0x74, 0xD0, 0x30, 0x28, 0x28, 0xD0, -0x31, 0x28, 0x3D, 0xD0, 0x32, 0x28, 0x3B, 0xD1, -0x27, 0xE0, 0x34, 0x28, 0x39, 0xD0, 0x42, 0x28, -0x6F, 0xD0, 0x43, 0x28, 0x34, 0xD1, 0x90, 0xE0, -0x60, 0x28, 0x78, 0xD0, 0x07, 0xDC, 0x5D, 0x26, -0x5A, 0x28, 0x0B, 0xD0, 0x5C, 0x28, 0x0C, 0xD0, -0x5D, 0x28, 0x29, 0xD1, 0x0D, 0xE0, 0x62, 0x28, -0x65, 0xD0, 0x70, 0x28, 0x53, 0xD0, 0x71, 0x28, -0x7A, 0xD1, 0x71, 0xE0, 0xFF, 0xF7, 0xB4, 0xF8, -0x01, 0xE0, 0xFF, 0xF7, 0xC7, 0xF8, 0x26, 0x70, -0x73, 0xE0, 0xC8, 0x78, 0x00, 0x28, 0x6F, 0xD0, -0x6F, 0xE0, 0x41, 0x49, 0x82, 0x20, 0x08, 0x70, -0x6B, 0xE0, 0x60, 0x88, 0x3F, 0x49, 0x54, 0xE0, -0x60, 0x88, 0x88, 0x82, 0xA5, 0x70, 0x28, 0x0A, -0xE0, 0x70, 0x88, 0x8A, 0x04, 0x28, 0x5F, 0xD1, -0xFF, 0xF7, 0x90, 0xF8, 0x00, 0x28, 0x02, 0xD1, -0x01, 0x20, 0xFB, 0xF7, 0xA9, 0xFD, 0x35, 0x70, -0x56, 0xE0, 0x60, 0x88, 0xC0, 0x07, 0x04, 0xD0, -0x48, 0x8B, 0x35, 0x4A, 0x80, 0x08, 0x80, 0x00, -0x15, 0xE0, 0x60, 0x88, 0x80, 0x07, 0x05, 0xD5, -0x48, 0x8B, 0x31, 0x4A, 0x80, 0x08, 0x80, 0x00, -0x52, 0x1C, 0x0C, 0xE0, 0x60, 0x88, 0xC0, 0x04, -0x48, 0x8B, 0x04, 0xD5, 0x80, 0x09, 0x2C, 0x4A, -0x80, 0x01, 0xD2, 0x1C, 0x03, 0xE0, 0x80, 0x09, -0x80, 0x01, 0x01, 0x22, 0xD2, 0x03, 0x10, 0x43, -0x48, 0x83, 0x60, 0x88, 0xC0, 0x05, 0x03, 0xD5, -0x48, 0x8B, 0x08, 0x22, 0x10, 0x43, 0x48, 0x83, -0x0D, 0x72, 0xA5, 0x70, 0x28, 0x0A, 0xE0, 0x70, -0x2A, 0xE0, 0x30, 0xE0, 0x03, 0xE0, 0x25, 0x70, -0x20, 0x48, 0x05, 0x70, 0x25, 0xE0, 0x60, 0x88, -0x00, 0x28, 0xC8, 0x79, 0x03, 0xD0, 0x10, 0x43, -0x03, 0xE0, 0x20, 0xE0, 0x0B, 0xE0, 0x40, 0x08, -0x40, 0x00, 0xC8, 0x71, 0x18, 0xE0, 0x48, 0x6A, -0x02, 0x80, 0x60, 0x30, 0xC2, 0x83, 0x60, 0x88, -0x17, 0x49, 0x08, 0x70, 0xE1, 0xE7, 0xC8, 0x89, -0xA0, 0x70, 0x00, 0x0A, 0xE0, 0x70, 0x15, 0x48, -0x00, 0x68, 0x60, 0x30, 0x00, 0x78, 0x04, 0xE0, -0x71, 0x20, 0xA0, 0x70, 0x00, 0x20, 0xE0, 0x70, -0x0B, 0x20, 0x20, 0x71, 0x00, 0x0A, 0x60, 0x71, -0x25, 0x70, 0x01, 0x20, 0x70, 0xBD, 0x0A, 0x70, -0xFB, 0xE7, 0x0D, 0x70, 0xF9, 0xE7, 0x01, 0x20, -0xFB, 0xF7, 0x46, 0xFD, 0x35, 0x70, 0xF4, 0xE7, -0x88, 0x02, 0x00, 0x20, 0xAA, 0x55, 0x00, 0x00, -0x90, 0x02, 0x00, 0x20, 0xE4, 0x02, 0x00, 0x20, -0x60, 0x07, 0x00, 0x20, 0x35, 0x00, 0x00, 0x20, -0x01, 0x80, 0x00, 0x00, 0x81, 0x02, 0x00, 0x20, -0x8E, 0x02, 0x00, 0x20, 0xAC, 0x04, 0x00, 0x20, -0x10, 0xB5, 0x01, 0x20, 0x00, 0x06, 0xFB, 0xF7, -0x1A, 0xFD, 0x10, 0xBD, 0x10, 0xB5, 0x00, 0x24, -0x00, 0xF0, 0xBC, 0xFD, 0x00, 0xF0, 0xE4, 0xFF, -0x01, 0x20, 0xFE, 0xF7, 0xD7, 0xFE, 0x04, 0x46, -0x00, 0x2C, 0x00, 0xD1, 0x10, 0xBD, 0x03, 0x49, -0x03, 0x48, 0x81, 0x70, 0x09, 0x0A, 0xC1, 0x70, -0x01, 0x20, 0xF7, 0xE7, 0xAA, 0x55, 0x00, 0x00, -0x88, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x00, 0x24, -0x1B, 0x48, 0x00, 0x78, 0x01, 0x28, 0x02, 0xD0, -0x02, 0x28, 0x2D, 0xD1, 0x1C, 0xE0, 0xFF, 0xF7, -0x1F, 0xF8, 0xFD, 0xF7, 0x17, 0xFE, 0x17, 0x48, -0x00, 0x7A, 0x00, 0x28, 0x03, 0xD1, 0x01, 0x20, -0x15, 0x49, 0x08, 0x70, 0x0F, 0xE0, 0xFB, 0xF7, -0x39, 0xFD, 0xFE, 0xF7, 0x9D, 0xFF, 0x13, 0x48, -0x00, 0x78, 0x80, 0x21, 0x08, 0x42, 0x03, 0xD0, -0x06, 0x20, 0x0F, 0x49, 0x08, 0x70, 0x02, 0xE0, -0x02, 0x20, 0x0B, 0x49, 0x08, 0x70, 0x0F, 0xE0, -0xFE, 0xF7, 0x7C, 0xFE, 0x00, 0x28, 0x04, 0xD0, -0x01, 0x20, 0x07, 0x49, 0x08, 0x70, 0x01, 0x24, -0x05, 0xE0, 0x01, 0x20, 0x04, 0x49, 0x08, 0x70, -0x06, 0x20, 0x05, 0x49, 0x08, 0x70, 0x00, 0xBF, -0x00, 0xBF, 0x20, 0x46, 0x10, 0xBD, 0x00, 0x00, -0x82, 0x02, 0x00, 0x20, 0x50, 0x02, 0x00, 0x20, -0x81, 0x02, 0x00, 0x20, 0x8E, 0x02, 0x00, 0x20, -0x0A, 0x07, 0x01, 0xD5, 0x40, 0x1C, 0x02, 0xE0, -0x4A, 0x07, 0x01, 0xD5, 0x40, 0x1E, 0x80, 0xB2, -0x07, 0x4A, 0xCB, 0x07, 0x12, 0x68, 0x02, 0xD0, -0x11, 0x7E, 0x40, 0x1A, 0x03, 0xE0, 0x89, 0x07, -0x02, 0xD5, 0x11, 0x7E, 0x08, 0x18, 0x80, 0xB2, -0x02, 0x49, 0x40, 0x00, 0x08, 0x5E, 0x70, 0x47, -0xAC, 0x04, 0x00, 0x20, 0x2C, 0x06, 0x00, 0x20, -0xF0, 0xB5, 0x41, 0x18, 0x15, 0x4C, 0x16, 0x4D, -0x8C, 0x46, 0x0E, 0xE0, 0x29, 0x78, 0x01, 0x23, -0x22, 0x88, 0x05, 0xE0, 0x1E, 0x46, 0x8E, 0x40, -0x16, 0x42, 0x09, 0xD0, 0x49, 0x1C, 0xC9, 0xB2, -0x02, 0x29, 0xF7, 0xD3, 0x40, 0x1C, 0x29, 0x70, -0xC0, 0xB2, 0x84, 0x45, 0xEE, 0xD8, 0xF0, 0xBD, -0x34, 0x27, 0x06, 0x46, 0x7E, 0x43, 0x0B, 0x4F, -0xF6, 0x19, 0x1F, 0x46, 0x31, 0x71, 0x8F, 0x40, -0x73, 0x72, 0x17, 0x43, 0x27, 0x80, 0x73, 0x71, -0xB3, 0x89, 0x73, 0x82, 0xF2, 0x89, 0xB2, 0x82, -0x73, 0x86, 0xB2, 0x86, 0xFF, 0x22, 0xF2, 0x72, -0xE4, 0xE7, 0x00, 0x00, 0x40, 0x00, 0x00, 0x20, -0x3B, 0x00, 0x00, 0x20, 0x7C, 0x07, 0x00, 0x20, -0xF0, 0xB5, 0x95, 0xB0, 0x00, 0x20, 0x0C, 0x90, -0x12, 0x90, 0x30, 0x21, 0x68, 0x46, 0xFB, 0xF7, -0xC8, 0xFB, 0xD3, 0x48, 0x01, 0x78, 0x40, 0x78, -0x08, 0x18, 0x01, 0x06, 0x09, 0x0E, 0xD1, 0x48, -0x0E, 0x91, 0x01, 0xD0, 0x00, 0x27, 0x99, 0xE0, -0x01, 0x78, 0x00, 0x20, 0xFF, 0xF7, 0xB4, 0xFF, -0x15, 0xB0, 0xF0, 0xBD, 0x34, 0x20, 0x39, 0x46, -0x41, 0x43, 0xCA, 0x48, 0x00, 0x25, 0x08, 0x18, -0x0F, 0x90, 0x00, 0x79, 0xFF, 0x28, 0x7D, 0xD1, -0x38, 0x01, 0x12, 0x9E, 0x10, 0x90, 0x75, 0xE0, -0x10, 0x9A, 0x68, 0x46, 0x14, 0x18, 0x60, 0x19, -0xFF, 0x21, 0x13, 0x90, 0x41, 0x70, 0xA8, 0x00, -0x20, 0x18, 0xC1, 0x49, 0x11, 0x90, 0x41, 0x60, -0x0F, 0x98, 0x0E, 0x21, 0x41, 0x5E, 0x0C, 0x22, -0x8C, 0x46, 0x82, 0x5E, 0x06, 0x20, 0x31, 0x46, -0x41, 0x43, 0xB9, 0x48, 0x00, 0x23, 0x08, 0x18, -0xA0, 0x30, 0x02, 0x21, 0xC3, 0x5E, 0x41, 0x5E, -0x18, 0x46, 0x63, 0x46, 0x01, 0xF0, 0x36, 0xF8, -0x0D, 0x90, 0xB6, 0x48, 0x01, 0x88, 0x00, 0x29, -0x07, 0xD0, 0xB5, 0x48, 0x00, 0x88, 0x88, 0x42, -0x03, 0xD9, 0xFB, 0xF7, 0x3B, 0xFB, 0xC0, 0xB2, -0x00, 0xE0, 0x00, 0x20, 0x34, 0x21, 0xAC, 0x4A, -0x71, 0x43, 0x89, 0x18, 0x09, 0x6A, 0xB0, 0x4B, -0x4A, 0x00, 0xAE, 0x49, 0x1B, 0x78, 0x09, 0x78, -0x40, 0x1C, 0x59, 0x43, 0x41, 0x43, 0x50, 0x18, -0xA7, 0x49, 0x88, 0x42, 0x01, 0xD9, 0x08, 0x46, -0x03, 0xE0, 0xAA, 0x49, 0x88, 0x42, 0x00, 0xD2, -0x08, 0x46, 0xA9, 0x49, 0x09, 0x68, 0xB0, 0x31, -0x4A, 0x7A, 0x0B, 0x7A, 0x11, 0x02, 0xA2, 0x4A, -0x19, 0x43, 0x12, 0x88, 0x91, 0x42, 0x04, 0xD2, -0x9B, 0x49, 0x09, 0x78, 0x01, 0x29, 0x00, 0xD1, -0x9B, 0x48, 0x0D, 0x99, 0x81, 0x42, 0x1B, 0xD8, -0x08, 0x46, 0x11, 0x99, 0x6D, 0x1C, 0x48, 0x60, -0x13, 0x98, 0xED, 0xB2, 0x46, 0x70, 0x01, 0x2D, -0x12, 0xD9, 0x68, 0x1E, 0x0D, 0xE0, 0x81, 0x00, -0x62, 0x18, 0x94, 0x46, 0x52, 0x68, 0x63, 0x58, -0x9A, 0x42, 0x05, 0xD2, 0x22, 0x18, 0x52, 0x78, -0x22, 0x54, 0x62, 0x46, 0x52, 0x68, 0x62, 0x50, -0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, 0xEE, 0xD1, -0x76, 0x1C, 0xF6, 0xB2, 0x0E, 0x98, 0x86, 0x42, -0x86, 0xD3, 0x00, 0xE0, 0x08, 0xE0, 0x10, 0x99, -0x68, 0x46, 0x45, 0x54, 0x00, 0x2D, 0x03, 0xD0, -0x0C, 0x98, 0x40, 0x1C, 0xC0, 0xB2, 0x0C, 0x90, -0x7F, 0x1C, 0xFF, 0xB2, 0x81, 0x48, 0x00, 0x78, -0x84, 0x46, 0xB8, 0x42, 0x00, 0xD9, 0x65, 0xE7, -0x0C, 0x98, 0x00, 0x28, 0x7D, 0xD0, 0x01, 0x28, -0x59, 0xD9, 0x00, 0x24, 0x55, 0xE0, 0x60, 0x1C, -0xC3, 0xB2, 0x0C, 0x90, 0x4D, 0xE0, 0x26, 0x01, -0x69, 0x46, 0x72, 0x18, 0x1D, 0x01, 0x69, 0x18, -0x50, 0x78, 0x4F, 0x78, 0xB8, 0x42, 0x42, 0xD1, -0x50, 0x68, 0x4F, 0x68, 0xB8, 0x42, 0x1F, 0xD2, -0x00, 0x20, 0x0C, 0xE0, 0x0A, 0x18, 0x56, 0x78, -0x97, 0x78, 0x57, 0x70, 0x96, 0x70, 0x82, 0x00, -0x8A, 0x18, 0x97, 0x68, 0x56, 0x68, 0x40, 0x1C, -0x57, 0x60, 0xC0, 0xB2, 0x96, 0x60, 0x6A, 0x46, -0x52, 0x5D, 0x52, 0x1E, 0x82, 0x42, 0xED, 0xDC, -0x6A, 0x46, 0x50, 0x5D, 0x00, 0x28, 0x04, 0xD0, -0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, 0x50, 0x55, -0x21, 0xD1, 0xFF, 0x20, 0x48, 0x70, 0x1E, 0xE0, -0x00, 0x20, 0x0C, 0xE0, 0x11, 0x18, 0x4D, 0x78, -0x8F, 0x78, 0x4F, 0x70, 0x8D, 0x70, 0x81, 0x00, -0x51, 0x18, 0x8F, 0x68, 0x4D, 0x68, 0x40, 0x1C, -0x4F, 0x60, 0xC0, 0xB2, 0x8D, 0x60, 0x69, 0x46, -0x89, 0x5D, 0x49, 0x1E, 0x81, 0x42, 0xED, 0xDC, -0x69, 0x46, 0x88, 0x5D, 0x00, 0x28, 0x04, 0xD0, -0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, 0x88, 0x55, -0x01, 0xD1, 0xFF, 0x20, 0x50, 0x70, 0x5B, 0x1C, -0xDB, 0xB2, 0x9C, 0x45, 0xAF, 0xD8, 0x0C, 0x98, -0xC4, 0xB2, 0xA4, 0x45, 0xA7, 0xD8, 0x00, 0x26, -0x80, 0xE0, 0x32, 0x01, 0x68, 0x46, 0x10, 0x18, -0x0D, 0x90, 0x40, 0x78, 0xFF, 0x28, 0x29, 0xD0, -0x34, 0x22, 0x50, 0x43, 0x48, 0x4A, 0x14, 0x23, -0x85, 0x18, 0x34, 0x20, 0x70, 0x43, 0x44, 0x18, -0x12, 0x22, 0xAA, 0x5E, 0x62, 0x82, 0xEB, 0x5E, -0xA3, 0x82, 0x0E, 0x21, 0x0C, 0x20, 0x61, 0x5E, -0x20, 0x5E, 0x00, 0xF0, 0x4F, 0xFF, 0x0C, 0x90, -0x47, 0x48, 0x07, 0x68, 0x38, 0x46, 0xA7, 0x30, -0x00, 0xE0, 0x65, 0xE0, 0xFB, 0xF7, 0xA5, 0xFA, -0x01, 0x46, 0x0C, 0x98, 0x81, 0x42, 0x0E, 0xD2, -0x38, 0x46, 0xB0, 0x30, 0xC1, 0x79, 0x82, 0x79, -0x08, 0x02, 0x3B, 0x49, 0x10, 0x43, 0x09, 0x88, -0x88, 0x42, 0x04, 0xD9, 0x01, 0x21, 0x30, 0x46, -0xFF, 0xF7, 0x82, 0xFE, 0x48, 0xE0, 0x0D, 0x98, -0x41, 0x68, 0x68, 0x7A, 0xC8, 0x28, 0x01, 0xD2, -0x40, 0x1C, 0x60, 0x72, 0x2A, 0x46, 0x20, 0x32, -0x20, 0x46, 0x13, 0x79, 0x20, 0x30, 0x0D, 0x90, -0x03, 0x71, 0x34, 0x48, 0x00, 0x78, 0x00, 0x28, -0x08, 0xD0, 0x60, 0x7A, 0x1E, 0x28, 0x05, 0xD1, -0x10, 0x7A, 0xC0, 0x07, 0x02, 0xD0, 0x0D, 0x98, -0x01, 0x22, 0x02, 0x71, 0x28, 0x6A, 0x40, 0x18, -0x40, 0x08, 0x20, 0x62, 0x28, 0x79, 0x20, 0x71, -0x03, 0x21, 0x61, 0x71, 0x80, 0x21, 0x08, 0x43, -0x28, 0x71, 0xA8, 0x79, 0xA0, 0x71, 0xE8, 0x79, -0xE0, 0x71, 0x28, 0x7A, 0x20, 0x72, 0x38, 0x46, -0xA3, 0x30, 0xFB, 0xF7, 0x62, 0xFA, 0x01, 0x46, -0x0C, 0x98, 0x81, 0x42, 0x03, 0xD2, 0xA0, 0x89, -0x60, 0x82, 0xE0, 0x89, 0xA0, 0x82, 0x60, 0x7A, -0x02, 0x28, 0x07, 0xD1, 0x0D, 0x98, 0x80, 0x7D, -0x00, 0x28, 0x03, 0xD1, 0xA0, 0x89, 0x68, 0x86, -0xE0, 0x89, 0xA8, 0x86, 0x68, 0x8E, 0x60, 0x86, -0xA8, 0x8E, 0xA0, 0x86, 0xE8, 0x7A, 0xE0, 0x72, -0x76, 0x1C, 0xF6, 0xB2, 0x0D, 0x49, 0x08, 0x78, -0xB0, 0x42, 0x00, 0xD9, 0x79, 0xE7, 0x7B, 0xE6, -0x00, 0x24, 0x0A, 0x4D, 0x0C, 0xE0, 0x20, 0x46, -0x34, 0x21, 0x48, 0x43, 0x40, 0x19, 0x00, 0x79, -0xFF, 0x28, 0x03, 0xD1, 0x01, 0x21, 0x20, 0x46, -0xFF, 0xF7, 0x22, 0xFE, 0x64, 0x1C, 0xE4, 0xB2, -0x28, 0x78, 0xA0, 0x42, 0xEF, 0xD8, 0x67, 0xE6, -0x1C, 0x08, 0x00, 0x20, 0x7C, 0x07, 0x00, 0x20, -0xFF, 0xFF, 0x07, 0x00, 0x16, 0x00, 0x00, 0x20, -0x14, 0x00, 0x00, 0x20, 0x36, 0x00, 0x00, 0x20, -0x37, 0x00, 0x00, 0x20, 0xFF, 0xFF, 0x00, 0x00, -0xAC, 0x04, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20, -0xF0, 0xB5, 0x00, 0x24, 0x72, 0x49, 0xA9, 0xB0, -0x02, 0x20, 0x08, 0x70, 0x71, 0x48, 0x25, 0x46, -0x86, 0x78, 0xB5, 0xE0, 0x34, 0x21, 0x6F, 0x48, -0x71, 0x43, 0x09, 0x18, 0x08, 0x7A, 0x00, 0x28, -0x19, 0xD1, 0x6D, 0x4A, 0x0C, 0x20, 0x08, 0x5E, -0x12, 0x78, 0x90, 0x42, 0x11, 0xDB, 0x6B, 0x4F, -0x00, 0x23, 0xFB, 0x5E, 0x9A, 0x1A, 0x90, 0x42, -0x0B, 0xDC, 0x69, 0x4A, 0x0E, 0x20, 0x08, 0x5E, -0x12, 0x78, 0x90, 0x42, 0x05, 0xDB, 0x67, 0x4F, -0x00, 0x23, 0xFB, 0x5E, 0x9A, 0x1A, 0x90, 0x42, -0x01, 0xDD, 0x02, 0x20, 0x08, 0x72, 0x88, 0x79, -0x5E, 0x4A, 0x00, 0x90, 0x00, 0x28, 0x06, 0xD0, -0x61, 0x4B, 0x52, 0x78, 0x1B, 0x78, 0x00, 0x20, -0x9C, 0x46, 0x96, 0x46, 0x84, 0xE0, 0x5E, 0x4F, -0x38, 0x78, 0x00, 0x19, 0x03, 0x28, 0x22, 0xD2, -0x01, 0x22, 0x8A, 0x71, 0x34, 0x22, 0x50, 0x43, -0xC0, 0x19, 0x09, 0x1D, 0x00, 0x1D, 0xFB, 0xF7, -0xAF, 0xF9, 0x39, 0x78, 0x34, 0x22, 0x09, 0x19, -0x00, 0x20, 0x51, 0x43, 0xC9, 0x19, 0x48, 0x71, -0x69, 0xE0, 0x0A, 0x79, 0x80, 0x23, 0x1A, 0x43, -0x63, 0x46, 0x1B, 0x18, 0x34, 0x27, 0x7B, 0x43, -0x4F, 0x4F, 0xDB, 0x19, 0x1B, 0x79, 0x9A, 0x42, -0x60, 0xD1, 0x4E, 0x48, 0x00, 0x78, 0x00, 0x28, -0x02, 0xD0, 0x00, 0x20, 0x88, 0x71, 0x5D, 0xE0, -0x00, 0x98, 0x40, 0x1C, 0xC0, 0xB2, 0x88, 0x71, -0x0F, 0x7F, 0x01, 0x2F, 0x0D, 0xD1, 0x40, 0x4A, -0x0B, 0x7A, 0x12, 0x78, 0x52, 0x1E, 0x9A, 0x18, -0x90, 0x42, 0x06, 0xD1, 0x8A, 0x89, 0x4A, 0x85, -0xCA, 0x89, 0x8A, 0x85, 0x8B, 0x7A, 0x27, 0x22, -0x53, 0x54, 0x39, 0x4A, 0x0B, 0x7A, 0x12, 0x78, -0x9A, 0x18, 0x90, 0x42, 0x30, 0xD1, 0x00, 0x20, -0x3D, 0x4A, 0x88, 0x71, 0x12, 0x78, 0x00, 0x2A, -0x08, 0xD0, 0x3C, 0x4A, 0x12, 0x78, 0x00, 0x2A, -0x04, 0xD1, 0x3B, 0x4A, 0x3B, 0x4B, 0x10, 0x70, -0x02, 0x22, 0x1A, 0x70, 0x3A, 0x4A, 0x12, 0x78, -0x13, 0x06, 0x05, 0xD5, 0x01, 0x2F, 0x03, 0xD1, -0x37, 0x4F, 0x01, 0x23, 0x1A, 0x43, 0x3A, 0x70, -0x0A, 0x46, 0x83, 0x23, 0x20, 0x32, 0x13, 0x72, -0x01, 0x23, 0x13, 0x71, 0x50, 0x71, 0x60, 0x46, -0x00, 0x28, 0x01, 0xD1, 0x31, 0x48, 0x03, 0x70, -0x34, 0x20, 0x68, 0x43, 0x01, 0xAA, 0x80, 0x18, -0x09, 0x1D, 0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7, -0x4B, 0xF9, 0x6D, 0x1C, 0xED, 0xB2, 0x11, 0xE0, -0x60, 0x46, 0x23, 0x4A, 0x00, 0x19, 0x34, 0x23, -0x58, 0x43, 0x80, 0x18, 0x09, 0x1D, 0x00, 0x1D, -0x1A, 0x46, 0xFB, 0xF7, 0x3D, 0xF9, 0x64, 0x1C, -0xE4, 0xB2, 0x03, 0xE0, 0x40, 0x1C, 0xC0, 0xB2, -0x86, 0x45, 0x8E, 0xD8, 0x76, 0x1C, 0xF6, 0xB2, -0x14, 0x48, 0x01, 0x78, 0xB1, 0x42, 0x00, 0xD9, -0x44, 0xE7, 0x06, 0x46, 0xB0, 0x78, 0x2A, 0x46, -0x41, 0x19, 0x31, 0x70, 0x34, 0x21, 0x48, 0x43, -0x4A, 0x43, 0x80, 0x19, 0x00, 0x1D, 0x02, 0xA9, -0xFB, 0xF7, 0x22, 0xF9, 0x10, 0x4D, 0x00, 0x20, -0x07, 0x46, 0x2B, 0x78, 0x0A, 0xE0, 0x19, 0x18, -0x34, 0x22, 0x51, 0x43, 0x4A, 0x19, 0x12, 0x79, -0x12, 0x06, 0x01, 0xD4, 0x89, 0x19, 0x8F, 0x71, -0x40, 0x1C, 0xC0, 0xB2, 0x71, 0x78, 0x81, 0x42, -0xF1, 0xD8, 0x74, 0x70, 0x29, 0xB0, 0xF0, 0xBD, -0x3A, 0x00, 0x00, 0x20, 0x7C, 0x07, 0x00, 0x20, -0x36, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, -0x37, 0x00, 0x00, 0x20, 0x46, 0x00, 0x00, 0x20, -0x1C, 0x08, 0x00, 0x20, 0x4F, 0x07, 0x00, 0x20, -0x11, 0x00, 0x00, 0x20, 0x12, 0x00, 0x00, 0x20, -0xCC, 0x00, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, -0xBD, 0x02, 0x00, 0x20, 0x10, 0x00, 0x00, 0x20, -0xF0, 0xB5, 0xA9, 0xB0, 0x53, 0x49, 0x00, 0x24, -0x8C, 0x70, 0x9C, 0x21, 0x02, 0xA8, 0xFB, 0xF7, -0x00, 0xF9, 0x00, 0x26, 0x6C, 0xE0, 0x34, 0x21, -0x71, 0x43, 0x0D, 0x18, 0x29, 0x79, 0xFF, 0x29, -0x64, 0xD0, 0x0A, 0x06, 0x35, 0xD5, 0x4B, 0x4F, -0x00, 0x20, 0x3A, 0x78, 0x94, 0x46, 0x2D, 0xE0, -0x34, 0x22, 0x42, 0x43, 0xD2, 0x19, 0x12, 0x79, -0x80, 0x23, 0x1A, 0x43, 0x8A, 0x42, 0x23, 0xD1, -0x34, 0x21, 0x48, 0x43, 0x43, 0x49, 0x22, 0x46, -0x41, 0x18, 0x34, 0x20, 0x42, 0x43, 0x01, 0xA8, -0x17, 0x18, 0x00, 0x91, 0x09, 0x1D, 0x38, 0x1D, -0x34, 0x22, 0xFB, 0xF7, 0xC1, 0xF8, 0x28, 0x79, -0x64, 0x1C, 0x40, 0x06, 0x40, 0x0E, 0x38, 0x71, -0x68, 0x79, 0x78, 0x71, 0x00, 0x99, 0x80, 0x20, -0x4A, 0x79, 0xE4, 0xB2, 0x02, 0x43, 0x4A, 0x71, -0xA8, 0x79, 0x00, 0x28, 0x36, 0xD1, 0x35, 0x48, -0x81, 0x78, 0x49, 0x1C, 0x81, 0x70, 0x31, 0xE0, -0x40, 0x1C, 0xC0, 0xB2, 0x84, 0x45, 0xCF, 0xD8, -0x2C, 0xE0, 0xA9, 0x79, 0x00, 0x29, 0x29, 0xD1, -0xE9, 0x79, 0x03, 0x29, 0x08, 0xD9, 0x00, 0x21, -0xE9, 0x71, 0x00, 0x78, 0x00, 0x28, 0x21, 0xD0, -0x2B, 0x49, 0x02, 0x20, 0x08, 0x70, 0x1D, 0xE0, -0x34, 0x20, 0x22, 0x46, 0x42, 0x43, 0x01, 0xA8, -0x17, 0x18, 0x29, 0x1D, 0x38, 0x1D, 0x34, 0x22, -0xFB, 0xF7, 0x8E, 0xF8, 0x06, 0x20, 0x31, 0x46, -0x41, 0x43, 0x24, 0x48, 0x64, 0x1C, 0x08, 0x18, -0xA0, 0x30, 0x01, 0x88, 0xB9, 0x81, 0x40, 0x88, -0xF8, 0x81, 0x1E, 0x48, 0xE4, 0xB2, 0x81, 0x78, -0x49, 0x1C, 0x81, 0x70, 0x28, 0x79, 0x80, 0x21, -0x08, 0x43, 0x28, 0x71, 0x76, 0x1C, 0xF6, 0xB2, -0x1A, 0x48, 0x01, 0x78, 0x42, 0x78, 0x89, 0x18, -0xB1, 0x42, 0x8C, 0xD8, 0x00, 0x25, 0x15, 0x4E, -0x1A, 0xE0, 0x34, 0x20, 0x68, 0x43, 0x81, 0x19, -0x48, 0x79, 0x02, 0x06, 0x03, 0xD5, 0x40, 0x06, -0x40, 0x0E, 0x48, 0x71, 0x0E, 0xE0, 0x02, 0x2C, -0x11, 0xD0, 0x00, 0x20, 0x88, 0x71, 0x34, 0x20, -0x60, 0x43, 0x01, 0xAA, 0x80, 0x18, 0x09, 0x1D, -0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7, 0x58, 0xF8, -0x64, 0x1C, 0xE4, 0xB2, 0x6D, 0x1C, 0xED, 0xB2, -0x30, 0x78, 0xA8, 0x42, 0xE1, 0xD8, 0x34, 0x70, -0x34, 0x21, 0x4C, 0x43, 0x22, 0x46, 0x02, 0xA9, -0x30, 0x1D, 0xFB, 0xF7, 0x49, 0xF8, 0x29, 0xB0, -0xF0, 0xBD, 0x00, 0x00, 0x7C, 0x07, 0x00, 0x20, -0x10, 0x00, 0x00, 0x20, 0x1C, 0x08, 0x00, 0x20, -0xF8, 0xB5, 0x76, 0x48, 0x00, 0x25, 0x07, 0x78, -0x75, 0x4E, 0x00, 0x2F, 0x04, 0xD0, 0x75, 0x48, -0x00, 0x78, 0x00, 0x28, 0x00, 0xD0, 0xB5, 0x70, -0x73, 0x4C, 0x16, 0x21, 0xE5, 0x70, 0x30, 0x1D, -0xFB, 0xF7, 0x47, 0xF8, 0x71, 0x48, 0x72, 0x49, -0x00, 0x78, 0x09, 0x78, 0xC0, 0x01, 0x08, 0x43, -0xC1, 0xB2, 0x70, 0x48, 0x70, 0x4A, 0x03, 0x78, -0x01, 0x20, 0x00, 0x2B, 0x03, 0xD0, 0x13, 0x78, -0x8B, 0x42, 0x00, 0xD0, 0xE0, 0x70, 0x11, 0x70, -0x6C, 0x4A, 0x3B, 0x00, 0x11, 0x78, 0x6C, 0x4F, -0x1E, 0xD0, 0x6C, 0x4B, 0x00, 0x29, 0x16, 0xD1, -0x6B, 0x4A, 0x11, 0x78, 0x00, 0x29, 0x12, 0xD0, -0x60, 0x49, 0x08, 0x70, 0x69, 0x49, 0x0D, 0x70, -0x69, 0x49, 0x09, 0x78, 0xB1, 0x70, 0x15, 0x70, -0x75, 0x70, 0x62, 0x4A, 0x25, 0x70, 0x05, 0x21, -0x11, 0x70, 0x06, 0x21, 0x39, 0x70, 0x65, 0x49, -0x0D, 0x70, 0x18, 0x70, 0x02, 0xE0, 0x19, 0x78, -0x00, 0x29, 0x00, 0xD0, 0xE0, 0x70, 0xF8, 0xBD, -0x61, 0x4B, 0x1B, 0x78, 0x00, 0x2B, 0x18, 0xD1, -0x60, 0x4B, 0x1B, 0x78, 0x18, 0x2B, 0x14, 0xD1, -0x5F, 0x4B, 0x1B, 0x78, 0x03, 0x2B, 0x10, 0xD9, -0xB1, 0x78, 0xC9, 0x07, 0xEF, 0xD1, 0x39, 0x78, -0x05, 0x29, 0xEC, 0xD0, 0xE0, 0x70, 0xB0, 0x70, -0x04, 0x21, 0x55, 0x48, 0xFA, 0xF7, 0xF9, 0xFF, -0x75, 0x70, 0x58, 0x48, 0x05, 0x70, 0x04, 0x20, -0x0B, 0xE0, 0xB3, 0x78, 0xDB, 0x07, 0x0A, 0xD0, -0xE0, 0x70, 0xB5, 0x70, 0x75, 0x70, 0x50, 0x48, -0x3C, 0x21, 0x01, 0x70, 0x03, 0x20, 0x10, 0x70, -0x05, 0x20, 0x38, 0x70, 0xF8, 0xBD, 0x00, 0x29, -0x04, 0xD0, 0x71, 0x78, 0x00, 0x29, 0xF9, 0xD0, -0x25, 0x70, 0x0A, 0xE0, 0x21, 0x78, 0x00, 0x29, -0x07, 0xD0, 0xE0, 0x70, 0x71, 0x70, 0x38, 0x48, -0x00, 0x24, 0x40, 0x1C, 0x48, 0x4F, 0x00, 0x90, -0x63, 0xE0, 0x71, 0x78, 0x00, 0x29, 0x00, 0xD0, -0xE0, 0x70, 0x75, 0x70, 0xF8, 0xBD, 0x34, 0x20, -0x33, 0x49, 0x60, 0x43, 0x45, 0x18, 0x20, 0x46, -0x0B, 0x26, 0x70, 0x43, 0x2A, 0x79, 0xC6, 0x18, -0x32, 0x71, 0xA8, 0x89, 0x76, 0x1C, 0x30, 0x71, -0x00, 0x0A, 0x70, 0x71, 0xE8, 0x89, 0xB0, 0x71, -0x00, 0x0A, 0xF0, 0x71, 0x28, 0x8A, 0x30, 0x72, -0x00, 0x0A, 0x70, 0x72, 0x28, 0x20, 0x40, 0x5D, -0xC0, 0x07, 0x07, 0xD0, 0xE8, 0x8D, 0x30, 0x71, -0x00, 0x0A, 0x70, 0x71, 0x28, 0x8E, 0xB0, 0x71, -0x00, 0x0A, 0xF0, 0x71, 0x08, 0x78, 0x01, 0x28, -0x2D, 0xD1, 0xC8, 0x79, 0x00, 0x28, 0x2A, 0xD0, -0x00, 0x98, 0xC1, 0x79, 0x80, 0x79, 0x09, 0x06, -0x0B, 0x14, 0x03, 0x43, 0x00, 0x98, 0x41, 0x79, -0x00, 0x79, 0x09, 0x06, 0x0A, 0x14, 0x02, 0x43, -0x2A, 0x48, 0x00, 0x21, 0x41, 0x5E, 0x00, 0x20, -0x38, 0x5E, 0x00, 0xF0, 0x3F, 0xFC, 0x28, 0x49, -0x0A, 0x68, 0xB0, 0x32, 0x51, 0x7B, 0x12, 0x7B, -0x09, 0x02, 0x11, 0x43, 0x81, 0x42, 0x0E, 0xD2, -0x00, 0x98, 0x41, 0x79, 0x02, 0x79, 0x08, 0x06, -0x00, 0x14, 0x10, 0x43, 0x38, 0x80, 0x00, 0x98, -0xC2, 0x79, 0x81, 0x79, 0x10, 0x06, 0x00, 0x14, -0x08, 0x43, 0x1C, 0x49, 0x08, 0x80, 0x28, 0x8B, -0xB0, 0x72, 0x00, 0x0A, 0xF0, 0x72, 0x68, 0x8B, -0x30, 0x73, 0x00, 0x0A, 0x70, 0x73, 0x64, 0x1C, -0xE4, 0xB2, 0x03, 0x4B, 0x58, 0x78, 0xA0, 0x42, -0x9D, 0xD8, 0xF8, 0xBD, 0x11, 0x00, 0x00, 0x20, -0x90, 0x02, 0x00, 0x20, 0x12, 0x00, 0x00, 0x20, -0x7C, 0x07, 0x00, 0x20, 0x50, 0x07, 0x00, 0x20, -0x87, 0x02, 0x00, 0x20, 0x35, 0x00, 0x00, 0x20, -0x34, 0x01, 0x00, 0x20, 0x51, 0x07, 0x00, 0x20, -0x10, 0x00, 0x00, 0x20, 0x13, 0x00, 0x00, 0x20, -0xCC, 0x00, 0x00, 0x20, 0xCD, 0x00, 0x00, 0x20, -0x48, 0x00, 0x00, 0x20, 0xBD, 0x02, 0x00, 0x20, -0x4E, 0x07, 0x00, 0x20, 0xB5, 0x02, 0x00, 0x20, -0x4F, 0x07, 0x00, 0x20, 0x84, 0x02, 0x00, 0x20, -0x18, 0x00, 0x00, 0x20, 0x1A, 0x00, 0x00, 0x20, -0xAC, 0x04, 0x00, 0x20, 0xF7, 0xB5, 0x00, 0x20, -0x84, 0xB0, 0x03, 0x90, 0x32, 0x48, 0x00, 0x27, -0x01, 0x90, 0x38, 0x46, 0x56, 0xE0, 0x00, 0x24, -0x00, 0x29, 0x05, 0xD0, 0x40, 0x1E, 0x88, 0x42, -0x00, 0xD1, 0x02, 0x24, 0x00, 0x26, 0x45, 0xE0, -0x01, 0x24, 0xFB, 0xE7, 0x78, 0x00, 0x2B, 0x4A, -0x02, 0x90, 0x10, 0x5E, 0x06, 0x9B, 0x98, 0x42, -0x38, 0xDD, 0xC3, 0x07, 0x36, 0xD1, 0x01, 0x9B, -0x98, 0x42, 0x33, 0xDD, 0xA4, 0x07, 0xA4, 0x0F, -0x00, 0x2E, 0x0B, 0xD0, 0x49, 0x1E, 0xB1, 0x42, -0x01, 0xD1, 0x08, 0x20, 0x04, 0x43, 0x00, 0x25, -0x21, 0x48, 0x41, 0x5D, 0x20, 0x46, 0x08, 0x42, -0x0F, 0xD1, 0x01, 0xE0, 0x04, 0x20, 0xF5, 0xE7, -0x38, 0x46, 0xFF, 0xF7, 0xE9, 0xFA, 0x1B, 0x4A, -0x02, 0x99, 0x04, 0x2D, 0x51, 0x5E, 0x02, 0xD2, -0x81, 0x42, 0x17, 0xDB, 0x01, 0xE0, 0x81, 0x42, -0x04, 0xDD, 0x6D, 0x1C, 0xED, 0xB2, 0x08, 0x2D, -0xE6, 0xD3, 0x01, 0xE0, 0x08, 0x2D, 0x0D, 0xD3, -0x12, 0x49, 0x02, 0x98, 0x08, 0x5E, 0x01, 0x90, -0x04, 0x99, 0x00, 0x98, 0x08, 0x70, 0x05, 0x98, -0x10, 0x49, 0x06, 0x70, 0x01, 0x20, 0x03, 0x90, -0x01, 0x98, 0x08, 0x80, 0x7F, 0x1C, 0x76, 0x1C, -0xBF, 0xB2, 0xF6, 0xB2, 0x0C, 0x48, 0x00, 0x68, -0x01, 0x7E, 0xB1, 0x42, 0xB6, 0xD8, 0x00, 0x98, -0x40, 0x1C, 0xC0, 0xB2, 0x08, 0x4A, 0x00, 0x90, -0x10, 0x68, 0x00, 0x99, 0x40, 0x7E, 0x88, 0x42, -0xA1, 0xD8, 0x03, 0x98, 0x07, 0xB0, 0xF0, 0xBD, -0x00, 0x80, 0xFF, 0xFF, 0x2C, 0x06, 0x00, 0x20, -0xD6, 0x62, 0x00, 0x00, 0x32, 0x00, 0x00, 0x20, -0xAC, 0x04, 0x00, 0x20, 0x05, 0x48, 0x01, 0x78, -0x82, 0x29, 0x05, 0xD1, 0xC0, 0x79, 0x01, 0x28, -0x02, 0xD1, 0x03, 0x49, 0xFF, 0x20, 0x08, 0x70, -0x70, 0x47, 0x00, 0x00, 0x60, 0x07, 0x00, 0x20, -0xEC, 0x02, 0x00, 0x20, 0x01, 0x20, 0x02, 0x49, -0x08, 0x70, 0x02, 0x49, 0x08, 0x70, 0x70, 0x47, -0x0C, 0x00, 0x00, 0x20, 0x82, 0x02, 0x00, 0x20, -0x70, 0xB5, 0x3D, 0x4D, 0x3D, 0x48, 0x2A, 0x78, -0x00, 0x21, 0x00, 0x78, 0x00, 0x2A, 0x17, 0xD1, -0x3B, 0x4C, 0x00, 0x23, 0x23, 0x80, 0x3B, 0x4E, -0x04, 0x24, 0x34, 0x70, 0x3A, 0x4E, 0x00, 0x28, -0x34, 0x70, 0x3A, 0x4C, 0x23, 0x70, 0x3A, 0x4C, -0x23, 0x70, 0x01, 0xD1, 0x39, 0x4C, 0x23, 0x70, -0x39, 0x4C, 0x23, 0x70, 0x39, 0x4C, 0x23, 0x70, -0x39, 0x4C, 0x23, 0x70, 0x39, 0x4C, 0x23, 0x70, -0x39, 0x4B, 0x1B, 0x78, 0x00, 0x2B, 0x3F, 0xD0, -0x00, 0x2A, 0x3D, 0xD0, 0x00, 0x28, 0x3B, 0xD1, -0x36, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x37, 0xD1, -0x35, 0x48, 0x00, 0x78, 0x00, 0x28, 0x33, 0xD0, -0x34, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x2F, 0xD1, -0xE8, 0x78, 0x00, 0x28, 0x2C, 0xD1, 0x32, 0x48, -0x00, 0x78, 0x00, 0x28, 0x28, 0xD1, 0xE8, 0x79, -0x02, 0x28, 0x02, 0xD0, 0x68, 0x7A, 0x01, 0x28, -0x22, 0xD9, 0x2E, 0x4A, 0x02, 0x20, 0x10, 0x70, -0x01, 0x20, 0x50, 0x70, 0x2C, 0x4A, 0x10, 0x70, -0x1A, 0xE0, 0x34, 0x20, 0x48, 0x43, 0x40, 0x19, -0x02, 0x46, 0x20, 0x32, 0x13, 0x7A, 0x1C, 0x06, -0x06, 0xD5, 0x5B, 0x06, 0x5B, 0x0E, 0x13, 0x72, -0x83, 0x89, 0xC3, 0x85, 0xC3, 0x89, 0x03, 0x86, -0x13, 0x7A, 0x9C, 0x07, 0x06, 0xD5, 0xFD, 0x24, -0x23, 0x40, 0x13, 0x72, 0x82, 0x89, 0x42, 0x85, -0xC2, 0x89, 0x82, 0x85, 0x49, 0x1C, 0xC9, 0xB2, -0x28, 0x78, 0x88, 0x42, 0xE1, 0xD8, 0x1D, 0x4E, -0x00, 0x24, 0x30, 0x70, 0x68, 0x78, 0x70, 0x70, -0x20, 0x46, 0x34, 0x21, 0x48, 0x43, 0x41, 0x19, -0x80, 0x19, 0x09, 0x1D, 0x00, 0x1D, 0x34, 0x22, -0xFA, 0xF7, 0x22, 0xFE, 0x64, 0x1C, 0xE4, 0xB2, -0x03, 0x2C, 0xF1, 0xD3, 0x70, 0xBD, 0x00, 0x00, -0x7C, 0x07, 0x00, 0x20, 0x4F, 0x07, 0x00, 0x20, -0x14, 0x00, 0x00, 0x20, 0xCF, 0x00, 0x00, 0x20, -0xD0, 0x00, 0x00, 0x20, 0xCE, 0x00, 0x00, 0x20, -0x34, 0x00, 0x00, 0x20, 0xBA, 0x02, 0x00, 0x20, -0xA8, 0x04, 0x00, 0x20, 0xBF, 0x02, 0x00, 0x20, -0xCA, 0x02, 0x00, 0x20, 0xCB, 0x02, 0x00, 0x20, -0x11, 0x00, 0x00, 0x20, 0x8F, 0x02, 0x00, 0x20, -0x84, 0x02, 0x00, 0x20, 0xBD, 0x02, 0x00, 0x20, -0x51, 0x07, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, -0xCC, 0x00, 0x00, 0x20, 0x1C, 0x08, 0x00, 0x20, -0x02, 0x49, 0x00, 0x20, 0x08, 0x70, 0x02, 0x49, -0x08, 0x80, 0x70, 0x47, 0x3B, 0x00, 0x00, 0x20, -0x40, 0x00, 0x00, 0x20, 0xF0, 0xB5, 0x3F, 0x4F, -0x00, 0x20, 0x38, 0x70, 0x01, 0x21, 0x3E, 0x4C, -0xC9, 0x03, 0x21, 0x80, 0x3D, 0x49, 0x3F, 0x4A, -0x08, 0x80, 0x3D, 0x4E, 0x12, 0x88, 0x0D, 0xE0, -0x43, 0x00, 0x00, 0x25, 0xF1, 0x5E, 0x65, 0x5F, -0xA9, 0x42, 0x02, 0xDD, 0x37, 0x4D, 0x21, 0x80, -0x28, 0x80, 0x49, 0x08, 0x49, 0x00, 0x40, 0x1C, -0xF1, 0x52, 0x80, 0xB2, 0x90, 0x42, 0xEF, 0xD3, -0x00, 0x20, 0x20, 0x5E, 0x41, 0x00, 0x41, 0x18, -0xCB, 0x17, 0x5B, 0x0F, 0x59, 0x18, 0xCB, 0x10, -0x31, 0x49, 0x09, 0x68, 0x90, 0x31, 0x4C, 0x7B, -0x0D, 0x7B, 0x21, 0x02, 0x29, 0x43, 0x81, 0x42, -0x0C, 0xDA, 0x00, 0x20, 0x08, 0xE0, 0x41, 0x00, -0x71, 0x5E, 0x99, 0x42, 0x02, 0xDD, 0x39, 0x78, -0x49, 0x1C, 0x39, 0x70, 0x40, 0x1C, 0x80, 0xB2, -0x90, 0x42, 0xF4, 0xD3, 0x38, 0x78, 0x27, 0x49, -0x28, 0x4A, 0x08, 0x70, 0x26, 0x48, 0x01, 0x78, -0x23, 0x48, 0x00, 0x68, 0x80, 0x30, 0x00, 0x29, -0x81, 0x7B, 0x02, 0xD0, 0xCB, 0x00, 0xC9, 0x18, -0xC9, 0x08, 0x11, 0x70, 0xC3, 0x7B, 0x22, 0x49, -0x00, 0x20, 0x08, 0x5E, 0x83, 0x42, 0x11, 0xDA, -0x20, 0x49, 0x09, 0x78, 0x02, 0x29, 0x0D, 0xD2, -0x11, 0x78, 0x3B, 0x78, 0x49, 0x08, 0x99, 0x42, -0x08, 0xD2, 0x1D, 0x49, 0x0B, 0x78, 0x11, 0x78, -0x49, 0x1E, 0x8B, 0x42, 0x02, 0xDD, 0x11, 0x78, -0x49, 0x1C, 0x39, 0x70, 0x19, 0x49, 0x00, 0x23, -0xCB, 0x5E, 0x59, 0x42, 0x81, 0x42, 0x01, 0xDD, -0x00, 0x20, 0x38, 0x70, 0x3B, 0x78, 0x12, 0x78, -0x15, 0x49, 0x93, 0x42, 0x08, 0x78, 0x06, 0xD9, -0x03, 0x28, 0x01, 0xD2, 0x40, 0x1C, 0x00, 0xE0, -0x0C, 0x20, 0x08, 0x70, 0xF0, 0xBD, 0x00, 0x28, -0xFC, 0xD0, 0x3A, 0x78, 0x03, 0x2A, 0xF9, 0xD2, -0x40, 0x1E, 0xF6, 0xE7, 0x0E, 0x00, 0x00, 0x20, -0x30, 0x00, 0x00, 0x20, 0x3C, 0x00, 0x00, 0x20, -0x2C, 0x06, 0x00, 0x20, 0x3E, 0x00, 0x00, 0x20, -0xAC, 0x04, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, -0x4C, 0x07, 0x00, 0x20, 0x0D, 0x00, 0x00, 0x20, -0x58, 0x07, 0x00, 0x20, 0x7C, 0x07, 0x00, 0x20, -0xB6, 0x02, 0x00, 0x20, 0x56, 0x07, 0x00, 0x20, -0x4F, 0x07, 0x00, 0x20, 0x70, 0xB5, 0x0E, 0x4C, -0x00, 0x25, 0xA5, 0x80, 0xE5, 0x80, 0xFF, 0xF7, -0x53, 0xFF, 0x0C, 0x48, 0x00, 0x78, 0x01, 0x28, -0x0E, 0xD0, 0x02, 0x28, 0x0C, 0xD0, 0x0A, 0x48, -0x65, 0x70, 0x05, 0x70, 0xA5, 0x70, 0xE5, 0x70, -0x25, 0x70, 0x08, 0x48, 0x20, 0x81, 0x60, 0x81, -0x07, 0x49, 0x03, 0x20, 0x08, 0x70, 0x70, 0xBD, -0x20, 0x20, 0x60, 0x70, 0xF2, 0xE7, 0x00, 0x00, -0x10, 0x00, 0x00, 0x20, 0x87, 0x02, 0x00, 0x20, -0x4F, 0x07, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, -0x83, 0x02, 0x00, 0x20, 0x10, 0xB5, 0xFF, 0xF7, -0x2F, 0xFF, 0x08, 0x49, 0x78, 0x20, 0x08, 0x70, -0x07, 0x49, 0x00, 0x20, 0x08, 0x70, 0x08, 0x48, -0x06, 0x49, 0x01, 0x81, 0x41, 0x81, 0x07, 0x49, -0x03, 0x20, 0x08, 0x70, 0x06, 0x49, 0x01, 0x20, -0x08, 0x70, 0x10, 0xBD, 0xB7, 0x02, 0x00, 0x20, -0x4F, 0x07, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, -0x10, 0x00, 0x00, 0x20, 0x83, 0x02, 0x00, 0x20, -0x84, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x12, 0x4D, -0x06, 0x46, 0x2C, 0x78, 0xE0, 0x07, 0x07, 0xD0, -0x80, 0x22, 0x10, 0x49, 0x10, 0x48, 0xFA, 0xF7, -0xFB, 0xFC, 0x60, 0x08, 0x40, 0x00, 0x28, 0x70, -0x28, 0x78, 0x80, 0x07, 0x12, 0xD5, 0x00, 0x2E, -0x10, 0xD1, 0x0C, 0x49, 0x00, 0x20, 0x09, 0x4D, -0x09, 0x4C, 0x0E, 0x88, 0x08, 0xE0, 0x41, 0x00, -0x62, 0x5E, 0x6B, 0x5E, 0xD2, 0x18, 0x52, 0x10, -0x62, 0x52, 0x40, 0x1C, 0x6A, 0x52, 0x80, 0xB2, -0xB0, 0x42, 0xF4, 0xD3, 0x70, 0xBD, 0x00, 0x00, -0x83, 0x02, 0x00, 0x20, 0x2C, 0x06, 0x00, 0x20, -0x28, 0x04, 0x00, 0x20, 0x3E, 0x00, 0x00, 0x20, -0x10, 0xB5, 0x04, 0x48, 0x40, 0x78, 0x80, 0x06, -0x01, 0xD5, 0xFE, 0xF7, 0x55, 0xF9, 0xFB, 0xF7, -0x5D, 0xF9, 0x10, 0xBD, 0x88, 0x02, 0x00, 0x20, -0xF8, 0xB5, 0x22, 0x49, 0x00, 0x20, 0x03, 0x46, -0x1F, 0x4A, 0x0C, 0x88, 0x06, 0xE0, 0x41, 0x00, -0x55, 0x5E, 0x00, 0x2D, 0x00, 0xDA, 0x53, 0x52, -0x40, 0x1C, 0xC0, 0xB2, 0xA0, 0x42, 0xF6, 0xD3, -0x1C, 0x48, 0x1B, 0x4D, 0x01, 0x78, 0x1D, 0x48, -0x1B, 0x4A, 0x04, 0x68, 0x2E, 0x78, 0x20, 0x46, -0xB0, 0x30, 0x12, 0x88, 0x02, 0x2E, 0x0F, 0xD0, -0xC3, 0x79, 0x84, 0x79, 0x18, 0x02, 0x20, 0x43, -0x90, 0x42, 0x01, 0xD8, 0x01, 0x29, 0x01, 0xD9, -0x03, 0x20, 0x28, 0x70, 0x00, 0x20, 0xFF, 0xF7, -0x9D, 0xFF, 0xFF, 0xF7, 0xB7, 0xFE, 0xF8, 0xBD, -0x90, 0x34, 0x66, 0x7B, 0x27, 0x7B, 0x34, 0x02, -0x3C, 0x43, 0x0F, 0x4E, 0x00, 0x27, 0xF7, 0x5F, -0xBC, 0x42, 0x05, 0xDA, 0x00, 0x29, 0x03, 0xD1, -0x0C, 0x49, 0x09, 0x78, 0x01, 0x29, 0xE7, 0xD0, -0xC1, 0x79, 0x84, 0x79, 0x08, 0x02, 0x20, 0x43, -0x90, 0x42, 0xE3, 0xD2, 0x2B, 0x70, 0xE1, 0xE7, -0x2C, 0x06, 0x00, 0x20, 0x3E, 0x00, 0x00, 0x20, -0x83, 0x02, 0x00, 0x20, 0x1C, 0x08, 0x00, 0x20, -0x14, 0x00, 0x00, 0x20, 0xAC, 0x04, 0x00, 0x20, -0x54, 0x07, 0x00, 0x20, 0x60, 0x07, 0x00, 0x20, -0x10, 0xB5, 0xFE, 0xF7, 0xB9, 0xFD, 0xFF, 0xF7, -0x4D, 0xFF, 0x10, 0xBD, 0xF8, 0xB5, 0x89, 0x4C, -0x20, 0x78, 0x01, 0x28, 0x2E, 0xD1, 0x60, 0x7A, -0x0A, 0x28, 0x2B, 0xD2, 0x86, 0x4E, 0x00, 0x20, -0x30, 0x5E, 0x86, 0x49, 0x88, 0x42, 0x25, 0xD0, -0x85, 0x4D, 0x0E, 0x23, 0x0C, 0x22, 0x00, 0x21, -0xE3, 0x5E, 0xA2, 0x5E, 0x69, 0x5E, 0x00, 0xF0, -0x21, 0xF9, 0x82, 0x49, 0x09, 0x68, 0xB0, 0x31, -0x4A, 0x7B, 0x0B, 0x7B, 0x11, 0x02, 0x19, 0x43, -0x81, 0x42, 0x13, 0xD3, 0x00, 0x20, 0x30, 0x5E, -0x41, 0x00, 0x40, 0x18, 0x0C, 0x21, 0x61, 0x5E, -0x40, 0x18, 0x80, 0x10, 0xA0, 0x81, 0x00, 0x21, -0x69, 0x5E, 0x4A, 0x00, 0x89, 0x18, 0x0E, 0x22, -0xA2, 0x5E, 0x89, 0x18, 0x89, 0x10, 0xE1, 0x81, -0x60, 0x85, 0xA1, 0x85, 0x00, 0x25, 0xD5, 0xE0, -0x73, 0x4E, 0x34, 0x23, 0x00, 0x20, 0x80, 0x27, -0x6B, 0x43, 0x31, 0x78, 0x9C, 0x18, 0x09, 0xE0, -0x34, 0x23, 0x43, 0x43, 0x22, 0x79, 0x9B, 0x19, -0x1B, 0x79, 0x3A, 0x43, 0x9A, 0x42, 0x03, 0xD0, -0x40, 0x1C, 0xC0, 0xB2, 0x81, 0x42, 0xF3, 0xD8, -0x81, 0x42, 0x7D, 0xD9, 0x34, 0x22, 0x50, 0x43, -0x87, 0x19, 0x18, 0x20, 0x38, 0x5E, 0x18, 0x21, -0x42, 0x00, 0x80, 0x18, 0x61, 0x5E, 0x01, 0x22, -0x08, 0x18, 0x80, 0x03, 0xD2, 0x03, 0x80, 0x18, -0x00, 0x14, 0x20, 0x83, 0x1A, 0x20, 0x38, 0x5E, -0x1A, 0x21, 0x61, 0x5E, 0x43, 0x00, 0xC0, 0x18, -0x08, 0x18, 0x80, 0x03, 0x80, 0x18, 0x00, 0x14, -0x60, 0x83, 0x39, 0x46, 0x20, 0x46, 0x28, 0x31, -0x28, 0x30, 0x0A, 0x22, 0xFA, 0xF7, 0x04, 0xFC, -0x26, 0x46, 0x20, 0x36, 0x30, 0x7A, 0x40, 0x07, -0x03, 0xD5, 0x00, 0x20, 0x55, 0x49, 0x30, 0x72, -0x08, 0x70, 0x30, 0x7A, 0xC1, 0x07, 0x66, 0xD0, -0x2C, 0x23, 0x2A, 0x22, 0x0E, 0x21, 0x0C, 0x20, -0xE3, 0x5E, 0xA2, 0x5E, 0x79, 0x5E, 0x38, 0x5E, -0x00, 0xF0, 0xB4, 0xF8, 0x00, 0x90, 0x30, 0x23, -0x2E, 0x22, 0x0E, 0x21, 0x0C, 0x20, 0xE3, 0x5E, -0xA2, 0x5E, 0x79, 0x5E, 0x38, 0x5E, 0x00, 0xF0, -0xA9, 0xF8, 0x46, 0x49, 0x92, 0x22, 0x09, 0x68, -0x53, 0x5C, 0x00, 0x9A, 0x93, 0x42, 0x1E, 0xD3, -0xFD, 0x28, 0x03, 0xD9, 0x44, 0x48, 0x00, 0x78, -0x00, 0x28, 0x18, 0xD0, 0x08, 0x46, 0xB0, 0x30, -0x42, 0x7A, 0x03, 0x7A, 0x10, 0x02, 0x18, 0x43, -0x82, 0x08, 0x40, 0x48, 0x00, 0x88, 0x82, 0x42, -0x03, 0xD2, 0x3C, 0x48, 0x00, 0x78, 0x00, 0x28, -0x09, 0xD0, 0x30, 0x79, 0x01, 0x28, 0x5B, 0xD1, -0xB0, 0x79, 0x00, 0x28, 0x58, 0xD1, 0x30, 0x7A, -0x02, 0x21, 0x08, 0x43, 0x53, 0xE0, 0x00, 0x20, -0x30, 0x71, 0x30, 0x7A, 0x04, 0x22, 0x10, 0x43, -0x30, 0x72, 0x35, 0x4A, 0x03, 0x20, 0x10, 0x70, -0xB0, 0x31, 0x48, 0x7A, 0x09, 0x7A, 0x00, 0x02, -0x08, 0x43, 0x30, 0x49, 0xE2, 0x7A, 0x09, 0x88, -0x88, 0x42, 0x41, 0xD9, 0x2E, 0x20, 0x00, 0xE0, -0x39, 0xE0, 0x38, 0x5E, 0x2D, 0x4B, 0x81, 0x01, -0x30, 0x20, 0x42, 0x43, 0xD0, 0x18, 0x81, 0x60, -0xC1, 0x60, 0x0C, 0x21, 0x61, 0x5E, 0x99, 0x50, -0x41, 0x60, 0x30, 0x21, 0x79, 0x5E, 0x89, 0x01, -0x81, 0x61, 0xC1, 0x61, 0x0E, 0x21, 0x61, 0x5E, -0x01, 0x61, 0x41, 0x61, 0x28, 0xE0, 0x00, 0x28, -0x26, 0xD1, 0x21, 0x4F, 0x03, 0x20, 0x38, 0x70, -0x2C, 0x23, 0x2A, 0x22, 0x0E, 0x21, 0x0C, 0x20, -0xE3, 0x5E, 0xA2, 0x5E, 0x61, 0x5E, 0x20, 0x5E, -0x00, 0xF0, 0x48, 0xF8, 0x15, 0x49, 0x09, 0x68, -0x80, 0x31, 0x89, 0x7C, 0x49, 0x08, 0x81, 0x42, -0x0E, 0xD9, 0x70, 0x7A, 0x40, 0x1C, 0xC0, 0xB2, -0x70, 0x72, 0x0A, 0x28, 0x0C, 0xD9, 0x00, 0x20, -0x70, 0x72, 0x83, 0x21, 0x31, 0x72, 0x01, 0x21, -0x31, 0x71, 0x70, 0x71, 0x39, 0x70, 0x03, 0xE0, -0x00, 0x20, 0x70, 0x72, 0x02, 0x20, 0x30, 0x72, -0x6D, 0x1C, 0xED, 0xB2, 0x03, 0x4A, 0x10, 0x78, -0xA8, 0x42, 0x00, 0xD9, 0x24, 0xE7, 0xFA, 0xF7, -0xB9, 0xFE, 0xF8, 0xBD, 0x7C, 0x07, 0x00, 0x20, -0x18, 0x00, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, -0x1A, 0x00, 0x00, 0x20, 0xAC, 0x04, 0x00, 0x20, -0x1C, 0x08, 0x00, 0x20, 0x51, 0x07, 0x00, 0x20, -0x34, 0x00, 0x00, 0x20, 0x14, 0x00, 0x00, 0x20, -0x10, 0x00, 0x00, 0x20, 0x38, 0x01, 0x00, 0x20, -0x10, 0xB5, 0x03, 0x48, 0x00, 0x78, 0x00, 0x06, -0x01, 0xD4, 0xFF, 0xF7, 0x01, 0xFB, 0x10, 0xBD, -0x8E, 0x02, 0x00, 0x20, 0x10, 0xB5, 0xFF, 0xF7, -0xA3, 0xFC, 0x10, 0xBD, 0x10, 0x1A, 0x59, 0x1A, -0x40, 0x43, 0x49, 0x43, 0xC2, 0x17, 0xCB, 0x17, -0x08, 0x18, 0x53, 0x41, 0x00, 0x22, 0xD2, 0x43, -0x00, 0x21, 0x12, 0x1A, 0x99, 0x41, 0x01, 0xD2, -0x00, 0x20, 0xC0, 0x43, 0x70, 0x47, 0x05, 0x01, -0x09, 0x04, 0x08, 0x02, 0x06, 0x0A, 0x00, 0x00, -0x00, 0x00, 0x00, 0x40, 0x10, 0x03, 0x42, 0x88, -0x0C, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, -0x00, 0x09, 0x00, 0x50, 0x1F, 0x00, 0x00, 0x00, -0x04, 0x03, 0x00, 0x50, 0x64, 0x00, 0x00, 0x00, -0x00, 0x03, 0x00, 0x50, 0x77, 0x00, 0x01, 0x48, -0x44, 0x09, 0x00, 0x50, 0x39, 0x5A, 0x5B, 0x00, -0x10, 0x06, 0x00, 0x50, 0x00, 0x00, 0x06, 0x07, -0x00, 0x06, 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, -0x08, 0x06, 0x00, 0x50, 0x0C, 0x30, 0x00, 0x00, -0x28, 0x06, 0x00, 0x50, 0x06, 0x00, 0x00, 0x00, -0x2C, 0x06, 0x00, 0x50, 0x0A, 0x66, 0x00, 0x00, -0x30, 0x06, 0x00, 0x50, 0xCC, 0x02, 0x00, 0x20, -0x34, 0x06, 0x00, 0x50, 0x00, 0x20, 0x00, 0x00, -0x44, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00, -0x1C, 0x00, 0x00, 0x50, 0x50, 0x71, 0x02, 0x00, -0x20, 0x00, 0x00, 0x50, 0x24, 0x29, 0x00, 0x00, -0x14, 0x00, 0x00, 0x40, 0x04, 0x04, 0x00, 0x00, -0x00, 0x00, 0x00, 0x40, 0x14, 0x33, 0x43, 0xC8, -0x0C, 0x00, 0x00, 0x40, 0x29, 0x0A, 0x00, 0x00, -0x04, 0x00, 0x00, 0x40, 0x10, 0x32, 0x00, 0x00, -0x1C, 0x0E, 0x00, 0x50, 0x03, 0x00, 0x00, 0x00, -0x54, 0x00, 0x00, 0x50, 0x14, 0x07, 0x00, 0x00, -0x28, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, -0x2C, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, -0x58, 0x10, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00, -0x04, 0x11, 0x00, 0x50, 0x78, 0x11, 0x00, 0x00, -0x0C, 0x11, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, -0x10, 0x11, 0x00, 0x50, 0x78, 0x01, 0x00, 0x00, -0x14, 0x11, 0x00, 0x50, 0xC8, 0x03, 0x60, 0x00, -0x18, 0x10, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, -0x1C, 0x10, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, -0x4C, 0x00, 0x00, 0x50, 0x31, 0x00, 0x00, 0x00, -0x3C, 0x10, 0x00, 0x50, 0x00, 0x00, 0x10, 0x00, -0xB4, 0x10, 0x00, 0x50, 0x00, 0x26, 0x31, 0x08, -0xC0, 0x10, 0x00, 0x50, 0x33, 0x03, 0x33, 0x03, -0xC4, 0x10, 0x00, 0x50, 0x33, 0x03, 0x33, 0x03, -0xC8, 0x10, 0x00, 0x50, 0x0C, 0x0A, 0x00, 0x00, -0xCC, 0x10, 0x00, 0x50, 0x1A, 0x00, 0x00, 0x00, -0xD0, 0x10, 0x00, 0x50, 0x03, 0x19, 0x19, 0x00, -0xF0, 0x11, 0x00, 0x50, 0x12, 0x00, 0x00, 0x00, -0xEC, 0x11, 0x00, 0x50, 0x5C, 0x00, 0x00, 0x00, -0xF4, 0x11, 0x00, 0x50, 0x01, 0x00, 0x01, 0x00, -0x2C, 0x10, 0x00, 0x50, 0x10, 0x00, 0x90, 0x00, -0x30, 0x10, 0x00, 0x50, 0x20, 0x0C, 0x90, 0x00, -0x34, 0x10, 0x00, 0x50, 0x30, 0x0C, 0x30, 0x0C, -0x38, 0x10, 0x00, 0x50, 0xFF, 0x0F, 0x00, 0x00, -0x7C, 0x10, 0x00, 0x50, 0x88, 0x88, 0xFE, 0x88, -0x80, 0x10, 0x00, 0x50, 0x88, 0xFF, 0x00, 0x00, -0x84, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55, -0x88, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55, -0x8C, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55, -0xE8, 0x10, 0x00, 0x50, 0x3F, 0x16, 0x3F, 0x15, -0x04, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, -0xC0, 0x64, 0x00, 0x00, 0x04, 0x00, 0x00, 0x20, -0x08, 0x00, 0x00, 0x00, 0x5C, 0x0A, 0x00, 0x00, -0xC8, 0x64, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x20, -0xC4, 0x0D, 0x00, 0x00, 0x6A, 0x0A, 0x00, 0x00, -0xFF, 0x00, 0x00, 0x00, 0x78, 0x7F, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0x9C, 0xD7, 0x90, 0x66, + 0xD0, 0x0C, 0x00, 0x20, 0xB1, 0x08, 0x00, 0x00, + 0xB9, 0x08, 0x00, 0x00, 0x95, 0x08, 0x00, 0x00, + 0xBD, 0x08, 0x00, 0x00, 0xBF, 0x08, 0x00, 0x00, + 0xC1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC3, 0x08, 0x00, 0x00, + 0xC5, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC7, 0x08, 0x00, 0x00, 0x81, 0x0F, 0x00, 0x00, + 0xE1, 0x0C, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00, + 0xCB, 0x08, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x00, + 0xA1, 0x0D, 0x00, 0x00, 0x41, 0x0A, 0x00, 0x00, + 0xCB, 0x08, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00, + 0xCB, 0x08, 0x00, 0x00, 0x41, 0x0B, 0x00, 0x00, + 0xE1, 0x0A, 0x00, 0x00, 0xA1, 0x0A, 0x00, 0x00, + 0xC1, 0x0A, 0x00, 0x00, 0x81, 0x0A, 0x00, 0x00, + 0xCB, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, + 0x03, 0x48, 0x85, 0x46, 0x00, 0xF0, 0x80, 0xF8, + 0x00, 0x48, 0x00, 0x47, 0x19, 0x44, 0x00, 0x00, + 0xD0, 0x0C, 0x00, 0x20, 0x04, 0x20, 0x71, 0x46, + 0x08, 0x42, 0x02, 0xD0, 0xEF, 0xF3, 0x09, 0x80, + 0x01, 0xE0, 0xEF, 0xF3, 0x08, 0x80, 0x71, 0x46, + 0x00, 0x4A, 0x10, 0x47, 0xC1, 0x42, 0x00, 0x00, + 0x06, 0x48, 0x80, 0x47, 0x06, 0x48, 0x00, 0x47, + 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, + 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, + 0xFE, 0xE7, 0xFE, 0xE7, 0x15, 0x0A, 0x00, 0x00, + 0x81, 0x08, 0x00, 0x00, 0x30, 0xB5, 0x0B, 0x46, + 0x01, 0x46, 0x00, 0x20, 0x20, 0x22, 0x01, 0x24, + 0x09, 0xE0, 0x0D, 0x46, 0xD5, 0x40, 0x9D, 0x42, + 0x05, 0xD3, 0x1D, 0x46, 0x95, 0x40, 0x49, 0x1B, + 0x25, 0x46, 0x95, 0x40, 0x40, 0x19, 0x15, 0x46, + 0x52, 0x1E, 0x00, 0x2D, 0xF1, 0xDC, 0x30, 0xBD, + 0x70, 0xB5, 0x00, 0x24, 0x25, 0x46, 0x00, 0x28, + 0x01, 0xDA, 0x01, 0x24, 0x40, 0x42, 0x00, 0x29, + 0x01, 0xDA, 0x01, 0x25, 0x49, 0x42, 0xFF, 0xF7, + 0xDD, 0xFF, 0xAC, 0x42, 0x00, 0xD0, 0x40, 0x42, + 0x00, 0x2C, 0x00, 0xD0, 0x49, 0x42, 0x70, 0xBD, + 0x03, 0x46, 0x0B, 0x43, 0x9B, 0x07, 0x03, 0xD0, + 0x09, 0xE0, 0x08, 0xC9, 0x12, 0x1F, 0x08, 0xC0, + 0x04, 0x2A, 0xFA, 0xD2, 0x03, 0xE0, 0x0B, 0x78, + 0x49, 0x1C, 0x03, 0x70, 0x40, 0x1C, 0x52, 0x1E, + 0xF9, 0xD2, 0x70, 0x47, 0xD2, 0xB2, 0x01, 0xE0, + 0x02, 0x70, 0x40, 0x1C, 0x49, 0x1E, 0xFB, 0xD2, + 0x70, 0x47, 0x00, 0x22, 0xF6, 0xE7, 0x10, 0xB5, + 0x04, 0x46, 0x08, 0x46, 0x11, 0x46, 0x02, 0x46, + 0x20, 0x46, 0xFF, 0xF7, 0xEF, 0xFF, 0x20, 0x46, + 0x10, 0xBD, 0x00, 0x1D, 0x03, 0x21, 0x40, 0x1E, + 0x03, 0x78, 0x12, 0x02, 0x1A, 0x43, 0x49, 0x1E, + 0xF9, 0xD5, 0x10, 0x46, 0x70, 0x47, 0x00, 0x00, + 0x06, 0x4C, 0x01, 0x25, 0x06, 0x4E, 0x05, 0xE0, + 0x20, 0x46, 0xE3, 0x68, 0x07, 0xC8, 0x2B, 0x43, + 0x98, 0x47, 0x10, 0x34, 0xB4, 0x42, 0xF7, 0xD3, + 0xFF, 0xF7, 0x72, 0xFF, 0x58, 0x5C, 0x00, 0x00, + 0x78, 0x5C, 0x00, 0x00, 0xC1, 0x06, 0xC9, 0x0E, + 0x01, 0x20, 0x88, 0x40, 0x01, 0x49, 0x08, 0x60, + 0x70, 0x47, 0x00, 0x00, 0x00, 0xE1, 0x00, 0xE0, + 0x0B, 0x49, 0x10, 0xB5, 0x88, 0x42, 0x01, 0xD9, + 0x01, 0x20, 0x10, 0xBD, 0x01, 0x02, 0x09, 0x0A, + 0x08, 0x48, 0x49, 0x1E, 0x41, 0x61, 0x08, 0x49, + 0x07, 0x23, 0xCA, 0x69, 0x12, 0x02, 0x12, 0x0A, + 0x04, 0x04, 0x22, 0x43, 0xCA, 0x61, 0x00, 0x21, + 0x81, 0x61, 0x03, 0x61, 0x08, 0x46, 0x10, 0xBD, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xE0, 0x00, 0xE0, + 0x04, 0xED, 0x00, 0xE0, 0x70, 0x47, 0x00, 0x00, + 0x03, 0x49, 0x02, 0x20, 0x08, 0x60, 0x02, 0x49, + 0x80, 0x39, 0x08, 0x60, 0x70, 0x47, 0x00, 0x00, + 0x80, 0xE1, 0x00, 0xE0, 0x62, 0xB6, 0x02, 0x48, + 0x00, 0x21, 0x01, 0x60, 0x70, 0x47, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x40, 0x30, 0xB4, 0x74, 0x46, + 0x64, 0x1E, 0x25, 0x78, 0x64, 0x1C, 0xAB, 0x42, + 0x00, 0xD2, 0x1D, 0x46, 0x63, 0x5D, 0x5B, 0x00, + 0xE3, 0x18, 0x30, 0xBC, 0x18, 0x47, 0x00, 0x00, + 0x05, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, + 0x01, 0x20, 0x80, 0x07, 0x01, 0x6A, 0x03, 0x22, + 0xD2, 0x03, 0x11, 0x43, 0x01, 0x62, 0x70, 0x47, + 0xD0, 0x00, 0x00, 0x20, 0x02, 0xE0, 0x08, 0xC8, + 0x12, 0x1F, 0x08, 0xC1, 0x00, 0x2A, 0xFA, 0xD1, + 0x70, 0x47, 0x00, 0x20, 0x01, 0xE0, 0x01, 0xC1, + 0x12, 0x1F, 0x00, 0x2A, 0xFB, 0xD1, 0x70, 0x47, + 0x01, 0x21, 0x89, 0x07, 0x48, 0x60, 0x70, 0x47, + 0x01, 0x20, 0x80, 0x07, 0x41, 0x6A, 0x82, 0x13, + 0x11, 0x43, 0x41, 0x62, 0x41, 0x6A, 0xC2, 0x13, + 0x11, 0x43, 0x41, 0x62, 0x70, 0x47, 0x10, 0xB5, + 0xFF, 0xF7, 0xEE, 0xFF, 0x10, 0xBD, 0x00, 0x00, + 0x03, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, + 0x02, 0x49, 0x01, 0x20, 0xC8, 0x60, 0x70, 0x47, + 0x08, 0x03, 0x00, 0x20, 0x00, 0x03, 0x00, 0x50, + 0x10, 0xB5, 0x02, 0xF0, 0x8F, 0xF8, 0x10, 0xBD, + 0x70, 0x47, 0x00, 0x00, 0x04, 0x49, 0x06, 0x22, + 0x00, 0x28, 0x08, 0x68, 0x01, 0xD0, 0x10, 0x43, + 0x00, 0xE0, 0x90, 0x43, 0x08, 0x60, 0x70, 0x47, + 0x00, 0x09, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x47, 0x00, 0x00, 0x64, 0x20, 0x05, 0x49, + 0x02, 0xE0, 0x00, 0xBF, 0x40, 0x1E, 0xC0, 0xB2, + 0x4A, 0x68, 0x12, 0x06, 0x01, 0xD5, 0x00, 0x28, + 0xF7, 0xD1, 0x70, 0x47, 0x00, 0x06, 0x00, 0x50, + 0x00, 0xB5, 0x07, 0x48, 0x01, 0x69, 0x02, 0x29, + 0x06, 0xD1, 0x01, 0x61, 0x05, 0x49, 0x8A, 0x78, + 0x00, 0x2A, 0x02, 0xD0, 0x02, 0xF0, 0x28, 0xFB, + 0x00, 0xBD, 0x01, 0x20, 0xC8, 0x70, 0x00, 0xBD, + 0x00, 0x02, 0x00, 0x50, 0x5C, 0x04, 0x00, 0x20, + 0x04, 0x49, 0x00, 0x20, 0x08, 0x60, 0x01, 0x20, + 0x80, 0x07, 0x41, 0x68, 0x42, 0x14, 0x11, 0x43, + 0x41, 0x60, 0x70, 0x47, 0xD0, 0x00, 0x00, 0x20, + 0x30, 0xB5, 0x1E, 0x4B, 0x58, 0x68, 0x99, 0x68, + 0x00, 0x28, 0x01, 0xDA, 0xDA, 0x04, 0x5A, 0x60, + 0x1B, 0x4A, 0x15, 0x68, 0x01, 0x24, 0x05, 0x40, + 0x00, 0x2D, 0x02, 0xD1, 0x50, 0x68, 0x08, 0x42, + 0x0F, 0xD0, 0x18, 0x48, 0x04, 0x70, 0x00, 0xF0, + 0x6F, 0xF9, 0x14, 0x72, 0x16, 0x48, 0x00, 0x78, + 0x00, 0x28, 0x06, 0xD0, 0x18, 0x68, 0x01, 0x21, + 0x00, 0x09, 0x00, 0x01, 0x89, 0x02, 0x08, 0x43, + 0x18, 0x60, 0x58, 0x68, 0x80, 0x02, 0x02, 0xD5, + 0x01, 0x20, 0x40, 0x05, 0x58, 0x60, 0x58, 0x68, + 0x00, 0x04, 0x03, 0xD5, 0x01, 0x20, 0xC0, 0x03, + 0x58, 0x60, 0x94, 0x72, 0x58, 0x68, 0x80, 0x00, + 0x03, 0xD5, 0x01, 0x20, 0x40, 0x07, 0x58, 0x60, + 0xD4, 0x72, 0x58, 0x68, 0x40, 0x00, 0x03, 0xD5, + 0x01, 0x20, 0x80, 0x07, 0x58, 0x60, 0x94, 0x72, + 0x30, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, + 0x50, 0x02, 0x00, 0x20, 0x99, 0x01, 0x00, 0x20, + 0xCF, 0x00, 0x00, 0x20, 0x30, 0xB5, 0x07, 0x49, + 0x07, 0x4B, 0x0A, 0x68, 0x00, 0x20, 0x41, 0x00, + 0xCD, 0x18, 0x54, 0x5A, 0x2D, 0x88, 0x40, 0x1C, + 0x64, 0x1B, 0xC0, 0xB2, 0x54, 0x52, 0x30, 0x28, + 0xF5, 0xD3, 0x30, 0xBD, 0xF8, 0x02, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x20, 0x10, 0xB5, 0x02, 0xF0, + 0x09, 0xF9, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00, + 0x70, 0xB5, 0x2B, 0x4B, 0x05, 0x20, 0x19, 0x7A, + 0x40, 0x04, 0x02, 0x29, 0x4C, 0xD1, 0x29, 0x4C, + 0x21, 0x78, 0x00, 0x29, 0x45, 0xD1, 0x28, 0x49, + 0x0A, 0x78, 0x00, 0x2A, 0x44, 0xD0, 0x00, 0x22, + 0x0A, 0x70, 0x27, 0x49, 0x25, 0x4A, 0x09, 0x78, + 0x26, 0x4D, 0x05, 0xE0, 0x16, 0x78, 0x81, 0x2E, + 0x0A, 0xD0, 0x00, 0x29, 0x08, 0xD1, 0x40, 0x1E, + 0x2E, 0x78, 0x00, 0x2E, 0x04, 0xD0, 0x00, 0x28, + 0x02, 0xD0, 0x1E, 0x7A, 0x02, 0x2E, 0xF1, 0xD0, + 0x18, 0x7A, 0x02, 0x28, 0x26, 0xD1, 0x10, 0x78, + 0x81, 0x28, 0x23, 0xD0, 0x00, 0x29, 0x21, 0xD1, + 0xFF, 0xF7, 0x44, 0xFF, 0x1A, 0x48, 0x40, 0x68, + 0x40, 0x01, 0x40, 0x0D, 0xBC, 0x28, 0x01, 0xD9, + 0xBC, 0x38, 0x80, 0xB2, 0x17, 0x49, 0x49, 0x68, + 0x09, 0x06, 0x0D, 0xD4, 0x16, 0x49, 0x09, 0x88, + 0x88, 0x42, 0x03, 0xD3, 0x15, 0x49, 0x09, 0x88, + 0x88, 0x42, 0x05, 0xD9, 0x01, 0x20, 0x80, 0x07, + 0x81, 0x68, 0x01, 0x22, 0x11, 0x43, 0x81, 0x60, + 0x00, 0x20, 0x00, 0xBF, 0x40, 0x1C, 0xC0, 0xB2, + 0x14, 0x28, 0xFA, 0xD3, 0x20, 0x78, 0x00, 0x28, + 0x02, 0xD0, 0x20, 0x78, 0x40, 0x1E, 0x20, 0x70, + 0xFF, 0xF7, 0x3E, 0xFF, 0x70, 0xBD, 0x00, 0x00, + 0x50, 0x02, 0x00, 0x20, 0xEC, 0x02, 0x00, 0x20, + 0x99, 0x01, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, + 0x72, 0x04, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, + 0x00, 0x11, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50, + 0x60, 0x02, 0x00, 0x20, 0x62, 0x02, 0x00, 0x20, + 0x10, 0xB5, 0x02, 0xF0, 0xF1, 0xF9, 0x10, 0xBD, + 0x70, 0xB5, 0x01, 0x25, 0xAD, 0x07, 0xE8, 0x68, + 0x40, 0x08, 0x40, 0x00, 0xE8, 0x60, 0x1B, 0x48, + 0x41, 0x68, 0x01, 0x26, 0x49, 0x07, 0x00, 0x29, + 0x2A, 0xDA, 0x42, 0x68, 0x04, 0x21, 0x0A, 0x43, + 0x42, 0x60, 0x17, 0x48, 0x00, 0x68, 0x17, 0x4C, + 0x40, 0x05, 0x40, 0x0F, 0x20, 0x73, 0x20, 0x7B, + 0x06, 0x28, 0x00, 0xD1, 0x21, 0x73, 0x20, 0x7B, + 0x02, 0x28, 0x1D, 0xD0, 0x20, 0x7B, 0x06, 0x28, + 0x05, 0xD8, 0x20, 0x7B, 0x04, 0x28, 0x02, 0xD3, + 0x01, 0x20, 0x01, 0xF0, 0xA1, 0xF9, 0x20, 0x7B, + 0x0D, 0x49, 0x09, 0x78, 0x88, 0x42, 0x0B, 0xD0, + 0x0C, 0x4C, 0xA0, 0x79, 0x00, 0x28, 0x01, 0xD0, + 0x01, 0xF0, 0xF8, 0xF9, 0x0A, 0x48, 0x01, 0x78, + 0x31, 0x43, 0x01, 0x70, 0x81, 0x20, 0x20, 0x70, + 0xE8, 0x68, 0x30, 0x43, 0xE8, 0x60, 0x70, 0xBD, + 0x00, 0x20, 0xE6, 0xE7, 0x40, 0x00, 0x00, 0x50, + 0x00, 0x11, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, + 0xE5, 0x02, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, + 0xCE, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x08, 0x4A, + 0x00, 0x21, 0x12, 0x68, 0x60, 0x32, 0x12, 0x78, + 0x52, 0x1C, 0xD4, 0xB2, 0x4A, 0x00, 0x83, 0x5E, + 0x49, 0x1C, 0x63, 0x43, 0x1B, 0x11, 0xC9, 0xB2, + 0x83, 0x52, 0x30, 0x29, 0xF6, 0xD3, 0x10, 0xBD, + 0xB8, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x10, 0xB5, 0x14, 0x48, 0x80, 0x69, 0x40, 0x04, + 0x23, 0xD5, 0x13, 0x4C, 0xE0, 0x68, 0xA2, 0x68, + 0x02, 0x40, 0xE0, 0x68, 0xA1, 0x68, 0x88, 0x43, + 0xE0, 0x60, 0x10, 0x07, 0x03, 0xD5, 0x08, 0x20, + 0xE0, 0x60, 0x04, 0xF0, 0x3F, 0xFB, 0x50, 0x07, + 0x0E, 0xD5, 0x04, 0x20, 0xE0, 0x60, 0x04, 0xF0, + 0x39, 0xFB, 0x02, 0xF0, 0xFD, 0xFC, 0x00, 0x28, + 0x06, 0xD1, 0x08, 0x48, 0x00, 0x78, 0x00, 0x28, + 0x02, 0xD0, 0x01, 0x20, 0xFF, 0xF7, 0x68, 0xFE, + 0xFF, 0x20, 0xF3, 0x30, 0xE0, 0x60, 0x01, 0x20, + 0xE0, 0x60, 0x10, 0xBD, 0x00, 0x09, 0x00, 0x50, + 0x00, 0x05, 0x00, 0x50, 0x90, 0x02, 0x00, 0x20, + 0x00, 0xB5, 0x03, 0x46, 0x00, 0x20, 0xFF, 0xF7, + 0x5D, 0xFE, 0x04, 0x49, 0x01, 0x20, 0x09, 0x68, + 0x0A, 0x88, 0x9A, 0x43, 0x0A, 0x80, 0xFF, 0xF7, + 0x55, 0xFE, 0x00, 0xBD, 0x08, 0x00, 0x00, 0x20, + 0x01, 0x28, 0x05, 0xD0, 0x02, 0x28, 0x05, 0xD0, + 0x04, 0x28, 0x06, 0xD0, 0x00, 0x20, 0x70, 0x47, + 0x03, 0x48, 0x70, 0x47, 0x02, 0x48, 0xC0, 0x30, + 0x70, 0x47, 0x02, 0x48, 0x70, 0x47, 0x00, 0x00, + 0x78, 0x7C, 0x00, 0x00, 0xF8, 0x7D, 0x00, 0x00, + 0x03, 0x48, 0x02, 0x49, 0x41, 0x60, 0x03, 0x49, + 0x81, 0x60, 0x70, 0x47, 0x1F, 0x1F, 0x5F, 0x1F, + 0x00, 0x10, 0x00, 0x50, 0x1F, 0x1F, 0x1F, 0x1F, + 0xF8, 0xB5, 0x2B, 0x48, 0x80, 0x69, 0x40, 0x04, + 0x51, 0xD5, 0x2A, 0x4D, 0xA8, 0x6A, 0xE9, 0x68, + 0xAC, 0x68, 0x0C, 0x40, 0xE9, 0x68, 0xAA, 0x68, + 0x91, 0x43, 0xE9, 0x60, 0x26, 0x4E, 0xC0, 0x07, + 0x15, 0xD0, 0x68, 0x69, 0x89, 0x27, 0xC0, 0xB2, + 0xEF, 0x60, 0xAA, 0x6A, 0x01, 0x21, 0x0A, 0x43, + 0xAA, 0x62, 0x0B, 0x28, 0x03, 0xD2, 0x31, 0x70, + 0x01, 0xF0, 0x04, 0xF8, 0x03, 0xE0, 0x00, 0x21, + 0x31, 0x70, 0x00, 0xF0, 0xDB, 0xFF, 0xBC, 0x43, + 0x01, 0x20, 0xFF, 0xF7, 0x05, 0xFE, 0x1B, 0x48, + 0xA1, 0x04, 0x04, 0xD5, 0x29, 0x6A, 0x81, 0x43, + 0x29, 0x62, 0x41, 0x14, 0xE9, 0x60, 0xE1, 0x04, + 0x05, 0xD5, 0x29, 0x6A, 0x81, 0x43, 0x29, 0x62, + 0x01, 0x20, 0x00, 0x03, 0xE8, 0x60, 0xA0, 0x05, + 0x02, 0xD5, 0x01, 0x20, 0x40, 0x02, 0xE8, 0x60, + 0x20, 0x07, 0x09, 0xD5, 0x30, 0x78, 0x01, 0x28, + 0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, 0xBA, 0xFF, + 0x64, 0x08, 0x64, 0x00, 0x09, 0x20, 0xE8, 0x60, + 0x60, 0x07, 0x07, 0xD5, 0x30, 0x78, 0x01, 0x28, + 0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, 0xAE, 0xFF, + 0x05, 0x20, 0xE8, 0x60, 0xFF, 0x20, 0xF3, 0x30, + 0xE8, 0x60, 0x01, 0x20, 0xE8, 0x60, 0xF8, 0xBD, + 0x00, 0x09, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50, + 0x9B, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, + 0x70, 0xB5, 0x41, 0x18, 0x49, 0x1E, 0x64, 0x24, + 0x09, 0x04, 0x0B, 0x4D, 0x01, 0x43, 0x69, 0x63, + 0xE8, 0x68, 0x81, 0x21, 0x09, 0x06, 0x08, 0x43, + 0xE8, 0x60, 0x02, 0xE0, 0x01, 0x20, 0xFF, 0xF7, + 0xCB, 0xFE, 0xE8, 0x68, 0xC0, 0x01, 0x04, 0xD5, + 0x20, 0x46, 0x64, 0x1E, 0xA4, 0xB2, 0x00, 0x28, + 0xF4, 0xD1, 0xA8, 0x6B, 0x70, 0xBD, 0x00, 0x00, + 0x40, 0x09, 0x00, 0x50, 0x04, 0x49, 0x29, 0x20, + 0xC8, 0x60, 0x04, 0x49, 0x35, 0x20, 0x08, 0x63, + 0x04, 0x20, 0x01, 0x07, 0x88, 0x60, 0x70, 0x47, + 0x40, 0x00, 0x00, 0x50, 0x80, 0x10, 0x00, 0x50, + 0x10, 0xB5, 0x00, 0xF0, 0x09, 0xF8, 0x10, 0xBD, + 0x02, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, + 0x70, 0x47, 0x00, 0x00, 0x80, 0x04, 0x00, 0x20, + 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, 0x82, 0x14, + 0x11, 0x43, 0x41, 0x60, 0x70, 0x47, 0x00, 0x00, + 0x05, 0x20, 0x00, 0x07, 0x82, 0x69, 0x0C, 0x49, + 0x00, 0x2A, 0x07, 0xDA, 0x82, 0x69, 0xC3, 0x00, + 0x92, 0x00, 0x92, 0x08, 0xD2, 0x18, 0x82, 0x61, + 0x01, 0x22, 0x4A, 0x72, 0x82, 0x69, 0x52, 0x00, + 0x08, 0xD5, 0x82, 0x69, 0x01, 0x23, 0x92, 0x00, + 0x92, 0x08, 0x9B, 0x07, 0xD2, 0x18, 0x82, 0x61, + 0x00, 0x20, 0x48, 0x72, 0x70, 0x47, 0x00, 0x00, + 0x84, 0x04, 0x00, 0x20, 0xF0, 0xB5, 0x00, 0x20, + 0x84, 0x46, 0x91, 0x48, 0x87, 0xB0, 0x00, 0x68, + 0x90, 0x49, 0xB0, 0x30, 0x00, 0x90, 0x00, 0x20, + 0x08, 0x5E, 0x8F, 0x49, 0x01, 0x90, 0x00, 0x20, + 0x08, 0x5E, 0x02, 0x90, 0x0B, 0xE1, 0x34, 0x21, + 0x48, 0x43, 0x81, 0x19, 0x0C, 0x46, 0x20, 0x34, + 0x60, 0x7D, 0xFF, 0x28, 0x31, 0xD1, 0x00, 0x22, + 0x00, 0x20, 0x08, 0xE0, 0x34, 0x25, 0x45, 0x43, + 0xAD, 0x19, 0x20, 0x35, 0x6D, 0x7D, 0x95, 0x42, + 0x03, 0xD0, 0x40, 0x1C, 0xC0, 0xB2, 0x83, 0x42, + 0xF4, 0xD8, 0x83, 0x42, 0x01, 0xD1, 0x62, 0x75, + 0x03, 0xE0, 0x52, 0x1C, 0xD2, 0xB2, 0x02, 0x2A, + 0xEA, 0xD3, 0x0A, 0x25, 0x4D, 0x5F, 0x30, 0x20, + 0x7C, 0x4E, 0x42, 0x43, 0xAB, 0x01, 0x90, 0x19, + 0x83, 0x60, 0xC3, 0x60, 0xB5, 0x50, 0x0C, 0x22, + 0x45, 0x60, 0x8A, 0x5E, 0x93, 0x01, 0x83, 0x61, + 0x02, 0x61, 0xC3, 0x61, 0x42, 0x61, 0x0E, 0x22, + 0x8A, 0x5E, 0x93, 0x01, 0x83, 0x62, 0x02, 0x62, + 0xC3, 0x62, 0x42, 0x62, 0x60, 0x7D, 0xFF, 0x28, + 0x7E, 0xD0, 0x71, 0x4A, 0xC7, 0xB2, 0x12, 0x78, + 0xC8, 0x69, 0x00, 0x2A, 0x11, 0xD1, 0x00, 0x9A, + 0xD3, 0x79, 0x94, 0x79, 0x1A, 0x02, 0x22, 0x43, + 0x82, 0x42, 0x01, 0xD2, 0x02, 0x22, 0x09, 0xE0, + 0x00, 0x9A, 0x53, 0x7A, 0x14, 0x7A, 0x1A, 0x02, + 0x22, 0x43, 0x82, 0x42, 0x01, 0xD2, 0x03, 0x22, + 0x00, 0xE0, 0x04, 0x22, 0x65, 0x4D, 0x28, 0x78, + 0x82, 0x42, 0x28, 0xD0, 0x82, 0x42, 0x02, 0xD3, + 0x13, 0x1A, 0x02, 0x2B, 0x23, 0xDA, 0x62, 0x4C, + 0x23, 0x78, 0x00, 0x2B, 0x14, 0xD0, 0x61, 0x4E, + 0x33, 0x78, 0x9A, 0x42, 0x02, 0xD0, 0x00, 0x23, + 0x23, 0x70, 0x32, 0x70, 0x22, 0x78, 0x5D, 0x4B, + 0x52, 0x1C, 0xD2, 0xB2, 0x22, 0x70, 0x1B, 0x78, + 0x98, 0x42, 0x08, 0xD9, 0x01, 0x2A, 0x06, 0xD9, + 0x00, 0x20, 0x20, 0x70, 0x2B, 0x70, 0x0A, 0xE0, + 0x56, 0x4B, 0x1A, 0x70, 0xEE, 0xE7, 0x98, 0x42, + 0x05, 0xD2, 0x02, 0x2A, 0x03, 0xD9, 0x00, 0x22, + 0x22, 0x70, 0x40, 0x1C, 0x28, 0x70, 0x28, 0x78, + 0x02, 0x28, 0x6B, 0xD0, 0x03, 0x28, 0x6D, 0xD0, + 0x29, 0x22, 0x64, 0x23, 0x05, 0x24, 0x3E, 0x46, + 0x30, 0x20, 0x46, 0x43, 0x47, 0x48, 0x30, 0x18, + 0xC5, 0x68, 0x03, 0x95, 0x87, 0x68, 0x5D, 0x43, + 0x57, 0x43, 0xED, 0x1B, 0x20, 0x35, 0xAD, 0x11, + 0xAE, 0x46, 0x0A, 0x25, 0x4D, 0x5F, 0x41, 0x4F, + 0x04, 0x95, 0xBF, 0x59, 0xEF, 0x19, 0x45, 0x68, + 0x6D, 0x00, 0x7F, 0x19, 0x67, 0x43, 0xBF, 0x1C, + 0xBF, 0x10, 0x75, 0x46, 0x7D, 0x19, 0xAE, 0x46, + 0xC5, 0x69, 0x87, 0x69, 0x5D, 0x43, 0x57, 0x43, + 0xED, 0x1B, 0x20, 0x35, 0xAD, 0x11, 0x06, 0x95, + 0x0C, 0x25, 0x4D, 0x5F, 0x05, 0x95, 0x07, 0x69, + 0xEF, 0x19, 0x45, 0x69, 0x6D, 0x00, 0x7D, 0x19, + 0x65, 0x43, 0xAD, 0x1C, 0xAF, 0x10, 0x00, 0xE0, + 0x4D, 0xE0, 0x06, 0x9D, 0x7F, 0x19, 0xC5, 0x6A, + 0x5D, 0x43, 0x83, 0x6A, 0x53, 0x43, 0xEA, 0x1A, + 0x0E, 0x23, 0x20, 0x32, 0xCB, 0x5E, 0x95, 0x11, + 0x06, 0x93, 0x02, 0x6A, 0x9A, 0x18, 0x43, 0x6A, + 0x5B, 0x00, 0xD2, 0x18, 0x62, 0x43, 0x92, 0x1C, + 0x92, 0x10, 0x52, 0x19, 0x03, 0x9D, 0x85, 0x60, + 0x75, 0x46, 0xC5, 0x60, 0xC3, 0x69, 0xC7, 0x61, + 0x83, 0x61, 0xC3, 0x6A, 0x83, 0x62, 0xC2, 0x62, + 0x20, 0x4C, 0x43, 0x68, 0xA3, 0x51, 0x04, 0x9D, + 0x45, 0x60, 0x43, 0x69, 0x03, 0x61, 0x05, 0x9D, + 0x45, 0x61, 0x43, 0x6A, 0x03, 0x62, 0x06, 0x9B, + 0x43, 0x62, 0x75, 0x46, 0x01, 0x9C, 0xA8, 0x11, + 0xBB, 0x11, 0x92, 0x11, 0xA0, 0x42, 0x09, 0xDB, + 0x60, 0x1E, 0x0A, 0xE0, 0x1C, 0x22, 0x4D, 0x23, + 0x0F, 0x24, 0x94, 0xE7, 0x22, 0x22, 0x58, 0x23, + 0x0A, 0x24, 0x90, 0xE7, 0x00, 0x28, 0x00, 0xDA, + 0x00, 0x20, 0x02, 0x9C, 0xA3, 0x42, 0x01, 0xDB, + 0x63, 0x1E, 0x02, 0xE0, 0x00, 0x2B, 0x00, 0xDA, + 0x00, 0x23, 0x00, 0x2A, 0x00, 0xDA, 0x00, 0x22, + 0x48, 0x81, 0x8B, 0x81, 0xCA, 0x81, 0x60, 0x46, + 0x40, 0x1C, 0xC0, 0xB2, 0x84, 0x46, 0x0C, 0x4E, + 0x60, 0x46, 0x33, 0x78, 0x63, 0x45, 0x00, 0xD9, + 0xED, 0xE6, 0x07, 0xB0, 0xF0, 0xBD, 0x00, 0x00, + 0xB8, 0x02, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20, + 0x44, 0x00, 0x00, 0x20, 0x38, 0x01, 0x00, 0x20, + 0x2A, 0x00, 0x00, 0x20, 0x4A, 0x00, 0x00, 0x20, + 0x49, 0x00, 0x00, 0x20, 0x4B, 0x00, 0x00, 0x20, + 0xA0, 0x04, 0x00, 0x20, 0xF0, 0xB5, 0x93, 0xB0, + 0x02, 0xF0, 0x62, 0xFD, 0x0F, 0x90, 0x8D, 0x48, + 0x00, 0x25, 0x41, 0x5F, 0x0F, 0x98, 0x81, 0x42, + 0x7D, 0xDD, 0x8B, 0x48, 0x30, 0x21, 0x00, 0x68, + 0x06, 0x7E, 0x40, 0x7E, 0x11, 0x90, 0x02, 0xA8, + 0xFF, 0xF7, 0x73, 0xFB, 0x87, 0x48, 0x00, 0x78, + 0x06, 0x28, 0x71, 0xD2, 0x01, 0x20, 0x10, 0x90, + 0x08, 0xA8, 0x0E, 0x90, 0x34, 0x20, 0x29, 0x46, + 0x41, 0x43, 0x83, 0x48, 0x00, 0x27, 0x0C, 0x18, + 0x27, 0x76, 0x10, 0x98, 0x00, 0x28, 0x04, 0xD0, + 0x7C, 0x48, 0x01, 0x88, 0x7F, 0x48, 0x01, 0x80, + 0x09, 0xE0, 0x01, 0xA9, 0x68, 0x46, 0x0F, 0x9A, + 0x04, 0xF0, 0x58, 0xF8, 0x00, 0x28, 0x57, 0xD0, + 0x10, 0x98, 0x00, 0x28, 0x0E, 0xD0, 0x7A, 0x48, + 0x31, 0x46, 0x00, 0x88, 0x10, 0x90, 0xFF, 0xF7, + 0x09, 0xFB, 0x69, 0x46, 0x08, 0x70, 0x10, 0x99, + 0x70, 0x43, 0x08, 0x1A, 0x69, 0x46, 0x08, 0x71, + 0x00, 0x20, 0x10, 0x90, 0x0F, 0x98, 0x02, 0xAA, + 0x83, 0xB2, 0x01, 0xA9, 0x68, 0x46, 0x02, 0xF0, + 0x65, 0xFD, 0x70, 0x49, 0x88, 0x42, 0x7D, 0xD0, + 0x6C, 0x4A, 0x00, 0x21, 0x51, 0x5E, 0x61, 0x86, + 0xE0, 0x81, 0x0A, 0x22, 0x50, 0x43, 0xFF, 0xF7, + 0x03, 0xFB, 0x34, 0x21, 0x08, 0x55, 0xFF, 0x20, + 0x20, 0x71, 0x00, 0x20, 0xE0, 0x61, 0x68, 0x46, + 0x01, 0x79, 0x02, 0xAA, 0x01, 0x20, 0x02, 0xF0, + 0xC7, 0xFC, 0x65, 0x49, 0x60, 0x81, 0x0A, 0x78, + 0x00, 0x2A, 0x0B, 0xD0, 0x63, 0x4A, 0x00, 0x23, + 0xD3, 0x5E, 0x63, 0x4A, 0x12, 0x78, 0x9B, 0x1A, + 0x98, 0x42, 0x03, 0xDA, 0x90, 0x42, 0x01, 0xDD, + 0x00, 0x22, 0x0A, 0x70, 0x6A, 0x46, 0x12, 0x79, + 0x00, 0x2A, 0x12, 0xD0, 0x73, 0x1E, 0x9A, 0x42, + 0x28, 0xD1, 0x5B, 0x4A, 0x00, 0x27, 0x12, 0x78, + 0x53, 0x00, 0xD2, 0x18, 0x93, 0x08, 0x57, 0x4A, + 0xD7, 0x5F, 0xFA, 0x1A, 0x90, 0x42, 0x14, 0xDD, + 0x01, 0x20, 0x08, 0x70, 0x14, 0xE0, 0x7E, 0xE0, + 0x87, 0xE0, 0x53, 0x4A, 0x12, 0x78, 0x53, 0x00, + 0xD2, 0x18, 0x92, 0x08, 0x90, 0x42, 0x02, 0xDA, + 0x01, 0x20, 0x08, 0x70, 0x02, 0xE0, 0x08, 0x78, + 0x00, 0x28, 0x0B, 0xD0, 0x02, 0xAA, 0x01, 0x21, + 0x04, 0xE0, 0x08, 0x78, 0x00, 0x28, 0x05, 0xD0, + 0x02, 0xAA, 0x00, 0x21, 0x01, 0x20, 0x02, 0xF0, + 0xEF, 0xFB, 0x60, 0x81, 0x68, 0x46, 0x01, 0x78, + 0x00, 0x20, 0x0E, 0x9A, 0x02, 0xF0, 0x80, 0xFC, + 0x44, 0x49, 0xA0, 0x81, 0x0A, 0x78, 0x00, 0x2A, + 0x0B, 0xD0, 0x43, 0x4A, 0x00, 0x23, 0xD3, 0x5E, + 0x42, 0x4A, 0x12, 0x78, 0x9B, 0x1A, 0x98, 0x42, + 0x03, 0xDA, 0x90, 0x42, 0x01, 0xDD, 0x00, 0x22, + 0x0A, 0x70, 0x6A, 0x46, 0x12, 0x78, 0x00, 0x2A, + 0x13, 0xD0, 0x11, 0x9B, 0x5B, 0x1E, 0x9A, 0x42, + 0x28, 0xD1, 0x3A, 0x4A, 0x12, 0x78, 0x53, 0x00, + 0xD2, 0x18, 0x93, 0x08, 0x36, 0x4A, 0x00, 0x27, + 0xD7, 0x5F, 0x00, 0xE0, 0x37, 0xE0, 0xFA, 0x1A, + 0x90, 0x42, 0x12, 0xDD, 0x01, 0x20, 0x08, 0x70, + 0x12, 0xE0, 0x32, 0x4A, 0x12, 0x78, 0x53, 0x00, + 0xD2, 0x18, 0x92, 0x08, 0x90, 0x42, 0x02, 0xDA, + 0x01, 0x20, 0x08, 0x70, 0x02, 0xE0, 0x08, 0x78, + 0x00, 0x28, 0x0B, 0xD0, 0x01, 0x21, 0x0E, 0x9A, + 0x04, 0xE0, 0x08, 0x78, 0x00, 0x28, 0x05, 0xD0, + 0x0E, 0x9A, 0x00, 0x21, 0x00, 0x20, 0x02, 0xF0, + 0xA7, 0xFB, 0xA0, 0x81, 0x07, 0x98, 0xA0, 0x82, + 0x0D, 0x98, 0xE0, 0x82, 0x68, 0x46, 0x00, 0x79, + 0x00, 0x28, 0x0A, 0xD0, 0x71, 0x1E, 0x88, 0x42, + 0x07, 0xD0, 0x68, 0x46, 0x00, 0x78, 0x00, 0x28, + 0x03, 0xD0, 0x11, 0x99, 0x49, 0x1E, 0x88, 0x42, + 0x01, 0xD1, 0x01, 0x20, 0x00, 0xE0, 0x00, 0x20, + 0x6D, 0x1C, 0x20, 0x76, 0xED, 0xB2, 0x03, 0x2D, + 0x00, 0xD2, 0x13, 0xE7, 0x09, 0xE0, 0x0E, 0x49, + 0x00, 0x20, 0x08, 0x70, 0x16, 0x48, 0x40, 0x88, + 0x0A, 0x28, 0x02, 0xD9, 0x00, 0x20, 0x13, 0xB0, + 0xF0, 0xBD, 0x08, 0x48, 0x00, 0x78, 0x03, 0x28, + 0x03, 0xD3, 0x07, 0x49, 0x00, 0x20, 0x08, 0x70, + 0x01, 0xE0, 0x05, 0x48, 0x05, 0x70, 0x01, 0x20, + 0xF1, 0xE7, 0x00, 0x00, 0x24, 0x00, 0x00, 0x20, + 0xB8, 0x02, 0x00, 0x20, 0x73, 0x04, 0x00, 0x20, + 0xA0, 0x04, 0x00, 0x20, 0x28, 0x00, 0x00, 0x20, + 0x32, 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0xFF, + 0xCC, 0x00, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20, + 0x2E, 0x00, 0x00, 0x20, 0xCD, 0x00, 0x00, 0x20, + 0x44, 0x00, 0x00, 0x20, 0x2F, 0x00, 0x00, 0x20, + 0x84, 0x04, 0x00, 0x20, 0xF0, 0xB5, 0x8E, 0x46, + 0x02, 0x21, 0x4B, 0x00, 0xC3, 0x5E, 0x00, 0x2B, + 0x03, 0xDD, 0x49, 0x1E, 0x09, 0xB2, 0x00, 0x29, + 0xF7, 0xDA, 0x49, 0x1C, 0x00, 0x27, 0x0B, 0xB2, + 0x9C, 0x46, 0x3C, 0x46, 0x01, 0x25, 0x0E, 0xE0, + 0x5E, 0x00, 0x86, 0x5F, 0x00, 0x2E, 0x02, 0xDC, + 0x5B, 0x1E, 0x1B, 0xB2, 0x09, 0xE0, 0x31, 0x46, + 0x69, 0x43, 0x6D, 0x1C, 0xCF, 0x19, 0x34, 0x19, + 0x5B, 0x1C, 0x2D, 0xB2, 0x1B, 0xB2, 0x05, 0x2B, + 0xEE, 0xDB, 0x61, 0x46, 0x59, 0x1A, 0x49, 0x1C, + 0x66, 0x08, 0x63, 0x46, 0x00, 0x25, 0x51, 0x61, + 0x06, 0xE0, 0x59, 0x00, 0x41, 0x5E, 0x4D, 0x19, + 0xB5, 0x42, 0x03, 0xD8, 0x5B, 0x1C, 0x1B, 0xB2, + 0x05, 0x2B, 0xF6, 0xDB, 0x5E, 0x00, 0x81, 0x5F, + 0x69, 0x1A, 0x11, 0x60, 0x80, 0x5F, 0x50, 0x60, + 0x60, 0x1B, 0xD4, 0x60, 0x90, 0x60, 0x60, 0x46, + 0x18, 0x1A, 0x60, 0x43, 0x38, 0x1A, 0x10, 0x61, + 0x70, 0x46, 0x00, 0x78, 0x9B, 0x1E, 0xC0, 0x18, + 0x71, 0x46, 0x08, 0x70, 0x20, 0xB2, 0xF0, 0xBD, + 0xF8, 0xB5, 0x8D, 0x4E, 0x00, 0x27, 0x30, 0x78, + 0x3C, 0x46, 0x00, 0x28, 0x01, 0xD0, 0x03, 0xF0, + 0xE7, 0xF9, 0x00, 0x20, 0x05, 0x46, 0x89, 0x4A, + 0x0D, 0xE0, 0x34, 0x21, 0x41, 0x43, 0x89, 0x18, + 0x0B, 0x79, 0x1B, 0x06, 0x01, 0xD5, 0xCD, 0x71, + 0x03, 0xE0, 0xCB, 0x79, 0x5B, 0x1C, 0xCB, 0x71, + 0x01, 0x27, 0x40, 0x1C, 0xC0, 0xB2, 0x11, 0x78, + 0x81, 0x42, 0xEE, 0xD8, 0x7F, 0x49, 0x30, 0x78, + 0x09, 0x78, 0x88, 0x42, 0x01, 0xD1, 0x00, 0x2F, + 0x02, 0xD0, 0x03, 0xF0, 0x63, 0xFD, 0x0E, 0xE0, + 0xB0, 0x70, 0x00, 0x20, 0x34, 0x21, 0x41, 0x43, + 0x89, 0x19, 0x8D, 0x71, 0x40, 0x1C, 0xCD, 0x71, + 0xC0, 0xB2, 0x0D, 0x72, 0x03, 0x28, 0xF5, 0xD3, + 0x74, 0x48, 0x75, 0x70, 0x45, 0x70, 0x30, 0x78, + 0xB1, 0x78, 0x88, 0x42, 0x01, 0xD0, 0x03, 0xF0, + 0x61, 0xFB, 0x02, 0xF0, 0x89, 0xFE, 0x70, 0x49, + 0x70, 0x4A, 0x08, 0x88, 0x10, 0x80, 0x84, 0x46, + 0x00, 0x20, 0x08, 0x80, 0x6A, 0x49, 0x6E, 0x4D, + 0x0A, 0x78, 0x2B, 0xE0, 0x34, 0x23, 0x43, 0x43, + 0x5E, 0x18, 0x69, 0x4B, 0xF1, 0x69, 0x1F, 0x88, + 0xB9, 0x42, 0x00, 0xD9, 0x19, 0x80, 0x61, 0x46, + 0x00, 0x29, 0x0B, 0xD1, 0x29, 0x68, 0xB0, 0x31, + 0x4F, 0x7A, 0x09, 0x7A, 0x3F, 0x02, 0x0F, 0x43, + 0x19, 0x88, 0x8F, 0x42, 0x02, 0xD2, 0x63, 0x4B, + 0x01, 0x21, 0x19, 0x70, 0x29, 0x68, 0x20, 0x36, + 0xA0, 0x31, 0x33, 0x7D, 0x49, 0x78, 0x8B, 0x42, + 0x17, 0xD9, 0x5F, 0x4B, 0x01, 0x24, 0x19, 0x78, + 0x49, 0x1C, 0xC9, 0xB2, 0x19, 0x70, 0x02, 0x29, + 0x02, 0xD9, 0x5C, 0x4B, 0x01, 0x21, 0x19, 0x70, + 0x40, 0x1C, 0xC0, 0xB2, 0x52, 0x49, 0x82, 0x42, + 0xD0, 0xD8, 0x59, 0x49, 0x00, 0x2C, 0x08, 0x78, + 0x06, 0xD0, 0x64, 0x28, 0x08, 0xD2, 0x40, 0x1C, + 0x05, 0xE0, 0x00, 0x21, 0x52, 0x4B, 0xEE, 0xE7, + 0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, 0x08, 0x70, + 0x03, 0x20, 0x81, 0x1A, 0x34, 0x20, 0x48, 0x4E, + 0x41, 0x43, 0x42, 0x43, 0x90, 0x19, 0x00, 0x1D, + 0xFF, 0xF7, 0x6F, 0xF9, 0x00, 0x24, 0x45, 0x4F, + 0x0F, 0xE0, 0x38, 0x78, 0x34, 0x21, 0x00, 0x19, + 0x48, 0x43, 0xC1, 0x19, 0x30, 0x78, 0x34, 0x22, + 0x00, 0x19, 0x50, 0x43, 0x80, 0x19, 0x09, 0x1D, + 0x00, 0x1D, 0xFF, 0xF7, 0x45, 0xF9, 0x64, 0x1C, + 0xE4, 0xB2, 0x71, 0x78, 0xA1, 0x42, 0xEC, 0xD8, + 0x32, 0x78, 0x00, 0x20, 0x53, 0x18, 0x0E, 0xE0, + 0x34, 0x22, 0x42, 0x43, 0x91, 0x19, 0x06, 0x22, + 0x42, 0x43, 0xD2, 0x19, 0xA0, 0x32, 0x4C, 0x89, + 0x14, 0x80, 0x8C, 0x89, 0x54, 0x80, 0xC9, 0x89, + 0x40, 0x1C, 0x91, 0x80, 0xC0, 0xB2, 0x31, 0x46, + 0x83, 0x42, 0xED, 0xD8, 0x00, 0x20, 0x37, 0x4C, + 0x01, 0x22, 0x20, 0x80, 0x0C, 0xE0, 0x34, 0x26, + 0x46, 0x43, 0x76, 0x18, 0x36, 0x79, 0x77, 0x06, + 0x7F, 0x0E, 0x16, 0x46, 0xBE, 0x40, 0x27, 0x88, + 0x3E, 0x43, 0x40, 0x1C, 0x26, 0x80, 0xC0, 0xB2, + 0x83, 0x42, 0xF0, 0xD8, 0x23, 0x88, 0x00, 0x20, + 0x16, 0x46, 0x32, 0x46, 0x82, 0x40, 0x1A, 0x42, + 0x05, 0xD0, 0x40, 0x1C, 0xC0, 0xB2, 0x02, 0x28, + 0xF7, 0xD3, 0x00, 0x26, 0x37, 0xE0, 0x28, 0x4A, + 0x10, 0x70, 0xFA, 0xE7, 0x31, 0x46, 0x34, 0x22, + 0x51, 0x43, 0x0C, 0x18, 0x27, 0x46, 0x20, 0x37, + 0x38, 0x7C, 0x02, 0x28, 0x29, 0xD2, 0x0C, 0x23, + 0x0A, 0x22, 0x2E, 0x21, 0x2C, 0x20, 0xE3, 0x5E, + 0xA2, 0x5E, 0x61, 0x5E, 0x20, 0x5E, 0x04, 0xF0, + 0x95, 0xF9, 0x29, 0x68, 0xB0, 0x31, 0xCA, 0x7B, + 0x8B, 0x7B, 0x11, 0x02, 0x19, 0x43, 0x81, 0x42, + 0x14, 0xD2, 0x38, 0x7C, 0x2C, 0x22, 0x0A, 0x23, + 0xA2, 0x5E, 0x02, 0x21, 0xE3, 0x5E, 0x09, 0x1A, + 0x4A, 0x43, 0x43, 0x43, 0xD2, 0x18, 0x52, 0x10, + 0x62, 0x81, 0x2E, 0x22, 0xA2, 0x5E, 0x4A, 0x43, + 0x0C, 0x21, 0x61, 0x5E, 0x41, 0x43, 0x50, 0x18, + 0x40, 0x10, 0xA0, 0x81, 0x38, 0x7C, 0x40, 0x1C, + 0x38, 0x74, 0x76, 0x1C, 0xF6, 0xB2, 0x02, 0x48, + 0x01, 0x78, 0xB1, 0x42, 0xC6, 0xD8, 0xF8, 0xBD, + 0xA0, 0x04, 0x00, 0x20, 0x40, 0x05, 0x00, 0x20, + 0x34, 0x00, 0x00, 0x20, 0x36, 0x00, 0x00, 0x20, + 0xB8, 0x02, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, + 0x46, 0x00, 0x00, 0x20, 0x47, 0x00, 0x00, 0x20, + 0x2A, 0x00, 0x00, 0x20, 0x3E, 0x00, 0x00, 0x20, + 0x31, 0x00, 0x00, 0x20, 0xF1, 0xB5, 0x59, 0x48, + 0x8A, 0xB0, 0x00, 0x68, 0x08, 0x90, 0x1B, 0x30, + 0x57, 0x4B, 0x09, 0x90, 0x00, 0x20, 0x18, 0x5E, + 0xC1, 0x0F, 0x08, 0x18, 0xC0, 0x03, 0x01, 0x0C, + 0x08, 0x98, 0x00, 0x91, 0x90, 0x30, 0x41, 0x7B, + 0x02, 0x7B, 0x08, 0x02, 0x10, 0x43, 0x00, 0x99, + 0x07, 0x90, 0x88, 0x42, 0x00, 0xD9, 0x00, 0x90, + 0x4F, 0x48, 0x4E, 0x4A, 0x02, 0x80, 0x4F, 0x49, + 0xD0, 0x43, 0x08, 0x80, 0x4E, 0x49, 0x00, 0x23, + 0x0A, 0x80, 0x49, 0x49, 0x1E, 0x46, 0x08, 0x80, + 0x4C, 0x49, 0x0B, 0x70, 0x01, 0x21, 0xC9, 0x03, + 0x74, 0x00, 0x01, 0xA8, 0x01, 0x53, 0x48, 0x1E, + 0x03, 0xA9, 0x08, 0x53, 0x00, 0x20, 0x01, 0x46, + 0x35, 0x46, 0x1C, 0xE0, 0x09, 0x9A, 0x52, 0x5D, + 0x41, 0x2A, 0x16, 0xD0, 0x0A, 0x9A, 0x6B, 0x00, + 0xD2, 0x5E, 0x01, 0xAB, 0x49, 0x1C, 0x1F, 0x5F, + 0x80, 0x18, 0xC9, 0xB2, 0x97, 0x42, 0x00, 0xDA, + 0x1A, 0x53, 0x03, 0xAB, 0x1F, 0x5F, 0x97, 0x42, + 0x00, 0xDD, 0x1A, 0x53, 0x00, 0x9B, 0x9A, 0x42, + 0x03, 0xDD, 0x3A, 0x4B, 0x1A, 0x78, 0x52, 0x1C, + 0x1A, 0x70, 0x2D, 0x1D, 0xED, 0xB2, 0x30, 0x2D, + 0xE0, 0xD3, 0x00, 0x29, 0x02, 0xD0, 0xFF, 0xF7, + 0x4B, 0xF8, 0x00, 0xE0, 0x00, 0x20, 0x05, 0xA9, + 0x08, 0x53, 0x2D, 0x49, 0x01, 0xAD, 0x00, 0x22, + 0x28, 0x5F, 0x8A, 0x5E, 0x90, 0x42, 0x00, 0xDD, + 0x08, 0x80, 0x2D, 0x49, 0x03, 0xAF, 0x00, 0x22, + 0x38, 0x5F, 0x8A, 0x5E, 0x90, 0x42, 0x00, 0xDA, + 0x08, 0x80, 0x76, 0x1C, 0xF6, 0xB2, 0x04, 0x2E, + 0xB8, 0xD3, 0x07, 0x98, 0x26, 0x49, 0x43, 0x08, + 0x00, 0x20, 0x08, 0x5E, 0x26, 0x4A, 0x83, 0x42, + 0x06, 0xDA, 0x1F, 0x49, 0x00, 0x23, 0xCB, 0x5E, + 0x19, 0x1A, 0x07, 0x98, 0x81, 0x42, 0x06, 0xDB, + 0x08, 0x98, 0x80, 0x30, 0x81, 0x7B, 0x1F, 0x48, + 0x00, 0x78, 0x81, 0x42, 0x01, 0xD9, 0x01, 0x20, + 0x00, 0xE0, 0x00, 0x20, 0x10, 0x70, 0x00, 0x24, + 0x05, 0xAE, 0x60, 0x00, 0x31, 0x5E, 0x00, 0x91, + 0x3B, 0x5E, 0x2A, 0x5E, 0x21, 0x46, 0x0A, 0x98, + 0x00, 0xF0, 0x5C, 0xF9, 0x64, 0x1C, 0xE4, 0xB2, + 0x04, 0x2C, 0xF2, 0xD3, 0x0D, 0x48, 0x00, 0x68, + 0x90, 0x30, 0x41, 0x7B, 0x02, 0x7B, 0x08, 0x02, + 0x10, 0x43, 0x0E, 0x49, 0x00, 0x22, 0x8A, 0x5E, + 0x82, 0x42, 0x0D, 0xDA, 0x0F, 0x48, 0x41, 0x88, + 0x0A, 0x29, 0x09, 0xD9, 0x40, 0x88, 0x0B, 0x28, + 0x03, 0xD1, 0x80, 0x21, 0x0C, 0x48, 0xFF, 0xF7, + 0x20, 0xF8, 0x00, 0x20, 0x0B, 0xB0, 0xF0, 0xBD, + 0x01, 0x20, 0xFB, 0xE7, 0xB8, 0x02, 0x00, 0x20, + 0x7C, 0x04, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, + 0x76, 0x04, 0x00, 0x20, 0x78, 0x04, 0x00, 0x20, + 0x7A, 0x04, 0x00, 0x20, 0x8F, 0x02, 0x00, 0x20, + 0x9A, 0x01, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, + 0xF4, 0x05, 0x00, 0x20, 0xF0, 0xB5, 0x00, 0x20, + 0x41, 0x1F, 0x8C, 0x46, 0x1E, 0x49, 0x05, 0x24, + 0x09, 0x78, 0xC9, 0x07, 0x36, 0xD1, 0x1D, 0x49, + 0x09, 0x68, 0x90, 0x31, 0x4A, 0x7B, 0x0B, 0x7B, + 0x11, 0x02, 0x1B, 0x4A, 0x19, 0x43, 0x13, 0x5E, + 0x99, 0x42, 0x06, 0xDA, 0x19, 0x49, 0x09, 0x88, + 0x00, 0x29, 0x01, 0xD0, 0x0A, 0x24, 0x00, 0xE0, + 0x5A, 0x24, 0x17, 0x4F, 0x17, 0x4D, 0x00, 0x26, + 0x17, 0x4A, 0x41, 0x00, 0x52, 0x5E, 0xC9, 0x19, + 0x00, 0x23, 0xCB, 0x5E, 0x9A, 0x42, 0x02, 0xDD, + 0x2A, 0x5C, 0x52, 0x1C, 0x03, 0xE0, 0x9A, 0x42, + 0x03, 0xDA, 0x2A, 0x5C, 0x52, 0x1E, 0x2A, 0x54, + 0x00, 0xE0, 0x2E, 0x54, 0x2A, 0x56, 0xA2, 0x42, + 0x02, 0xDB, 0x0A, 0x88, 0x52, 0x1C, 0x03, 0xE0, + 0x62, 0x45, 0x03, 0xDC, 0x0A, 0x88, 0x52, 0x1E, + 0x0A, 0x80, 0x2E, 0x54, 0x40, 0x1C, 0xC0, 0xB2, + 0x30, 0x28, 0xDD, 0xD3, 0xF0, 0xBD, 0x00, 0x00, + 0xEB, 0x02, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, + 0x78, 0x04, 0x00, 0x20, 0xF0, 0x02, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x20, 0x0C, 0x03, 0x00, 0x20, + 0xD4, 0x00, 0x00, 0x20, 0xFE, 0xB5, 0x04, 0x46, + 0x37, 0x48, 0x00, 0x78, 0x00, 0x28, 0x46, 0xD0, + 0x1E, 0x20, 0x00, 0x90, 0x40, 0x42, 0x01, 0x90, + 0x00, 0x29, 0x12, 0xD0, 0x33, 0x48, 0x00, 0x78, + 0x15, 0x28, 0x40, 0xD0, 0x32, 0x4B, 0x00, 0x20, + 0x41, 0x00, 0x5A, 0x5A, 0x40, 0x1C, 0xD5, 0x00, + 0xAA, 0x1A, 0x65, 0x5A, 0xC0, 0xB2, 0x52, 0x19, + 0x12, 0xB2, 0xD2, 0x10, 0x5A, 0x52, 0x30, 0x28, + 0xF2, 0xD3, 0x00, 0x25, 0x00, 0x21, 0x0A, 0x46, + 0x08, 0x46, 0x2A, 0x4E, 0x2B, 0x18, 0x36, 0x68, + 0x9E, 0x19, 0xF6, 0x7E, 0x41, 0x2E, 0x19, 0xD0, + 0x5F, 0x00, 0x25, 0x4B, 0xE6, 0x5F, 0xDB, 0x5F, + 0xB4, 0x46, 0xF3, 0x1A, 0xDE, 0x0F, 0xF3, 0x18, + 0x5B, 0x10, 0x00, 0x9E, 0x1B, 0xB2, 0xB3, 0x42, + 0x02, 0xDA, 0x66, 0x46, 0xF6, 0x1A, 0xE6, 0x53, + 0x00, 0x9E, 0xB3, 0x42, 0x04, 0xDA, 0x01, 0x9E, + 0xB3, 0x42, 0x01, 0xDD, 0xD2, 0x18, 0x12, 0xB2, + 0x49, 0x1C, 0xC9, 0xB2, 0x40, 0x1C, 0xC0, 0xB2, + 0x03, 0x28, 0xDA, 0xD3, 0x00, 0x29, 0x21, 0xD0, + 0x01, 0x29, 0x0A, 0xD0, 0x0A, 0xE0, 0x0F, 0x20, + 0x00, 0x90, 0x40, 0x42, 0xB7, 0xE7, 0x60, 0x22, + 0x21, 0x46, 0x11, 0x48, 0xFE, 0xF7, 0x48, 0xFF, + 0xC7, 0xE7, 0x02, 0x21, 0x10, 0x46, 0xFE, 0xF7, + 0x2F, 0xFF, 0x02, 0xB2, 0x0D, 0x4B, 0x00, 0x20, + 0x1E, 0x68, 0x29, 0x18, 0x8E, 0x19, 0xF6, 0x7E, + 0x41, 0x2E, 0x03, 0xD0, 0x49, 0x00, 0x66, 0x5A, + 0xB6, 0x1A, 0x66, 0x52, 0x40, 0x1C, 0xC0, 0xB2, + 0x03, 0x28, 0xF1, 0xD3, 0x2D, 0x1D, 0xED, 0xB2, + 0x30, 0x2D, 0xAF, 0xD3, 0xFE, 0xBD, 0x00, 0x00, + 0x85, 0x02, 0x00, 0x20, 0x70, 0x04, 0x00, 0x20, + 0x3C, 0x03, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, + 0x10, 0xB5, 0x21, 0x49, 0x1F, 0x48, 0xC8, 0x61, + 0x20, 0x48, 0x81, 0x68, 0x01, 0x22, 0x52, 0x02, + 0x11, 0x43, 0x81, 0x60, 0x1E, 0x4C, 0x20, 0x68, + 0x30, 0x21, 0x88, 0x43, 0x20, 0x60, 0x1D, 0x48, + 0x00, 0x78, 0x00, 0x28, 0x11, 0xD0, 0x1C, 0xA0, + 0x03, 0xF0, 0xA4, 0xFA, 0x1C, 0x48, 0x01, 0x68, + 0x4A, 0x06, 0x90, 0x21, 0x00, 0x2A, 0x02, 0x68, + 0x02, 0xDA, 0x0A, 0x43, 0x02, 0x60, 0x10, 0xBD, + 0x8A, 0x43, 0x02, 0x60, 0x17, 0x49, 0x41, 0x61, + 0x10, 0xBD, 0x17, 0xA0, 0x03, 0xF0, 0x92, 0xFA, + 0x05, 0x21, 0x17, 0x48, 0x09, 0x07, 0x48, 0x62, + 0x80, 0x07, 0x41, 0x6A, 0x82, 0x13, 0x11, 0x43, + 0x41, 0x62, 0x14, 0x49, 0x0A, 0x68, 0x03, 0x12, + 0x1A, 0x43, 0x0A, 0x60, 0xE2, 0x68, 0x09, 0x68, + 0x0A, 0x43, 0xE2, 0x60, 0x01, 0x68, 0x02, 0x14, + 0x11, 0x43, 0x01, 0x60, 0x0E, 0x49, 0x40, 0x14, + 0x08, 0x60, 0x10, 0xBD, 0x0B, 0x0B, 0x0B, 0x0B, + 0x40, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50, + 0x00, 0x10, 0x00, 0x50, 0xCF, 0x00, 0x00, 0x20, + 0x41, 0x53, 0x49, 0x0D, 0x0A, 0x00, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x50, 0x16, 0x00, 0x03, 0x00, + 0x41, 0x53, 0x54, 0x0D, 0x0A, 0x00, 0x00, 0x00, + 0xF1, 0x2A, 0x02, 0x00, 0x50, 0x02, 0x00, 0x20, + 0x00, 0xE1, 0x00, 0xE0, 0xFF, 0xB5, 0x04, 0x46, + 0x55, 0x48, 0x1D, 0x46, 0x02, 0x68, 0x00, 0x21, + 0x17, 0x46, 0x1B, 0x32, 0x94, 0x46, 0x3A, 0x46, + 0x80, 0x32, 0x53, 0x7A, 0x16, 0x7A, 0x1A, 0x06, + 0x12, 0x14, 0x03, 0x23, 0x32, 0x43, 0xDB, 0x03, + 0x5A, 0x43, 0x12, 0x14, 0x96, 0x46, 0x4D, 0x4A, + 0x83, 0xB0, 0x16, 0x78, 0x3A, 0x46, 0x90, 0x32, + 0x57, 0x7B, 0x12, 0x7B, 0x3F, 0x02, 0x3A, 0x43, + 0x08, 0x46, 0x00, 0x2E, 0x03, 0xD1, 0x48, 0x4B, + 0x1B, 0x78, 0x14, 0x2B, 0x01, 0xD2, 0x53, 0x08, + 0x00, 0xE0, 0x93, 0x08, 0x00, 0x93, 0x45, 0x4B, + 0x76, 0x46, 0x1E, 0x80, 0x0C, 0x9B, 0x00, 0x2B, + 0x07, 0xDA, 0x43, 0x4E, 0x00, 0x27, 0xF7, 0x5F, + 0x00, 0x9E, 0xB7, 0x42, 0x01, 0xDA, 0x53, 0x42, + 0x01, 0xE0, 0x53, 0x08, 0x5B, 0x42, 0x0C, 0x9F, + 0x1B, 0xB2, 0x05, 0x9E, 0x01, 0x93, 0xF6, 0x1B, + 0x3B, 0x46, 0xEF, 0x1B, 0x00, 0x2E, 0x00, 0xDA, + 0x76, 0x42, 0x00, 0x2F, 0x00, 0xDA, 0x7F, 0x42, + 0xBE, 0x42, 0x01, 0xDA, 0x00, 0x2B, 0x15, 0xDD, + 0x00, 0x9B, 0x0C, 0x9E, 0xDF, 0x00, 0xF3, 0x1B, + 0xAB, 0x42, 0x06, 0xDD, 0x00, 0x2D, 0x04, 0xDA, + 0x05, 0x9B, 0x5B, 0x19, 0xD2, 0x18, 0x52, 0x10, + 0x01, 0xE0, 0x00, 0x9A, 0xAA, 0x18, 0x13, 0xB2, + 0x72, 0x46, 0x73, 0x45, 0x00, 0xDD, 0x13, 0x46, + 0x04, 0x9A, 0x19, 0xE0, 0x05, 0x9B, 0x9A, 0x1A, + 0x13, 0xB2, 0x72, 0x46, 0x52, 0x42, 0x93, 0x42, + 0x00, 0xDA, 0x13, 0xB2, 0x04, 0x9A, 0x1F, 0xE0, + 0x66, 0x46, 0xB6, 0x5C, 0x41, 0x2E, 0x09, 0xD0, + 0x56, 0x00, 0xA6, 0x5F, 0x9E, 0x42, 0x05, 0xDA, + 0x01, 0x9F, 0xBE, 0x42, 0x02, 0xDD, 0x30, 0x18, + 0x49, 0x1C, 0xC9, 0xB2, 0x12, 0x1D, 0xD2, 0xB2, + 0x30, 0x2A, 0xED, 0xD3, 0x0E, 0xE0, 0x66, 0x46, + 0xB6, 0x5C, 0x41, 0x2E, 0x06, 0xD0, 0x56, 0x00, + 0xA6, 0x5F, 0x9E, 0x42, 0x02, 0xDD, 0x30, 0x18, + 0x49, 0x1C, 0xC9, 0xB2, 0x12, 0x1D, 0xD2, 0xB2, + 0x30, 0x2A, 0xF0, 0xD3, 0x00, 0x29, 0x02, 0xD0, + 0xFE, 0xF7, 0x22, 0xFE, 0x01, 0x46, 0x04, 0x98, + 0x05, 0xE0, 0x42, 0x00, 0xA3, 0x5A, 0x5B, 0x1A, + 0x00, 0x1D, 0xA3, 0x52, 0xC0, 0xB2, 0x30, 0x28, + 0xF7, 0xD3, 0x0E, 0x4A, 0x00, 0x23, 0x68, 0x1A, + 0xD3, 0x5E, 0x00, 0xB2, 0x98, 0x42, 0x00, 0xDA, + 0x10, 0x80, 0x05, 0x98, 0x00, 0x22, 0x40, 0x1A, + 0x09, 0x49, 0x00, 0xB2, 0x8A, 0x5E, 0x90, 0x42, + 0x00, 0xDD, 0x08, 0x80, 0x07, 0xB0, 0xF0, 0xBD, + 0xB8, 0x02, 0x00, 0x20, 0x9A, 0x01, 0x00, 0x20, + 0xEA, 0x02, 0x00, 0x20, 0xEE, 0x02, 0x00, 0x20, + 0x7C, 0x04, 0x00, 0x20, 0x76, 0x04, 0x00, 0x20, + 0x78, 0x04, 0x00, 0x20, 0xF8, 0xB5, 0x06, 0x46, + 0x0D, 0x46, 0x30, 0x23, 0xFF, 0xF7, 0x80, 0xF8, + 0x04, 0x00, 0x28, 0xD0, 0x19, 0x4F, 0x00, 0x2A, + 0x21, 0xD0, 0x00, 0x20, 0xFE, 0xF7, 0xCA, 0xFE, + 0x02, 0xE0, 0x98, 0x00, 0x29, 0x58, 0x21, 0x50, + 0x18, 0x46, 0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, + 0xF7, 0xD1, 0x38, 0x68, 0x01, 0x88, 0x31, 0x43, + 0x01, 0x80, 0x11, 0x49, 0x09, 0x68, 0xCA, 0x79, + 0x8B, 0x79, 0x11, 0x02, 0x19, 0x43, 0x41, 0x80, + 0xC1, 0x21, 0x89, 0x00, 0x0D, 0x48, 0xFF, 0xF7, + 0xDF, 0xF8, 0x0D, 0x49, 0xC8, 0x63, 0x01, 0x20, + 0xFE, 0xF7, 0xAC, 0xFE, 0x0C, 0xE0, 0x38, 0x68, + 0x00, 0x88, 0x30, 0x40, 0x03, 0xD1, 0xF8, 0xBD, + 0x98, 0x00, 0x21, 0x58, 0x29, 0x50, 0x18, 0x46, + 0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, 0xF7, 0xD1, + 0x01, 0x20, 0xF8, 0xBD, 0x08, 0x00, 0x00, 0x20, + 0xB8, 0x02, 0x00, 0x20, 0x78, 0x7C, 0x00, 0x00, + 0x40, 0x7F, 0x00, 0x00, 0x70, 0xB5, 0x17, 0x48, + 0x5A, 0x25, 0x01, 0x78, 0x16, 0x48, 0x00, 0x29, + 0x25, 0xD0, 0x09, 0x21, 0x01, 0x70, 0x15, 0x4C, + 0x01, 0x21, 0xE0, 0x89, 0x89, 0x02, 0x88, 0x42, + 0x01, 0xD0, 0x03, 0x20, 0x60, 0x71, 0x11, 0x48, + 0x30, 0x21, 0x28, 0x30, 0xFE, 0xF7, 0xC9, 0xFD, + 0x00, 0x20, 0x60, 0x72, 0xA0, 0x71, 0x60, 0x62, + 0x16, 0x21, 0x21, 0x72, 0x0C, 0x49, 0x20, 0x71, + 0x08, 0x70, 0xA5, 0x81, 0x60, 0x81, 0x60, 0x8A, + 0x09, 0x21, 0x09, 0x03, 0x08, 0x43, 0x60, 0x82, + 0x03, 0x20, 0x40, 0x02, 0xE0, 0x61, 0x0D, 0x20, + 0xC0, 0x01, 0x20, 0x62, 0x70, 0xBD, 0x05, 0x70, + 0xD9, 0xE7, 0x00, 0x00, 0x85, 0x02, 0x00, 0x20, + 0xB5, 0x02, 0x00, 0x20, 0xE4, 0x02, 0x00, 0x20, + 0x87, 0x02, 0x00, 0x20, 0x0E, 0x48, 0x03, 0x21, + 0x41, 0x71, 0x0E, 0x49, 0x41, 0x61, 0x0D, 0x49, + 0x60, 0x31, 0x81, 0x61, 0x01, 0x21, 0x01, 0x70, + 0x07, 0x22, 0x42, 0x70, 0x0A, 0x4B, 0x05, 0x22, + 0x1A, 0x70, 0x0A, 0x4B, 0x1A, 0x70, 0x0A, 0x4B, + 0x55, 0x22, 0xDA, 0x70, 0x04, 0x22, 0x02, 0x82, + 0x00, 0x22, 0xC2, 0x70, 0x09, 0x22, 0x12, 0x03, + 0x42, 0x82, 0x81, 0x70, 0x70, 0x47, 0x00, 0x00, + 0xE4, 0x02, 0x00, 0x20, 0x00, 0x00, 0x04, 0x20, + 0x85, 0x02, 0x00, 0x20, 0x71, 0x04, 0x00, 0x20, + 0x90, 0x02, 0x00, 0x20, 0x04, 0x22, 0x0F, 0x49, + 0x0C, 0x28, 0x10, 0xD0, 0x8B, 0x05, 0x0D, 0x28, + 0x08, 0x6A, 0x10, 0xD0, 0x18, 0x43, 0x08, 0x62, + 0x88, 0x6A, 0x10, 0x43, 0x88, 0x62, 0x0A, 0x4A, + 0x01, 0x20, 0x10, 0x70, 0xC8, 0x68, 0xC8, 0x60, + 0x88, 0x6A, 0x88, 0x62, 0x70, 0x47, 0x08, 0x6A, + 0x40, 0x00, 0x40, 0x08, 0x00, 0xE0, 0x18, 0x43, + 0x08, 0x62, 0x88, 0x6A, 0x90, 0x43, 0x88, 0x62, + 0xF0, 0xE7, 0x00, 0x00, 0x00, 0x06, 0x00, 0x50, + 0x9B, 0x01, 0x00, 0x20, 0x00, 0xB5, 0x08, 0x49, + 0x0A, 0x28, 0x05, 0xD0, 0x07, 0x48, 0x00, 0x0C, + 0x48, 0x63, 0x07, 0x48, 0x08, 0x63, 0x00, 0xBD, + 0x06, 0x48, 0x00, 0x68, 0x08, 0x62, 0x0D, 0x20, + 0xFF, 0xF7, 0xCC, 0xFF, 0x00, 0xBD, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x50, 0xBC, 0x02, 0x00, 0x20, + 0xCC, 0x02, 0x00, 0x20, 0xC0, 0x02, 0x00, 0x20, + 0x10, 0xB5, 0x1B, 0x49, 0x00, 0x20, 0x03, 0x00, + 0xFE, 0xF7, 0xA0, 0xFD, 0x0C, 0x07, 0x0A, 0x0E, + 0x26, 0x26, 0x11, 0x14, 0x17, 0x1A, 0x1D, 0x20, + 0x23, 0x26, 0x16, 0x4A, 0x0A, 0x80, 0x1E, 0xE0, + 0x14, 0x4A, 0x12, 0x1D, 0x4A, 0x80, 0x1A, 0xE0, + 0x13, 0x4A, 0x8A, 0x80, 0x17, 0xE0, 0x13, 0x4A, + 0x4A, 0x81, 0x14, 0xE0, 0x12, 0x4A, 0x8A, 0x81, + 0x11, 0xE0, 0x12, 0x4A, 0xCA, 0x81, 0x0E, 0xE0, + 0x11, 0x4A, 0x0A, 0x82, 0x0B, 0xE0, 0x11, 0x4A, + 0x4A, 0x82, 0x08, 0xE0, 0x10, 0x4A, 0x8A, 0x82, + 0x05, 0xE0, 0x10, 0x4A, 0xCA, 0x82, 0x02, 0xE0, + 0x0F, 0x4A, 0x43, 0x00, 0xCA, 0x52, 0x40, 0x1C, + 0x80, 0xB2, 0x0C, 0x28, 0xCF, 0xD3, 0x0D, 0xA0, + 0x03, 0xF0, 0x88, 0xF8, 0x10, 0xBD, 0x00, 0x00, + 0xCC, 0x02, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, + 0x88, 0x02, 0x00, 0x20, 0x34, 0x01, 0x00, 0x20, + 0xE0, 0x06, 0x00, 0x20, 0xE4, 0x06, 0x00, 0x20, + 0xD8, 0x06, 0x00, 0x20, 0xC0, 0x02, 0x00, 0x20, + 0xBC, 0x02, 0x00, 0x20, 0x9C, 0x01, 0x00, 0x20, + 0xC4, 0x02, 0x00, 0x20, 0x49, 0x32, 0x43, 0x20, + 0x4F, 0x4B, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0x70, 0xB5, 0x01, 0x25, 0xCA, 0x07, 0x0A, 0xD0, + 0x00, 0x20, 0x70, 0xBD, 0x93, 0x00, 0xC4, 0x58, + 0x66, 0x1C, 0x02, 0xD0, 0x1B, 0x18, 0x5B, 0x68, + 0x23, 0x60, 0x92, 0x1C, 0x92, 0xB2, 0x8A, 0x42, + 0xF4, 0xD3, 0x28, 0x46, 0x70, 0xBD, 0x00, 0x00, + 0x10, 0xB5, 0x1D, 0x49, 0x0A, 0x68, 0x1C, 0x48, + 0x40, 0x30, 0x02, 0x61, 0x4A, 0x68, 0x42, 0x61, + 0x8A, 0x68, 0x82, 0x61, 0xC9, 0x68, 0xC1, 0x61, + 0x82, 0x69, 0xF0, 0x21, 0x8A, 0x43, 0x82, 0x61, + 0x82, 0x69, 0x0A, 0x43, 0x82, 0x61, 0x41, 0x69, + 0x49, 0x04, 0x04, 0xD5, 0x41, 0x69, 0x01, 0x22, + 0x52, 0x03, 0x89, 0x1A, 0x41, 0x61, 0x10, 0x48, + 0x40, 0x38, 0x01, 0x68, 0x01, 0x22, 0x12, 0x06, + 0x11, 0x43, 0x01, 0x60, 0x30, 0x21, 0x0D, 0x48, + 0xFF, 0xF7, 0xC6, 0xFF, 0x0B, 0x48, 0x40, 0x21, + 0xC0, 0x30, 0xFF, 0xF7, 0xC1, 0xFF, 0x0B, 0x20, + 0xFE, 0xF7, 0xD4, 0xFC, 0x03, 0x20, 0xFE, 0xF7, + 0xD1, 0xFC, 0x00, 0x20, 0xFE, 0xF7, 0xCE, 0xFC, + 0x05, 0x20, 0xFE, 0xF7, 0xCB, 0xFC, 0x09, 0x20, + 0xFE, 0xF7, 0xC8, 0xFC, 0x01, 0x20, 0x10, 0xBD, + 0x40, 0x14, 0x00, 0x50, 0x98, 0x5A, 0x00, 0x00, + 0x0E, 0x4A, 0x00, 0x21, 0x11, 0x60, 0x0E, 0x4A, + 0x20, 0x21, 0x11, 0x60, 0x5A, 0x28, 0x13, 0xD0, + 0x0C, 0x48, 0x01, 0x23, 0x00, 0x05, 0x00, 0x0D, + 0x5B, 0x03, 0xC0, 0x18, 0x05, 0x22, 0x12, 0x07, + 0x10, 0x62, 0x90, 0x00, 0xC2, 0x68, 0x0A, 0x43, + 0xC2, 0x60, 0x05, 0x4A, 0x80, 0x3A, 0x11, 0x60, + 0x41, 0x68, 0x19, 0x43, 0x41, 0x60, 0x70, 0x47, + 0xFF, 0x20, 0xEA, 0xE7, 0xD0, 0x00, 0x00, 0x20, + 0x80, 0xE1, 0x00, 0xE0, 0x24, 0x09, 0x00, 0x00, + 0x10, 0xB5, 0x17, 0x4A, 0x11, 0x78, 0x81, 0x42, + 0x29, 0xD0, 0x16, 0x49, 0x0B, 0x68, 0x01, 0x24, + 0xA4, 0x02, 0x23, 0x43, 0x0B, 0x60, 0x10, 0x70, + 0x05, 0x22, 0x40, 0x24, 0x12, 0x49, 0x12, 0x07, + 0x80, 0x23, 0x00, 0x28, 0x09, 0xD0, 0x08, 0x68, + 0x40, 0x06, 0x0C, 0xD4, 0x08, 0x68, 0x20, 0x43, + 0x08, 0x60, 0xD0, 0x68, 0x98, 0x43, 0xD0, 0x60, + 0x05, 0xE0, 0xD0, 0x68, 0x18, 0x43, 0xD0, 0x60, + 0x08, 0x68, 0xA0, 0x43, 0x08, 0x60, 0x08, 0x68, + 0x18, 0x43, 0x08, 0x60, 0xD0, 0x68, 0x01, 0x21, + 0xC9, 0x03, 0x08, 0x43, 0xD0, 0x60, 0xE1, 0x20, + 0x00, 0x02, 0x01, 0xF0, 0x61, 0xF8, 0x10, 0xBD, + 0x04, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, + 0x00, 0x11, 0x00, 0x50, 0x10, 0xB5, 0x10, 0x4C, + 0xA0, 0x79, 0x00, 0x28, 0x18, 0xD1, 0x00, 0xF0, + 0xA5, 0xFF, 0x00, 0xF0, 0xA7, 0xFE, 0xFF, 0xF7, + 0xF7, 0xFC, 0x0C, 0x48, 0x01, 0x78, 0x0C, 0x48, + 0x00, 0x29, 0x02, 0xD0, 0x09, 0x21, 0x09, 0x02, + 0x00, 0xE0, 0x0A, 0x49, 0x01, 0x60, 0x01, 0x20, + 0xA0, 0x71, 0xE0, 0x71, 0x08, 0x48, 0x00, 0x78, + 0xC0, 0x07, 0x02, 0xD0, 0x00, 0xF0, 0x0E, 0xF8, + 0x10, 0xBD, 0x00, 0xF0, 0x77, 0xFE, 0x10, 0xBD, + 0x84, 0x04, 0x00, 0x20, 0xCF, 0x00, 0x00, 0x20, + 0x00, 0x10, 0x00, 0x50, 0x04, 0x08, 0x00, 0x00, + 0xCE, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x1D, 0x4C, + 0xA0, 0x79, 0x00, 0x28, 0x35, 0xD0, 0x1C, 0xA0, + 0x02, 0xF0, 0x88, 0xFF, 0x00, 0x20, 0xA0, 0x71, + 0xE0, 0x71, 0x1E, 0x48, 0x81, 0x68, 0x01, 0x22, + 0x52, 0x02, 0x91, 0x43, 0x81, 0x60, 0x1C, 0x48, + 0x01, 0x68, 0x92, 0x00, 0x91, 0x43, 0x01, 0x60, + 0x1A, 0x48, 0x01, 0x68, 0x10, 0x22, 0x91, 0x43, + 0x01, 0x60, 0x01, 0x68, 0x80, 0x22, 0x11, 0x43, + 0x01, 0x60, 0x17, 0x49, 0x41, 0x61, 0x17, 0x48, + 0x00, 0x78, 0x00, 0x28, 0x15, 0xD1, 0x05, 0x20, + 0x00, 0x07, 0x41, 0x6A, 0x92, 0x02, 0x91, 0x43, + 0x41, 0x62, 0x41, 0x6A, 0x09, 0x22, 0x12, 0x05, + 0x11, 0x43, 0x41, 0x62, 0x80, 0x00, 0xC2, 0x68, + 0x41, 0x14, 0x8A, 0x43, 0xC2, 0x60, 0x02, 0x68, + 0x03, 0x14, 0x9A, 0x43, 0x02, 0x60, 0x0C, 0x48, + 0x01, 0x60, 0x10, 0xBD, 0x84, 0x04, 0x00, 0x20, + 0x4C, 0x65, 0x61, 0x76, 0x65, 0x20, 0x41, 0x75, + 0x74, 0x6F, 0x53, 0x63, 0x61, 0x6E, 0x0D, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x50, + 0x00, 0x10, 0x00, 0x50, 0x00, 0x11, 0x00, 0x50, + 0xC8, 0x03, 0x60, 0x00, 0xCF, 0x00, 0x00, 0x20, + 0x80, 0xE1, 0x00, 0xE0, 0x00, 0xB5, 0x08, 0x49, + 0x83, 0x20, 0x08, 0x70, 0x07, 0x49, 0x00, 0x20, + 0x08, 0x70, 0x07, 0x48, 0x00, 0x68, 0x07, 0x49, + 0x40, 0x05, 0x40, 0x0F, 0x08, 0x73, 0x5A, 0x20, + 0xFF, 0xF7, 0x16, 0xFF, 0x00, 0xBD, 0x00, 0x00, + 0x84, 0x04, 0x00, 0x20, 0xCE, 0x00, 0x00, 0x20, + 0x00, 0x11, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, + 0xF0, 0xB5, 0x04, 0x46, 0x8F, 0xB0, 0xA6, 0x48, + 0x0A, 0x90, 0xA5, 0x48, 0xA6, 0x4D, 0x60, 0x30, + 0x0B, 0x90, 0xA4, 0x48, 0x03, 0x90, 0xA5, 0x48, + 0x05, 0x95, 0x01, 0x68, 0x22, 0x46, 0xA4, 0xA0, + 0x02, 0xF0, 0x10, 0xFF, 0x03, 0x98, 0x08, 0x90, + 0xA5, 0x48, 0xA6, 0x4A, 0x00, 0x21, 0x09, 0x95, + 0x8B, 0x00, 0x08, 0x9D, 0x49, 0x1C, 0xE8, 0x50, + 0x09, 0x9D, 0x89, 0xB2, 0xEA, 0x50, 0x18, 0x29, + 0xF6, 0xD3, 0x01, 0x20, 0x80, 0x02, 0x06, 0x90, + 0x20, 0x46, 0x07, 0x94, 0x50, 0x30, 0x00, 0xB2, + 0x50, 0x3C, 0x0C, 0x90, 0x20, 0xB2, 0x0D, 0x90, + 0x00, 0xF0, 0x6E, 0xFC, 0x00, 0xF0, 0xE2, 0xFE, + 0x00, 0xF0, 0x6E, 0xF9, 0x98, 0x48, 0x00, 0x78, + 0xC0, 0x07, 0x18, 0xD1, 0x01, 0x90, 0x00, 0x20, + 0x08, 0x9A, 0x81, 0x00, 0x53, 0x58, 0x0A, 0x9A, + 0x40, 0x1C, 0x53, 0x50, 0x09, 0x9A, 0x80, 0xB2, + 0x53, 0x58, 0x0B, 0x9A, 0x18, 0x28, 0x53, 0x50, + 0xF2, 0xD3, 0x00, 0xF0, 0xB7, 0xFD, 0x00, 0xF0, + 0xC9, 0xFE, 0x00, 0xF0, 0x55, 0xF9, 0x8C, 0x48, + 0x00, 0x78, 0xC0, 0x07, 0x02, 0xD0, 0x00, 0x20, + 0x0F, 0xB0, 0xF0, 0xBD, 0x3B, 0x46, 0x89, 0xA0, + 0x00, 0x9A, 0x01, 0x99, 0x02, 0xF0, 0xCA, 0xFE, + 0x00, 0x25, 0x2D, 0xE0, 0x00, 0x2D, 0x2B, 0xD0, + 0x01, 0x98, 0x05, 0x28, 0x73, 0xD9, 0x30, 0x2F, + 0x71, 0xD2, 0x7A, 0x4C, 0x05, 0x98, 0x40, 0x3C, + 0x04, 0x90, 0x60, 0x6B, 0x03, 0x26, 0x36, 0x05, + 0x01, 0x21, 0xB0, 0x43, 0x49, 0x05, 0x40, 0x18, + 0x60, 0x63, 0x00, 0xF0, 0x8F, 0xFD, 0x00, 0xF0, + 0xA1, 0xFE, 0x00, 0xF0, 0x2D, 0xF9, 0x60, 0x6B, + 0x01, 0x21, 0xB0, 0x43, 0x09, 0x05, 0x40, 0x18, + 0x60, 0x63, 0x00, 0x27, 0x00, 0x26, 0x79, 0x48, + 0x00, 0x68, 0x80, 0x19, 0xC0, 0x7E, 0x41, 0x28, + 0x0E, 0xD0, 0x70, 0x00, 0x04, 0x99, 0x02, 0x90, + 0x0C, 0x5A, 0x16, 0x2E, 0x12, 0xD0, 0x1A, 0xE0, + 0x00, 0x98, 0x30, 0x28, 0x77, 0xD0, 0x03, 0x98, + 0x04, 0x90, 0x00, 0x20, 0x00, 0x90, 0xE9, 0xE7, + 0x00, 0x2D, 0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, + 0x68, 0xE0, 0x00, 0x98, 0x40, 0x1C, 0x80, 0xB2, + 0x00, 0x90, 0x63, 0xE0, 0x6A, 0x48, 0x2C, 0x23, + 0x00, 0x68, 0x22, 0x46, 0xC3, 0x5E, 0x31, 0x46, + 0x68, 0xA0, 0x02, 0xF0, 0x7F, 0xFE, 0x01, 0x98, + 0x0A, 0x28, 0x0C, 0xD2, 0x64, 0x49, 0x02, 0x98, + 0x09, 0x68, 0x09, 0x5E, 0x07, 0x98, 0x81, 0x42, + 0x01, 0xDA, 0x06, 0x98, 0x84, 0x43, 0x06, 0x98, + 0x40, 0x08, 0x04, 0x43, 0x32, 0xE0, 0x5E, 0x48, + 0x02, 0x99, 0x00, 0x68, 0x41, 0x5E, 0x00, 0x29, + 0x01, 0xDB, 0x08, 0x46, 0x00, 0xE0, 0x48, 0x42, + 0x00, 0xB2, 0x40, 0x30, 0xC2, 0x17, 0x52, 0x0E, + 0x10, 0x18, 0x0D, 0x9A, 0xC0, 0x11, 0x91, 0x42, + 0x10, 0xDA, 0x21, 0x05, 0x06, 0xD5, 0x20, 0x1A, + 0x84, 0xB2, 0x01, 0x20, 0xC0, 0x02, 0x84, 0x42, + 0x11, 0xD3, 0x17, 0xE0, 0x84, 0x42, 0x03, 0xDD, + 0x20, 0x1A, 0x84, 0xB2, 0x12, 0xE0, 0x2E, 0xE0, + 0x00, 0x24, 0x0F, 0xE0, 0x0C, 0x9A, 0x91, 0x42, + 0x15, 0xDD, 0x21, 0x05, 0x05, 0xD5, 0x20, 0x18, + 0x84, 0xB2, 0x4D, 0x48, 0x04, 0xE0, 0x04, 0x46, + 0x04, 0xE0, 0x20, 0x18, 0x84, 0xB2, 0x4B, 0x48, + 0x84, 0x42, 0xF8, 0xD8, 0x00, 0x2D, 0x0F, 0xD0, + 0x01, 0x20, 0xC0, 0x02, 0x04, 0x43, 0x05, 0x99, + 0x02, 0x98, 0x0C, 0x52, 0x0E, 0xE0, 0x00, 0x2D, + 0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, 0xF3, 0xE7, + 0x00, 0x98, 0x40, 0x1C, 0x80, 0xB2, 0x00, 0x90, + 0x03, 0x99, 0x02, 0x98, 0x0C, 0x52, 0x01, 0x98, + 0x05, 0x28, 0xE9, 0xD9, 0x76, 0x1C, 0xB6, 0xB2, + 0x30, 0x2E, 0x00, 0xD2, 0x77, 0xE7, 0x6D, 0x1C, + 0xED, 0xB2, 0x02, 0x2D, 0x00, 0xD2, 0x51, 0xE7, + 0x06, 0x98, 0x40, 0x08, 0x06, 0x90, 0x38, 0xA0, + 0x02, 0xF0, 0x14, 0xFE, 0x00, 0x98, 0x30, 0x28, + 0x03, 0xD1, 0x30, 0x2F, 0x01, 0xD1, 0x40, 0x20, + 0x01, 0x90, 0x01, 0x98, 0x40, 0x1C, 0x80, 0xB2, + 0x01, 0x90, 0x40, 0x28, 0x00, 0xD8, 0x1A, 0xE7, + 0x1C, 0x4C, 0x40, 0x3C, 0x60, 0x6B, 0x03, 0x25, + 0x2D, 0x05, 0x01, 0x21, 0xA8, 0x43, 0x09, 0x05, + 0x40, 0x18, 0x60, 0x63, 0x00, 0xF0, 0xD6, 0xFC, + 0x00, 0xF0, 0xE8, 0xFD, 0x00, 0xF0, 0x74, 0xF8, + 0x21, 0x4E, 0x01, 0x21, 0x30, 0x68, 0x00, 0xF0, + 0x31, 0xFE, 0x60, 0x6B, 0x01, 0x21, 0xA8, 0x43, + 0x49, 0x05, 0x40, 0x18, 0x60, 0x63, 0x00, 0xF0, + 0xC5, 0xFC, 0x00, 0xF0, 0xD7, 0xFD, 0x00, 0xF0, + 0x63, 0xF8, 0x01, 0x21, 0x30, 0x68, 0x00, 0xF0, + 0x21, 0xFE, 0x60, 0x6B, 0x28, 0x43, 0x60, 0x63, + 0x00, 0xF0, 0xB8, 0xFC, 0x00, 0xF0, 0xCA, 0xFD, + 0x00, 0xF0, 0x56, 0xF8, 0x01, 0x21, 0x30, 0x68, + 0x00, 0xF0, 0x14, 0xFE, 0x01, 0x20, 0xFF, 0xE6, + 0x00, 0x20, 0x00, 0x50, 0xF4, 0x05, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x20, 0xC0, 0x10, 0x00, 0x50, + 0x5B, 0x43, 0x46, 0x42, 0x3A, 0x25, 0x78, 0x3A, + 0x25, 0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x0C, + 0xCE, 0x00, 0x00, 0x20, 0x5B, 0x25, 0x64, 0x3A, + 0x25, 0x64, 0x3A, 0x25, 0x64, 0x5D, 0x20, 0x0D, + 0x0A, 0x00, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20, + 0xF8, 0x02, 0x00, 0x20, 0x25, 0x64, 0x2C, 0x30, + 0x78, 0x25, 0x78, 0x2C, 0x25, 0x64, 0x2C, 0x00, + 0xFF, 0x0F, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, + 0x0D, 0x0A, 0x00, 0x00, 0x10, 0xB5, 0x0B, 0x49, + 0x30, 0x24, 0x00, 0x28, 0x0A, 0x4A, 0x0B, 0x4B, + 0x08, 0x68, 0x06, 0xD0, 0x20, 0x43, 0x08, 0x60, + 0x09, 0x48, 0x10, 0x60, 0x08, 0x48, 0x60, 0x30, + 0x05, 0xE0, 0xA0, 0x43, 0x08, 0x60, 0x07, 0x48, + 0x10, 0x60, 0x06, 0x48, 0x60, 0x30, 0x18, 0x60, + 0x10, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, + 0xF8, 0x02, 0x00, 0x20, 0xFC, 0x02, 0x00, 0x20, + 0x00, 0x00, 0x04, 0x20, 0x00, 0x10, 0x04, 0x20, + 0xF0, 0xB5, 0x11, 0x48, 0x01, 0x68, 0x11, 0x4D, + 0x89, 0x06, 0x30, 0x22, 0x60, 0x35, 0x00, 0x29, + 0x0E, 0x4B, 0x0F, 0x4C, 0x0F, 0x4E, 0x10, 0x4F, + 0x01, 0x68, 0x0A, 0xDA, 0x91, 0x43, 0x01, 0x60, + 0x23, 0x60, 0x0E, 0x48, 0x35, 0x60, 0x07, 0x60, + 0x0B, 0x48, 0x0D, 0x49, 0x60, 0x30, 0x08, 0x60, + 0xF0, 0xBD, 0x11, 0x43, 0x01, 0x60, 0x08, 0x48, + 0x27, 0x60, 0x60, 0x30, 0x30, 0x60, 0x07, 0x48, + 0x03, 0x60, 0x07, 0x48, 0x05, 0x60, 0xF0, 0xBD, + 0x00, 0x10, 0x00, 0x50, 0x00, 0x00, 0x04, 0x20, + 0x64, 0x02, 0x00, 0x20, 0x68, 0x02, 0x00, 0x20, + 0x00, 0x10, 0x04, 0x20, 0xF8, 0x02, 0x00, 0x20, + 0xFC, 0x02, 0x00, 0x20, 0x00, 0xB5, 0x09, 0x48, + 0x00, 0x78, 0x00, 0x28, 0x08, 0xD0, 0x01, 0x28, + 0x09, 0xD0, 0x02, 0x28, 0x03, 0xD1, 0x06, 0x49, + 0x04, 0x20, 0x00, 0xF0, 0x65, 0xF8, 0x00, 0xBD, + 0x04, 0x49, 0x01, 0x20, 0xF9, 0xE7, 0x04, 0x49, + 0x02, 0x20, 0xF6, 0xE7, 0x71, 0x04, 0x00, 0x20, + 0xFC, 0x03, 0x00, 0x20, 0x9C, 0x03, 0x00, 0x20, + 0xEC, 0x06, 0x00, 0x20, 0xF8, 0xB5, 0x07, 0x46, + 0x00, 0xF0, 0xAA, 0xFA, 0x21, 0x4D, 0x60, 0x21, + 0x28, 0x46, 0xFE, 0xF7, 0xAE, 0xF9, 0x00, 0x24, + 0x00, 0xF0, 0x18, 0xFD, 0xFF, 0xF7, 0xA4, 0xFF, + 0xFE, 0xF7, 0xF4, 0xFA, 0x00, 0x2C, 0x0C, 0xD0, + 0x1B, 0x49, 0x00, 0x20, 0x0E, 0x68, 0x42, 0x00, + 0x51, 0x19, 0x0B, 0x88, 0xB2, 0x5A, 0x40, 0x1C, + 0x9A, 0x18, 0xC0, 0xB2, 0x0A, 0x80, 0x30, 0x28, + 0xF5, 0xD3, 0x64, 0x1C, 0xE4, 0xB2, 0x05, 0x2C, + 0xE6, 0xD3, 0x00, 0x20, 0x41, 0x00, 0x49, 0x19, + 0x00, 0x22, 0x8A, 0x5E, 0x40, 0x1C, 0x92, 0x10, + 0xC0, 0xB2, 0x0A, 0x80, 0x30, 0x28, 0xF5, 0xD3, + 0x39, 0x46, 0x0E, 0xA0, 0x02, 0xF0, 0x02, 0xFD, + 0x01, 0x21, 0x28, 0x46, 0x00, 0xF0, 0x3E, 0xFD, + 0x01, 0x20, 0xFE, 0xF7, 0x3D, 0xFB, 0x0C, 0x4C, + 0x20, 0x78, 0x38, 0x42, 0x08, 0xD1, 0x0B, 0x48, + 0x60, 0x22, 0x29, 0x46, 0x00, 0x68, 0xFE, 0xF7, + 0x5B, 0xF9, 0x20, 0x78, 0x38, 0x43, 0x20, 0x70, + 0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, 0x01, 0x20, + 0xF8, 0x02, 0x00, 0x20, 0x42, 0x43, 0x5F, 0x4F, + 0x4B, 0x5F, 0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, + 0x98, 0x01, 0x00, 0x20, 0x6C, 0x02, 0x00, 0x20, + 0xF0, 0xB5, 0x21, 0x48, 0x00, 0x22, 0x00, 0x68, + 0x20, 0x4C, 0xA0, 0x42, 0x28, 0xD8, 0x20, 0x48, + 0x00, 0x78, 0x04, 0x28, 0x27, 0xD1, 0x1F, 0x4D, + 0x1F, 0x4C, 0x2D, 0x68, 0x00, 0x20, 0x2E, 0x18, + 0xF6, 0x7E, 0x41, 0x2E, 0x08, 0xD0, 0x43, 0x00, + 0x1F, 0x19, 0x00, 0x26, 0xBE, 0x5F, 0xCB, 0x5E, + 0xF3, 0x1A, 0x00, 0xD5, 0x5B, 0x42, 0x1B, 0xB2, + 0x9A, 0x42, 0x00, 0xDA, 0x1A, 0x46, 0x40, 0x1C, + 0xC0, 0xB2, 0x30, 0x28, 0xEB, 0xD3, 0x60, 0x20, + 0x40, 0x5D, 0x90, 0x35, 0x40, 0x1C, 0x50, 0x43, + 0x6A, 0x7B, 0x2B, 0x7B, 0x00, 0x11, 0x12, 0x02, + 0x00, 0xB2, 0x1A, 0x43, 0x82, 0x42, 0x03, 0xDA, + 0x0E, 0x49, 0x00, 0x20, 0x08, 0x70, 0xF0, 0xBD, + 0x00, 0x20, 0x42, 0x00, 0x8B, 0x5E, 0x00, 0x26, + 0xDD, 0x00, 0xEB, 0x1A, 0x15, 0x19, 0xAE, 0x5F, + 0x40, 0x1C, 0x9B, 0x19, 0xDB, 0x10, 0xC0, 0xB2, + 0x8B, 0x52, 0x30, 0x28, 0xF1, 0xD3, 0xF0, 0xBD, + 0x08, 0x03, 0x00, 0x20, 0x40, 0x77, 0x1B, 0x00, + 0x87, 0x02, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x20, 0x98, 0x01, 0x00, 0x20, + 0x70, 0xB5, 0x21, 0x48, 0x21, 0x49, 0x00, 0x78, + 0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x08, 0xD0, + 0x02, 0x28, 0x16, 0xD0, 0x05, 0x28, 0x2F, 0xD1, + 0x34, 0xE0, 0x01, 0x25, 0x2C, 0x46, 0x1C, 0x48, + 0x02, 0xE0, 0x02, 0x25, 0x1B, 0x48, 0x2C, 0x46, + 0x08, 0x60, 0x00, 0x22, 0x1A, 0x49, 0x28, 0x46, + 0xFF, 0xF7, 0xD4, 0xFA, 0x00, 0x28, 0x18, 0xD0, + 0x18, 0x48, 0x00, 0x78, 0x00, 0x28, 0x04, 0xD0, + 0x13, 0xE0, 0x04, 0x25, 0x2C, 0x46, 0x16, 0x48, + 0xEE, 0xE7, 0x16, 0x48, 0x41, 0x6B, 0x03, 0x22, + 0x12, 0x05, 0x11, 0x43, 0x41, 0x63, 0x14, 0x48, + 0x00, 0xF0, 0x30, 0xFA, 0x00, 0x28, 0x0B, 0xD1, + 0x20, 0x46, 0xFF, 0xF7, 0x27, 0xFF, 0x00, 0x28, + 0x06, 0xD1, 0x01, 0x22, 0x21, 0x46, 0x28, 0x46, + 0x00, 0xF0, 0x1E, 0xF8, 0x00, 0x28, 0x04, 0xD0, + 0x0C, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, + 0x00, 0x20, 0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD, + 0x85, 0x02, 0x00, 0x20, 0x6C, 0x02, 0x00, 0x20, + 0x9C, 0x03, 0x00, 0x20, 0xEC, 0x06, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x50, 0xE7, 0x02, 0x00, 0x20, + 0xFC, 0x03, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, + 0x00, 0x00, 0x01, 0x20, 0xCE, 0x00, 0x00, 0x20, + 0xF7, 0xB5, 0x07, 0x46, 0xFE, 0xF7, 0xD0, 0xFB, + 0x14, 0x48, 0x00, 0x25, 0x05, 0x70, 0x78, 0x07, + 0x1D, 0xD0, 0x01, 0x21, 0x02, 0x20, 0x00, 0xF0, + 0x33, 0xFB, 0x11, 0x4C, 0x60, 0x6B, 0x03, 0x26, + 0x36, 0x05, 0x01, 0x21, 0xB0, 0x43, 0x09, 0x05, + 0x40, 0x18, 0x60, 0x63, 0x00, 0x20, 0xFF, 0xF7, + 0xFB, 0xFC, 0x00, 0x28, 0x0D, 0xD0, 0x01, 0x98, + 0xFF, 0xF7, 0xE4, 0xFE, 0x01, 0x22, 0x09, 0x49, + 0x38, 0x46, 0xFF, 0xF7, 0x73, 0xFA, 0x08, 0x48, + 0x05, 0x70, 0x08, 0x48, 0x05, 0x70, 0x01, 0x20, + 0xFE, 0xBD, 0x60, 0x6B, 0x30, 0x43, 0x60, 0x63, + 0x00, 0x20, 0xFE, 0xBD, 0x82, 0x02, 0x00, 0x20, + 0x80, 0x10, 0x00, 0x50, 0x00, 0x20, 0x00, 0x50, + 0xE7, 0x02, 0x00, 0x20, 0xE8, 0x02, 0x00, 0x20, + 0xF0, 0xB5, 0xB4, 0x48, 0x01, 0x22, 0x23, 0x23, + 0x13, 0x24, 0x92, 0x02, 0x5B, 0x01, 0xA4, 0x01, + 0x05, 0x46, 0x1C, 0xC5, 0x29, 0x21, 0x49, 0x01, + 0xC1, 0x60, 0xAF, 0x48, 0x07, 0x68, 0x38, 0x46, + 0x40, 0x30, 0x45, 0x7C, 0x06, 0x7C, 0x2D, 0x04, + 0x36, 0x02, 0x35, 0x43, 0x0C, 0x35, 0x15, 0x60, + 0x45, 0x7C, 0x06, 0x7C, 0x2D, 0x04, 0x36, 0x02, + 0x35, 0x43, 0x0C, 0x35, 0x55, 0x60, 0x01, 0x25, + 0x95, 0x60, 0xD5, 0x60, 0x05, 0x7D, 0xBC, 0x46, + 0x6E, 0x1E, 0x6D, 0x08, 0x6D, 0x1E, 0xF6, 0x05, + 0xED, 0x05, 0xF6, 0x09, 0xED, 0x0D, 0x2E, 0x43, + 0x16, 0x61, 0xA0, 0x4D, 0x55, 0x61, 0x03, 0x25, + 0x95, 0x61, 0x03, 0x26, 0x36, 0x02, 0x00, 0x25, + 0x16, 0x62, 0xD5, 0x61, 0x06, 0x7E, 0xD6, 0x62, + 0x06, 0x7E, 0x87, 0x7E, 0xF6, 0x19, 0x16, 0x63, + 0x87, 0x7E, 0x06, 0x7E, 0x7F, 0x00, 0xF6, 0x19, + 0x56, 0x63, 0x95, 0x63, 0x06, 0x7E, 0xBC, 0x36, + 0xD6, 0x63, 0x06, 0x7E, 0x87, 0x7E, 0xF6, 0x19, + 0xBC, 0x36, 0x16, 0x64, 0x07, 0x7E, 0x86, 0x7E, + 0x95, 0x64, 0x76, 0x00, 0xBC, 0x36, 0xBE, 0x19, + 0x56, 0x64, 0x67, 0x46, 0x55, 0x62, 0x8E, 0x4E, + 0x50, 0x37, 0x96, 0x62, 0xBC, 0x46, 0xFE, 0x7B, + 0xBF, 0x7B, 0x36, 0x02, 0x3E, 0x43, 0xF6, 0x1C, + 0xB7, 0x05, 0x8A, 0x4E, 0xBF, 0x0D, 0xBE, 0x19, + 0xD6, 0x64, 0x89, 0x4E, 0x16, 0x65, 0x46, 0x7C, + 0x0C, 0x3E, 0xF7, 0xB2, 0x05, 0x26, 0x76, 0x02, + 0xBE, 0x19, 0x56, 0x65, 0x06, 0x7D, 0x36, 0x02, + 0x21, 0x36, 0x96, 0x65, 0xD5, 0x65, 0x42, 0x7C, + 0x06, 0x7C, 0x12, 0x04, 0x36, 0x02, 0x32, 0x43, + 0x0C, 0x32, 0x1A, 0x60, 0x42, 0x7C, 0x06, 0x7C, + 0x12, 0x04, 0x36, 0x02, 0x32, 0x43, 0x0C, 0x32, + 0x5A, 0x60, 0x01, 0x22, 0x9A, 0x60, 0xDA, 0x60, + 0x42, 0x7D, 0x56, 0x1E, 0x52, 0x08, 0x52, 0x1E, + 0xF6, 0x05, 0xD2, 0x05, 0xF7, 0x09, 0xD2, 0x0D, + 0x17, 0x43, 0x72, 0x4A, 0x1F, 0x61, 0x5A, 0x61, + 0x03, 0x22, 0xDD, 0x61, 0x9A, 0x61, 0x12, 0x02, + 0x1A, 0x62, 0x02, 0x7E, 0xDA, 0x62, 0x02, 0x7E, + 0xC6, 0x7E, 0x92, 0x19, 0x1A, 0x63, 0xC6, 0x7E, + 0x02, 0x7E, 0x76, 0x00, 0x92, 0x19, 0x9D, 0x63, + 0x5A, 0x63, 0x02, 0x7E, 0x67, 0x46, 0xBC, 0x32, + 0xDA, 0x63, 0x02, 0x7E, 0xC6, 0x7E, 0x92, 0x19, + 0xBC, 0x32, 0x1A, 0x64, 0xC6, 0x7E, 0x02, 0x7E, + 0x76, 0x00, 0xBC, 0x36, 0x9D, 0x64, 0x92, 0x19, + 0x5D, 0x62, 0x5A, 0x64, 0x60, 0x4A, 0x9A, 0x62, + 0xFA, 0x7B, 0xBE, 0x7B, 0x12, 0x02, 0x32, 0x43, + 0xD2, 0x1C, 0x96, 0x05, 0x5D, 0x4A, 0xB6, 0x0D, + 0xB2, 0x18, 0xDA, 0x64, 0x5C, 0x4A, 0x1A, 0x65, + 0x42, 0x7C, 0x0C, 0x3A, 0xD6, 0xB2, 0x05, 0x22, + 0x52, 0x02, 0xB2, 0x18, 0x5A, 0x65, 0x42, 0x7D, + 0xDD, 0x65, 0x12, 0x02, 0x21, 0x32, 0x9A, 0x65, + 0xC2, 0x7C, 0x83, 0x7C, 0x12, 0x04, 0x1B, 0x02, + 0x1A, 0x43, 0x0C, 0x32, 0x23, 0x46, 0x1A, 0x60, + 0xC2, 0x7C, 0x83, 0x7C, 0x12, 0x04, 0x1B, 0x02, + 0x1A, 0x43, 0x0C, 0x32, 0x23, 0x46, 0x5A, 0x60, + 0x82, 0x7D, 0x52, 0x1E, 0xD3, 0x05, 0xDB, 0x0D, + 0x22, 0x46, 0x93, 0x60, 0x82, 0x7D, 0x52, 0x08, + 0x52, 0x1E, 0xD2, 0x05, 0x23, 0x46, 0xD2, 0x0D, + 0x1D, 0x61, 0xDA, 0x60, 0x47, 0x4A, 0x5A, 0x61, + 0x26, 0x46, 0x01, 0x23, 0xF5, 0x61, 0xB3, 0x61, + 0x13, 0x12, 0x33, 0x62, 0x43, 0x7E, 0xF3, 0x62, + 0x43, 0x7E, 0x06, 0x7F, 0x9B, 0x19, 0x26, 0x46, + 0x33, 0x63, 0x06, 0x7F, 0x43, 0x7E, 0x76, 0x00, + 0x9B, 0x19, 0x26, 0x46, 0xB5, 0x63, 0xF5, 0x63, + 0x35, 0x64, 0x73, 0x63, 0x65, 0x64, 0x38, 0x4B, + 0xA5, 0x64, 0x3F, 0x33, 0x65, 0x62, 0xA3, 0x62, + 0x36, 0x4B, 0xDB, 0x1C, 0xE3, 0x64, 0x36, 0x4B, + 0x23, 0x65, 0xC6, 0x7C, 0x0C, 0x3E, 0xF7, 0xB2, + 0x05, 0x26, 0x76, 0x02, 0xBF, 0x19, 0x67, 0x65, + 0x87, 0x7D, 0x7F, 0x08, 0x3F, 0x02, 0x21, 0x37, + 0xA7, 0x65, 0xE5, 0x65, 0xC4, 0x7C, 0x80, 0x7C, + 0x24, 0x04, 0x00, 0x02, 0x04, 0x43, 0x0C, 0x34, + 0x27, 0x48, 0x0C, 0x60, 0x00, 0x68, 0x40, 0x30, + 0xC4, 0x7C, 0x87, 0x7C, 0x24, 0x04, 0x3F, 0x02, + 0x3C, 0x43, 0x0C, 0x34, 0x4C, 0x60, 0xC4, 0x7D, + 0x64, 0x1E, 0xE4, 0x05, 0xE4, 0x0D, 0x8C, 0x60, + 0xC4, 0x7D, 0x0D, 0x61, 0x64, 0x08, 0x64, 0x1E, + 0xE4, 0x05, 0xE4, 0x0D, 0xCC, 0x60, 0x4A, 0x61, + 0x01, 0x22, 0xCD, 0x61, 0x8A, 0x61, 0x12, 0x02, + 0x0A, 0x62, 0x42, 0x7E, 0xCA, 0x62, 0x42, 0x7E, + 0x44, 0x7F, 0x12, 0x19, 0x0A, 0x63, 0x44, 0x7F, + 0x42, 0x7E, 0x64, 0x00, 0x12, 0x19, 0x8D, 0x63, + 0x4A, 0x63, 0x42, 0x7E, 0xBC, 0x32, 0xCA, 0x63, + 0x42, 0x7E, 0x44, 0x7F, 0x12, 0x19, 0xBC, 0x32, + 0x0A, 0x64, 0x42, 0x7F, 0x44, 0x7E, 0x52, 0x00, + 0xBC, 0x32, 0xA2, 0x18, 0x8D, 0x64, 0x4A, 0x64, + 0x01, 0x22, 0x92, 0x04, 0x4A, 0x62, 0x0C, 0x4A, + 0x3F, 0x32, 0x8A, 0x62, 0x0B, 0x4A, 0x0B, 0x65, + 0xD2, 0x1C, 0xCA, 0x64, 0xC2, 0x7C, 0x0C, 0x3A, + 0xD2, 0xB2, 0x92, 0x19, 0x4A, 0x65, 0xC0, 0x7D, + 0xCD, 0x65, 0x40, 0x08, 0x00, 0x02, 0x21, 0x30, + 0x88, 0x65, 0xF0, 0xBD, 0x70, 0x02, 0x00, 0x20, + 0xB8, 0x02, 0x00, 0x20, 0x03, 0x00, 0x03, 0x00, + 0x00, 0x3F, 0x3F, 0x3F, 0x00, 0x30, 0xBC, 0x00, + 0x00, 0x26, 0x31, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x10, 0xB5, 0x00, 0xF0, 0x73, 0xFA, 0x00, 0xF0, + 0x75, 0xF9, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, + 0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x00, 0x20, + 0xFF, 0xF7, 0xD4, 0xFC, 0x09, 0x48, 0x01, 0x78, + 0x09, 0x48, 0x00, 0x29, 0x04, 0xD0, 0x01, 0x68, + 0xFF, 0x22, 0x01, 0x32, 0x11, 0x43, 0x01, 0x60, + 0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x91, 0x43, + 0x01, 0x60, 0x04, 0x49, 0x02, 0x20, 0x08, 0x72, + 0x10, 0xBD, 0x00, 0x00, 0xCF, 0x00, 0x00, 0x20, + 0x00, 0x10, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, + 0x10, 0xB5, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, + 0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x09, 0x48, + 0x07, 0x49, 0x41, 0x60, 0x08, 0x49, 0x81, 0x60, + 0xFF, 0xF7, 0x52, 0xFE, 0x80, 0x21, 0x07, 0x48, + 0xFD, 0xF7, 0xCF, 0xFE, 0x80, 0x21, 0x06, 0x48, + 0xFD, 0xF7, 0xCB, 0xFE, 0x10, 0xBD, 0x00, 0x00, + 0x1F, 0x1F, 0x5F, 0x1F, 0x00, 0x10, 0x00, 0x50, + 0x1F, 0x1F, 0x1F, 0x1F, 0x4C, 0x07, 0x00, 0x20, + 0x9C, 0x01, 0x00, 0x20, 0x01, 0x21, 0x89, 0x07, + 0x0A, 0x15, 0x00, 0x28, 0x48, 0x69, 0x02, 0xD0, + 0x10, 0x43, 0x48, 0x61, 0x70, 0x47, 0x90, 0x43, + 0xFB, 0xE7, 0x00, 0x00, 0x10, 0xB5, 0x0F, 0x49, + 0x0A, 0x78, 0x0F, 0x49, 0x00, 0x2A, 0x09, 0x78, + 0x04, 0xD0, 0x01, 0x2A, 0x07, 0xD0, 0x02, 0x2A, + 0x12, 0xD1, 0x09, 0xE0, 0xC9, 0x07, 0x0F, 0xD0, + 0x60, 0x22, 0x0A, 0x49, 0x08, 0xE0, 0x89, 0x07, + 0x0A, 0xD5, 0x60, 0x22, 0x08, 0x49, 0x03, 0xE0, + 0x49, 0x07, 0x05, 0xD5, 0x60, 0x22, 0x07, 0x49, + 0xFD, 0xF7, 0x7E, 0xFE, 0x01, 0x20, 0x10, 0xBD, + 0x00, 0x20, 0x10, 0xBD, 0x85, 0x02, 0x00, 0x20, + 0x98, 0x01, 0x00, 0x20, 0x9C, 0x03, 0x00, 0x20, + 0xEC, 0x06, 0x00, 0x20, 0xFC, 0x03, 0x00, 0x20, + 0x10, 0xB5, 0x13, 0x49, 0x13, 0x4B, 0x09, 0x68, + 0x13, 0x4A, 0x40, 0x31, 0x00, 0x28, 0x0F, 0xD0, + 0x01, 0x28, 0x12, 0xD0, 0x02, 0x28, 0x15, 0xD0, + 0x03, 0x28, 0x08, 0xD1, 0x48, 0x7E, 0x84, 0x1E, + 0x1C, 0x80, 0x49, 0x7F, 0x4B, 0x00, 0xC9, 0x18, + 0x89, 0x1C, 0x40, 0x18, 0x10, 0x80, 0x10, 0xBD, + 0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, 0x89, 0x7E, + 0xF4, 0xE7, 0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, + 0xC9, 0x7E, 0xEF, 0xE7, 0x48, 0x7E, 0x84, 0x1E, + 0x1C, 0x80, 0x09, 0x7F, 0xEA, 0xE7, 0x00, 0x00, + 0xB8, 0x02, 0x00, 0x20, 0x60, 0x02, 0x00, 0x20, + 0x62, 0x02, 0x00, 0x20, 0xF8, 0xB5, 0x05, 0x46, + 0x23, 0x48, 0x00, 0x21, 0x01, 0x60, 0x41, 0x60, + 0x22, 0x49, 0x01, 0x20, 0x08, 0x70, 0x22, 0x48, + 0x22, 0x4C, 0x00, 0x78, 0x00, 0x28, 0x06, 0xD0, + 0x2D, 0x26, 0x09, 0x27, 0x01, 0x28, 0x10, 0xD0, + 0x02, 0x28, 0x1D, 0xD1, 0x21, 0xE0, 0x1E, 0xA0, + 0x02, 0xF0, 0xC0, 0xF9, 0x5A, 0x20, 0x00, 0x2D, + 0x20, 0x70, 0x01, 0xD0, 0x00, 0x20, 0x00, 0xE0, + 0x01, 0x20, 0x00, 0xF0, 0x23, 0xF9, 0x5A, 0x20, + 0x0C, 0xE0, 0x1A, 0xA0, 0x02, 0xF0, 0xB2, 0xF9, + 0x00, 0x2D, 0x02, 0xD0, 0x26, 0x70, 0x02, 0x20, + 0x01, 0xE0, 0x27, 0x70, 0x03, 0x20, 0x00, 0xF0, + 0x15, 0xF9, 0x01, 0x20, 0xFF, 0xF7, 0x90, 0xF9, + 0x05, 0x20, 0x14, 0x49, 0x00, 0x02, 0x08, 0x60, + 0xF8, 0xBD, 0x13, 0xA0, 0x02, 0xF0, 0x9E, 0xF9, + 0x00, 0x2D, 0x02, 0xD0, 0x26, 0x70, 0x02, 0x20, + 0x01, 0xE0, 0x27, 0x70, 0x03, 0x20, 0x00, 0xF0, + 0x01, 0xF9, 0x10, 0x48, 0x81, 0x6A, 0x10, 0x4A, + 0x11, 0x40, 0x81, 0x62, 0xE5, 0xE7, 0x00, 0x00, + 0x50, 0x02, 0x00, 0x20, 0xCF, 0x00, 0x00, 0x20, + 0x85, 0x02, 0x00, 0x20, 0xB5, 0x02, 0x00, 0x20, + 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x0D, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x64, 0x6C, 0x65, + 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, + 0x47, 0x65, 0x73, 0x74, 0x75, 0x72, 0x65, 0x0D, + 0x0A, 0x00, 0x00, 0x00, 0xC0, 0x11, 0x00, 0x50, + 0xFF, 0xFF, 0x00, 0xF8, 0x70, 0xB5, 0x1C, 0x4D, + 0x1C, 0x4C, 0x28, 0x70, 0x02, 0x46, 0x21, 0x78, + 0x1B, 0xA0, 0x02, 0xF0, 0x67, 0xF9, 0x1E, 0x49, + 0x01, 0x20, 0x08, 0x70, 0x00, 0xF0, 0x7E, 0xF9, + 0x00, 0xF0, 0x50, 0xF9, 0x00, 0xF0, 0x52, 0xF8, + 0x01, 0x20, 0xFD, 0xF7, 0x8D, 0xFE, 0xFF, 0xF7, + 0x09, 0xFC, 0x01, 0x20, 0xFF, 0xF7, 0x7E, 0xFF, + 0x00, 0xF0, 0xC6, 0xFF, 0xFE, 0xF7, 0xF6, 0xFF, + 0x28, 0x78, 0x20, 0x70, 0x05, 0x28, 0x12, 0xD0, + 0xFF, 0xF7, 0xBE, 0xFC, 0x00, 0x28, 0x0F, 0xD0, + 0x10, 0x48, 0x00, 0x7A, 0x00, 0x28, 0x03, 0xD1, + 0x00, 0x21, 0x02, 0x20, 0x00, 0xF0, 0x4C, 0xF8, + 0x00, 0x20, 0x00, 0xF0, 0x5B, 0xF9, 0x0C, 0x48, + 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, 0x00, 0x20, + 0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD, 0x00, 0x00, + 0x85, 0x02, 0x00, 0x20, 0x71, 0x04, 0x00, 0x20, + 0x53, 0x4D, 0x3D, 0x5B, 0x25, 0x64, 0x3A, 0x25, + 0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0x83, 0x02, 0x00, 0x20, 0x50, 0x02, 0x00, 0x20, + 0xCE, 0x00, 0x00, 0x20, 0x08, 0x49, 0x02, 0x20, + 0x08, 0x72, 0x08, 0x48, 0x01, 0x78, 0x08, 0x48, + 0x00, 0x29, 0x01, 0x68, 0x04, 0xD0, 0x01, 0x22, + 0x92, 0x02, 0x91, 0x43, 0x01, 0x60, 0x70, 0x47, + 0x01, 0x22, 0x11, 0x43, 0xFA, 0xE7, 0x00, 0x00, + 0x50, 0x02, 0x00, 0x20, 0xCF, 0x00, 0x00, 0x20, + 0x00, 0x10, 0x00, 0x50, 0x10, 0xB5, 0x08, 0x48, + 0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x11, 0x43, + 0x01, 0x60, 0x00, 0x68, 0x05, 0x4C, 0xC0, 0x07, + 0x03, 0xD0, 0x02, 0x20, 0x20, 0x72, 0x00, 0xF0, + 0xED, 0xF8, 0x00, 0x20, 0x20, 0x72, 0x10, 0xBD, + 0x00, 0x10, 0x00, 0x50, 0x50, 0x02, 0x00, 0x20, + 0x70, 0xB5, 0x05, 0x00, 0x0E, 0x46, 0x16, 0xD0, + 0xFF, 0xF7, 0x6A, 0xFE, 0x00, 0x24, 0x6D, 0x1E, + 0x07, 0xE0, 0x00, 0xF0, 0xDB, 0xF8, 0xFF, 0xF7, + 0x67, 0xFB, 0xFF, 0xF7, 0xC3, 0xFF, 0x64, 0x1C, + 0xE4, 0xB2, 0xAC, 0x42, 0xF5, 0xDB, 0x00, 0xF0, + 0xD1, 0xF8, 0xFF, 0xF7, 0x5D, 0xFB, 0x00, 0x2E, + 0x02, 0xD0, 0xFF, 0xF7, 0xCF, 0xFF, 0x70, 0xBD, + 0xFF, 0xF7, 0xB4, 0xFF, 0x70, 0xBD, 0x00, 0x00, + 0x30, 0xB5, 0x01, 0x24, 0x1C, 0x4A, 0xA4, 0x07, + 0x23, 0x13, 0x1B, 0x49, 0x40, 0x32, 0x00, 0x28, + 0x21, 0xD0, 0x01, 0x28, 0x1F, 0xD0, 0x02, 0x28, + 0x01, 0xD0, 0x03, 0x28, 0x1A, 0xD1, 0x25, 0x68, + 0x1D, 0x43, 0x25, 0x60, 0x62, 0x23, 0x93, 0x61, + 0xD3, 0x61, 0x14, 0x4B, 0xCB, 0x63, 0x14, 0x49, + 0x09, 0x68, 0x40, 0x31, 0x02, 0x28, 0x48, 0x7E, + 0x17, 0xD0, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D, + 0x10, 0x60, 0x4B, 0x7E, 0x48, 0x7F, 0x41, 0x00, + 0x40, 0x18, 0x80, 0x1C, 0x18, 0x18, 0x40, 0x05, + 0x40, 0x0D, 0x90, 0x60, 0x30, 0xBD, 0x20, 0x68, + 0x98, 0x43, 0x20, 0x60, 0x01, 0x20, 0x90, 0x61, + 0xD0, 0x61, 0x00, 0x20, 0xC8, 0x63, 0x10, 0x60, + 0xF3, 0xE7, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D, + 0x10, 0x60, 0x4B, 0x7E, 0x08, 0x7F, 0xE6, 0xE7, + 0x80, 0x11, 0x00, 0x50, 0x01, 0x00, 0x01, 0x00, + 0xB8, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x04, 0x46, + 0x81, 0x00, 0x25, 0x48, 0x41, 0x58, 0x25, 0x48, + 0x0A, 0x68, 0x42, 0x60, 0x4A, 0x68, 0x82, 0x60, + 0x8A, 0x68, 0x02, 0x61, 0xCA, 0x68, 0x42, 0x61, + 0x21, 0x4D, 0x08, 0x69, 0x28, 0x62, 0x1F, 0x4A, + 0x48, 0x69, 0xC0, 0x32, 0x90, 0x61, 0x88, 0x69, + 0xD0, 0x61, 0x48, 0x6A, 0x10, 0x62, 0x88, 0x6A, + 0x50, 0x62, 0x1A, 0x48, 0xCB, 0x69, 0x40, 0x38, + 0xC3, 0x60, 0x0B, 0x6A, 0x03, 0x61, 0xCB, 0x6A, + 0x93, 0x62, 0x0B, 0x6B, 0xD3, 0x63, 0x16, 0x4B, + 0x4E, 0x6B, 0x80, 0x3B, 0x1E, 0x61, 0x8E, 0x6B, + 0x5E, 0x62, 0xCE, 0x6B, 0xD6, 0x62, 0x0A, 0x6C, + 0x1A, 0x60, 0x4A, 0x6C, 0x5A, 0x61, 0x8A, 0x6C, + 0x9A, 0x62, 0xCA, 0x6C, 0xAA, 0x62, 0x0D, 0x4B, + 0x0A, 0x6D, 0x40, 0x33, 0x5A, 0x63, 0x0B, 0x4B, + 0x4A, 0x6D, 0x80, 0x33, 0x9A, 0x60, 0x0B, 0x4A, + 0x8B, 0x6D, 0x13, 0x60, 0xC9, 0x6D, 0x51, 0x60, + 0xC2, 0x68, 0x09, 0x49, 0x0A, 0x60, 0x00, 0x69, + 0x48, 0x60, 0x20, 0x46, 0xFF, 0xF7, 0x44, 0xFE, + 0x20, 0x46, 0xFF, 0xF7, 0x71, 0xFF, 0x70, 0xBD, + 0x70, 0x02, 0x00, 0x20, 0x40, 0x10, 0x00, 0x50, + 0xC0, 0x11, 0x00, 0x50, 0x00, 0x19, 0x00, 0x50, + 0x50, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x0E, 0x4C, + 0x21, 0x78, 0x81, 0x42, 0x17, 0xD0, 0x20, 0x70, + 0x01, 0x46, 0x0C, 0xA0, 0x02, 0xF0, 0x32, 0xF8, + 0xFF, 0xF7, 0x24, 0xFF, 0x20, 0x78, 0xFF, 0xF7, + 0x55, 0xFE, 0xFF, 0xF7, 0xA9, 0xFA, 0x0B, 0x48, + 0x80, 0x79, 0x01, 0x28, 0x05, 0xD1, 0x0A, 0x48, + 0x01, 0x68, 0x01, 0x22, 0xD2, 0x02, 0x11, 0x43, + 0x01, 0x60, 0xFF, 0xF7, 0xFB, 0xFE, 0x10, 0xBD, + 0x74, 0x04, 0x00, 0x20, 0x54, 0x50, 0x20, 0x53, + 0x70, 0x65, 0x65, 0x64, 0x20, 0x25, 0x64, 0x0D, + 0x0A, 0x00, 0x00, 0x00, 0x84, 0x04, 0x00, 0x20, + 0x00, 0x10, 0x00, 0x50, 0x70, 0xB5, 0x05, 0x24, + 0x64, 0x04, 0xFD, 0xF7, 0xA9, 0xFF, 0x00, 0x22, + 0x0C, 0x49, 0x0D, 0x48, 0x0D, 0x4B, 0x05, 0xE0, + 0x05, 0x78, 0xED, 0x07, 0x01, 0xD0, 0x0A, 0x72, + 0x09, 0xE0, 0x64, 0x1E, 0x0D, 0x7A, 0x02, 0x2D, + 0x02, 0xD0, 0x1D, 0x68, 0xED, 0x07, 0x02, 0xD0, + 0x00, 0x2C, 0xF1, 0xD1, 0x01, 0xE0, 0x00, 0x2C, + 0x03, 0xD1, 0x0A, 0x72, 0x04, 0xA0, 0x01, 0xF0, + 0xF1, 0xFF, 0x70, 0xBD, 0x50, 0x02, 0x00, 0x20, + 0xCE, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, + 0x57, 0x53, 0x46, 0x20, 0x54, 0x4F, 0x0D, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xB5, 0x03, 0x46, + 0x00, 0x20, 0x00, 0xF0, 0xF1, 0xF8, 0x05, 0x21, + 0x09, 0x07, 0xC8, 0x69, 0x00, 0x2B, 0x03, 0xD0, + 0xC0, 0x0B, 0xC0, 0x03, 0x05, 0x4A, 0x03, 0xE0, + 0xC0, 0x0B, 0x04, 0x4A, 0xC0, 0x03, 0xFC, 0x3A, + 0x80, 0x18, 0xC8, 0x61, 0x01, 0x20, 0x00, 0xF0, + 0xDF, 0xF8, 0x00, 0xBD, 0x50, 0x71, 0x00, 0x00, + 0x03, 0x49, 0x0A, 0x68, 0x10, 0x18, 0x0A, 0x68, + 0x90, 0x42, 0xFC, 0xD1, 0x70, 0x47, 0x00, 0x00, + 0x08, 0x03, 0x00, 0x20, 0xF8, 0xB5, 0x1B, 0x4E, + 0x05, 0x46, 0x0C, 0x46, 0x80, 0x21, 0x30, 0x46, + 0xFD, 0xF7, 0x3B, 0xFC, 0x00, 0x2C, 0x04, 0xD0, + 0x31, 0x46, 0x28, 0x46, 0x00, 0xF0, 0x5C, 0xFB, + 0x04, 0xE0, 0x80, 0x22, 0x29, 0x46, 0x30, 0x46, + 0xFD, 0xF7, 0x16, 0xFC, 0x29, 0x46, 0x12, 0xA0, + 0x01, 0xF0, 0xA8, 0xFF, 0x00, 0x25, 0x14, 0x4F, + 0x13, 0xE0, 0x00, 0x24, 0x08, 0xE0, 0x68, 0x43, + 0x00, 0x19, 0x40, 0x00, 0x31, 0x5E, 0x11, 0xA0, + 0x01, 0xF0, 0x9C, 0xFF, 0x64, 0x1C, 0xE4, 0xB2, + 0x38, 0x68, 0x00, 0x7E, 0xA0, 0x42, 0xF2, 0xD8, + 0x0E, 0xA0, 0x01, 0xF0, 0x93, 0xFF, 0x6D, 0x1C, + 0xED, 0xB2, 0x38, 0x68, 0x40, 0x7E, 0xA8, 0x42, + 0xE7, 0xD8, 0x0A, 0xA0, 0x01, 0xF0, 0x8A, 0xFF, + 0xF8, 0xBD, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x20, + 0x49, 0x6D, 0x61, 0x67, 0x65, 0x3A, 0x30, 0x78, + 0x25, 0x78, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0xB8, 0x02, 0x00, 0x20, 0x25, 0x36, 0x64, 0x2C, + 0x00, 0x00, 0x00, 0x00, 0x0D, 0x0A, 0x00, 0x00, + 0x08, 0x49, 0x8A, 0x78, 0x52, 0x1E, 0x8A, 0x70, + 0x4B, 0x78, 0x0A, 0x1D, 0xD2, 0x5C, 0x02, 0x70, + 0x48, 0x78, 0x40, 0x1C, 0x48, 0x70, 0x48, 0x78, + 0x10, 0x28, 0x01, 0xD1, 0x00, 0x20, 0x48, 0x70, + 0x70, 0x47, 0x00, 0x00, 0x5C, 0x04, 0x00, 0x20, + 0xF8, 0xB5, 0x01, 0x27, 0x05, 0x46, 0xBF, 0x07, + 0x38, 0x68, 0x08, 0x21, 0x08, 0x43, 0x38, 0x60, + 0x01, 0x23, 0x15, 0x48, 0x80, 0x22, 0x02, 0x60, + 0x14, 0x48, 0x00, 0x21, 0x81, 0x70, 0x01, 0x70, + 0x41, 0x70, 0xC3, 0x70, 0x12, 0x4B, 0x98, 0x68, + 0x02, 0x21, 0x88, 0x43, 0x98, 0x60, 0xF8, 0x68, + 0x10, 0x43, 0xF8, 0x60, 0x0F, 0x4C, 0x61, 0x61, + 0x0F, 0x48, 0x00, 0x68, 0xE9, 0x00, 0x46, 0x06, + 0x0E, 0x48, 0xFD, 0xF7, 0x7F, 0xFB, 0xE0, 0x60, + 0x30, 0x20, 0xA0, 0x60, 0x06, 0x49, 0x80, 0x20, + 0x80, 0x39, 0x08, 0x60, 0x08, 0x20, 0x78, 0x60, + 0xE0, 0x68, 0x68, 0x43, 0xC1, 0x00, 0x08, 0xA0, + 0x01, 0xF0, 0x30, 0xFF, 0xF8, 0xBD, 0x00, 0x00, + 0x80, 0xE1, 0x00, 0xE0, 0x5C, 0x04, 0x00, 0x20, + 0x40, 0x09, 0x00, 0x50, 0x00, 0x02, 0x00, 0x50, + 0x00, 0x11, 0x00, 0x50, 0x00, 0x36, 0x6E, 0x01, + 0x55, 0x41, 0x52, 0x54, 0x28, 0x25, 0x64, 0x29, + 0x21, 0x0D, 0x0A, 0x00, 0x70, 0xB5, 0x14, 0x4A, + 0x91, 0x78, 0x14, 0x4C, 0x0E, 0x29, 0x02, 0xD3, + 0x61, 0x68, 0x49, 0x07, 0xFC, 0xD5, 0x61, 0x69, + 0x02, 0x25, 0xA9, 0x43, 0x61, 0x61, 0x91, 0x78, + 0x00, 0x26, 0x10, 0x29, 0x0D, 0xD2, 0x0C, 0x49, + 0x13, 0x78, 0x09, 0x1D, 0xC8, 0x54, 0x10, 0x78, + 0x40, 0x1C, 0x10, 0x70, 0x10, 0x78, 0x10, 0x28, + 0x00, 0xD1, 0x16, 0x70, 0x90, 0x78, 0x40, 0x1C, + 0x90, 0x70, 0xD0, 0x78, 0x00, 0x28, 0x03, 0xD0, + 0xD6, 0x70, 0x20, 0x46, 0xFF, 0xF7, 0x80, 0xFF, + 0x60, 0x69, 0x28, 0x43, 0x60, 0x61, 0x70, 0xBD, + 0x5C, 0x04, 0x00, 0x20, 0x00, 0x02, 0x00, 0x50, + 0x01, 0x21, 0x89, 0x07, 0x00, 0xB5, 0x8A, 0x14, + 0x00, 0x28, 0x08, 0x68, 0x04, 0xD0, 0x10, 0x43, + 0x08, 0x60, 0xFD, 0xF7, 0x81, 0xFE, 0x00, 0xBD, + 0x90, 0x43, 0x08, 0x60, 0x00, 0xBD, 0x00, 0x00, + 0xFE, 0xB5, 0x63, 0x49, 0x61, 0x48, 0x09, 0x68, + 0x00, 0x78, 0x90, 0x31, 0x4A, 0x7B, 0x0B, 0x7B, + 0x11, 0x02, 0x19, 0x43, 0x5F, 0x4B, 0x00, 0x24, + 0x04, 0x25, 0x0F, 0x26, 0x5A, 0x22, 0x1C, 0x5F, + 0x00, 0x28, 0x01, 0xD0, 0x01, 0x28, 0x15, 0xD1, + 0x58, 0x4B, 0x02, 0x27, 0x38, 0x43, 0x18, 0x70, + 0x59, 0x48, 0x00, 0x78, 0x15, 0x28, 0x14, 0xD1, + 0x58, 0x48, 0x00, 0x23, 0xC3, 0x5E, 0x99, 0x42, + 0x0F, 0xDC, 0x13, 0x20, 0xC0, 0x43, 0x84, 0x42, + 0x01, 0xDA, 0x55, 0x48, 0x02, 0x70, 0x17, 0x20, + 0x54, 0x4B, 0x18, 0x70, 0x54, 0x48, 0x00, 0x27, + 0xC7, 0x5F, 0xB9, 0x42, 0x03, 0xDA, 0x04, 0x20, + 0x02, 0xE0, 0x00, 0x20, 0xF4, 0xE7, 0x02, 0x20, + 0x00, 0x90, 0x50, 0x48, 0x40, 0x88, 0x00, 0x28, + 0x67, 0xD0, 0x46, 0x48, 0x00, 0x78, 0x01, 0x90, + 0x83, 0x07, 0x4D, 0x48, 0x01, 0x78, 0x48, 0x1C, + 0xC0, 0xB2, 0x00, 0x2B, 0x06, 0xDA, 0x10, 0x2F, + 0x04, 0xDA, 0x02, 0x21, 0x00, 0x91, 0x48, 0x49, + 0x08, 0x70, 0x19, 0xE0, 0x47, 0x4B, 0x1B, 0x78, + 0xDB, 0x07, 0x11, 0xD0, 0x44, 0x49, 0x00, 0x20, + 0x08, 0x70, 0x13, 0x20, 0xC0, 0x43, 0x84, 0x42, + 0x05, 0xDA, 0x40, 0x48, 0x80, 0x88, 0x05, 0x28, + 0x01, 0xD2, 0x41, 0x48, 0x02, 0x80, 0x30, 0x21, + 0x40, 0x48, 0xFD, 0xF7, 0x02, 0xFB, 0x03, 0xE0, + 0x3B, 0x4A, 0x05, 0x29, 0x00, 0xD2, 0x10, 0x70, + 0x39, 0x48, 0x01, 0x78, 0x00, 0x98, 0x81, 0x42, + 0x37, 0xD3, 0x3B, 0x49, 0x10, 0x2F, 0x19, 0xDA, + 0x1D, 0x20, 0xC0, 0x43, 0x84, 0x42, 0x15, 0xDD, + 0x08, 0x78, 0xF0, 0x28, 0x01, 0xD2, 0x40, 0x1C, + 0x08, 0x70, 0xC0, 0xB2, 0x02, 0x28, 0x0F, 0xD9, + 0x2C, 0x49, 0x08, 0x78, 0x16, 0x28, 0x03, 0xD2, + 0x02, 0x28, 0x01, 0xD9, 0x17, 0x20, 0x08, 0x70, + 0x27, 0x49, 0x00, 0x20, 0x08, 0x70, 0x2C, 0x49, + 0x08, 0x80, 0x01, 0xE0, 0x00, 0x20, 0x08, 0x70, + 0x01, 0x98, 0x1E, 0x4A, 0x04, 0x28, 0x12, 0xD0, + 0x22, 0x49, 0x08, 0x78, 0x18, 0x28, 0x32, 0xD2, + 0x40, 0x1C, 0xC0, 0xB2, 0x08, 0x70, 0x10, 0x2F, + 0x02, 0xDB, 0x62, 0x42, 0xBA, 0x42, 0x13, 0xDD, + 0x1E, 0x4A, 0x12, 0x7A, 0x01, 0x2A, 0x05, 0xD9, + 0x03, 0x26, 0x02, 0x25, 0x04, 0xE0, 0xFE, 0xF7, + 0xB9, 0xFA, 0xFE, 0xBD, 0x01, 0x26, 0x35, 0x46, + 0x09, 0x22, 0xD2, 0x43, 0x94, 0x42, 0x03, 0xDD, + 0x02, 0x28, 0x01, 0xD9, 0x18, 0x20, 0x08, 0x70, + 0xC1, 0xB2, 0x2B, 0x46, 0x32, 0x46, 0x19, 0xA0, + 0x01, 0xF0, 0x2C, 0xFE, 0x1C, 0x4C, 0x1D, 0x4F, + 0x00, 0x20, 0x42, 0x00, 0x11, 0x19, 0x00, 0x23, + 0xCB, 0x5E, 0xBA, 0x5E, 0x73, 0x43, 0x9A, 0x18, + 0x2A, 0x41, 0x40, 0x1C, 0xC0, 0xB2, 0x0A, 0x80, + 0x30, 0x28, 0xF2, 0xD3, 0xFE, 0xBD, 0x04, 0x20, + 0x10, 0x70, 0xFE, 0xBD, 0x87, 0x02, 0x00, 0x20, + 0xB8, 0x02, 0x00, 0x20, 0x7A, 0x04, 0x00, 0x20, + 0x70, 0x04, 0x00, 0x20, 0x78, 0x04, 0x00, 0x20, + 0x75, 0x04, 0x00, 0x20, 0x8E, 0x02, 0x00, 0x20, + 0x7C, 0x04, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, + 0xED, 0x02, 0x00, 0x20, 0xEB, 0x02, 0x00, 0x20, + 0xF0, 0x02, 0x00, 0x20, 0x0C, 0x03, 0x00, 0x20, + 0xEA, 0x02, 0x00, 0x20, 0x46, 0x61, 0x73, 0x74, + 0x4B, 0x25, 0x64, 0x28, 0x25, 0x64, 0x3A, 0x25, + 0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x20, 0xD4, 0x00, 0x00, 0x20, + 0xF8, 0xB5, 0x3F, 0x4F, 0x01, 0x24, 0x38, 0x7B, + 0x3E, 0x49, 0x0A, 0x78, 0x3E, 0x4E, 0x3F, 0x4D, + 0x90, 0x42, 0x05, 0xD1, 0x28, 0x78, 0x81, 0x28, + 0x02, 0xD0, 0x30, 0x78, 0xC0, 0x07, 0x66, 0xD0, + 0x00, 0x20, 0x30, 0x70, 0x37, 0x48, 0x00, 0x78, + 0x07, 0x28, 0x0C, 0xD3, 0x38, 0x48, 0x00, 0x68, + 0x40, 0x05, 0x40, 0x0F, 0x38, 0x73, 0x01, 0x20, + 0xFF, 0xF7, 0x06, 0xFE, 0x00, 0x20, 0x30, 0x70, + 0x38, 0x7B, 0x02, 0x28, 0x18, 0xD0, 0xA8, 0x79, + 0x00, 0x28, 0x01, 0xD0, 0xFE, 0xF7, 0x36, 0xFE, + 0x38, 0x7B, 0x2C, 0x4F, 0x38, 0x70, 0x81, 0x20, + 0x28, 0x70, 0x2E, 0x4A, 0x01, 0x21, 0x10, 0x88, + 0x09, 0x03, 0x08, 0x43, 0x10, 0x80, 0x38, 0x78, + 0x03, 0x00, 0xFD, 0xF7, 0x9F, 0xFA, 0x07, 0x25, + 0x35, 0x35, 0x3B, 0x0B, 0x18, 0x0B, 0x3B, 0x00, + 0x83, 0x20, 0x28, 0x70, 0x05, 0x20, 0xFF, 0xF7, + 0x3D, 0xFC, 0xF8, 0xBD, 0x24, 0xA0, 0x01, 0xF0, + 0xA9, 0xFD, 0x01, 0x20, 0xFE, 0xF7, 0xB4, 0xFD, + 0x00, 0x20, 0xFF, 0xF7, 0x33, 0xFC, 0x00, 0x28, + 0x1C, 0xD0, 0x01, 0x20, 0x18, 0xE0, 0x20, 0xA0, + 0x01, 0xF0, 0x9C, 0xFD, 0x01, 0x20, 0xFE, 0xF7, + 0xA7, 0xFD, 0x01, 0x20, 0xFF, 0xF7, 0x26, 0xFC, + 0x00, 0x28, 0x0F, 0xD0, 0x04, 0x20, 0x0B, 0xE0, + 0x1B, 0xA0, 0x01, 0xF0, 0x8F, 0xFD, 0x00, 0x20, + 0xFE, 0xF7, 0x9A, 0xFD, 0x02, 0x20, 0xFF, 0xF7, + 0x19, 0xFC, 0x00, 0x28, 0x02, 0xD0, 0x02, 0x20, + 0x28, 0x70, 0x07, 0xE0, 0x00, 0x24, 0x05, 0xE0, + 0x83, 0x20, 0x28, 0x70, 0x05, 0x20, 0xFF, 0xF7, + 0x0D, 0xFC, 0x04, 0x46, 0x2A, 0x78, 0x39, 0x78, + 0x11, 0xA0, 0x01, 0xF0, 0x77, 0xFD, 0x30, 0x78, + 0xC0, 0x07, 0x02, 0xD0, 0x81, 0x20, 0x28, 0x70, + 0x00, 0x24, 0x20, 0x46, 0xF8, 0xBD, 0x00, 0x00, + 0x50, 0x02, 0x00, 0x20, 0xE5, 0x02, 0x00, 0x20, + 0xCE, 0x00, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, + 0x00, 0x11, 0x00, 0x50, 0xF6, 0x02, 0x00, 0x20, + 0x44, 0x41, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x49, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x53, 0x54, 0x42, 0x0D, 0x0A, 0x00, 0x00, + 0x44, 0x53, 0x50, 0x3D, 0x25, 0x64, 0x2C, 0x50, + 0x57, 0x52, 0x3D, 0x25, 0x64, 0x20, 0x0D, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x70, 0xB5, 0x0E, 0x4D, + 0x28, 0x68, 0xFE, 0xF7, 0xFB, 0xF8, 0x04, 0x00, + 0x07, 0xD0, 0x80, 0x21, 0x0B, 0x48, 0xFD, 0xF7, + 0xC4, 0xF9, 0x0A, 0x49, 0x28, 0x68, 0x00, 0xF0, + 0xE7, 0xF8, 0x09, 0x48, 0x00, 0x88, 0x81, 0x07, + 0x06, 0xD0, 0x00, 0x07, 0x04, 0xD5, 0x80, 0x21, + 0x04, 0x48, 0xFD, 0xF7, 0xB6, 0xF9, 0x00, 0x24, + 0xFF, 0xF7, 0x52, 0xFE, 0x20, 0x46, 0x70, 0xBD, + 0xF8, 0x02, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, + 0xF6, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x46, + 0x01, 0x24, 0xFE, 0xF7, 0xFF, 0xFB, 0x00, 0x2D, + 0x09, 0xD0, 0xF0, 0x20, 0xFD, 0xF7, 0xD4, 0xF9, + 0xFE, 0xF7, 0xC6, 0xFC, 0xFE, 0xF7, 0x5C, 0xFC, + 0x00, 0x20, 0xFE, 0xF7, 0x25, 0xFD, 0xFE, 0xF7, + 0xD9, 0xFD, 0xFF, 0xF7, 0xB5, 0xFA, 0x20, 0x46, + 0x70, 0xBD, 0x00, 0x00, 0x70, 0xB5, 0x35, 0x49, + 0x08, 0x78, 0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, + 0x08, 0x70, 0x33, 0x4B, 0x33, 0x49, 0x18, 0x78, + 0x09, 0x78, 0x00, 0x25, 0x32, 0x4A, 0x33, 0x4C, + 0x00, 0x28, 0x07, 0xD1, 0x5E, 0x78, 0x00, 0x2E, + 0x04, 0xD1, 0x9B, 0x78, 0x00, 0x2B, 0x01, 0xD1, + 0x00, 0x29, 0x12, 0xD0, 0x65, 0x80, 0xA3, 0x88, + 0xE1, 0x26, 0xB6, 0x00, 0xB3, 0x42, 0x08, 0xD2, + 0x01, 0x43, 0x06, 0xD0, 0x2A, 0x49, 0x09, 0x78, + 0x04, 0x29, 0xA1, 0x88, 0x00, 0xD1, 0x49, 0x1C, + 0xA1, 0x80, 0x00, 0x28, 0x19, 0xD0, 0x15, 0x70, + 0x1C, 0xE0, 0xA0, 0x88, 0x00, 0x28, 0x05, 0xD0, + 0x20, 0x7A, 0xFF, 0x28, 0x02, 0xD2, 0x20, 0x7A, + 0x40, 0x1C, 0x20, 0x72, 0xA5, 0x80, 0x60, 0x88, + 0x01, 0x21, 0x09, 0x03, 0x88, 0x42, 0x02, 0xD2, + 0x60, 0x88, 0x40, 0x1C, 0x60, 0x80, 0x1D, 0x49, + 0x08, 0x78, 0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, + 0x08, 0x70, 0x10, 0x78, 0x00, 0x28, 0x01, 0xD0, + 0x40, 0x1E, 0x10, 0x70, 0x20, 0x78, 0x01, 0x28, + 0x16, 0xD0, 0x02, 0x28, 0x14, 0xD0, 0x04, 0x28, + 0x12, 0xD0, 0x82, 0x28, 0x12, 0xD1, 0x14, 0x4E, + 0x30, 0x78, 0x30, 0x28, 0x0E, 0xD1, 0x13, 0xA0, + 0x01, 0xF0, 0xB8, 0xFC, 0x05, 0x20, 0xFF, 0xF7, + 0xEB, 0xFC, 0xFD, 0xF7, 0x37, 0xFC, 0x13, 0xA0, + 0x01, 0xF0, 0xB0, 0xFC, 0x35, 0x70, 0x01, 0xE0, + 0x00, 0xF0, 0xDE, 0xF8, 0x14, 0x48, 0x00, 0x78, + 0xC0, 0x07, 0x01, 0xD0, 0x81, 0x20, 0x20, 0x70, + 0x70, 0xBD, 0x00, 0x00, 0x70, 0x04, 0x00, 0x20, + 0xA0, 0x04, 0x00, 0x20, 0x73, 0x04, 0x00, 0x20, + 0x72, 0x04, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, + 0xE5, 0x02, 0x00, 0x20, 0x75, 0x04, 0x00, 0x20, + 0x88, 0x02, 0x00, 0x20, 0x73, 0x6C, 0x65, 0x65, + 0x70, 0x20, 0x6D, 0x6F, 0x64, 0x65, 0x0D, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x6C, 0x65, 0x61, 0x76, + 0x65, 0x20, 0x73, 0x6C, 0x65, 0x65, 0x70, 0x20, + 0x6D, 0x6F, 0x64, 0x65, 0x0D, 0x0A, 0x00, 0x00, + 0xCE, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x10, 0x4D, + 0x68, 0x78, 0x00, 0x07, 0x01, 0xD5, 0x00, 0xF0, + 0x45, 0xF9, 0x0E, 0x4C, 0x60, 0x22, 0x0E, 0x48, + 0x21, 0x68, 0xFD, 0xF7, 0xDD, 0xF8, 0xFD, 0xF7, + 0x2D, 0xFA, 0x68, 0x78, 0x96, 0x21, 0x08, 0x42, + 0x01, 0xD0, 0x00, 0xF0, 0x37, 0xF9, 0x09, 0x48, + 0x00, 0x78, 0x00, 0x06, 0x06, 0xD4, 0x01, 0x21, + 0x20, 0x68, 0xFE, 0xF7, 0x2F, 0xF9, 0x20, 0x68, + 0xFD, 0xF7, 0xF0, 0xFA, 0x70, 0xBD, 0x00, 0x00, + 0x88, 0x02, 0x00, 0x20, 0xF8, 0x02, 0x00, 0x20, + 0xD4, 0x00, 0x00, 0x20, 0x86, 0x02, 0x00, 0x20, + 0x30, 0xB5, 0x08, 0x4A, 0x14, 0x68, 0x1B, 0x34, + 0x00, 0x22, 0xA3, 0x5C, 0x41, 0x2B, 0x03, 0xD0, + 0x55, 0x00, 0x45, 0x5B, 0x5B, 0x00, 0xCD, 0x52, + 0x52, 0x1C, 0xD2, 0xB2, 0x30, 0x2A, 0xF4, 0xD3, + 0x30, 0xBD, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20, + 0x01, 0x20, 0x80, 0x07, 0x40, 0x69, 0x40, 0x05, + 0x01, 0xD5, 0x01, 0x20, 0x70, 0x47, 0x00, 0x20, + 0x70, 0x47, 0x00, 0x00, 0x10, 0xB5, 0xFF, 0xF7, + 0x25, 0xFC, 0x04, 0x48, 0x00, 0x78, 0x00, 0x28, + 0x02, 0xD1, 0x01, 0x20, 0xFF, 0xF7, 0xF2, 0xF9, + 0x10, 0xBD, 0x00, 0x00, 0x90, 0x02, 0x00, 0x20, + 0x36, 0x03, 0x35, 0x03, 0x2C, 0x03, 0x9C, 0x03, + 0xE9, 0x02, 0x28, 0x03, 0x29, 0x03, 0x1C, 0x03, + 0x9D, 0x03, 0x2F, 0x03, 0x4A, 0x03, 0x4D, 0x03, + 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, + 0x3D, 0x03, 0x3C, 0x03, 0x2C, 0x03, 0x9C, 0x03, + 0xD3, 0x02, 0x28, 0x03, 0x29, 0x03, 0xD6, 0x02, + 0x9E, 0x03, 0x30, 0x03, 0x37, 0x03, 0x3A, 0x03, + 0xFF, 0x03, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0xB5, 0x00, 0x20, 0xFD, 0xF7, 0xCC, 0xFA, + 0x05, 0x49, 0x08, 0x20, 0x08, 0x70, 0x05, 0x49, + 0x06, 0x20, 0x08, 0x70, 0x01, 0x20, 0x04, 0x49, + 0x80, 0x02, 0x08, 0x80, 0x10, 0xBD, 0x00, 0x00, + 0xE5, 0x02, 0x00, 0x20, 0x71, 0x04, 0x00, 0x20, + 0xF2, 0x02, 0x00, 0x20, 0x06, 0x49, 0x01, 0x20, + 0x08, 0x70, 0x06, 0x49, 0x08, 0x20, 0x08, 0x70, + 0x05, 0x49, 0x06, 0x20, 0x08, 0x70, 0x01, 0x20, + 0x04, 0x49, 0x80, 0x02, 0x08, 0x80, 0x70, 0x47, + 0xE7, 0x02, 0x00, 0x20, 0xE5, 0x02, 0x00, 0x20, + 0x71, 0x04, 0x00, 0x20, 0xF2, 0x02, 0x00, 0x20, + 0xF8, 0xB5, 0x3E, 0x4C, 0x3E, 0x4A, 0x20, 0x88, + 0x3E, 0x4D, 0xC1, 0x04, 0x0C, 0xD5, 0x51, 0x10, + 0x88, 0x43, 0x20, 0x80, 0x29, 0x78, 0x00, 0x29, + 0x03, 0xD0, 0x01, 0x29, 0x01, 0xD0, 0x02, 0x29, + 0x65, 0xD1, 0x10, 0x43, 0x20, 0x80, 0x62, 0xE0, + 0x81, 0x04, 0x03, 0xD5, 0x90, 0x43, 0x20, 0x80, + 0xFE, 0xF7, 0xF8, 0xFB, 0x20, 0x88, 0x81, 0x07, + 0x0E, 0xD0, 0x01, 0x04, 0x57, 0xD5, 0x40, 0x04, + 0x40, 0x0C, 0x20, 0x80, 0x81, 0x07, 0x01, 0xD5, + 0x00, 0x20, 0x02, 0xE0, 0xC0, 0x07, 0x4E, 0xD0, + 0x01, 0x20, 0xFF, 0xF7, 0x67, 0xFB, 0x4A, 0xE0, + 0x00, 0x04, 0x5A, 0x21, 0x2A, 0x4F, 0x2B, 0x4E, + 0x00, 0x28, 0x05, 0xDA, 0x38, 0x78, 0x00, 0x28, + 0x00, 0xD0, 0x31, 0x80, 0x00, 0x20, 0x20, 0x80, + 0x27, 0x48, 0x40, 0x88, 0x00, 0x28, 0x19, 0xD0, + 0x26, 0x48, 0x00, 0x78, 0x04, 0x28, 0x15, 0xD3, + 0x25, 0x4A, 0x00, 0x20, 0x10, 0x5E, 0x27, 0x22, + 0xD2, 0x43, 0x90, 0x42, 0x0E, 0xDB, 0x23, 0x48, + 0x00, 0x68, 0x90, 0x30, 0x42, 0x7B, 0x03, 0x7B, + 0x10, 0x02, 0x18, 0x43, 0x42, 0x00, 0x80, 0x18, + 0x1F, 0x4A, 0x00, 0x23, 0xD3, 0x5E, 0x80, 0x08, + 0x98, 0x42, 0x01, 0xDA, 0x31, 0x80, 0x04, 0xE0, + 0x30, 0x88, 0x00, 0x28, 0x1B, 0xD0, 0x5A, 0x28, + 0x08, 0xD1, 0x38, 0x78, 0x00, 0x28, 0x05, 0xD1, + 0x18, 0xA0, 0x01, 0xF0, 0x6B, 0xFB, 0x01, 0x20, + 0xFF, 0xF7, 0x2C, 0xFB, 0x28, 0x78, 0x02, 0x28, + 0x04, 0xD1, 0x30, 0x88, 0x5A, 0x28, 0x01, 0xD1, + 0x2D, 0x20, 0x30, 0x80, 0x30, 0x88, 0x40, 0x1E, + 0x80, 0xB2, 0x30, 0x80, 0x39, 0x78, 0x00, 0x29, + 0x01, 0xD0, 0x00, 0x28, 0x02, 0xD0, 0xFD, 0xF7, + 0x2B, 0xF9, 0xF8, 0xBD, 0x0D, 0xA0, 0x01, 0xF0, + 0x51, 0xFB, 0xA5, 0xE7, 0xF6, 0x02, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x00, 0x85, 0x02, 0x00, 0x20, + 0x74, 0x04, 0x00, 0x20, 0x7E, 0x04, 0x00, 0x20, + 0x84, 0x04, 0x00, 0x20, 0x87, 0x02, 0x00, 0x20, + 0x76, 0x04, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, + 0x78, 0x04, 0x00, 0x20, 0x53, 0x50, 0x55, 0x0D, + 0x0A, 0x00, 0x00, 0x00, 0x53, 0x50, 0x44, 0x0D, + 0x0A, 0x00, 0x00, 0x00, 0xF8, 0xB5, 0x55, 0x4E, + 0x30, 0x78, 0x00, 0x28, 0x7E, 0xD1, 0x70, 0x78, + 0x44, 0x08, 0x70, 0x88, 0x64, 0x00, 0x00, 0x0A, + 0xC0, 0x07, 0x03, 0xD0, 0x70, 0x88, 0x00, 0x0A, + 0x00, 0x02, 0x04, 0x43, 0x30, 0x78, 0x00, 0x28, + 0x01, 0xD1, 0x70, 0x88, 0x04, 0x43, 0x70, 0x78, + 0xC0, 0x07, 0x6B, 0xD1, 0x4A, 0x48, 0x42, 0x2C, + 0x41, 0x69, 0x2F, 0xD0, 0x0F, 0xDC, 0x49, 0x4F, + 0x08, 0x2C, 0x2F, 0xD0, 0x05, 0xDC, 0x02, 0x2C, + 0x1A, 0xD0, 0x04, 0x2C, 0x7C, 0xD1, 0x46, 0x4F, + 0x16, 0xE0, 0x10, 0x2C, 0x26, 0xD0, 0x20, 0x2C, + 0x76, 0xD1, 0x80, 0x22, 0x5B, 0xE0, 0x50, 0x2C, + 0x04, 0xD0, 0x05, 0xDC, 0x44, 0x2C, 0x1B, 0xD0, + 0x48, 0x2C, 0x6D, 0xD1, 0x87, 0x69, 0x05, 0xE0, + 0xFF, 0x3C, 0x09, 0x3C, 0x01, 0xD0, 0x08, 0x2C, + 0x66, 0xD1, 0x0F, 0x46, 0x00, 0x2F, 0x63, 0xD0, + 0x80, 0x21, 0x3A, 0x48, 0xFC, 0xF7, 0x75, 0xFF, + 0x39, 0x48, 0x00, 0x78, 0x00, 0x06, 0x5C, 0xD5, + 0x60, 0x22, 0x39, 0x46, 0x35, 0x48, 0xFC, 0xF7, + 0x53, 0xFF, 0x5A, 0xE0, 0x35, 0x4F, 0xEF, 0xE7, + 0x35, 0x4F, 0xED, 0xE7, 0x60, 0x22, 0x35, 0x48, + 0xFC, 0xF7, 0x4A, 0xFF, 0x33, 0x4D, 0x20, 0x07, + 0x0A, 0xD5, 0x2F, 0x20, 0x41, 0x00, 0xCB, 0x19, + 0x6A, 0x5A, 0x1B, 0x88, 0x40, 0x1E, 0xD2, 0x1A, + 0x40, 0xB2, 0x6A, 0x52, 0x00, 0x28, 0xF5, 0xDA, + 0x00, 0x21, 0x2C, 0x48, 0xFD, 0xF7, 0x9A, 0xFF, + 0x2A, 0x48, 0xFD, 0xF7, 0x5B, 0xF9, 0x26, 0x48, + 0x00, 0x78, 0x00, 0x06, 0x04, 0xD5, 0x60, 0x22, + 0x26, 0x49, 0x27, 0x48, 0xFC, 0xF7, 0x2C, 0xFF, + 0x24, 0x48, 0xFD, 0xF7, 0x73, 0xFE, 0x20, 0x07, + 0x0A, 0xD5, 0x2F, 0x20, 0x41, 0x00, 0xCB, 0x19, + 0x6A, 0x5A, 0x1B, 0x88, 0x40, 0x1E, 0xD2, 0x18, + 0x40, 0xB2, 0x6A, 0x52, 0x00, 0x28, 0xF5, 0xDA, + 0x19, 0x48, 0x00, 0xE0, 0x1C, 0xE0, 0x00, 0x78, + 0x00, 0x06, 0x05, 0xD5, 0x60, 0x22, 0x19, 0x49, + 0x14, 0x48, 0xFC, 0xF7, 0x11, 0xFF, 0x0F, 0xE0, + 0x18, 0x49, 0x12, 0x4B, 0x0A, 0x68, 0x00, 0x20, + 0x11, 0x18, 0xC9, 0x7E, 0x41, 0x29, 0x03, 0xD0, + 0x44, 0x00, 0x2C, 0x5B, 0x49, 0x00, 0x5C, 0x52, + 0x40, 0x1C, 0x40, 0xB2, 0x30, 0x28, 0xF3, 0xDB, + 0x70, 0x78, 0x01, 0x21, 0x08, 0x43, 0x70, 0x70, + 0xF8, 0xBD, 0x08, 0x49, 0x38, 0x46, 0xFF, 0xF7, + 0x37, 0xFE, 0x71, 0x78, 0x01, 0x20, 0x01, 0x43, + 0x71, 0x70, 0xF8, 0xBD, 0x88, 0x02, 0x00, 0x20, + 0xE4, 0x02, 0x00, 0x20, 0x00, 0x00, 0x01, 0x20, + 0x00, 0x20, 0x00, 0x50, 0x9C, 0x01, 0x00, 0x20, + 0x86, 0x02, 0x00, 0x20, 0x00, 0x00, 0x02, 0x20, + 0x00, 0x30, 0x00, 0x50, 0x4C, 0x07, 0x00, 0x20, + 0x4C, 0x00, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, + 0xF7, 0xB5, 0x45, 0x49, 0x84, 0xB0, 0x00, 0x28, + 0x13, 0xD0, 0x44, 0x48, 0x00, 0x88, 0x84, 0xB2, + 0x05, 0x98, 0x00, 0x28, 0x03, 0xD0, 0x90, 0x68, + 0x01, 0x26, 0x10, 0x60, 0x00, 0xE0, 0x26, 0x46, + 0x20, 0x20, 0x03, 0x90, 0x08, 0x68, 0x28, 0x27, + 0x80, 0x30, 0x00, 0x7C, 0x02, 0x90, 0x3C, 0x48, + 0x13, 0xE0, 0x3C, 0x48, 0x00, 0x88, 0x84, 0xB2, + 0x05, 0x98, 0x00, 0x28, 0x03, 0xD0, 0x90, 0x68, + 0x01, 0x26, 0x10, 0x60, 0x00, 0xE0, 0x26, 0x46, + 0x20, 0x20, 0x03, 0x90, 0x08, 0x68, 0x28, 0x27, + 0x80, 0x30, 0x40, 0x7C, 0x40, 0x08, 0x02, 0x90, + 0x33, 0x48, 0x05, 0x78, 0x10, 0x68, 0x00, 0x28, + 0x01, 0xDC, 0x01, 0x20, 0x10, 0x60, 0x53, 0x68, + 0x01, 0x46, 0xC0, 0x18, 0x00, 0x04, 0x40, 0x0C, + 0x83, 0x42, 0x01, 0xDB, 0x18, 0x46, 0x00, 0xE0, + 0x50, 0x60, 0x41, 0x18, 0x68, 0x43, 0xFC, 0xF7, + 0x87, 0xFE, 0x00, 0xB2, 0x00, 0x90, 0x29, 0x48, + 0x00, 0x88, 0xC1, 0x00, 0x40, 0x18, 0xC0, 0x08, + 0x28, 0x1A, 0x81, 0xB2, 0x00, 0x98, 0x01, 0x91, + 0x88, 0x42, 0x12, 0xDD, 0x09, 0x21, 0xE8, 0x08, + 0xFC, 0xF7, 0x76, 0xFE, 0x01, 0x06, 0x09, 0x0E, + 0x00, 0xD1, 0x01, 0x21, 0x01, 0x9A, 0x00, 0x98, + 0x80, 0x1A, 0xFC, 0xF7, 0x6D, 0xFE, 0x20, 0x30, + 0xC0, 0xB2, 0x28, 0x28, 0x02, 0xD2, 0x07, 0x46, + 0x00, 0xE0, 0x03, 0x9F, 0x00, 0x98, 0x69, 0x00, + 0x47, 0x43, 0x08, 0x37, 0x38, 0x11, 0x08, 0x1A, + 0x00, 0xB2, 0x00, 0x28, 0x01, 0xDA, 0x00, 0x20, + 0x02, 0xE0, 0xA8, 0x42, 0x00, 0xDD, 0x28, 0x46, + 0x05, 0x99, 0x00, 0x29, 0x01, 0xD0, 0x30, 0x18, + 0x00, 0xE0, 0x30, 0x1A, 0x02, 0x99, 0x65, 0x08, + 0x49, 0x00, 0x40, 0x1B, 0x61, 0x1A, 0x60, 0x43, + 0x4A, 0x10, 0x80, 0x18, 0xFC, 0xF7, 0x48, 0xFE, + 0x28, 0x18, 0x01, 0x28, 0x01, 0xDA, 0x01, 0x20, + 0x02, 0xE0, 0xA0, 0x42, 0x00, 0xDD, 0x20, 0x46, + 0x40, 0x1E, 0x00, 0xB2, 0x07, 0xB0, 0xF0, 0xBD, + 0xB8, 0x02, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20, + 0x2E, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, + 0x2F, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x20, + 0xF7, 0xB5, 0x1A, 0x49, 0x09, 0x68, 0x80, 0x31, + 0x00, 0x28, 0x06, 0xD0, 0x18, 0x48, 0x0E, 0x7C, + 0x00, 0x88, 0xCD, 0x7C, 0x84, 0xB2, 0x4F, 0x7D, + 0x05, 0xE0, 0x16, 0x48, 0x4E, 0x7C, 0x00, 0x88, + 0x0D, 0x7D, 0x8F, 0x7D, 0x84, 0xB2, 0x10, 0x69, + 0xD1, 0x68, 0x68, 0x43, 0x4A, 0x10, 0x80, 0x18, + 0xFC, 0xF7, 0x16, 0xFE, 0xC1, 0x19, 0x01, 0x98, + 0x40, 0x1E, 0x68, 0x43, 0x08, 0x18, 0x69, 0x08, + 0x40, 0x1A, 0x65, 0x08, 0x71, 0x00, 0x40, 0x1B, + 0x61, 0x1A, 0x60, 0x43, 0x4A, 0x10, 0x80, 0x18, + 0xFC, 0xF7, 0x06, 0xFE, 0x28, 0x18, 0x01, 0x28, + 0x01, 0xDA, 0x01, 0x20, 0x02, 0xE0, 0xA0, 0x42, + 0x00, 0xDD, 0x20, 0x46, 0x40, 0x1E, 0x00, 0xB2, + 0xFE, 0xBD, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20, + 0x42, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, + 0x05, 0x48, 0x00, 0x68, 0x90, 0x30, 0x41, 0x7B, + 0x02, 0x7B, 0x08, 0x02, 0x10, 0x43, 0x03, 0x4A, + 0x02, 0x21, 0x11, 0x80, 0x00, 0xB2, 0x70, 0x47, + 0xB8, 0x02, 0x00, 0x20, 0x26, 0x00, 0x00, 0x20, + 0x70, 0xB5, 0x1A, 0x4D, 0x16, 0x20, 0x28, 0x70, + 0x68, 0x78, 0x29, 0x46, 0x00, 0x24, 0x14, 0x31, + 0x02, 0x28, 0x0C, 0xD8, 0x00, 0x28, 0x05, 0xD1, + 0xA8, 0x78, 0x00, 0x28, 0x02, 0xD0, 0x08, 0x46, + 0x1C, 0x30, 0x04, 0x70, 0x48, 0x88, 0x00, 0x28, + 0x01, 0xD1, 0x03, 0x20, 0x68, 0x71, 0xAC, 0x70, + 0x2C, 0x81, 0xEC, 0x80, 0xAC, 0x81, 0x6C, 0x81, + 0x4C, 0x80, 0x8C, 0x80, 0x0C, 0x72, 0x0C, 0x48, + 0x44, 0x70, 0x84, 0x70, 0x04, 0x70, 0x09, 0x48, + 0xB4, 0x21, 0xD0, 0x30, 0xFC, 0xF7, 0xE9, 0xFD, + 0x06, 0x48, 0xA0, 0x21, 0x30, 0x30, 0xFC, 0xF7, + 0xE4, 0xFD, 0xEC, 0x70, 0x5A, 0x20, 0xE8, 0x81, + 0x2C, 0x71, 0x2C, 0x61, 0x01, 0xF0, 0x66, 0xFC, + 0x70, 0xBD, 0x00, 0x00, 0x70, 0x04, 0x00, 0x20, + 0x90, 0x02, 0x00, 0x20, 0xFF, 0xB5, 0xA5, 0xB0, + 0x18, 0xB2, 0x17, 0x90, 0x00, 0x20, 0x0D, 0x90, + 0x25, 0x98, 0x01, 0x78, 0x03, 0x91, 0x26, 0x98, + 0x03, 0x78, 0xFE, 0x4A, 0x04, 0x93, 0x10, 0x68, + 0x01, 0x46, 0x90, 0x31, 0xCB, 0x7A, 0x89, 0x7A, + 0x1B, 0x02, 0x0B, 0x43, 0xA0, 0x21, 0x1C, 0x93, + 0x0B, 0x56, 0x00, 0x21, 0x1F, 0x93, 0x18, 0x91, + 0x03, 0x7E, 0x07, 0x93, 0x41, 0x7E, 0x1A, 0x91, + 0x03, 0x99, 0x59, 0x43, 0x04, 0x9B, 0xC9, 0x18, + 0x89, 0xB2, 0x16, 0x91, 0x55, 0x21, 0x0A, 0x91, + 0x38, 0x21, 0x13, 0x91, 0x16, 0x99, 0xF0, 0x4B, + 0x49, 0x00, 0x22, 0x91, 0x59, 0x5E, 0x1C, 0x9B, + 0x99, 0x42, 0x04, 0xDA, 0x0A, 0x23, 0x59, 0x43, + 0x49, 0x11, 0x11, 0x91, 0x07, 0xE0, 0xCB, 0x00, + 0xC9, 0x18, 0x49, 0x11, 0x11, 0x91, 0x40, 0x21, + 0x0A, 0x91, 0x2A, 0x21, 0x13, 0x91, 0xB0, 0x30, + 0x41, 0x7A, 0x02, 0x7A, 0x08, 0x02, 0xE5, 0x49, + 0x10, 0x43, 0x0A, 0x88, 0xE4, 0x49, 0x90, 0x42, + 0x02, 0xD2, 0x08, 0x78, 0x01, 0x28, 0x03, 0xD0, + 0xE2, 0x48, 0x00, 0x78, 0x00, 0x28, 0x0B, 0xD0, + 0x0A, 0x98, 0x42, 0x00, 0x80, 0x18, 0xC0, 0x05, + 0x00, 0x0E, 0x0A, 0x90, 0x13, 0x98, 0x42, 0x00, + 0x80, 0x18, 0xC0, 0x05, 0x00, 0x0E, 0x13, 0x90, + 0x07, 0x9A, 0x04, 0x98, 0x10, 0x1A, 0x40, 0x1E, + 0x23, 0x90, 0x16, 0x98, 0x02, 0x90, 0x00, 0x20, + 0x08, 0x90, 0x0C, 0x90, 0x32, 0x21, 0xD6, 0x48, + 0xFC, 0xF7, 0x6F, 0xFD, 0x0A, 0x21, 0xD5, 0x48, + 0xFC, 0xF7, 0x6B, 0xFD, 0x0A, 0x21, 0xD4, 0x48, + 0xFC, 0xF7, 0x67, 0xFD, 0xD2, 0x48, 0x69, 0x46, + 0x00, 0x1D, 0x12, 0x90, 0xCF, 0x48, 0x00, 0x1D, + 0x09, 0x90, 0x00, 0x20, 0x48, 0x70, 0xCA, 0x48, + 0x00, 0x78, 0x01, 0x28, 0x01, 0xD9, 0x02, 0x20, + 0x00, 0xE0, 0x0E, 0x20, 0x08, 0x70, 0x88, 0x70, + 0x01, 0x20, 0x00, 0x24, 0x0E, 0x90, 0x2C, 0xE0, + 0x00, 0x2C, 0x2A, 0xD0, 0x0E, 0x98, 0x00, 0x28, + 0x03, 0x98, 0x13, 0xD0, 0x84, 0x42, 0x06, 0xD9, + 0x16, 0x98, 0x00, 0x24, 0x02, 0x90, 0x0E, 0x94, + 0x68, 0x46, 0x01, 0x70, 0x6D, 0xE1, 0x07, 0x99, + 0x02, 0x98, 0x40, 0x1A, 0x80, 0xB2, 0x02, 0x90, + 0x02, 0x20, 0x00, 0x1B, 0x41, 0x00, 0xBB, 0x48, + 0x08, 0x18, 0x0C, 0xE0, 0x01, 0x19, 0x1A, 0x98, + 0x81, 0x42, 0x77, 0xD0, 0x07, 0x99, 0x02, 0x98, + 0x40, 0x18, 0x80, 0xB2, 0x02, 0x90, 0xB5, 0x48, + 0x61, 0x00, 0x08, 0x18, 0x00, 0x1D, 0x69, 0x46, + 0x09, 0x90, 0x08, 0x78, 0x48, 0x70, 0x00, 0x20, + 0x08, 0x70, 0x00, 0x20, 0x19, 0x90, 0x15, 0x90, + 0x12, 0xE0, 0x00, 0x28, 0x10, 0xD0, 0x04, 0x98, + 0x14, 0x90, 0x00, 0x20, 0x0F, 0x90, 0x68, 0x46, + 0x40, 0x78, 0x01, 0x25, 0x08, 0x27, 0x18, 0x21, + 0x08, 0x40, 0x0B, 0x90, 0x14, 0x98, 0x02, 0x28, + 0x01, 0xD9, 0x02, 0x20, 0x14, 0x90, 0x17, 0xE1, + 0x23, 0x98, 0x00, 0x25, 0xC0, 0xB2, 0x14, 0x90, + 0x01, 0x20, 0x0F, 0x90, 0x68, 0x46, 0x40, 0x78, + 0x04, 0x27, 0x40, 0x07, 0x40, 0x0F, 0xEC, 0xE7, + 0x0F, 0x98, 0x00, 0x28, 0x01, 0xD0, 0x10, 0x95, + 0x01, 0xE0, 0x68, 0x42, 0x10, 0x90, 0x0B, 0x98, + 0xB8, 0x43, 0x0B, 0x90, 0x10, 0x99, 0x02, 0x98, + 0x40, 0x18, 0x1B, 0x90, 0x40, 0x00, 0x92, 0x49, + 0x1D, 0x90, 0x0E, 0x5E, 0x68, 0x46, 0x40, 0x78, + 0x1E, 0x90, 0x38, 0x42, 0x01, 0xD1, 0x00, 0x2C, + 0x7E, 0xD1, 0x00, 0x2E, 0x7C, 0xDD, 0xF0, 0x07, + 0x0B, 0xD1, 0x09, 0x98, 0x09, 0x99, 0x00, 0x88, + 0x80, 0x19, 0x08, 0x80, 0x10, 0x98, 0x12, 0x99, + 0x40, 0x00, 0x09, 0x5A, 0x12, 0x9A, 0x89, 0x19, + 0x11, 0x52, 0x11, 0x98, 0x86, 0x42, 0x6B, 0xDB, + 0xF0, 0x07, 0x69, 0xD1, 0x0A, 0x98, 0x70, 0x43, + 0x40, 0x11, 0x00, 0xB2, 0x20, 0x90, 0x13, 0x98, + 0x70, 0x43, 0x40, 0x11, 0x00, 0xB2, 0x21, 0x90, + 0x00, 0x20, 0x01, 0x90, 0x06, 0x90, 0x0E, 0x98, + 0x00, 0x28, 0x08, 0xD0, 0x00, 0x2C, 0x10, 0xD0, + 0x03, 0x98, 0x00, 0x1B, 0x00, 0x28, 0x0C, 0xDD, + 0x01, 0x20, 0x09, 0xE0, 0xFC, 0xE0, 0x00, 0x2C, + 0x07, 0xD0, 0x03, 0x98, 0x01, 0x19, 0x1A, 0x98, + 0x40, 0x1E, 0x81, 0x42, 0x01, 0xDA, 0x02, 0x20, + 0x01, 0x90, 0x0F, 0x98, 0x00, 0x28, 0x0A, 0xD0, + 0x00, 0x2D, 0x12, 0xD0, 0x04, 0x98, 0x41, 0x19, + 0x07, 0x98, 0x40, 0x1E, 0x81, 0x42, 0x0C, 0xDA, + 0x08, 0x21, 0x01, 0x98, 0x07, 0xE0, 0x00, 0x2D, + 0x07, 0xD0, 0x04, 0x98, 0x40, 0x1B, 0x00, 0x28, + 0x03, 0xDD, 0x01, 0x98, 0x04, 0x21, 0x08, 0x43, + 0x01, 0x90, 0x03, 0x98, 0x00, 0x1B, 0x00, 0x28, + 0x01, 0xDC, 0x01, 0x20, 0x06, 0xE0, 0x03, 0x98, + 0x01, 0x19, 0x1A, 0x98, 0x40, 0x1E, 0x81, 0x42, + 0x01, 0xDB, 0x02, 0x20, 0x06, 0x90, 0x04, 0x98, + 0x41, 0x19, 0x07, 0x98, 0x40, 0x1E, 0x81, 0x42, + 0x02, 0xDB, 0x08, 0x21, 0x06, 0x98, 0x05, 0xE0, + 0x04, 0x98, 0x40, 0x1B, 0x00, 0x28, 0x03, 0xDC, + 0x06, 0x98, 0x04, 0x21, 0x08, 0x43, 0x06, 0x90, + 0x20, 0x46, 0x28, 0x43, 0x4F, 0xD0, 0x00, 0x20, + 0x05, 0x90, 0x00, 0x2C, 0x14, 0xD0, 0x00, 0x2D, + 0x18, 0xD0, 0x41, 0x00, 0x3C, 0x20, 0xC8, 0x40, + 0x01, 0x99, 0x08, 0x40, 0x01, 0x07, 0x00, 0xE0, + 0x39, 0xE0, 0x09, 0x0F, 0x06, 0x98, 0x81, 0x43, + 0x1F, 0xD0, 0x1B, 0x98, 0x80, 0xB2, 0x00, 0xF0, + 0xFF, 0xFB, 0xC1, 0x07, 0x19, 0xD1, 0x0C, 0xE0, + 0x01, 0x46, 0x04, 0x20, 0xC8, 0x40, 0x81, 0x07, + 0x89, 0x0F, 0x03, 0xE0, 0x02, 0x21, 0x81, 0x40, + 0x0C, 0x20, 0x01, 0x40, 0x01, 0x98, 0x01, 0x43, + 0xE8, 0xE7, 0x18, 0x9A, 0x0D, 0x99, 0x11, 0x43, + 0x04, 0xD1, 0x21, 0x99, 0x88, 0x42, 0x01, 0xDD, + 0x01, 0x21, 0x18, 0x91, 0x20, 0x99, 0x88, 0x42, + 0x05, 0xDC, 0x05, 0x98, 0x40, 0x1C, 0xC0, 0xB2, + 0x05, 0x90, 0x03, 0x28, 0xC9, 0xD3, 0x05, 0x98, + 0x03, 0x28, 0x14, 0xD0, 0x09, 0x98, 0x09, 0x9A, + 0x01, 0x88, 0x70, 0x10, 0x09, 0x1A, 0x11, 0x80, + 0x10, 0x99, 0x12, 0x9A, 0x49, 0x00, 0x52, 0x5A, + 0x10, 0x1A, 0x12, 0x9A, 0x50, 0x52, 0x0B, 0x98, + 0x00, 0x28, 0x39, 0xD0, 0x0F, 0x98, 0x00, 0x28, + 0x2E, 0xD0, 0x7F, 0x08, 0x2E, 0xE0, 0x08, 0x99, + 0x1B, 0x98, 0x4A, 0x00, 0x2E, 0x49, 0x88, 0x52, + 0x08, 0x98, 0x40, 0x1C, 0x80, 0xB2, 0x08, 0x90, + 0x0C, 0x98, 0x86, 0x42, 0x00, 0xDD, 0x0C, 0x96, + 0x25, 0x48, 0x1D, 0x99, 0x42, 0x5A, 0x01, 0x21, + 0x0A, 0x43, 0x1D, 0x99, 0x42, 0x52, 0x69, 0x46, + 0x08, 0x78, 0x38, 0x43, 0x08, 0x70, 0x00, 0x2C, + 0x0D, 0xD0, 0x0F, 0x98, 0x00, 0x28, 0x1E, 0x98, + 0x05, 0xD0, 0x79, 0x08, 0x08, 0x43, 0x69, 0x46, + 0x48, 0x70, 0x00, 0x2D, 0x03, 0xD1, 0x79, 0x00, + 0x08, 0x43, 0x69, 0x46, 0x48, 0x70, 0x19, 0x98, + 0x40, 0x1C, 0xC0, 0xB2, 0x19, 0x90, 0xCD, 0xE7, + 0x78, 0x06, 0x07, 0x0E, 0x6D, 0x1C, 0xED, 0xB2, + 0x14, 0x98, 0x85, 0x42, 0x00, 0xD8, 0xEF, 0xE6, + 0x15, 0x98, 0x40, 0x1C, 0xC0, 0xB2, 0x15, 0x90, + 0x02, 0x28, 0x00, 0xD2, 0xC9, 0xE6, 0x00, 0x2C, + 0x02, 0xD1, 0x69, 0x46, 0x08, 0x78, 0x88, 0x70, + 0x19, 0x98, 0x00, 0x28, 0x01, 0xD0, 0x02, 0x2C, + 0x09, 0xD1, 0x0E, 0x98, 0x00, 0x28, 0x1F, 0xD0, + 0x16, 0x98, 0x00, 0x24, 0x69, 0x46, 0x0E, 0x94, + 0x02, 0x90, 0x88, 0x78, 0x08, 0x70, 0x68, 0x46, + 0x81, 0x78, 0x64, 0x1C, 0xE4, 0xB2, 0x02, 0x2C, + 0x10, 0xE0, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20, + 0x4C, 0x07, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20, + 0x40, 0x05, 0x00, 0x20, 0x2A, 0x00, 0x00, 0x20, + 0x1C, 0x02, 0x00, 0x20, 0x1A, 0x00, 0x00, 0x20, + 0x10, 0x00, 0x00, 0x20, 0x00, 0xD8, 0x6B, 0xE6, + 0x08, 0x98, 0x01, 0x28, 0x1B, 0xD0, 0x1F, 0x99, + 0x88, 0x42, 0x18, 0xDA, 0x2C, 0x48, 0x00, 0x68, + 0xB0, 0x30, 0x41, 0x7A, 0x02, 0x7A, 0x08, 0x02, + 0x2A, 0x49, 0x10, 0x43, 0x09, 0x88, 0x88, 0x42, + 0x03, 0xD2, 0x29, 0x48, 0x00, 0x78, 0x01, 0x28, + 0x09, 0xD0, 0x0D, 0x98, 0x01, 0x28, 0x06, 0xD0, + 0x18, 0x98, 0x00, 0x28, 0x03, 0xD0, 0x0D, 0x98, + 0x00, 0x28, 0x05, 0xD0, 0x2E, 0xE0, 0x17, 0x99, + 0x0C, 0x98, 0x88, 0x42, 0x2A, 0xDC, 0x38, 0xE0, + 0x01, 0x20, 0x0D, 0x90, 0x38, 0x20, 0x0A, 0x90, + 0x1E, 0x4A, 0x22, 0x98, 0x1C, 0x99, 0x10, 0x5E, + 0x88, 0x42, 0x05, 0xDA, 0x09, 0x21, 0x09, 0x03, + 0x48, 0x43, 0x00, 0x14, 0x11, 0x90, 0x06, 0xE0, + 0x05, 0x21, 0x49, 0x03, 0x48, 0x43, 0x00, 0x14, + 0x11, 0x90, 0x2A, 0x20, 0x0A, 0x90, 0x00, 0x20, + 0x15, 0x4B, 0x08, 0xE0, 0x41, 0x00, 0x59, 0x5A, + 0x49, 0x00, 0x54, 0x5A, 0x64, 0x08, 0x64, 0x00, + 0x40, 0x1C, 0x54, 0x52, 0x80, 0xB2, 0x08, 0x99, + 0x88, 0x42, 0xF3, 0xD3, 0x0D, 0x98, 0x02, 0x28, + 0x00, 0xD2, 0xF6, 0xE5, 0x27, 0x9A, 0x0D, 0x48, + 0x18, 0x32, 0x25, 0x99, 0xFD, 0xF7, 0x1E, 0xF9, + 0x0B, 0x48, 0x27, 0x9A, 0x26, 0x99, 0xFD, 0xF7, + 0x19, 0xF9, 0x17, 0x9A, 0x0C, 0x99, 0x91, 0x42, + 0x00, 0xDC, 0x08, 0x48, 0x29, 0xB0, 0xF0, 0xBD, + 0xB8, 0x02, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20, + 0x40, 0x05, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, + 0x1C, 0x02, 0x00, 0x20, 0x1A, 0x00, 0x00, 0x20, + 0x10, 0x00, 0x00, 0x20, 0x00, 0x80, 0xFF, 0xFF, + 0x06, 0x4A, 0x07, 0x4B, 0x00, 0x21, 0x00, 0x20, + 0x40, 0x1C, 0x90, 0x42, 0xFC, 0xDB, 0x49, 0x1C, + 0x99, 0x42, 0xF8, 0xDB, 0x03, 0x48, 0x01, 0x21, + 0x01, 0x60, 0x70, 0x47, 0x10, 0x27, 0x00, 0x00, + 0xB8, 0x0B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, + 0xF0, 0xB5, 0x1A, 0x48, 0x8F, 0xB0, 0x01, 0x78, + 0x02, 0x29, 0x01, 0xD9, 0x02, 0x21, 0x01, 0x70, + 0x00, 0x25, 0x24, 0xE0, 0x68, 0x1C, 0xC4, 0xB2, + 0x0D, 0x90, 0x1A, 0xE0, 0x34, 0x21, 0x69, 0x43, + 0x34, 0x22, 0x0E, 0x18, 0x62, 0x43, 0x17, 0x18, + 0x31, 0x79, 0x38, 0x79, 0x81, 0x42, 0x0E, 0xD9, + 0x31, 0x1D, 0x34, 0x22, 0x68, 0x46, 0xFC, 0xF7, + 0x03, 0xFB, 0x39, 0x1D, 0x30, 0x1D, 0x34, 0x22, + 0xFC, 0xF7, 0xFE, 0xFA, 0x38, 0x1D, 0x34, 0x22, + 0x69, 0x46, 0xFC, 0xF7, 0xF9, 0xFA, 0x64, 0x1C, + 0xE4, 0xB2, 0x06, 0x48, 0x01, 0x78, 0xA1, 0x42, + 0xE0, 0xD8, 0x0D, 0x98, 0xC5, 0xB2, 0x03, 0x49, + 0x08, 0x78, 0xA8, 0x42, 0xD6, 0xD8, 0x0F, 0xB0, + 0xF0, 0xBD, 0x00, 0x00, 0xA0, 0x04, 0x00, 0x20, + 0x70, 0xB5, 0x7B, 0x24, 0x21, 0x48, 0x24, 0x02, + 0x04, 0x60, 0xF7, 0x20, 0xC0, 0x01, 0x20, 0x4A, + 0xC1, 0x7C, 0x11, 0x70, 0x1F, 0x4A, 0x01, 0x7D, + 0x11, 0x70, 0x1F, 0x4A, 0x41, 0x7D, 0x11, 0x70, + 0x1E, 0x49, 0x80, 0x7D, 0x08, 0x70, 0x60, 0x7C, + 0x21, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, + 0x1B, 0x49, 0x05, 0x46, 0x08, 0x80, 0xE0, 0x7C, + 0xA1, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, + 0x18, 0x49, 0x08, 0x80, 0x20, 0x7E, 0x61, 0x7E, + 0x48, 0x43, 0x17, 0x49, 0x08, 0x80, 0x17, 0x48, + 0xFC, 0xF7, 0xE3, 0xFA, 0x10, 0x21, 0xC8, 0x41, + 0x15, 0x49, 0x08, 0x60, 0x04, 0x22, 0x21, 0x1D, + 0x14, 0x48, 0xFC, 0xF7, 0xB5, 0xFA, 0x11, 0x49, + 0x06, 0x22, 0x89, 0x1F, 0x12, 0x48, 0xFC, 0xF7, + 0xAF, 0xFA, 0x60, 0x7D, 0x22, 0x7D, 0x01, 0x02, + 0x11, 0x43, 0x28, 0x46, 0x0A, 0x22, 0x50, 0x43, + 0xFC, 0xF7, 0x92, 0xFA, 0x0D, 0x49, 0x08, 0x80, + 0x70, 0xBD, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x20, + 0x2C, 0x00, 0x00, 0x20, 0x2D, 0x00, 0x00, 0x20, + 0x2E, 0x00, 0x00, 0x20, 0x2F, 0x00, 0x00, 0x20, + 0x42, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, + 0x3C, 0x00, 0x00, 0x20, 0x10, 0x7B, 0x00, 0x00, + 0xD8, 0x06, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20, + 0xE4, 0x06, 0x00, 0x20, 0x40, 0x00, 0x00, 0x20, + 0x64, 0x24, 0x00, 0x20, 0x56, 0x49, 0x08, 0x70, + 0xA9, 0xE0, 0x55, 0x48, 0x00, 0x78, 0x03, 0x00, + 0xFC, 0xF7, 0xFC, 0xFA, 0x07, 0x05, 0x10, 0x1E, + 0x18, 0x37, 0x48, 0x50, 0xA3, 0x00, 0x00, 0xF0, + 0xCB, 0xF9, 0x01, 0x28, 0x02, 0xD1, 0x4E, 0x49, + 0x08, 0x70, 0x02, 0xE0, 0x03, 0x20, 0x4C, 0x49, + 0x08, 0x70, 0x93, 0xE0, 0x00, 0xF0, 0xD6, 0xF8, + 0x01, 0x28, 0x02, 0xD1, 0x02, 0x20, 0x48, 0x49, + 0x08, 0x70, 0x8B, 0xE0, 0x00, 0xF0, 0xB2, 0xF9, + 0x00, 0x20, 0x45, 0x49, 0x08, 0x70, 0x85, 0xE0, + 0xFF, 0xF7, 0x06, 0xF8, 0x01, 0x28, 0x10, 0xD1, + 0xFF, 0xF7, 0xE0, 0xF8, 0x41, 0x48, 0x00, 0x78, + 0x80, 0x21, 0x08, 0x42, 0x03, 0xD0, 0x01, 0x20, + 0x3D, 0x49, 0x08, 0x70, 0x02, 0xE0, 0x04, 0x20, + 0x3B, 0x49, 0x08, 0x70, 0xFC, 0xF7, 0x74, 0xFD, + 0x02, 0xE0, 0x01, 0x20, 0x38, 0x49, 0x08, 0x70, + 0x6C, 0xE0, 0x00, 0xF0, 0xBB, 0xF9, 0x01, 0x28, + 0x0B, 0xD1, 0x36, 0x48, 0x00, 0x78, 0x80, 0x21, + 0x08, 0x42, 0x03, 0xD0, 0x01, 0x20, 0x32, 0x49, + 0x08, 0x70, 0x02, 0xE0, 0x05, 0x20, 0x30, 0x49, + 0x08, 0x70, 0x5B, 0xE0, 0x00, 0xF0, 0x6A, 0xF8, + 0x01, 0x28, 0x02, 0xD1, 0x06, 0x20, 0x2C, 0x49, + 0x08, 0x70, 0x53, 0xE0, 0x2C, 0x48, 0x00, 0x78, + 0x00, 0x28, 0x09, 0xD1, 0x01, 0x20, 0xFC, 0xF7, + 0xEF, 0xFA, 0x00, 0x20, 0x29, 0x49, 0x08, 0x70, + 0x01, 0x20, 0x25, 0x49, 0x08, 0x70, 0x45, 0xE0, + 0xFF, 0xF7, 0x72, 0xF9, 0x01, 0x28, 0x2E, 0xD1, + 0x24, 0x48, 0x00, 0x78, 0x00, 0x28, 0x2A, 0xD1, + 0x01, 0xF0, 0xAA, 0xFA, 0x22, 0x48, 0xC0, 0x78, + 0x01, 0x28, 0x04, 0xD0, 0x21, 0x48, 0x40, 0x78, + 0xC0, 0x07, 0xC0, 0x0F, 0x2D, 0xD0, 0x01, 0x25, + 0xAD, 0x07, 0xA9, 0x14, 0x28, 0x68, 0x08, 0x40, + 0x00, 0x0A, 0x0A, 0x21, 0x08, 0x43, 0x19, 0x49, + 0xC8, 0x70, 0xB4, 0x24, 0x1A, 0x48, 0x00, 0x78, + 0xFF, 0x28, 0x05, 0xDA, 0x18, 0x48, 0x00, 0x78, + 0x40, 0x1C, 0x17, 0x49, 0x08, 0x70, 0x02, 0xE0, + 0x01, 0x20, 0x15, 0x49, 0x08, 0x70, 0x14, 0x48, + 0x00, 0x78, 0x10, 0x49, 0x08, 0x70, 0x00, 0x20, + 0xFC, 0xF7, 0xB6, 0xFA, 0x0D, 0xE0, 0x00, 0x2C, + 0x05, 0xD0, 0x60, 0x1E, 0xC4, 0xB2, 0x01, 0x20, + 0xFC, 0xF7, 0xBE, 0xFB, 0x05, 0xE0, 0x00, 0x20, + 0x08, 0x49, 0x08, 0x70, 0x01, 0x20, 0xFC, 0xF7, + 0xA7, 0xFA, 0x01, 0x20, 0x02, 0x49, 0x08, 0x70, + 0x00, 0xE0, 0x00, 0xBF, 0x00, 0xBF, 0x54, 0xE7, + 0x80, 0x02, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, + 0xE4, 0x02, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, + 0xA0, 0x04, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20, + 0x84, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x18, 0x48, + 0x00, 0x78, 0x03, 0x00, 0xFC, 0xF7, 0x42, 0xFA, + 0x06, 0x28, 0x04, 0x0A, 0x16, 0x1C, 0x22, 0x28, + 0x01, 0xF0, 0xD2, 0xF8, 0x02, 0x20, 0x12, 0x49, + 0x08, 0x70, 0x1D, 0xE0, 0x01, 0xF0, 0xC0, 0xF8, + 0x00, 0x28, 0x03, 0xD0, 0x03, 0x20, 0x0E, 0x49, + 0x08, 0x70, 0x02, 0xE0, 0x05, 0x20, 0x0C, 0x49, + 0x08, 0x70, 0x11, 0xE0, 0x01, 0xF0, 0xB0, 0xF8, + 0x04, 0x20, 0x09, 0x49, 0x08, 0x70, 0x0B, 0xE0, + 0x01, 0xF0, 0x14, 0xF9, 0x05, 0x20, 0x06, 0x49, + 0x08, 0x70, 0x05, 0xE0, 0x01, 0xF0, 0x3E, 0xFA, + 0x01, 0x20, 0x03, 0x49, 0x08, 0x70, 0x10, 0xBD, + 0x00, 0xBF, 0x00, 0x20, 0xFB, 0xE7, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x63, 0x4C, + 0x20, 0x78, 0x00, 0x28, 0x41, 0xD0, 0x02, 0x20, + 0xFC, 0xF7, 0x66, 0xFB, 0x62, 0x88, 0x21, 0x78, + 0x5F, 0xA0, 0x00, 0xF0, 0x1F, 0xFD, 0x60, 0x88, + 0x61, 0x4A, 0x00, 0x25, 0x90, 0x42, 0x02, 0xD1, + 0xA5, 0x70, 0x28, 0x0A, 0xE0, 0x70, 0x20, 0x78, + 0x5E, 0x4E, 0x5F, 0x49, 0x44, 0x28, 0x75, 0xD0, + 0x13, 0xDC, 0x01, 0x22, 0x33, 0x28, 0x34, 0xD0, + 0x08, 0xDC, 0x20, 0x28, 0x74, 0xD0, 0x30, 0x28, + 0x28, 0xD0, 0x31, 0x28, 0x3D, 0xD0, 0x32, 0x28, + 0x3B, 0xD1, 0x27, 0xE0, 0x34, 0x28, 0x39, 0xD0, + 0x42, 0x28, 0x6F, 0xD0, 0x43, 0x28, 0x34, 0xD1, + 0x90, 0xE0, 0x60, 0x28, 0x78, 0xD0, 0x07, 0xDC, + 0x5D, 0x26, 0x5A, 0x28, 0x0B, 0xD0, 0x5C, 0x28, + 0x0C, 0xD0, 0x5D, 0x28, 0x29, 0xD1, 0x0D, 0xE0, + 0x62, 0x28, 0x65, 0xD0, 0x70, 0x28, 0x53, 0xD0, + 0x71, 0x28, 0x7A, 0xD1, 0x71, 0xE0, 0xFF, 0xF7, + 0xEF, 0xF8, 0x01, 0xE0, 0xFF, 0xF7, 0x02, 0xF9, + 0x26, 0x70, 0x73, 0xE0, 0xC8, 0x78, 0x00, 0x28, + 0x6F, 0xD0, 0x6F, 0xE0, 0x45, 0x49, 0x82, 0x20, + 0x08, 0x70, 0x6B, 0xE0, 0x60, 0x88, 0x44, 0x49, + 0x54, 0xE0, 0x60, 0x88, 0x08, 0x82, 0xA5, 0x70, + 0x28, 0x0A, 0xE0, 0x70, 0x08, 0x8A, 0x04, 0x28, + 0x5F, 0xD1, 0xFF, 0xF7, 0x8D, 0xF8, 0x00, 0x28, + 0x02, 0xD1, 0x01, 0x20, 0xFC, 0xF7, 0xFC, 0xF9, + 0x35, 0x70, 0x56, 0xE0, 0x60, 0x88, 0xC0, 0x07, + 0x04, 0xD0, 0x48, 0x8A, 0x39, 0x4A, 0x80, 0x08, + 0x80, 0x00, 0x15, 0xE0, 0x60, 0x88, 0x80, 0x07, + 0x05, 0xD5, 0x48, 0x8A, 0x35, 0x4A, 0x80, 0x08, + 0x80, 0x00, 0x52, 0x1C, 0x0C, 0xE0, 0x60, 0x88, + 0xC0, 0x04, 0x48, 0x8A, 0x04, 0xD5, 0x80, 0x09, + 0x30, 0x4A, 0x80, 0x01, 0xD2, 0x1C, 0x03, 0xE0, + 0x80, 0x09, 0x80, 0x01, 0x01, 0x22, 0xD2, 0x03, + 0x10, 0x43, 0x48, 0x82, 0x60, 0x88, 0xC0, 0x05, + 0x03, 0xD5, 0x48, 0x8A, 0x08, 0x22, 0x10, 0x43, + 0x48, 0x82, 0x0D, 0x72, 0xA5, 0x70, 0x28, 0x0A, + 0xE0, 0x70, 0x2A, 0xE0, 0x30, 0xE0, 0x03, 0xE0, + 0x25, 0x70, 0x25, 0x48, 0x05, 0x70, 0x25, 0xE0, + 0x60, 0x88, 0x00, 0x28, 0xC8, 0x79, 0x03, 0xD0, + 0x10, 0x43, 0x03, 0xE0, 0x20, 0xE0, 0x0B, 0xE0, + 0x40, 0x08, 0x40, 0x00, 0xC8, 0x71, 0x18, 0xE0, + 0xC8, 0x69, 0x02, 0x80, 0x60, 0x30, 0xC2, 0x83, + 0x60, 0x88, 0x1C, 0x49, 0x08, 0x70, 0xE1, 0xE7, + 0x48, 0x89, 0xA0, 0x70, 0x00, 0x0A, 0xE0, 0x70, + 0x19, 0x48, 0x00, 0x68, 0x60, 0x30, 0x00, 0x78, + 0x04, 0xE0, 0x71, 0x20, 0xA0, 0x70, 0x00, 0x20, + 0xE0, 0x70, 0x0B, 0x20, 0x20, 0x71, 0x00, 0x0A, + 0x60, 0x71, 0x25, 0x70, 0x01, 0x20, 0x70, 0xBD, + 0x0A, 0x70, 0xFB, 0xE7, 0x0D, 0x70, 0xF9, 0xE7, + 0x01, 0x20, 0xFC, 0xF7, 0x99, 0xF9, 0x35, 0x70, + 0xF4, 0xE7, 0x00, 0x00, 0x88, 0x02, 0x00, 0x20, + 0x43, 0x4D, 0x44, 0x3A, 0x30, 0x78, 0x25, 0x78, + 0x2C, 0x30, 0x78, 0x25, 0x78, 0x0D, 0x0A, 0x00, + 0xAA, 0x55, 0x00, 0x00, 0x90, 0x02, 0x00, 0x20, + 0xE4, 0x02, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, + 0x2B, 0x00, 0x00, 0x20, 0x01, 0x80, 0x00, 0x00, + 0x80, 0x02, 0x00, 0x20, 0x86, 0x02, 0x00, 0x20, + 0xB8, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x01, 0x20, + 0x00, 0x06, 0xFC, 0xF7, 0x64, 0xF9, 0x10, 0xBD, + 0x10, 0xB5, 0x00, 0x24, 0x00, 0xF0, 0x46, 0xFE, + 0x01, 0xF0, 0x0E, 0xF8, 0x01, 0x20, 0xFE, 0xF7, + 0x11, 0xFF, 0x04, 0x46, 0x00, 0x2C, 0x04, 0xD1, + 0x05, 0xA0, 0x00, 0xF0, 0x33, 0xFC, 0x00, 0x20, + 0x10, 0xBD, 0x06, 0x49, 0x06, 0x48, 0x81, 0x70, + 0x09, 0x0A, 0xC1, 0x70, 0x01, 0x20, 0xF7, 0xE7, + 0x49, 0x4E, 0x49, 0x54, 0x20, 0x4E, 0x47, 0x0D, + 0x0A, 0x00, 0x00, 0x00, 0xAA, 0x55, 0x00, 0x00, + 0x88, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x1C, 0x48, + 0x00, 0x78, 0x01, 0x28, 0x02, 0xD0, 0x02, 0x28, + 0x2E, 0xD1, 0x1E, 0xE0, 0xFE, 0xF7, 0xDE, 0xFF, + 0xFD, 0xF7, 0x92, 0xFE, 0x17, 0x48, 0x00, 0x7A, + 0x00, 0x28, 0x04, 0xD1, 0x01, 0x20, 0x16, 0x49, + 0x08, 0x70, 0x00, 0x20, 0x10, 0xBD, 0xFC, 0xF7, + 0xD9, 0xF9, 0xFE, 0xF7, 0x87, 0xFF, 0x13, 0x48, + 0x00, 0x78, 0x80, 0x21, 0x08, 0x42, 0x04, 0xD0, + 0x06, 0x20, 0x0F, 0x49, 0x08, 0x70, 0x00, 0x20, + 0xF0, 0xE7, 0x02, 0x20, 0x0A, 0x49, 0x08, 0x70, + 0x0E, 0xE0, 0xFE, 0xF7, 0xAB, 0xFE, 0x00, 0x28, + 0x03, 0xD0, 0x01, 0x20, 0x06, 0x49, 0x08, 0x70, + 0xE4, 0xE7, 0x01, 0x20, 0x04, 0x49, 0x08, 0x70, + 0x06, 0x20, 0x05, 0x49, 0x08, 0x70, 0x00, 0xBF, + 0x00, 0xBF, 0x00, 0x20, 0xDA, 0xE7, 0x00, 0x00, + 0x81, 0x02, 0x00, 0x20, 0x50, 0x02, 0x00, 0x20, + 0x80, 0x02, 0x00, 0x20, 0x86, 0x02, 0x00, 0x20, + 0x0A, 0x07, 0x01, 0xD5, 0x40, 0x1C, 0x02, 0xE0, + 0x4A, 0x07, 0x01, 0xD5, 0x40, 0x1E, 0x80, 0xB2, + 0x07, 0x4A, 0xCB, 0x07, 0x12, 0x68, 0x02, 0xD0, + 0x11, 0x7E, 0x40, 0x1A, 0x03, 0xE0, 0x89, 0x07, + 0x02, 0xD5, 0x11, 0x7E, 0x08, 0x18, 0x80, 0xB2, + 0x02, 0x49, 0x40, 0x00, 0x08, 0x5E, 0x70, 0x47, + 0xB8, 0x02, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, + 0xF0, 0xB5, 0x41, 0x18, 0x15, 0x4C, 0x16, 0x4D, + 0x8C, 0x46, 0x0E, 0xE0, 0x29, 0x78, 0x01, 0x23, + 0x22, 0x88, 0x05, 0xE0, 0x1E, 0x46, 0x8E, 0x40, + 0x16, 0x42, 0x09, 0xD0, 0x49, 0x1C, 0xC9, 0xB2, + 0x02, 0x29, 0xF7, 0xD3, 0x40, 0x1C, 0x29, 0x70, + 0xC0, 0xB2, 0x84, 0x45, 0xEE, 0xD8, 0xF0, 0xBD, + 0x34, 0x27, 0x06, 0x46, 0x7E, 0x43, 0x0B, 0x4F, + 0xF6, 0x19, 0x1F, 0x46, 0x31, 0x71, 0x8F, 0x40, + 0x73, 0x72, 0x17, 0x43, 0x27, 0x80, 0x73, 0x71, + 0x73, 0x89, 0x33, 0x82, 0xB2, 0x89, 0x72, 0x82, + 0xB3, 0x85, 0xF2, 0x85, 0xFF, 0x22, 0x20, 0x36, + 0x72, 0x75, 0xE3, 0xE7, 0x3E, 0x00, 0x00, 0x20, + 0x31, 0x00, 0x00, 0x20, 0xA0, 0x04, 0x00, 0x20, + 0xF0, 0xB5, 0x95, 0xB0, 0x00, 0x20, 0x0C, 0x90, + 0x12, 0x90, 0x30, 0x21, 0x68, 0x46, 0xFC, 0xF7, + 0x08, 0xF8, 0xC9, 0x48, 0x01, 0x78, 0x40, 0x78, + 0x08, 0x18, 0x01, 0x06, 0x09, 0x0E, 0xC7, 0x48, + 0x0E, 0x91, 0x01, 0xD0, 0x00, 0x27, 0x99, 0xE0, + 0x01, 0x78, 0x00, 0x20, 0xFF, 0xF7, 0xB4, 0xFF, + 0x15, 0xB0, 0xF0, 0xBD, 0x34, 0x20, 0x39, 0x46, + 0x41, 0x43, 0xC0, 0x48, 0x00, 0x25, 0x08, 0x18, + 0x0F, 0x90, 0x00, 0x79, 0xFF, 0x28, 0x7D, 0xD1, + 0x38, 0x01, 0x12, 0x9E, 0x10, 0x90, 0x75, 0xE0, + 0x10, 0x9A, 0x68, 0x46, 0x14, 0x18, 0x60, 0x19, + 0xFF, 0x21, 0x13, 0x90, 0x41, 0x70, 0xA8, 0x00, + 0x20, 0x18, 0xB7, 0x49, 0x11, 0x90, 0x41, 0x60, + 0x0F, 0x98, 0x0C, 0x21, 0x41, 0x5E, 0x0A, 0x22, + 0x8C, 0x46, 0x82, 0x5E, 0x06, 0x20, 0x31, 0x46, + 0x41, 0x43, 0xAF, 0x48, 0x00, 0x23, 0x08, 0x18, + 0xA0, 0x30, 0x02, 0x21, 0xC3, 0x5E, 0x41, 0x5E, + 0x18, 0x46, 0x63, 0x46, 0x01, 0xF0, 0x52, 0xF8, + 0x0D, 0x90, 0xAC, 0x48, 0x01, 0x88, 0x00, 0x29, + 0x07, 0xD0, 0xAB, 0x48, 0x00, 0x88, 0x88, 0x42, + 0x03, 0xD9, 0xFB, 0xF7, 0x7B, 0xFF, 0xC0, 0xB2, + 0x00, 0xE0, 0x00, 0x20, 0x34, 0x21, 0xA2, 0x4A, + 0x71, 0x43, 0x89, 0x18, 0xC9, 0x69, 0xA6, 0x4B, + 0x4A, 0x00, 0xA4, 0x49, 0x1B, 0x78, 0x09, 0x78, + 0x40, 0x1C, 0x59, 0x43, 0x41, 0x43, 0x50, 0x18, + 0x9D, 0x49, 0x88, 0x42, 0x01, 0xD9, 0x08, 0x46, + 0x03, 0xE0, 0xA0, 0x49, 0x88, 0x42, 0x00, 0xD2, + 0x08, 0x46, 0x9F, 0x49, 0x09, 0x68, 0xB0, 0x31, + 0x4A, 0x7A, 0x0B, 0x7A, 0x11, 0x02, 0x98, 0x4A, + 0x19, 0x43, 0x12, 0x88, 0x91, 0x42, 0x04, 0xD2, + 0x91, 0x49, 0x09, 0x78, 0x01, 0x29, 0x00, 0xD1, + 0x91, 0x48, 0x0D, 0x99, 0x81, 0x42, 0x1B, 0xD8, + 0x08, 0x46, 0x11, 0x99, 0x6D, 0x1C, 0x48, 0x60, + 0x13, 0x98, 0xED, 0xB2, 0x46, 0x70, 0x01, 0x2D, + 0x12, 0xD9, 0x68, 0x1E, 0x0D, 0xE0, 0x81, 0x00, + 0x62, 0x18, 0x94, 0x46, 0x52, 0x68, 0x63, 0x58, + 0x9A, 0x42, 0x05, 0xD2, 0x22, 0x18, 0x52, 0x78, + 0x22, 0x54, 0x62, 0x46, 0x52, 0x68, 0x62, 0x50, + 0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, 0xEE, 0xD1, + 0x76, 0x1C, 0xF6, 0xB2, 0x0E, 0x98, 0x86, 0x42, + 0x86, 0xD3, 0x00, 0xE0, 0x08, 0xE0, 0x10, 0x98, + 0x69, 0x46, 0x0D, 0x54, 0x00, 0x2D, 0x03, 0xD0, + 0x0C, 0x98, 0x40, 0x1C, 0xC0, 0xB2, 0x0C, 0x90, + 0x7F, 0x1C, 0xFF, 0xB2, 0x77, 0x48, 0x00, 0x78, + 0x84, 0x46, 0xB8, 0x42, 0x00, 0xD9, 0x65, 0xE7, + 0x0C, 0x98, 0x00, 0x28, 0x7D, 0xD0, 0x01, 0x28, + 0x59, 0xD9, 0x00, 0x24, 0x55, 0xE0, 0x60, 0x1C, + 0xC3, 0xB2, 0x0C, 0x90, 0x4D, 0xE0, 0x26, 0x01, + 0x69, 0x46, 0x72, 0x18, 0x1D, 0x01, 0x69, 0x18, + 0x50, 0x78, 0x4F, 0x78, 0xB8, 0x42, 0x42, 0xD1, + 0x50, 0x68, 0x4F, 0x68, 0xB8, 0x42, 0x1F, 0xD2, + 0x00, 0x20, 0x0C, 0xE0, 0x0A, 0x18, 0x56, 0x78, + 0x97, 0x78, 0x57, 0x70, 0x96, 0x70, 0x82, 0x00, + 0x8A, 0x18, 0x97, 0x68, 0x56, 0x68, 0x40, 0x1C, + 0x57, 0x60, 0xC0, 0xB2, 0x96, 0x60, 0x6A, 0x46, + 0x52, 0x5D, 0x52, 0x1E, 0x82, 0x42, 0xED, 0xDC, + 0x6A, 0x46, 0x50, 0x5D, 0x00, 0x28, 0x04, 0xD0, + 0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, 0x50, 0x55, + 0x21, 0xD1, 0xFF, 0x20, 0x48, 0x70, 0x1E, 0xE0, + 0x00, 0x20, 0x0C, 0xE0, 0x11, 0x18, 0x4D, 0x78, + 0x8F, 0x78, 0x4F, 0x70, 0x8D, 0x70, 0x81, 0x00, + 0x51, 0x18, 0x8F, 0x68, 0x4D, 0x68, 0x40, 0x1C, + 0x4F, 0x60, 0xC0, 0xB2, 0x8D, 0x60, 0x69, 0x46, + 0x89, 0x5D, 0x49, 0x1E, 0x81, 0x42, 0xED, 0xDC, + 0x69, 0x46, 0x88, 0x5D, 0x00, 0x28, 0x04, 0xD0, + 0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, 0x88, 0x55, + 0x01, 0xD1, 0xFF, 0x20, 0x50, 0x70, 0x5B, 0x1C, + 0xDB, 0xB2, 0x9C, 0x45, 0xAF, 0xD8, 0x0C, 0x98, + 0xC4, 0xB2, 0xA4, 0x45, 0xA7, 0xD8, 0x00, 0x26, + 0x6E, 0xE0, 0x32, 0x01, 0x68, 0x46, 0x10, 0x18, + 0x0D, 0x90, 0x40, 0x78, 0xFF, 0x28, 0x29, 0xD0, + 0x34, 0x22, 0x50, 0x43, 0x3E, 0x4A, 0x12, 0x23, + 0x85, 0x18, 0x34, 0x20, 0x70, 0x43, 0x44, 0x18, + 0x10, 0x22, 0xAA, 0x5E, 0x22, 0x82, 0xEB, 0x5E, + 0x63, 0x82, 0x0C, 0x21, 0x0A, 0x20, 0x61, 0x5E, + 0x20, 0x5E, 0x00, 0xF0, 0x6B, 0xFF, 0x0C, 0x90, + 0x3D, 0x48, 0x07, 0x68, 0x38, 0x46, 0xA7, 0x30, + 0x00, 0xE0, 0x52, 0xE0, 0xFB, 0xF7, 0xE5, 0xFE, + 0x01, 0x46, 0x0C, 0x98, 0x81, 0x42, 0x0E, 0xD2, + 0x38, 0x46, 0xB0, 0x30, 0xC1, 0x79, 0x82, 0x79, + 0x08, 0x02, 0x31, 0x49, 0x10, 0x43, 0x09, 0x88, + 0x88, 0x42, 0x04, 0xD9, 0x01, 0x21, 0x30, 0x46, + 0xFF, 0xF7, 0x82, 0xFE, 0x36, 0xE0, 0x0D, 0x98, + 0x41, 0x68, 0x68, 0x7A, 0xC8, 0x28, 0x01, 0xD2, + 0x40, 0x1C, 0x60, 0x72, 0xE8, 0x69, 0x40, 0x18, + 0x40, 0x08, 0xE0, 0x61, 0x28, 0x79, 0x20, 0x71, + 0x03, 0x21, 0x61, 0x71, 0x80, 0x21, 0x08, 0x43, + 0x28, 0x71, 0xA8, 0x79, 0xA0, 0x71, 0xE8, 0x79, + 0xE0, 0x71, 0x28, 0x7A, 0x20, 0x72, 0x38, 0x46, + 0xA3, 0x30, 0xFB, 0xF7, 0xB6, 0xFE, 0x01, 0x46, + 0x0C, 0x98, 0x81, 0x42, 0x03, 0xD2, 0x60, 0x89, + 0x20, 0x82, 0xA0, 0x89, 0x60, 0x82, 0x60, 0x7A, + 0x02, 0x28, 0x07, 0xD1, 0x30, 0x20, 0x00, 0x5D, + 0x00, 0x28, 0x03, 0xD1, 0x60, 0x89, 0xA8, 0x85, + 0xA0, 0x89, 0xE8, 0x85, 0xA8, 0x8D, 0xA0, 0x85, + 0xE8, 0x8D, 0xE0, 0x85, 0x20, 0x35, 0x68, 0x7D, + 0x20, 0x34, 0x60, 0x75, 0x76, 0x1C, 0xF6, 0xB2, + 0x0C, 0x49, 0x08, 0x78, 0xB0, 0x42, 0x8C, 0xD8, + 0x8E, 0xE6, 0x00, 0x24, 0x09, 0x4D, 0x0B, 0xE0, + 0x34, 0x20, 0x60, 0x43, 0x40, 0x19, 0x00, 0x79, + 0xFF, 0x28, 0x03, 0xD1, 0x01, 0x21, 0x20, 0x46, + 0xFF, 0xF7, 0x36, 0xFE, 0x64, 0x1C, 0xE4, 0xB2, + 0x28, 0x78, 0xA0, 0x42, 0xF0, 0xD8, 0x7B, 0xE6, + 0x40, 0x05, 0x00, 0x20, 0xA0, 0x04, 0x00, 0x20, + 0xFF, 0xFF, 0x07, 0x00, 0x36, 0x00, 0x00, 0x20, + 0x34, 0x00, 0x00, 0x20, 0x2C, 0x00, 0x00, 0x20, + 0x2D, 0x00, 0x00, 0x20, 0xFF, 0xFF, 0x00, 0x00, + 0xB8, 0x02, 0x00, 0x20, 0xF0, 0xB5, 0x00, 0x24, + 0x63, 0x49, 0xA9, 0xB0, 0x02, 0x20, 0x08, 0x70, + 0x62, 0x48, 0x25, 0x46, 0x86, 0x78, 0x97, 0xE0, + 0x34, 0x21, 0x60, 0x48, 0x71, 0x43, 0x09, 0x18, + 0x08, 0x7A, 0x00, 0x28, 0x19, 0xD1, 0x5E, 0x4A, + 0x0A, 0x20, 0x08, 0x5E, 0x12, 0x78, 0x90, 0x42, + 0x11, 0xDB, 0x5C, 0x4F, 0x00, 0x23, 0xFB, 0x5E, + 0x9A, 0x1A, 0x90, 0x42, 0x0B, 0xDC, 0x5A, 0x4A, + 0x0C, 0x20, 0x08, 0x5E, 0x12, 0x78, 0x90, 0x42, + 0x05, 0xDB, 0x58, 0x4F, 0x00, 0x23, 0xFB, 0x5E, + 0x9A, 0x1A, 0x90, 0x42, 0x01, 0xDD, 0x02, 0x20, + 0x08, 0x72, 0x88, 0x79, 0x4F, 0x4A, 0x00, 0x90, + 0x00, 0x28, 0x06, 0xD0, 0x52, 0x4B, 0x52, 0x78, + 0x1B, 0x78, 0x00, 0x20, 0x9C, 0x46, 0x96, 0x46, + 0x66, 0xE0, 0x4F, 0x4F, 0x38, 0x78, 0x00, 0x19, + 0x03, 0x28, 0x63, 0xD2, 0x01, 0x22, 0x8A, 0x71, + 0x34, 0x22, 0x50, 0x43, 0xC0, 0x19, 0x09, 0x1D, + 0x00, 0x1D, 0xFB, 0xF7, 0x05, 0xFE, 0x39, 0x78, + 0x34, 0x22, 0x09, 0x19, 0x51, 0x43, 0x00, 0x20, + 0xC9, 0x19, 0x48, 0x71, 0x45, 0x49, 0x64, 0x1C, + 0x08, 0x70, 0x45, 0x49, 0xE4, 0xB2, 0x08, 0x70, + 0x4C, 0xE0, 0x0A, 0x79, 0x80, 0x23, 0x1A, 0x43, + 0x63, 0x46, 0x1B, 0x18, 0x34, 0x27, 0x7B, 0x43, + 0x3D, 0x4F, 0xDB, 0x19, 0x1B, 0x79, 0x9A, 0x42, + 0x3C, 0xD1, 0x3E, 0x48, 0x00, 0x78, 0x00, 0x28, + 0x04, 0xD1, 0x0F, 0x46, 0x20, 0x37, 0x38, 0x7D, + 0x46, 0x28, 0x02, 0xD9, 0x00, 0x20, 0x88, 0x71, + 0x34, 0xE0, 0x00, 0x98, 0x40, 0x1C, 0xC0, 0xB2, + 0x88, 0x71, 0x0A, 0x7E, 0x00, 0x2A, 0x05, 0xD0, + 0x02, 0x28, 0x03, 0xD1, 0x4A, 0x89, 0x4A, 0x84, + 0x8A, 0x89, 0x8A, 0x84, 0x28, 0x4A, 0x0B, 0x7A, + 0x12, 0x78, 0x9A, 0x18, 0x90, 0x42, 0x0F, 0xD3, + 0x00, 0x20, 0x88, 0x71, 0x83, 0x20, 0x38, 0x70, + 0x34, 0x20, 0x68, 0x43, 0x01, 0xAA, 0x80, 0x18, + 0x09, 0x1D, 0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7, + 0xBF, 0xFD, 0x6D, 0x1C, 0xED, 0xB2, 0x11, 0xE0, + 0x60, 0x46, 0x23, 0x4A, 0x00, 0x19, 0x34, 0x23, + 0x58, 0x43, 0x80, 0x18, 0x09, 0x1D, 0x00, 0x1D, + 0x1A, 0x46, 0xFB, 0xF7, 0xB1, 0xFD, 0x64, 0x1C, + 0xE4, 0xB2, 0x03, 0xE0, 0x40, 0x1C, 0xC0, 0xB2, + 0x86, 0x45, 0xB2, 0xD8, 0x76, 0x1C, 0xF6, 0xB2, + 0x14, 0x48, 0x01, 0x78, 0xB1, 0x42, 0x00, 0xD9, + 0x62, 0xE7, 0x06, 0x46, 0xB0, 0x78, 0x2A, 0x46, + 0x41, 0x19, 0x31, 0x70, 0x34, 0x21, 0x48, 0x43, + 0x4A, 0x43, 0x80, 0x19, 0x00, 0x1D, 0x02, 0xA9, + 0xFB, 0xF7, 0x96, 0xFD, 0x10, 0x4D, 0x00, 0x20, + 0x07, 0x46, 0x2B, 0x78, 0x0A, 0xE0, 0x19, 0x18, + 0x34, 0x22, 0x51, 0x43, 0x4A, 0x19, 0x12, 0x79, + 0x12, 0x06, 0x01, 0xD4, 0x89, 0x19, 0x8F, 0x71, + 0x40, 0x1C, 0xC0, 0xB2, 0x71, 0x78, 0x81, 0x42, + 0xF1, 0xD8, 0x74, 0x70, 0x29, 0xB0, 0xF0, 0xBD, + 0x30, 0x00, 0x00, 0x20, 0xA0, 0x04, 0x00, 0x20, + 0x2C, 0x00, 0x00, 0x20, 0x42, 0x00, 0x00, 0x20, + 0x2D, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, + 0x40, 0x05, 0x00, 0x20, 0xCC, 0x00, 0x00, 0x20, + 0xCD, 0x00, 0x00, 0x20, 0x73, 0x04, 0x00, 0x20, + 0xF7, 0xB5, 0x00, 0x25, 0x84, 0xB0, 0x0C, 0x46, + 0x16, 0x46, 0x61, 0x27, 0x5C, 0xE0, 0x25, 0x28, + 0x54, 0xD1, 0x64, 0x1C, 0x00, 0x22, 0x20, 0x78, + 0x13, 0x46, 0x00, 0x28, 0x57, 0xD0, 0x25, 0x28, + 0x4C, 0xD0, 0x2D, 0x28, 0x01, 0xD1, 0x64, 0x1C, + 0x01, 0x23, 0x02, 0x20, 0x21, 0x78, 0x30, 0x29, + 0x07, 0xD1, 0x64, 0x1C, 0x03, 0x43, 0xF9, 0xE7, + 0x0A, 0x21, 0x4A, 0x43, 0x30, 0x3A, 0x82, 0x18, + 0x64, 0x1C, 0x20, 0x78, 0x01, 0x46, 0x30, 0x39, + 0x09, 0x29, 0xF5, 0xD9, 0xC1, 0xB2, 0x73, 0x29, + 0x0A, 0xD0, 0x64, 0x28, 0x10, 0xD0, 0x78, 0x28, + 0x13, 0xD0, 0x58, 0x28, 0x19, 0xD0, 0x75, 0x28, + 0x1F, 0xD0, 0x63, 0x28, 0x23, 0xD0, 0x2E, 0xE0, + 0x02, 0xCE, 0x00, 0x29, 0x00, 0xD1, 0x1D, 0xA1, + 0x04, 0x98, 0x00, 0xF0, 0x90, 0xF8, 0x0A, 0xE0, + 0x68, 0x46, 0x8C, 0xC0, 0x02, 0xCE, 0x01, 0x23, + 0x13, 0xE0, 0x68, 0x46, 0x8C, 0xC0, 0x08, 0xE0, + 0x04, 0x98, 0x00, 0xF0, 0x3C, 0xF8, 0x45, 0x19, + 0x19, 0xE0, 0x41, 0x20, 0x01, 0x93, 0x00, 0x92, + 0x02, 0x90, 0x02, 0xCE, 0x00, 0x23, 0x10, 0x22, + 0xF2, 0xE7, 0x68, 0x46, 0x8C, 0xC0, 0x02, 0xCE, + 0x00, 0x23, 0x0A, 0x22, 0xEC, 0xE7, 0x02, 0xCE, + 0x68, 0x46, 0x01, 0x73, 0x00, 0x21, 0x41, 0x73, + 0x03, 0xA9, 0xD9, 0xE7, 0xC1, 0xB2, 0x04, 0x98, + 0x00, 0xF0, 0x14, 0xF8, 0x6D, 0x1C, 0x64, 0x1C, + 0x20, 0x78, 0x00, 0x28, 0x9F, 0xD1, 0x04, 0x98, + 0x00, 0x28, 0x03, 0xD0, 0x04, 0x99, 0x00, 0x20, + 0x09, 0x68, 0x08, 0x70, 0x28, 0x46, 0x07, 0xB0, + 0xF0, 0xBD, 0x00, 0x00, 0x28, 0x6E, 0x75, 0x6C, + 0x6C, 0x29, 0x00, 0x00, 0x10, 0xB5, 0x00, 0x28, + 0x05, 0xD0, 0x02, 0x68, 0x11, 0x70, 0x01, 0x68, + 0x49, 0x1C, 0x01, 0x60, 0x10, 0xBD, 0xC8, 0xB2, + 0xFE, 0xF7, 0x64, 0xF9, 0x10, 0xBD, 0xFF, 0xB5, + 0x00, 0x27, 0x83, 0xB0, 0x0C, 0x9D, 0x3E, 0x46, + 0x08, 0x00, 0x3A, 0x46, 0x05, 0xD0, 0x00, 0x2B, + 0x12, 0xD0, 0x05, 0x9B, 0x0A, 0x2B, 0x0B, 0xD0, + 0x0E, 0xE0, 0x30, 0x20, 0x69, 0x46, 0x08, 0x70, + 0x4A, 0x70, 0x2A, 0x46, 0x0D, 0x9B, 0x03, 0x98, + 0x00, 0xF0, 0x31, 0xF8, 0x07, 0xB0, 0xF0, 0xBD, + 0x00, 0x29, 0x01, 0xDA, 0x01, 0x27, 0x40, 0x42, + 0x02, 0xAC, 0x69, 0x46, 0x03, 0x34, 0xCA, 0x72, + 0x0A, 0xE0, 0x05, 0x99, 0xFB, 0xF7, 0x9A, 0xFC, + 0x0A, 0x29, 0x02, 0xDB, 0x0E, 0x9A, 0x89, 0x18, + 0x3A, 0x39, 0x30, 0x31, 0x64, 0x1E, 0x21, 0x70, + 0x00, 0x28, 0xF2, 0xD1, 0x00, 0x2F, 0x0E, 0xD0, + 0x00, 0x2D, 0x09, 0xD0, 0x0D, 0x98, 0x80, 0x07, + 0x06, 0xD5, 0x2D, 0x21, 0x03, 0x98, 0xFF, 0xF7, + 0xB9, 0xFF, 0x76, 0x1C, 0x6D, 0x1E, 0x02, 0xE0, + 0x2D, 0x20, 0x64, 0x1E, 0x20, 0x70, 0x2A, 0x46, + 0x21, 0x46, 0x0D, 0x9B, 0x03, 0x98, 0x00, 0xF0, + 0x02, 0xF8, 0x80, 0x19, 0xCE, 0xE7, 0xFF, 0xB5, + 0x00, 0x25, 0x20, 0x27, 0x81, 0xB0, 0x0E, 0x46, + 0x14, 0x46, 0x00, 0x2A, 0x0E, 0xDD, 0x00, 0x20, + 0x01, 0xE0, 0x40, 0x1C, 0x49, 0x1C, 0x0A, 0x78, + 0x00, 0x2A, 0xFA, 0xD1, 0xA0, 0x42, 0x01, 0xDB, + 0x00, 0x24, 0x00, 0xE0, 0x24, 0x1A, 0x98, 0x07, + 0x00, 0xD5, 0x30, 0x27, 0xD8, 0x07, 0x06, 0xD0, + 0x0D, 0xE0, 0x39, 0x46, 0x01, 0x98, 0xFF, 0xF7, + 0x8D, 0xFF, 0x6D, 0x1C, 0x64, 0x1E, 0x00, 0x2C, + 0xF7, 0xDC, 0x04, 0xE0, 0x01, 0x98, 0xFF, 0xF7, + 0x85, 0xFF, 0x6D, 0x1C, 0x76, 0x1C, 0x31, 0x78, + 0x00, 0x29, 0xF7, 0xD1, 0x05, 0xE0, 0x39, 0x46, + 0x01, 0x98, 0xFF, 0xF7, 0x7B, 0xFF, 0x6D, 0x1C, + 0x64, 0x1E, 0x00, 0x2C, 0xF7, 0xDC, 0x28, 0x46, + 0x05, 0xB0, 0xF0, 0xBD, 0x0F, 0xB4, 0x10, 0xB5, + 0x03, 0xAA, 0x00, 0x20, 0x02, 0x99, 0xFF, 0xF7, + 0xF7, 0xFE, 0x10, 0xBC, 0x08, 0xBC, 0x04, 0xB0, + 0x18, 0x47, 0x00, 0x00, 0xF0, 0xB5, 0xA9, 0xB0, + 0x50, 0x49, 0x00, 0x24, 0x8C, 0x70, 0x9C, 0x21, + 0x02, 0xA8, 0xFB, 0xF7, 0x6E, 0xFC, 0x00, 0x26, + 0x66, 0xE0, 0x34, 0x21, 0x71, 0x43, 0x0D, 0x18, + 0x29, 0x79, 0xFF, 0x29, 0x5E, 0xD0, 0x0A, 0x06, + 0x36, 0xD5, 0x48, 0x4F, 0x00, 0x20, 0x3A, 0x78, + 0x94, 0x46, 0x2E, 0xE0, 0x34, 0x22, 0x42, 0x43, + 0xD2, 0x19, 0x12, 0x79, 0x80, 0x23, 0x1A, 0x43, + 0x8A, 0x42, 0x24, 0xD1, 0x34, 0x21, 0x48, 0x43, + 0x40, 0x49, 0x22, 0x46, 0x47, 0x18, 0x34, 0x20, + 0x42, 0x43, 0x01, 0xA8, 0x10, 0x18, 0x00, 0x90, + 0x39, 0x1D, 0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7, + 0x2F, 0xFC, 0x28, 0x79, 0x64, 0x1C, 0x41, 0x06, + 0x00, 0x98, 0x49, 0x0E, 0x01, 0x71, 0x00, 0x98, + 0x69, 0x79, 0x41, 0x71, 0x78, 0x79, 0x80, 0x21, + 0x08, 0x43, 0x78, 0x71, 0xA8, 0x79, 0xE4, 0xB2, + 0x00, 0x28, 0x2F, 0xD1, 0x31, 0x48, 0x81, 0x78, + 0x49, 0x1C, 0x81, 0x70, 0x2A, 0xE0, 0x40, 0x1C, + 0xC0, 0xB2, 0x84, 0x45, 0xCE, 0xD8, 0x25, 0xE0, + 0xA9, 0x79, 0x00, 0x29, 0x22, 0xD1, 0xE9, 0x79, + 0x03, 0x29, 0x02, 0xD9, 0x00, 0x20, 0xE8, 0x71, + 0x1C, 0xE0, 0x34, 0x20, 0x22, 0x46, 0x42, 0x43, + 0x01, 0xA8, 0x17, 0x18, 0x29, 0x1D, 0x38, 0x1D, + 0x34, 0x22, 0xFB, 0xF7, 0x01, 0xFC, 0x06, 0x20, + 0x23, 0x49, 0x70, 0x43, 0x40, 0x18, 0xA0, 0x30, + 0x01, 0x88, 0x79, 0x81, 0x40, 0x88, 0xB8, 0x81, + 0x1E, 0x48, 0x64, 0x1C, 0x81, 0x78, 0xE4, 0xB2, + 0x49, 0x1C, 0x81, 0x70, 0x28, 0x79, 0x80, 0x21, + 0x08, 0x43, 0x28, 0x71, 0x76, 0x1C, 0xF6, 0xB2, + 0x19, 0x48, 0x01, 0x78, 0x42, 0x78, 0x89, 0x18, + 0xB1, 0x42, 0x92, 0xD8, 0x00, 0x25, 0x15, 0x4E, + 0x1A, 0xE0, 0x34, 0x20, 0x68, 0x43, 0x81, 0x19, + 0x48, 0x79, 0x02, 0x06, 0x03, 0xD5, 0x40, 0x06, + 0x40, 0x0E, 0x48, 0x71, 0x0E, 0xE0, 0x02, 0x2C, + 0x11, 0xD0, 0x00, 0x20, 0x88, 0x71, 0x34, 0x20, + 0x60, 0x43, 0x01, 0xAA, 0x80, 0x18, 0x09, 0x1D, + 0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7, 0xCC, 0xFB, + 0x64, 0x1C, 0xE4, 0xB2, 0x6D, 0x1C, 0xED, 0xB2, + 0x30, 0x78, 0xA8, 0x42, 0xE1, 0xD8, 0x34, 0x70, + 0x34, 0x20, 0x44, 0x43, 0x22, 0x46, 0x02, 0xA9, + 0x30, 0x1D, 0xFB, 0xF7, 0xBD, 0xFB, 0x29, 0xB0, + 0xF0, 0xBD, 0x00, 0x00, 0xA0, 0x04, 0x00, 0x20, + 0x40, 0x05, 0x00, 0x20, 0xF8, 0xB5, 0x59, 0x4C, + 0x59, 0x4D, 0x00, 0x26, 0xE6, 0x70, 0x16, 0x21, + 0x28, 0x1D, 0xFB, 0xF7, 0xC6, 0xFB, 0x57, 0x48, + 0x57, 0x49, 0x00, 0x78, 0x09, 0x78, 0xC0, 0x01, + 0x08, 0x43, 0xC1, 0xB2, 0x55, 0x48, 0x01, 0x70, + 0x55, 0x48, 0x02, 0x78, 0x01, 0x20, 0x00, 0x2A, + 0x04, 0xD0, 0xAA, 0x7E, 0x8A, 0x42, 0x01, 0xD0, + 0xA9, 0x76, 0xE0, 0x70, 0x51, 0x49, 0x0A, 0x78, + 0x00, 0x2A, 0x10, 0xD1, 0x50, 0x4A, 0x12, 0x78, + 0x18, 0x2A, 0x0C, 0xD1, 0x4F, 0x4A, 0x12, 0x78, + 0x03, 0x2A, 0x08, 0xD3, 0xA9, 0x78, 0xC9, 0x07, + 0x04, 0xD1, 0xE0, 0x70, 0xA8, 0x70, 0x6E, 0x70, + 0x4B, 0x48, 0x06, 0x70, 0xF8, 0xBD, 0xAA, 0x78, + 0xD3, 0x07, 0x4A, 0x4A, 0x07, 0xD0, 0xE0, 0x70, + 0xAE, 0x70, 0x6E, 0x70, 0x2D, 0x20, 0x08, 0x70, + 0x03, 0x20, 0x10, 0x70, 0xF8, 0xBD, 0x11, 0x78, + 0x00, 0x29, 0xFB, 0xD1, 0x21, 0x78, 0x00, 0x29, + 0x07, 0xD0, 0xE0, 0x70, 0x69, 0x70, 0x38, 0x48, + 0x00, 0x24, 0x40, 0x1C, 0x40, 0x4F, 0x00, 0x90, + 0x63, 0xE0, 0x69, 0x78, 0x00, 0x29, 0x00, 0xD0, + 0xE0, 0x70, 0x6E, 0x70, 0xF8, 0xBD, 0x34, 0x20, + 0x30, 0x49, 0x60, 0x43, 0x45, 0x18, 0x20, 0x46, + 0x0B, 0x26, 0x70, 0x43, 0x2A, 0x79, 0xC6, 0x18, + 0x32, 0x71, 0x68, 0x89, 0x76, 0x1C, 0x30, 0x71, + 0x00, 0x0A, 0x70, 0x71, 0xA8, 0x89, 0xB0, 0x71, + 0x00, 0x0A, 0xF0, 0x71, 0xE8, 0x89, 0x30, 0x72, + 0x00, 0x0A, 0x70, 0x72, 0x20, 0x20, 0x40, 0x5D, + 0xC0, 0x07, 0x07, 0xD0, 0xE8, 0x8C, 0x30, 0x71, + 0x00, 0x0A, 0x70, 0x71, 0x28, 0x8D, 0xB0, 0x71, + 0x00, 0x0A, 0xF0, 0x71, 0x08, 0x78, 0x01, 0x28, + 0x2D, 0xD1, 0xC8, 0x79, 0x00, 0x28, 0x2A, 0xD0, + 0x00, 0x98, 0xC1, 0x79, 0x80, 0x79, 0x09, 0x06, + 0x0B, 0x14, 0x03, 0x43, 0x00, 0x98, 0x41, 0x79, + 0x00, 0x79, 0x09, 0x06, 0x0A, 0x14, 0x02, 0x43, + 0x22, 0x48, 0x00, 0x21, 0x41, 0x5E, 0x00, 0x20, + 0x38, 0x5E, 0x00, 0xF0, 0xCB, 0xFB, 0x20, 0x49, + 0x0A, 0x68, 0xB0, 0x32, 0x51, 0x7B, 0x12, 0x7B, + 0x09, 0x02, 0x11, 0x43, 0x81, 0x42, 0x0E, 0xD2, + 0x00, 0x98, 0x41, 0x79, 0x02, 0x79, 0x08, 0x06, + 0x00, 0x14, 0x10, 0x43, 0x38, 0x80, 0x00, 0x98, + 0xC2, 0x79, 0x81, 0x79, 0x10, 0x06, 0x00, 0x14, + 0x08, 0x43, 0x14, 0x49, 0x08, 0x80, 0xA8, 0x8A, + 0xB0, 0x72, 0x00, 0x0A, 0xF0, 0x72, 0xE8, 0x8A, + 0x30, 0x73, 0x00, 0x0A, 0x70, 0x73, 0x64, 0x1C, + 0xE4, 0xB2, 0x03, 0x4B, 0x58, 0x78, 0xA0, 0x42, + 0x9D, 0xD8, 0xF8, 0xBD, 0xA0, 0x04, 0x00, 0x20, + 0x90, 0x02, 0x00, 0x20, 0x74, 0x04, 0x00, 0x20, + 0x85, 0x02, 0x00, 0x20, 0x34, 0x01, 0x00, 0x20, + 0x2B, 0x00, 0x00, 0x20, 0x72, 0x04, 0x00, 0x20, + 0x8E, 0x02, 0x00, 0x20, 0x73, 0x04, 0x00, 0x20, + 0x83, 0x02, 0x00, 0x20, 0x75, 0x04, 0x00, 0x20, + 0x38, 0x00, 0x00, 0x20, 0x3A, 0x00, 0x00, 0x20, + 0xB8, 0x02, 0x00, 0x20, 0xF7, 0xB5, 0x00, 0x20, + 0x84, 0xB0, 0x03, 0x90, 0x32, 0x48, 0x00, 0x27, + 0x01, 0x90, 0x38, 0x46, 0x56, 0xE0, 0x00, 0x24, + 0x00, 0x29, 0x05, 0xD0, 0x40, 0x1E, 0x88, 0x42, + 0x00, 0xD1, 0x02, 0x24, 0x00, 0x26, 0x45, 0xE0, + 0x01, 0x24, 0xFB, 0xE7, 0x78, 0x00, 0x2B, 0x4A, + 0x02, 0x90, 0x10, 0x5E, 0x06, 0x9B, 0x98, 0x42, + 0x38, 0xDD, 0xC3, 0x07, 0x36, 0xD1, 0x01, 0x9B, + 0x98, 0x42, 0x33, 0xDD, 0xA4, 0x07, 0xA4, 0x0F, + 0x00, 0x2E, 0x0B, 0xD0, 0x49, 0x1E, 0xB1, 0x42, + 0x01, 0xD1, 0x08, 0x20, 0x04, 0x43, 0x00, 0x25, + 0x21, 0x48, 0x41, 0x5D, 0x20, 0x46, 0x08, 0x42, + 0x0F, 0xD1, 0x01, 0xE0, 0x04, 0x20, 0xF5, 0xE7, + 0x38, 0x46, 0xFF, 0xF7, 0x69, 0xFA, 0x1B, 0x4A, + 0x02, 0x99, 0x04, 0x2D, 0x51, 0x5E, 0x02, 0xD2, + 0x81, 0x42, 0x17, 0xDB, 0x01, 0xE0, 0x81, 0x42, + 0x04, 0xDD, 0x6D, 0x1C, 0xED, 0xB2, 0x08, 0x2D, + 0xE6, 0xD3, 0x01, 0xE0, 0x08, 0x2D, 0x0D, 0xD3, + 0x12, 0x49, 0x02, 0x98, 0x08, 0x5E, 0x01, 0x90, + 0x04, 0x99, 0x00, 0x98, 0x08, 0x70, 0x05, 0x98, + 0x10, 0x49, 0x06, 0x70, 0x01, 0x20, 0x03, 0x90, + 0x01, 0x98, 0x08, 0x80, 0x7F, 0x1C, 0x76, 0x1C, + 0xBF, 0xB2, 0xF6, 0xB2, 0x0C, 0x48, 0x00, 0x68, + 0x01, 0x7E, 0xB1, 0x42, 0xB6, 0xD8, 0x00, 0x98, + 0x40, 0x1C, 0xC0, 0xB2, 0x08, 0x4A, 0x00, 0x90, + 0x10, 0x68, 0x00, 0x99, 0x40, 0x7E, 0x88, 0x42, + 0xA1, 0xD8, 0x03, 0x98, 0x07, 0xB0, 0xF0, 0xBD, + 0x00, 0x80, 0xFF, 0xFF, 0x4C, 0x07, 0x00, 0x20, + 0x8E, 0x5A, 0x00, 0x00, 0x28, 0x00, 0x00, 0x20, + 0xB8, 0x02, 0x00, 0x20, 0x05, 0x48, 0x01, 0x78, + 0x82, 0x29, 0x05, 0xD1, 0xC0, 0x79, 0x01, 0x28, + 0x02, 0xD1, 0x03, 0x49, 0xFF, 0x20, 0x08, 0x70, + 0x70, 0x47, 0x00, 0x00, 0x84, 0x04, 0x00, 0x20, + 0xEC, 0x02, 0x00, 0x20, 0x01, 0x20, 0x02, 0x49, + 0x08, 0x70, 0x02, 0x49, 0x08, 0x70, 0x70, 0x47, + 0x0C, 0x00, 0x00, 0x20, 0x81, 0x02, 0x00, 0x20, + 0x70, 0xB5, 0x24, 0x4D, 0x00, 0x21, 0x28, 0x78, + 0x00, 0x28, 0x2C, 0xD1, 0x22, 0x4A, 0x23, 0x4B, + 0x10, 0x80, 0x04, 0x22, 0x1A, 0x70, 0x22, 0x4B, + 0x1A, 0x70, 0x22, 0x4A, 0x10, 0x70, 0x22, 0x4A, + 0x10, 0x70, 0x22, 0x4A, 0x10, 0x70, 0x22, 0x4A, + 0x10, 0x70, 0x22, 0x4A, 0x10, 0x70, 0x1A, 0xE0, + 0x34, 0x20, 0x48, 0x43, 0x40, 0x19, 0x02, 0x46, + 0x20, 0x32, 0x13, 0x78, 0x1C, 0x06, 0x06, 0xD5, + 0x5B, 0x06, 0x5B, 0x0E, 0x13, 0x70, 0x43, 0x89, + 0xC3, 0x84, 0x83, 0x89, 0x03, 0x85, 0x13, 0x78, + 0x9C, 0x07, 0x06, 0xD5, 0xFD, 0x24, 0x23, 0x40, + 0x13, 0x70, 0x42, 0x89, 0x42, 0x84, 0x82, 0x89, + 0x82, 0x84, 0x49, 0x1C, 0xC9, 0xB2, 0x28, 0x78, + 0x88, 0x42, 0xE1, 0xD8, 0x12, 0x4E, 0x00, 0x24, + 0x30, 0x70, 0x68, 0x78, 0x70, 0x70, 0x20, 0x46, + 0x34, 0x21, 0x48, 0x43, 0x41, 0x19, 0x80, 0x19, + 0x09, 0x1D, 0x00, 0x1D, 0x34, 0x22, 0xFB, 0xF7, + 0x13, 0xFA, 0x64, 0x1C, 0xE4, 0xB2, 0x03, 0x2C, + 0xF1, 0xD3, 0x70, 0xBD, 0xA0, 0x04, 0x00, 0x20, + 0x34, 0x00, 0x00, 0x20, 0x4A, 0x00, 0x00, 0x20, + 0x4B, 0x00, 0x00, 0x20, 0x49, 0x00, 0x00, 0x20, + 0x2A, 0x00, 0x00, 0x20, 0x47, 0x00, 0x00, 0x20, + 0x46, 0x00, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, + 0x40, 0x05, 0x00, 0x20, 0x02, 0x49, 0x00, 0x20, + 0x08, 0x70, 0x02, 0x49, 0x08, 0x80, 0x70, 0x47, + 0x31, 0x00, 0x00, 0x20, 0x3E, 0x00, 0x00, 0x20, + 0xF0, 0xB5, 0x3B, 0x4F, 0x00, 0x20, 0x38, 0x70, + 0x01, 0x21, 0x3A, 0x4C, 0xC9, 0x03, 0x21, 0x80, + 0x39, 0x4B, 0x3B, 0x49, 0x18, 0x80, 0x39, 0x4E, + 0x0A, 0x88, 0x0D, 0xE0, 0x43, 0x00, 0x00, 0x25, + 0xF1, 0x5E, 0x65, 0x5F, 0xA9, 0x42, 0x02, 0xDD, + 0x33, 0x4D, 0x21, 0x80, 0x28, 0x80, 0x49, 0x08, + 0x49, 0x00, 0x40, 0x1C, 0xF1, 0x52, 0x80, 0xB2, + 0x90, 0x42, 0xEF, 0xD3, 0x00, 0x20, 0x20, 0x5E, + 0x41, 0x00, 0x41, 0x18, 0xCB, 0x17, 0x5B, 0x0F, + 0x59, 0x18, 0xCB, 0x10, 0x2D, 0x49, 0x09, 0x68, + 0x90, 0x31, 0x4C, 0x7B, 0x0D, 0x7B, 0x21, 0x02, + 0x29, 0x43, 0x81, 0x42, 0x0C, 0xDA, 0x00, 0x20, + 0x08, 0xE0, 0x41, 0x00, 0x71, 0x5E, 0x99, 0x42, + 0x02, 0xDD, 0x39, 0x78, 0x49, 0x1C, 0x39, 0x70, + 0x40, 0x1C, 0x80, 0xB2, 0x90, 0x42, 0xF4, 0xD3, + 0x23, 0x48, 0x24, 0x4A, 0x01, 0x78, 0x21, 0x48, + 0x00, 0x68, 0x80, 0x30, 0x00, 0x29, 0x81, 0x7B, + 0x02, 0xD0, 0xCB, 0x00, 0xC9, 0x18, 0xC9, 0x08, + 0x11, 0x70, 0xC3, 0x7B, 0x1E, 0x49, 0x00, 0x20, + 0x08, 0x5E, 0x83, 0x42, 0x0C, 0xDA, 0x1D, 0x49, + 0x09, 0x78, 0x02, 0x29, 0x08, 0xD2, 0x1C, 0x49, + 0x0B, 0x78, 0x11, 0x78, 0x49, 0x1E, 0x8B, 0x42, + 0x02, 0xDD, 0x11, 0x78, 0x49, 0x1C, 0x39, 0x70, + 0x18, 0x49, 0x00, 0x23, 0xCB, 0x5E, 0x59, 0x42, + 0x81, 0x42, 0x01, 0xDD, 0x00, 0x20, 0x38, 0x70, + 0x3B, 0x78, 0x12, 0x78, 0x14, 0x49, 0x93, 0x42, + 0x08, 0x78, 0x06, 0xD9, 0x03, 0x28, 0x01, 0xD2, + 0x40, 0x1C, 0x00, 0xE0, 0x0C, 0x20, 0x08, 0x70, + 0xF0, 0xBD, 0x00, 0x28, 0xFC, 0xD0, 0x3A, 0x78, + 0x03, 0x2A, 0xF9, 0xD2, 0x40, 0x1E, 0xF6, 0xE7, + 0x0E, 0x00, 0x00, 0x20, 0x24, 0x00, 0x00, 0x20, + 0x32, 0x00, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, + 0x3C, 0x00, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, + 0x70, 0x04, 0x00, 0x20, 0x0D, 0x00, 0x00, 0x20, + 0x7C, 0x04, 0x00, 0x20, 0xA0, 0x04, 0x00, 0x20, + 0x8F, 0x02, 0x00, 0x20, 0x7A, 0x04, 0x00, 0x20, + 0x73, 0x04, 0x00, 0x20, 0x10, 0xB5, 0x06, 0x4C, + 0x00, 0x20, 0x20, 0x80, 0x60, 0x80, 0xFF, 0xF7, + 0x5D, 0xFF, 0x04, 0x48, 0xA0, 0x80, 0xE0, 0x80, + 0x03, 0x49, 0x03, 0x20, 0x08, 0x70, 0x10, 0xBD, + 0x34, 0x00, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, + 0x82, 0x02, 0x00, 0x20, 0x10, 0xB5, 0xFF, 0xF7, + 0x4D, 0xFF, 0x08, 0x49, 0x5A, 0x20, 0x08, 0x70, + 0x07, 0x49, 0x00, 0x20, 0x08, 0x70, 0x08, 0x48, + 0x06, 0x49, 0x81, 0x80, 0xC1, 0x80, 0x07, 0x49, + 0x03, 0x20, 0x08, 0x70, 0x06, 0x49, 0x01, 0x20, + 0x08, 0x70, 0x10, 0xBD, 0xB5, 0x02, 0x00, 0x20, + 0x73, 0x04, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, + 0x34, 0x00, 0x00, 0x20, 0x82, 0x02, 0x00, 0x20, + 0x83, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x12, 0x4D, + 0x06, 0x46, 0x2C, 0x78, 0xE0, 0x07, 0x07, 0xD0, + 0x80, 0x22, 0x10, 0x49, 0x10, 0x48, 0xFB, 0xF7, + 0x1F, 0xF9, 0x60, 0x08, 0x40, 0x00, 0x28, 0x70, + 0x28, 0x78, 0x80, 0x07, 0x12, 0xD5, 0x00, 0x2E, + 0x10, 0xD1, 0x0C, 0x49, 0x00, 0x20, 0x09, 0x4D, + 0x09, 0x4C, 0x0E, 0x88, 0x08, 0xE0, 0x41, 0x00, + 0x62, 0x5E, 0x6B, 0x5E, 0xD2, 0x18, 0x52, 0x10, + 0x62, 0x52, 0x40, 0x1C, 0x6A, 0x52, 0x80, 0xB2, + 0xB0, 0x42, 0xF4, 0xD3, 0x70, 0xBD, 0x00, 0x00, + 0x82, 0x02, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, + 0xF4, 0x05, 0x00, 0x20, 0x3C, 0x00, 0x00, 0x20, + 0x10, 0xB5, 0xFB, 0xF7, 0x11, 0xFF, 0x10, 0xBD, + 0x10, 0xB5, 0x04, 0x48, 0x40, 0x78, 0x80, 0x06, + 0x01, 0xD5, 0xFE, 0xF7, 0x53, 0xF9, 0xFB, 0xF7, + 0x81, 0xFD, 0x10, 0xBD, 0x88, 0x02, 0x00, 0x20, + 0xF8, 0xB5, 0x22, 0x49, 0x00, 0x20, 0x03, 0x46, + 0x1F, 0x4A, 0x0C, 0x88, 0x06, 0xE0, 0x41, 0x00, + 0x55, 0x5E, 0x00, 0x2D, 0x00, 0xDA, 0x53, 0x52, + 0x40, 0x1C, 0xC0, 0xB2, 0xA0, 0x42, 0xF6, 0xD3, + 0x1C, 0x48, 0x1B, 0x4D, 0x01, 0x78, 0x1D, 0x48, + 0x1B, 0x4A, 0x04, 0x68, 0x2E, 0x78, 0x20, 0x46, + 0xB0, 0x30, 0x12, 0x88, 0x02, 0x2E, 0x0F, 0xD0, + 0xC3, 0x79, 0x84, 0x79, 0x18, 0x02, 0x20, 0x43, + 0x90, 0x42, 0x01, 0xD8, 0x01, 0x29, 0x01, 0xD9, + 0x03, 0x20, 0x28, 0x70, 0x00, 0x20, 0xFF, 0xF7, + 0x99, 0xFF, 0xFF, 0xF7, 0xD1, 0xFE, 0xF8, 0xBD, + 0x90, 0x34, 0x66, 0x7B, 0x27, 0x7B, 0x34, 0x02, + 0x3C, 0x43, 0x0F, 0x4E, 0x00, 0x27, 0xF7, 0x5F, + 0xBC, 0x42, 0x05, 0xDA, 0x00, 0x29, 0x03, 0xD1, + 0x0C, 0x49, 0x09, 0x78, 0x01, 0x29, 0xE7, 0xD0, + 0xC1, 0x79, 0x84, 0x79, 0x08, 0x02, 0x20, 0x43, + 0x90, 0x42, 0xE3, 0xD2, 0x2B, 0x70, 0xE1, 0xE7, + 0x4C, 0x07, 0x00, 0x20, 0x3C, 0x00, 0x00, 0x20, + 0x82, 0x02, 0x00, 0x20, 0x40, 0x05, 0x00, 0x20, + 0x34, 0x00, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, + 0x78, 0x04, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, + 0x10, 0xB5, 0xFE, 0xF7, 0xAD, 0xFD, 0xFF, 0xF7, + 0x49, 0xFF, 0x10, 0xBD, 0xF8, 0xB5, 0x00, 0x26, + 0xFB, 0xF7, 0xE8, 0xFB, 0x83, 0x4C, 0x20, 0x78, + 0x01, 0x28, 0x2A, 0xD1, 0x60, 0x7A, 0x0A, 0x28, + 0x27, 0xD2, 0x81, 0x4F, 0x81, 0x49, 0xB8, 0x5F, + 0x88, 0x42, 0x22, 0xD0, 0x80, 0x4D, 0x0C, 0x23, + 0x0A, 0x22, 0xE3, 0x5E, 0xA2, 0x5E, 0xA9, 0x5F, + 0x00, 0xF0, 0x1C, 0xF9, 0x7D, 0x49, 0x0A, 0x68, + 0xB0, 0x32, 0x51, 0x7B, 0x12, 0x7B, 0x09, 0x02, + 0x11, 0x43, 0x81, 0x42, 0x11, 0xD3, 0xB8, 0x5F, + 0x41, 0x00, 0x40, 0x18, 0x0A, 0x21, 0x61, 0x5E, + 0x40, 0x18, 0x80, 0x10, 0x60, 0x81, 0xA9, 0x5F, + 0x4A, 0x00, 0x89, 0x18, 0x0C, 0x22, 0xA2, 0x5E, + 0x89, 0x18, 0x89, 0x10, 0xA1, 0x81, 0x60, 0x84, + 0xA1, 0x84, 0x00, 0x25, 0xCC, 0xE0, 0x70, 0x4F, + 0x34, 0x23, 0x00, 0x20, 0x6B, 0x43, 0x39, 0x78, + 0x9C, 0x18, 0x0A, 0xE0, 0x22, 0x79, 0x80, 0x23, + 0x1A, 0x43, 0x34, 0x23, 0x43, 0x43, 0xDB, 0x19, + 0x1B, 0x79, 0x9A, 0x42, 0x03, 0xD0, 0x40, 0x1C, + 0xC0, 0xB2, 0x81, 0x42, 0xF2, 0xD8, 0x81, 0x42, + 0x7E, 0xD9, 0x34, 0x21, 0x48, 0x43, 0xC1, 0x19, + 0x14, 0x20, 0x08, 0x5E, 0x14, 0x22, 0x43, 0x00, + 0xC0, 0x18, 0xA2, 0x5E, 0x01, 0x23, 0x10, 0x18, + 0x80, 0x03, 0xDB, 0x03, 0xC0, 0x18, 0x00, 0x14, + 0xA0, 0x82, 0x16, 0x20, 0x08, 0x5E, 0x16, 0x22, + 0x47, 0x00, 0xC0, 0x19, 0xA2, 0x5E, 0x27, 0x46, + 0x10, 0x18, 0x80, 0x03, 0xC0, 0x18, 0x00, 0x14, + 0xE0, 0x82, 0x20, 0x37, 0x20, 0x31, 0x0A, 0x22, + 0x38, 0x46, 0xFB, 0xF7, 0x25, 0xF8, 0x38, 0x78, + 0x40, 0x07, 0x07, 0xD5, 0x00, 0x20, 0x53, 0x49, + 0x38, 0x70, 0x08, 0x70, 0x52, 0x49, 0x08, 0x70, + 0x52, 0x49, 0x08, 0x70, 0x24, 0x23, 0x22, 0x22, + 0x0C, 0x21, 0x0A, 0x20, 0xE3, 0x5E, 0xA2, 0x5E, + 0x61, 0x5E, 0x20, 0x5E, 0x00, 0xF0, 0xB2, 0xF8, + 0x39, 0x78, 0xCA, 0x07, 0x67, 0xD0, 0x62, 0x7A, + 0x2D, 0x2A, 0x09, 0xD2, 0x48, 0x4A, 0x12, 0x78, + 0x00, 0x2A, 0x05, 0xD0, 0x22, 0x7E, 0x00, 0x2A, + 0x02, 0xD1, 0x02, 0x22, 0x11, 0x43, 0x39, 0x70, + 0x40, 0x49, 0x0A, 0x68, 0x92, 0x21, 0x89, 0x5C, + 0x81, 0x42, 0x01, 0xD2, 0x01, 0x26, 0x16, 0xE0, + 0x10, 0x46, 0xB0, 0x30, 0x41, 0x7A, 0x03, 0x7A, + 0x08, 0x02, 0x18, 0x43, 0x81, 0x08, 0x3E, 0x48, + 0x00, 0x88, 0x81, 0x42, 0x07, 0xD2, 0x39, 0x48, + 0x00, 0x78, 0x00, 0x28, 0x03, 0xD1, 0x3B, 0x48, + 0x00, 0x78, 0x00, 0x28, 0x36, 0xD0, 0x00, 0x20, + 0xB8, 0x72, 0x00, 0x2E, 0x4E, 0xD0, 0x38, 0x78, + 0x04, 0x21, 0x08, 0x43, 0x38, 0x70, 0xB0, 0x32, + 0x50, 0x7A, 0x12, 0x7A, 0x00, 0x02, 0x10, 0x43, + 0x31, 0x4A, 0x79, 0x7D, 0x12, 0x88, 0x90, 0x42, + 0x40, 0xD9, 0x26, 0x23, 0xE3, 0x5E, 0x30, 0x20, + 0x2F, 0x4A, 0x9F, 0x01, 0x41, 0x43, 0x88, 0x18, + 0x87, 0x60, 0xC7, 0x60, 0x2D, 0x4F, 0x00, 0xE0, + 0x34, 0xE0, 0xBF, 0x88, 0x0A, 0x2F, 0x01, 0xD8, + 0x0A, 0x23, 0xE3, 0x5E, 0x53, 0x50, 0x0A, 0x21, + 0x61, 0x5E, 0x41, 0x60, 0x28, 0x21, 0x61, 0x5E, + 0x8A, 0x01, 0x82, 0x61, 0xC2, 0x61, 0x25, 0x4A, + 0x92, 0x88, 0x0A, 0x2A, 0x01, 0xD8, 0x0C, 0x21, + 0x61, 0x5E, 0x01, 0x61, 0x0C, 0x21, 0x61, 0x5E, + 0x41, 0x61, 0x1B, 0xE0, 0xB8, 0x7A, 0x40, 0x1C, + 0xC0, 0xB2, 0xB8, 0x72, 0x02, 0x28, 0xC4, 0xD9, + 0x00, 0x20, 0xB8, 0x72, 0xAA, 0xE7, 0x00, 0x29, + 0x10, 0xD1, 0x12, 0x49, 0x09, 0x68, 0x80, 0x31, + 0x89, 0x7C, 0x49, 0x08, 0x81, 0x42, 0x11, 0xD9, + 0x78, 0x78, 0x40, 0x1C, 0xC0, 0xB2, 0x78, 0x70, + 0x0A, 0x28, 0x03, 0xD9, 0x00, 0x20, 0x78, 0x70, + 0x83, 0x20, 0x38, 0x70, 0x6D, 0x1C, 0xED, 0xB2, + 0x04, 0x4A, 0x10, 0x78, 0xA8, 0x42, 0x00, 0xD9, + 0x2D, 0xE7, 0xF8, 0xBD, 0x00, 0x20, 0x78, 0x70, + 0x02, 0x20, 0xF2, 0xE7, 0xA0, 0x04, 0x00, 0x20, + 0x38, 0x00, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, + 0x3A, 0x00, 0x00, 0x20, 0xB8, 0x02, 0x00, 0x20, + 0x40, 0x05, 0x00, 0x20, 0x75, 0x04, 0x00, 0x20, + 0x47, 0x00, 0x00, 0x20, 0x46, 0x00, 0x00, 0x20, + 0x34, 0x00, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, + 0x38, 0x01, 0x00, 0x20, 0x84, 0x04, 0x00, 0x20, + 0x10, 0xB5, 0x03, 0x48, 0x00, 0x78, 0x00, 0x06, + 0x01, 0xD4, 0xFF, 0xF7, 0xAF, 0xFB, 0x10, 0xBD, + 0x86, 0x02, 0x00, 0x20, 0x10, 0xB5, 0xFF, 0xF7, + 0x07, 0xFD, 0x10, 0xBD, 0x10, 0x1A, 0x59, 0x1A, + 0x40, 0x43, 0x49, 0x43, 0xC2, 0x17, 0xCB, 0x17, + 0x08, 0x18, 0x53, 0x41, 0x00, 0x22, 0xD2, 0x43, + 0x00, 0x21, 0x12, 0x1A, 0x99, 0x41, 0x01, 0xD2, + 0x00, 0x20, 0xC0, 0x43, 0x70, 0x47, 0x05, 0x01, + 0x09, 0x04, 0x08, 0x02, 0x06, 0x0A, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x10, 0x03, 0x42, 0x88, + 0x0C, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x09, 0x00, 0x50, 0x1F, 0x00, 0x00, 0x00, + 0x04, 0x03, 0x00, 0x50, 0x64, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x50, 0x77, 0x00, 0x01, 0x48, + 0x44, 0x09, 0x00, 0x50, 0x39, 0x5A, 0x5B, 0x00, + 0x10, 0x06, 0x00, 0x50, 0x00, 0x00, 0x06, 0x07, + 0x00, 0x06, 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, + 0x08, 0x06, 0x00, 0x50, 0x0C, 0x30, 0x00, 0x00, + 0x28, 0x06, 0x00, 0x50, 0x06, 0x00, 0x00, 0x00, + 0x2C, 0x06, 0x00, 0x50, 0x0A, 0x66, 0x00, 0x00, + 0x30, 0x06, 0x00, 0x50, 0xCC, 0x02, 0x00, 0x20, + 0x34, 0x06, 0x00, 0x50, 0x00, 0x20, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x50, 0x50, 0x71, 0x02, 0x00, + 0x20, 0x00, 0x00, 0x50, 0x24, 0x29, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x40, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x14, 0x33, 0x43, 0xC8, + 0x0C, 0x00, 0x00, 0x40, 0x29, 0x0A, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x40, 0x10, 0x32, 0x00, 0x00, + 0x1C, 0x0E, 0x00, 0x50, 0x03, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x50, 0x14, 0x07, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x10, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x11, 0x00, 0x50, 0x78, 0x11, 0x00, 0x00, + 0x0C, 0x11, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x11, 0x00, 0x50, 0x78, 0x01, 0x00, 0x00, + 0x14, 0x11, 0x00, 0x50, 0xC8, 0x03, 0x60, 0x00, + 0x4C, 0x00, 0x00, 0x50, 0x31, 0x00, 0x00, 0x00, + 0x3C, 0x10, 0x00, 0x50, 0x00, 0x00, 0x10, 0x00, + 0xB4, 0x10, 0x00, 0x50, 0x00, 0x26, 0x31, 0x00, + 0xC0, 0x10, 0x00, 0x50, 0x33, 0x03, 0x33, 0x03, + 0xC4, 0x10, 0x00, 0x50, 0x33, 0x03, 0x33, 0x03, + 0xC8, 0x10, 0x00, 0x50, 0x0C, 0x0A, 0x00, 0x00, + 0xCC, 0x10, 0x00, 0x50, 0x1A, 0x00, 0x00, 0x00, + 0xD0, 0x10, 0x00, 0x50, 0x03, 0x19, 0x19, 0x00, + 0xF0, 0x11, 0x00, 0x50, 0x12, 0x00, 0x00, 0x00, + 0xEC, 0x11, 0x00, 0x50, 0x5C, 0x00, 0x00, 0x00, + 0xF4, 0x11, 0x00, 0x50, 0x01, 0x00, 0x01, 0x00, + 0x2C, 0x10, 0x00, 0x50, 0x10, 0x00, 0x90, 0x00, + 0x30, 0x10, 0x00, 0x50, 0x20, 0x0C, 0x90, 0x00, + 0x34, 0x10, 0x00, 0x50, 0x30, 0x0C, 0x30, 0x0C, + 0x38, 0x10, 0x00, 0x50, 0xFF, 0x0F, 0x00, 0x00, + 0x7C, 0x10, 0x00, 0x50, 0x88, 0x88, 0xFE, 0x88, + 0x80, 0x10, 0x00, 0x50, 0x88, 0xFF, 0x00, 0x00, + 0x84, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55, + 0x88, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55, + 0x8C, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55, + 0xE8, 0x10, 0x00, 0x50, 0x3F, 0x16, 0x3F, 0x15, + 0x04, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x78, 0x5C, 0x00, 0x00, 0x04, 0x00, 0x00, 0x20, + 0x08, 0x00, 0x00, 0x00, 0x5C, 0x0A, 0x00, 0x00, + 0x80, 0x5C, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x20, + 0xC4, 0x0C, 0x00, 0x00, 0x6A, 0x0A, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x78, 0x7F, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x6E, 0x53, 0x58, 0x91, }; const unsigned char u8_rad_para_30[] = { -0xA2, 0x00, 0x03, 0xF3, 0x02, 0x02, 0x00, 0x04, -0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, -0x40, 0x01, 0x68, 0x01, 0xFA, 0x00, 0x18, 0x01, -0x05, 0x05, 0x00, 0x00, 0x01, 0x05, 0x41, 0x06, -0x0A, 0x0B, 0x41, 0x0F, 0x10, 0x14, 0x41, 0x15, -0x02, 0x07, 0x41, 0x16, 0x41, 0x41, 0x41, 0x41, -0x41, 0x41, 0x41, 0x04, 0x03, 0x09, 0x41, 0x08, -0x0E, 0x0D, 0x41, 0x13, 0x12, 0x18, 0x41, 0x17, -0x0C, 0x11, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, -0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, -0x0C, 0x3E, 0x0C, 0x3E, 0x06, 0x06, 0x02, 0x02, -0x14, 0x03, 0x06, 0x06, 0x01, 0x01, 0xE6, 0x00, -0x1F, 0x01, 0x08, 0x14, 0x82, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, 0x01, -0xB2, 0x00, 0x73, 0x00, 0x3C, 0x00, 0x09, 0x78, -0x0F, 0x08, 0x1D, 0x40, 0x48, 0x40, 0x48, 0x00, -0x00, 0x00, 0x88, 0x00, 0x62, 0x00, 0x24, 0x00, -0x06, 0x32, 0x6C, 0x41, 0x0F, 0x00, 0x00, 0x45, -0x4C, 0x00, 0x00, 0x06, 0x00, 0xA0, 0x00, 0x78, -0x00, 0x64, 0xCE, 0x15, 0xF4, 0x0D, 0x7D, 0x03, -0xDF, 0x00, 0x16, 0x0B, 0xD8, 0x01, 0x7C, 0x06, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x55, 0xAA, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xDE, 0x68, 0xE3, 0xA1, 0xE3, 0x4C, 0x06, 0xD3, + 0xA1, 0x00, 0x03, 0xF3, 0x02, 0x02, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x01, 0x68, 0x01, 0x44, 0x01, 0x44, 0x01, + 0x05, 0x05, 0x00, 0x00, 0x01, 0x05, 0x41, 0x06, + 0x0A, 0x0B, 0x41, 0x0F, 0x10, 0x14, 0x41, 0x15, + 0x02, 0x07, 0x41, 0x16, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x04, 0x03, 0x09, 0x41, 0x08, + 0x0E, 0x0D, 0x41, 0x13, 0x12, 0x18, 0x41, 0x17, + 0x0C, 0x11, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x3E, 0x0C, 0x3E, 0x08, 0x08, 0x02, 0x02, + 0x14, 0x03, 0x08, 0x08, 0x01, 0x01, 0xC8, 0x00, + 0x1A, 0x01, 0x08, 0x14, 0x82, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x01, + 0xA0, 0x00, 0x64, 0x00, 0x4A, 0x00, 0x0A, 0x78, + 0x0F, 0x08, 0x1A, 0x4A, 0x4A, 0x3C, 0x3C, 0x00, + 0x00, 0x00, 0x6A, 0x00, 0x38, 0x00, 0x1C, 0x00, + 0x06, 0x32, 0x69, 0xE3, 0x0D, 0x00, 0x00, 0x72, + 0x45, 0x00, 0x00, 0x06, 0x00, 0xA0, 0x00, 0x78, + 0x00, 0x64, 0xDB, 0x13, 0xB5, 0x0C, 0x2D, 0x03, + 0xCB, 0x00, 0x14, 0x0A, 0xB1, 0x01, 0xF1, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x55, 0xAA, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xD6, 0x9D, 0x10, 0x3D, 0xCB, 0x25, 0xBF, 0xD8, }; const unsigned char u8_rad_testfw_30[] = { -0xA0, 0x0F, 0x00, 0x20, 0xB1, 0x08, 0x00, 0x00, -0xB9, 0x08, 0x00, 0x00, 0x95, 0x08, 0x00, 0x00, -0xBD, 0x08, 0x00, 0x00, 0xBF, 0x08, 0x00, 0x00, -0xC1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xC3, 0x08, 0x00, 0x00, -0xC5, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xC7, 0x08, 0x00, 0x00, 0x81, 0x0F, 0x00, 0x00, -0xE1, 0x0C, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00, -0xCB, 0x08, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x00, -0xA1, 0x0D, 0x00, 0x00, 0x41, 0x0A, 0x00, 0x00, -0xCB, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, -0xCB, 0x08, 0x00, 0x00, 0x41, 0x0B, 0x00, 0x00, -0xE1, 0x0A, 0x00, 0x00, 0xA1, 0x0A, 0x00, 0x00, -0xC1, 0x0A, 0x00, 0x00, 0x81, 0x0A, 0x00, 0x00, -0xCB, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, -0x03, 0x48, 0x85, 0x46, 0x00, 0xF0, 0x80, 0xF8, -0x00, 0x48, 0x00, 0x47, 0xE9, 0x33, 0x00, 0x00, -0xA0, 0x0F, 0x00, 0x20, 0x04, 0x20, 0x71, 0x46, -0x08, 0x42, 0x02, 0xD0, 0xEF, 0xF3, 0x09, 0x80, -0x01, 0xE0, 0xEF, 0xF3, 0x08, 0x80, 0x71, 0x46, -0x00, 0x4A, 0x10, 0x47, 0x01, 0x33, 0x00, 0x00, -0x06, 0x48, 0x80, 0x47, 0x06, 0x48, 0x00, 0x47, -0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, -0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, -0xFE, 0xE7, 0xFE, 0xE7, 0x15, 0x0A, 0x00, 0x00, -0x81, 0x08, 0x00, 0x00, 0x30, 0xB5, 0x0B, 0x46, -0x01, 0x46, 0x00, 0x20, 0x20, 0x22, 0x01, 0x24, -0x09, 0xE0, 0x0D, 0x46, 0xD5, 0x40, 0x9D, 0x42, -0x05, 0xD3, 0x1D, 0x46, 0x95, 0x40, 0x49, 0x1B, -0x25, 0x46, 0x95, 0x40, 0x40, 0x19, 0x15, 0x46, -0x52, 0x1E, 0x00, 0x2D, 0xF1, 0xDC, 0x30, 0xBD, -0x70, 0xB5, 0x00, 0x24, 0x25, 0x46, 0x00, 0x28, -0x01, 0xDA, 0x01, 0x24, 0x40, 0x42, 0x00, 0x29, -0x01, 0xDA, 0x01, 0x25, 0x49, 0x42, 0xFF, 0xF7, -0xDD, 0xFF, 0xAC, 0x42, 0x00, 0xD0, 0x40, 0x42, -0x00, 0x2C, 0x00, 0xD0, 0x49, 0x42, 0x70, 0xBD, -0x03, 0x46, 0x0B, 0x43, 0x9B, 0x07, 0x03, 0xD0, -0x09, 0xE0, 0x08, 0xC9, 0x12, 0x1F, 0x08, 0xC0, -0x04, 0x2A, 0xFA, 0xD2, 0x03, 0xE0, 0x0B, 0x78, -0x49, 0x1C, 0x03, 0x70, 0x40, 0x1C, 0x52, 0x1E, -0xF9, 0xD2, 0x70, 0x47, 0xD2, 0xB2, 0x01, 0xE0, -0x02, 0x70, 0x40, 0x1C, 0x49, 0x1E, 0xFB, 0xD2, -0x70, 0x47, 0x00, 0x22, 0xF6, 0xE7, 0x10, 0xB5, -0x04, 0x46, 0x08, 0x46, 0x11, 0x46, 0x02, 0x46, -0x20, 0x46, 0xFF, 0xF7, 0xEF, 0xFF, 0x20, 0x46, -0x10, 0xBD, 0x00, 0x1D, 0x03, 0x21, 0x40, 0x1E, -0x03, 0x78, 0x12, 0x02, 0x1A, 0x43, 0x49, 0x1E, -0xF9, 0xD5, 0x10, 0x46, 0x70, 0x47, 0x00, 0x00, -0x06, 0x4C, 0x01, 0x25, 0x06, 0x4E, 0x05, 0xE0, -0x20, 0x46, 0xE3, 0x68, 0x07, 0xC8, 0x2B, 0x43, -0x98, 0x47, 0x10, 0x34, 0xB4, 0x42, 0xF7, 0xD3, -0xFF, 0xF7, 0x72, 0xFF, 0x9C, 0x36, 0x00, 0x00, -0xBC, 0x36, 0x00, 0x00, 0xC1, 0x06, 0xC9, 0x0E, -0x01, 0x20, 0x88, 0x40, 0x01, 0x49, 0x08, 0x60, -0x70, 0x47, 0x00, 0x00, 0x00, 0xE1, 0x00, 0xE0, -0x0B, 0x49, 0x10, 0xB5, 0x88, 0x42, 0x01, 0xD9, -0x01, 0x20, 0x10, 0xBD, 0x01, 0x02, 0x09, 0x0A, -0x08, 0x48, 0x49, 0x1E, 0x41, 0x61, 0x08, 0x49, -0x07, 0x23, 0xCA, 0x69, 0x12, 0x02, 0x12, 0x0A, -0x04, 0x04, 0x22, 0x43, 0xCA, 0x61, 0x00, 0x21, -0x81, 0x61, 0x03, 0x61, 0x08, 0x46, 0x10, 0xBD, -0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xE0, 0x00, 0xE0, -0x04, 0xED, 0x00, 0xE0, 0x70, 0x47, 0x00, 0x00, -0x03, 0x49, 0x02, 0x20, 0x08, 0x60, 0x02, 0x49, -0x80, 0x39, 0x08, 0x60, 0x70, 0x47, 0x00, 0x00, -0x80, 0xE1, 0x00, 0xE0, 0x62, 0xB6, 0x02, 0x48, -0x00, 0x21, 0x01, 0x60, 0x70, 0x47, 0x00, 0x00, -0x0C, 0x00, 0x00, 0x40, 0x30, 0xB4, 0x74, 0x46, -0x64, 0x1E, 0x25, 0x78, 0x64, 0x1C, 0xAB, 0x42, -0x00, 0xD2, 0x1D, 0x46, 0x63, 0x5D, 0x5B, 0x00, -0xE3, 0x18, 0x30, 0xBC, 0x18, 0x47, 0x00, 0x00, -0x05, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, -0x01, 0x20, 0x80, 0x07, 0x01, 0x6A, 0x03, 0x22, -0xD2, 0x03, 0x11, 0x43, 0x01, 0x62, 0x70, 0x47, -0x40, 0x00, 0x00, 0x20, 0x02, 0xE0, 0x08, 0xC8, -0x12, 0x1F, 0x08, 0xC1, 0x00, 0x2A, 0xFA, 0xD1, -0x70, 0x47, 0x00, 0x20, 0x01, 0xE0, 0x01, 0xC1, -0x12, 0x1F, 0x00, 0x2A, 0xFB, 0xD1, 0x70, 0x47, -0x10, 0xB5, 0x00, 0xF0, 0xDF, 0xF9, 0x10, 0xBD, -0x01, 0x20, 0x80, 0x07, 0x41, 0x6A, 0x82, 0x13, -0x11, 0x43, 0x41, 0x62, 0x41, 0x6A, 0xC2, 0x13, -0x11, 0x43, 0x41, 0x62, 0x70, 0x47, 0x10, 0xB5, -0x02, 0xF0, 0x66, 0xF8, 0x10, 0xBD, 0x00, 0x00, -0x03, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, -0x02, 0x49, 0x01, 0x20, 0xC8, 0x60, 0x70, 0x47, -0x64, 0x01, 0x00, 0x20, 0x00, 0x03, 0x00, 0x50, -0x10, 0xB5, 0x00, 0xF0, 0xE7, 0xFA, 0x10, 0xBD, -0x70, 0x47, 0x00, 0x00, 0x04, 0x49, 0x06, 0x22, -0x00, 0x28, 0x08, 0x68, 0x01, 0xD0, 0x10, 0x43, -0x00, 0xE0, 0x90, 0x43, 0x08, 0x60, 0x70, 0x47, -0x00, 0x09, 0x00, 0x50, 0x00, 0x20, 0x70, 0x47, -0x70, 0x47, 0x00, 0x00, 0x01, 0x28, 0x05, 0xD0, -0x02, 0x28, 0x05, 0xD0, 0x04, 0x28, 0x06, 0xD0, -0x00, 0x20, 0x70, 0x47, 0x03, 0x48, 0x70, 0x47, -0x02, 0x48, 0xC0, 0x30, 0x70, 0x47, 0x02, 0x48, -0x70, 0x47, 0x00, 0x00, 0x78, 0x7C, 0x00, 0x00, -0xF8, 0x7D, 0x00, 0x00, 0x70, 0xB5, 0x05, 0x46, -0x06, 0x4C, 0x01, 0xF0, 0xE3, 0xFC, 0x00, 0x21, -0x8A, 0x00, 0x49, 0x1C, 0xA0, 0x58, 0xC9, 0xB2, -0xA8, 0x50, 0x18, 0x29, 0xF8, 0xD3, 0x01, 0x20, -0x70, 0xBD, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, -0x01, 0x20, 0x80, 0x07, 0x41, 0x68, 0x82, 0x14, -0x11, 0x43, 0x41, 0x60, 0x70, 0x47, 0x00, 0x00, -0x30, 0xB5, 0x1E, 0x4B, 0x58, 0x68, 0x99, 0x68, -0x00, 0x28, 0x01, 0xDA, 0xDA, 0x04, 0x5A, 0x60, -0x1B, 0x4A, 0x15, 0x68, 0x01, 0x24, 0x05, 0x40, -0x00, 0x2D, 0x02, 0xD1, 0x50, 0x68, 0x08, 0x42, -0x0F, 0xD0, 0x18, 0x48, 0x04, 0x70, 0x00, 0xF0, -0xAF, 0xF8, 0x14, 0x72, 0x16, 0x48, 0x00, 0x78, -0x00, 0x28, 0x06, 0xD0, 0x18, 0x68, 0x01, 0x21, -0x00, 0x09, 0x00, 0x01, 0x89, 0x02, 0x08, 0x43, -0x18, 0x60, 0x58, 0x68, 0x80, 0x02, 0x02, 0xD5, -0x01, 0x20, 0x40, 0x05, 0x58, 0x60, 0x58, 0x68, -0x00, 0x04, 0x03, 0xD5, 0x01, 0x20, 0xC0, 0x03, -0x58, 0x60, 0x94, 0x72, 0x58, 0x68, 0x80, 0x00, -0x03, 0xD5, 0x01, 0x20, 0x40, 0x07, 0x58, 0x60, -0xD4, 0x72, 0x58, 0x68, 0x40, 0x00, 0x03, 0xD5, -0x01, 0x20, 0x80, 0x07, 0x58, 0x60, 0x94, 0x72, -0x30, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, -0x24, 0x00, 0x00, 0x20, 0x37, 0x00, 0x00, 0x20, -0x35, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x02, 0x46, -0x07, 0x49, 0x60, 0x32, 0x11, 0x80, 0x00, 0x21, -0x51, 0x80, 0x4C, 0x00, 0x53, 0x88, 0x04, 0x5B, -0x49, 0x1C, 0x1B, 0x19, 0xC9, 0xB2, 0x53, 0x80, -0x31, 0x29, 0xF6, 0xD3, 0x10, 0xBD, 0x00, 0x00, -0xAA, 0x55, 0x00, 0x00, 0x10, 0xB5, 0x00, 0xF0, -0xB9, 0xF9, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00, -0x70, 0x47, 0x00, 0x00, 0xF8, 0xB5, 0x06, 0x46, -0x0D, 0x46, 0x30, 0x23, 0xFF, 0xF7, 0x6A, 0xFF, -0x04, 0x00, 0x28, 0xD0, 0x19, 0x4F, 0x00, 0x2A, -0x21, 0xD0, 0x00, 0x20, 0xFF, 0xF7, 0x52, 0xFF, -0x02, 0xE0, 0x98, 0x00, 0x29, 0x58, 0x21, 0x50, -0x18, 0x46, 0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, -0xF7, 0xD1, 0x38, 0x68, 0x01, 0x88, 0x31, 0x43, -0x01, 0x80, 0x11, 0x49, 0x09, 0x68, 0xCA, 0x79, -0x8B, 0x79, 0x11, 0x02, 0x19, 0x43, 0x41, 0x80, -0xC1, 0x21, 0x89, 0x00, 0x0D, 0x48, 0x00, 0xF0, -0x1D, 0xF8, 0x0D, 0x49, 0xC8, 0x63, 0x01, 0x20, -0xFF, 0xF7, 0x34, 0xFF, 0x0C, 0xE0, 0x38, 0x68, -0x00, 0x88, 0x30, 0x40, 0x03, 0xD1, 0xF8, 0xBD, -0x98, 0x00, 0x21, 0x58, 0x29, 0x50, 0x18, 0x46, -0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, 0xF7, 0xD1, -0x01, 0x20, 0xF8, 0xBD, 0x08, 0x00, 0x00, 0x20, -0x80, 0x02, 0x00, 0x20, 0x78, 0x7C, 0x00, 0x00, -0x40, 0x7F, 0x00, 0x00, 0x70, 0xB5, 0x41, 0x18, -0x49, 0x1E, 0x64, 0x24, 0x09, 0x04, 0x0B, 0x4D, -0x01, 0x43, 0x69, 0x63, 0xE8, 0x68, 0x81, 0x21, -0x09, 0x06, 0x08, 0x43, 0xE8, 0x60, 0x02, 0xE0, -0x01, 0x20, 0xFF, 0xF7, 0xA3, 0xFF, 0xE8, 0x68, -0xC0, 0x01, 0x04, 0xD5, 0x20, 0x46, 0x64, 0x1E, -0xA4, 0xB2, 0x00, 0x28, 0xF4, 0xD1, 0xA8, 0x6B, -0x70, 0xBD, 0x00, 0x00, 0x40, 0x09, 0x00, 0x50, -0x03, 0x48, 0x02, 0x49, 0x41, 0x60, 0x03, 0x49, -0x81, 0x60, 0x70, 0x47, 0x1F, 0x1F, 0x5F, 0x1F, -0x00, 0x10, 0x00, 0x50, 0x1F, 0x1F, 0x1F, 0x1F, -0x70, 0xB5, 0x01, 0x25, 0xAD, 0x07, 0xE8, 0x68, -0x40, 0x08, 0x40, 0x00, 0xE8, 0x60, 0x1D, 0x48, -0x41, 0x68, 0x01, 0x26, 0x49, 0x07, 0x00, 0x29, -0x2E, 0xDA, 0x42, 0x68, 0x04, 0x21, 0x0A, 0x43, -0x42, 0x60, 0x19, 0x48, 0x00, 0x68, 0x19, 0x4A, -0x40, 0x05, 0x40, 0x0F, 0x01, 0x28, 0x07, 0xD0, -0x00, 0x23, 0x17, 0x4C, 0x13, 0x70, 0x20, 0x73, -0x20, 0x7B, 0x06, 0x28, 0x03, 0xD0, 0x03, 0xE0, -0x03, 0x20, 0x10, 0x70, 0x18, 0xE0, 0x21, 0x73, -0x20, 0x7B, 0x02, 0x28, 0x18, 0xD0, 0x20, 0x7B, -0x06, 0x28, 0x05, 0xD8, 0x20, 0x7B, 0x04, 0x28, -0x02, 0xD3, 0x01, 0x20, 0x01, 0xF0, 0x4E, 0xF8, -0x20, 0x7B, 0x0C, 0x49, 0x09, 0x78, 0x88, 0x42, -0x06, 0xD0, 0x0B, 0x48, 0x01, 0x78, 0x31, 0x43, -0x01, 0x70, 0x0A, 0x49, 0x81, 0x20, 0x08, 0x70, -0xE8, 0x68, 0x30, 0x43, 0xE8, 0x60, 0x70, 0xBD, -0x00, 0x20, 0xEB, 0xE7, 0x40, 0x00, 0x00, 0x50, -0x00, 0x11, 0x00, 0x50, 0x38, 0x00, 0x00, 0x20, -0x24, 0x00, 0x00, 0x20, 0x39, 0x01, 0x00, 0x20, -0x34, 0x00, 0x00, 0x20, 0xE0, 0x08, 0x00, 0x20, -0x10, 0xB5, 0x01, 0x22, 0x92, 0x07, 0x13, 0x68, -0x03, 0x4C, 0x23, 0x43, 0x13, 0x60, 0x89, 0x04, -0x09, 0x0C, 0x01, 0xF0, 0x6B, 0xF8, 0x10, 0xBD, -0x10, 0x01, 0x42, 0x88, 0x00, 0x00, 0x00, 0x00, -0x10, 0xB5, 0x14, 0x48, 0x80, 0x69, 0x40, 0x04, -0x23, 0xD5, 0x13, 0x4C, 0xE0, 0x68, 0xA2, 0x68, -0x02, 0x40, 0xE0, 0x68, 0xA1, 0x68, 0x88, 0x43, -0xE0, 0x60, 0x10, 0x07, 0x03, 0xD5, 0x08, 0x20, -0xE0, 0x60, 0x02, 0xF0, 0x49, 0xFB, 0x50, 0x07, -0x0E, 0xD5, 0x04, 0x20, 0xE0, 0x60, 0x02, 0xF0, -0x43, 0xFB, 0x02, 0xF0, 0x56, 0xFA, 0x00, 0x28, -0x06, 0xD1, 0x08, 0x48, 0x00, 0x78, 0x00, 0x28, -0x02, 0xD0, 0x01, 0x20, 0xFF, 0xF7, 0x48, 0xFE, -0xFF, 0x20, 0xF3, 0x30, 0xE0, 0x60, 0x01, 0x20, -0xE0, 0x60, 0x10, 0xBD, 0x00, 0x09, 0x00, 0x50, -0x00, 0x05, 0x00, 0x50, 0x90, 0x02, 0x00, 0x20, -0x70, 0xB5, 0x0C, 0x49, 0x00, 0x20, 0x0C, 0x4B, -0x89, 0x25, 0x0C, 0x68, 0xAD, 0x00, 0x21, 0x18, -0xC9, 0x7E, 0x41, 0x29, 0x08, 0xD0, 0x42, 0x00, -0x99, 0x5A, 0x49, 0x05, 0x49, 0x0D, 0x99, 0x52, -0x4E, 0x05, 0x01, 0xD5, 0x49, 0x1B, 0x99, 0x52, -0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, 0xEE, 0xD3, -0x01, 0x20, 0x70, 0xBD, 0x80, 0x02, 0x00, 0x20, -0xAC, 0x03, 0x00, 0x20, 0x01, 0x21, 0x89, 0x07, -0x0A, 0x15, 0x00, 0x28, 0x48, 0x69, 0x02, 0xD0, -0x10, 0x43, 0x48, 0x61, 0x70, 0x47, 0x90, 0x43, -0xFB, 0xE7, 0x00, 0x00, 0x01, 0x20, 0x01, 0x49, -0x08, 0x70, 0x70, 0x47, 0xD2, 0x00, 0x00, 0x20, -0xF8, 0xB5, 0x3C, 0x48, 0x80, 0x69, 0x40, 0x04, -0x72, 0xD5, 0x3B, 0x4D, 0xA8, 0x6A, 0xE9, 0x68, -0xAC, 0x68, 0x0C, 0x40, 0xE9, 0x68, 0xAA, 0x68, -0x91, 0x43, 0xE9, 0x60, 0x37, 0x4E, 0xC0, 0x07, -0x15, 0xD0, 0x68, 0x69, 0x89, 0x27, 0xC0, 0xB2, -0xEF, 0x60, 0xAA, 0x6A, 0x01, 0x21, 0x0A, 0x43, -0xAA, 0x62, 0x0B, 0x28, 0x03, 0xD2, 0x31, 0x70, -0x00, 0xF0, 0x1E, 0xFD, 0x03, 0xE0, 0x00, 0x21, -0x31, 0x70, 0x00, 0xF0, 0xF5, 0xFC, 0xBC, 0x43, -0x01, 0x20, 0xFF, 0xF7, 0xE5, 0xFD, 0x2C, 0x48, -0xA1, 0x04, 0x04, 0xD5, 0x29, 0x6A, 0x81, 0x43, -0x29, 0x62, 0x41, 0x14, 0xE9, 0x60, 0xE1, 0x04, -0x16, 0xD5, 0x29, 0x6A, 0x81, 0x43, 0x29, 0x62, -0x01, 0x20, 0x00, 0x03, 0xE8, 0x60, 0xE8, 0x6B, -0x24, 0x4F, 0x00, 0x04, 0x79, 0x78, 0x00, 0x0E, -0x02, 0x29, 0x02, 0xD0, 0x00, 0x28, 0x02, 0xD0, -0x06, 0xE0, 0x03, 0x28, 0x04, 0xD1, 0x01, 0x20, -0xFF, 0xF7, 0xC6, 0xFD, 0x00, 0x20, 0x38, 0x70, -0xA0, 0x05, 0x02, 0xD5, 0x01, 0x20, 0x40, 0x02, -0xE8, 0x60, 0x20, 0x07, 0x09, 0xD5, 0x30, 0x78, -0x01, 0x28, 0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, -0xC3, 0xFC, 0x64, 0x08, 0x64, 0x00, 0x09, 0x20, -0xE8, 0x60, 0x60, 0x07, 0x17, 0xD5, 0x30, 0x78, -0x01, 0x28, 0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, -0xB7, 0xFC, 0x05, 0x20, 0xE8, 0x60, 0xA8, 0x69, -0x0F, 0x49, 0x88, 0x42, 0x08, 0xD0, 0xA9, 0x69, -0x0D, 0x48, 0x40, 0x1C, 0x81, 0x42, 0x03, 0xD0, -0xA9, 0x69, 0xC0, 0x1C, 0x81, 0x42, 0x02, 0xD1, -0x01, 0x20, 0xFF, 0xF7, 0x99, 0xFD, 0xFF, 0x20, -0xF3, 0x30, 0xE8, 0x60, 0x01, 0x20, 0xE8, 0x60, -0xF8, 0xBD, 0x00, 0x00, 0x00, 0x09, 0x00, 0x50, -0x00, 0x06, 0x00, 0x50, 0xD0, 0x00, 0x00, 0x20, -0x00, 0x00, 0x00, 0x40, 0x90, 0x02, 0x00, 0x20, -0x88, 0x02, 0x00, 0x20, 0x03, 0x49, 0x0A, 0x68, -0x10, 0x18, 0x0A, 0x68, 0x90, 0x42, 0xFC, 0xD1, -0x70, 0x47, 0x00, 0x00, 0x64, 0x01, 0x00, 0x20, -0x02, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, -0x70, 0x47, 0x00, 0x00, 0xDC, 0x08, 0x00, 0x20, -0x10, 0xB5, 0x02, 0xF0, 0xC9, 0xF9, 0x02, 0xF0, -0x83, 0xFA, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00, -0x05, 0x20, 0x00, 0x07, 0x82, 0x69, 0x0C, 0x49, -0x00, 0x2A, 0x07, 0xDA, 0x82, 0x69, 0xC3, 0x00, -0x92, 0x00, 0x92, 0x08, 0xD2, 0x18, 0x82, 0x61, -0x01, 0x22, 0x4A, 0x72, 0x82, 0x69, 0x52, 0x00, -0x08, 0xD5, 0x82, 0x69, 0x01, 0x23, 0x92, 0x00, -0x92, 0x08, 0x9B, 0x07, 0xD2, 0x18, 0x82, 0x61, -0x00, 0x20, 0x48, 0x72, 0x70, 0x47, 0x00, 0x00, -0xE0, 0x08, 0x00, 0x20, 0x70, 0xB5, 0x1D, 0x4D, -0x68, 0x78, 0x00, 0x06, 0x2A, 0xD5, 0x05, 0x20, -0xFF, 0xF7, 0x04, 0xFE, 0x01, 0x20, 0xFF, 0xF7, -0x43, 0xFD, 0x68, 0x78, 0x40, 0x06, 0x40, 0x0E, -0x68, 0x70, 0x68, 0x78, 0x16, 0x4C, 0x10, 0x38, -0x03, 0x00, 0xFF, 0xF7, 0x0F, 0xFD, 0x05, 0x04, -0x06, 0x0C, 0x1C, 0x1E, 0x0E, 0x00, 0x06, 0x20, -0x06, 0xE0, 0x01, 0x20, 0x20, 0x70, 0x11, 0xA0, -0xFF, 0xF7, 0x60, 0xFD, 0x01, 0xE0, 0x02, 0x20, -0x20, 0x70, 0x68, 0x78, 0x80, 0x21, 0x08, 0x43, -0x28, 0x71, 0x00, 0x0A, 0x68, 0x71, 0x00, 0x20, -0xFF, 0xF7, 0x22, 0xFD, 0xA9, 0x88, 0x0C, 0xA0, -0xFF, 0xF7, 0x50, 0xFD, 0x70, 0xBD, 0x03, 0x20, -0xEE, 0xE7, 0x0D, 0x48, 0x69, 0x78, 0x82, 0x88, -0x0C, 0xA0, 0xFF, 0xF7, 0x47, 0xFD, 0x05, 0x20, -0xE6, 0xE7, 0x00, 0x00, 0x88, 0x02, 0x00, 0x20, -0x98, 0x01, 0x00, 0x20, 0x43, 0x4D, 0x44, 0x5F, -0x49, 0x4E, 0x49, 0x54, 0x0D, 0x0A, 0x00, 0x00, -0x43, 0x4D, 0x44, 0x20, 0x45, 0x78, 0x69, 0x74, -0x5B, 0x25, 0x78, 0x5D, 0x3D, 0x0D, 0x0A, 0x00, -0x74, 0x06, 0x00, 0x20, 0x43, 0x4D, 0x44, 0x3D, -0x30, 0x78, 0x25, 0x78, 0x3A, 0x25, 0x78, 0x0D, -0x0A, 0x00, 0x00, 0x00, 0xF8, 0xB5, 0x1A, 0x4C, -0x01, 0x20, 0x20, 0x70, 0x19, 0x4E, 0x00, 0x20, -0x30, 0x71, 0x70, 0x71, 0x18, 0x48, 0xB0, 0x70, -0x00, 0x0A, 0xF0, 0x70, 0x00, 0x20, 0x02, 0xF0, -0x31, 0xF8, 0x16, 0x4F, 0x04, 0x25, 0x20, 0x78, -0x03, 0x00, 0xFF, 0xF7, 0xB7, 0xFC, 0x07, 0x14, -0x05, 0x12, 0x12, 0x16, 0x19, 0x1F, 0x14, 0x00, -0x37, 0x71, 0x38, 0x0A, 0x70, 0x71, 0x25, 0x70, -0x70, 0x20, 0x30, 0x70, 0x0E, 0x48, 0x30, 0x71, -0x00, 0x0A, 0x70, 0x71, 0x00, 0xF0, 0x1A, 0xFB, -0xE9, 0xE7, 0x00, 0xF0, 0x17, 0xFB, 0x25, 0x70, -0xE5, 0xE7, 0xFF, 0xF7, 0x7B, 0xFF, 0xE2, 0xE7, -0x08, 0xA0, 0xFF, 0xF7, 0xF7, 0xFC, 0x00, 0xF0, -0x11, 0xF8, 0xF2, 0xE7, 0xF8, 0xBD, 0x00, 0x00, -0x98, 0x01, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20, -0xCC, 0x55, 0x00, 0x00, 0x55, 0xAA, 0x00, 0x00, -0x11, 0x5A, 0x00, 0x00, 0x5B, 0x53, 0x54, 0x5D, -0x0D, 0x0A, 0x00, 0x00, 0x70, 0xB5, 0x1F, 0x48, -0x00, 0x25, 0x84, 0x88, 0x01, 0x21, 0x00, 0x2C, -0x1A, 0xD0, 0x0B, 0x00, 0xFF, 0xF7, 0x7E, 0xFC, -0x07, 0x16, 0x05, 0x13, 0x1A, 0x16, 0x0F, 0x1F, -0x16, 0x00, 0x00, 0xF0, 0x55, 0xF8, 0x00, 0xF0, -0x91, 0xF9, 0x01, 0x28, 0x01, 0xD1, 0x05, 0x21, -0x08, 0xE0, 0x06, 0x21, 0x06, 0xE0, 0x01, 0xF0, -0xB5, 0xFF, 0x02, 0x21, 0x02, 0xE0, 0x00, 0xF0, -0xC5, 0xFD, 0x03, 0x21, 0x00, 0x2C, 0xE4, 0xD1, -0x28, 0x46, 0x70, 0xBD, 0x01, 0xF0, 0x18, 0xFF, -0x00, 0xF0, 0x84, 0xFD, 0xED, 0xE7, 0x0C, 0xA0, -0xFF, 0xF7, 0xB8, 0xFC, 0x0E, 0x48, 0xFF, 0xF7, -0x2D, 0xFD, 0x0E, 0x48, 0xFF, 0xF7, 0x2A, 0xFD, -0x0D, 0x48, 0xFF, 0xF7, 0x27, 0xFD, 0x0D, 0x48, -0xFF, 0xF7, 0x24, 0xFD, 0x00, 0xF0, 0x2E, 0xFA, -0x0B, 0x48, 0x01, 0x68, 0x0B, 0xA0, 0xFF, 0xF7, -0xA5, 0xFC, 0xE1, 0xE7, 0x74, 0x06, 0x00, 0x20, -0x54, 0x45, 0x53, 0x54, 0x5F, 0x43, 0x4D, 0x44, -0x5F, 0x45, 0x58, 0x49, 0x54, 0x0D, 0x0A, 0x00, -0xE4, 0x02, 0x00, 0x20, 0x48, 0x03, 0x00, 0x20, -0xAC, 0x03, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20, -0x0C, 0x05, 0x00, 0x20, 0x67, 0x5F, 0x75, 0x33, -0x32, 0x5F, 0x77, 0x65, 0x61, 0x72, 0x61, 0x62, -0x6C, 0x65, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x5F, -0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x3A, 0x20, -0x30, 0x78, 0x25, 0x78, 0x0D, 0x0A, 0x00, 0x00, -0x10, 0xB5, 0xC8, 0x21, 0x0E, 0x48, 0xFF, 0xF7, -0xB8, 0xFB, 0xC8, 0x21, 0x0D, 0x48, 0xFF, 0xF7, -0xB4, 0xFB, 0xC8, 0x21, 0x0C, 0x48, 0xFF, 0xF7, -0xB0, 0xFB, 0xC8, 0x21, 0x0B, 0x48, 0xFF, 0xF7, -0xAC, 0xFB, 0x64, 0x21, 0x0A, 0x48, 0xFF, 0xF7, -0xA8, 0xFB, 0x32, 0x21, 0x09, 0x48, 0xFF, 0xF7, -0xA4, 0xFB, 0x09, 0x49, 0x00, 0x20, 0x08, 0x60, -0x08, 0xA0, 0xFF, 0xF7, 0x5F, 0xFC, 0x10, 0xBD, -0xE4, 0x02, 0x00, 0x20, 0x48, 0x03, 0x00, 0x20, -0xAC, 0x03, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20, -0x74, 0x04, 0x00, 0x20, 0xD8, 0x04, 0x00, 0x20, -0x0C, 0x05, 0x00, 0x20, 0x46, 0x54, 0x20, 0x52, -0x65, 0x73, 0x75, 0x6C, 0x74, 0x20, 0x49, 0x6E, -0x69, 0x74, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0xF3, 0xB5, 0xC1, 0xB0, 0x42, 0x98, 0x02, 0x28, -0x01, 0xD2, 0x02, 0x20, 0x42, 0x90, 0xC0, 0x21, -0x68, 0x46, 0xFF, 0xF7, 0x7A, 0xFB, 0x01, 0xF0, -0x57, 0xFB, 0x01, 0xF0, 0xFD, 0xFD, 0x00, 0xF0, -0xB5, 0xFF, 0x00, 0x24, 0x6E, 0x46, 0x1A, 0xE0, -0x01, 0xF0, 0x76, 0xFC, 0x01, 0xF0, 0xF4, 0xFD, -0x00, 0xF0, 0xAC, 0xFF, 0x18, 0x49, 0x00, 0x20, -0x0A, 0x68, 0x18, 0x49, 0x0F, 0x68, 0x39, 0x18, -0xC9, 0x7E, 0x41, 0x29, 0x05, 0xD0, 0x81, 0x00, -0x45, 0x00, 0x73, 0x58, 0x55, 0x5F, 0x5B, 0x19, -0x73, 0x50, 0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, -0xF1, 0xD3, 0x64, 0x1C, 0xE4, 0xB2, 0x42, 0x98, -0x84, 0x42, 0xE1, 0xD3, 0x01, 0xF0, 0xD8, 0xFD, -0x01, 0xF0, 0x6E, 0xFC, 0x0B, 0x4D, 0x00, 0x24, -0x28, 0x68, 0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, -0x07, 0xD0, 0xA0, 0x00, 0x30, 0x58, 0x42, 0x99, -0xFF, 0xF7, 0x16, 0xFB, 0x41, 0x99, 0x62, 0x00, -0x88, 0x52, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C, -0xEE, 0xD3, 0x01, 0x20, 0x43, 0xB0, 0xF0, 0xBD, -0x4C, 0x01, 0x00, 0x20, 0x80, 0x02, 0x00, 0x20, -0x70, 0xB5, 0x14, 0x48, 0x78, 0x24, 0x41, 0x6B, -0x03, 0x22, 0x12, 0x05, 0x91, 0x43, 0x01, 0x22, -0x12, 0x05, 0x89, 0x18, 0x41, 0x63, 0x10, 0x4D, -0x10, 0x48, 0xA9, 0x7E, 0xFF, 0xF7, 0xA0, 0xFF, -0x0C, 0x49, 0x0F, 0x48, 0x40, 0x31, 0x88, 0x60, -0xA9, 0x7E, 0x0E, 0x48, 0xFF, 0xF7, 0x98, 0xFF, -0x0D, 0x49, 0x0C, 0x4B, 0x0A, 0x68, 0x00, 0x20, -0x11, 0x18, 0xC9, 0x7E, 0x41, 0x29, 0x03, 0xD0, -0x41, 0x00, 0x5D, 0x5A, 0x2D, 0x1B, 0x5D, 0x52, -0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, 0xF3, 0xD3, -0x01, 0x20, 0x70, 0xBD, 0x80, 0x10, 0x00, 0x50, -0xA8, 0x06, 0x00, 0x20, 0x74, 0x04, 0x00, 0x20, -0x24, 0x0A, 0x00, 0x00, 0x48, 0x03, 0x00, 0x20, -0x80, 0x02, 0x00, 0x20, 0xF8, 0xB5, 0x3A, 0x48, -0x00, 0x24, 0x60, 0x21, 0x00, 0x68, 0xFF, 0xF7, -0xF8, 0xFA, 0x38, 0x4E, 0x38, 0x48, 0x00, 0x68, -0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x5A, 0xD0, -0xA5, 0x07, 0xAD, 0x0F, 0xA7, 0x08, 0x0C, 0x21, -0x20, 0x46, 0xFF, 0xF7, 0xA7, 0xFA, 0x88, 0x08, -0x00, 0x21, 0xC9, 0x43, 0x71, 0x62, 0x09, 0x0C, -0xB1, 0x62, 0x20, 0x2C, 0x05, 0xD2, 0x72, 0x6A, -0x01, 0x21, 0xA1, 0x40, 0x8A, 0x43, 0x72, 0x62, -0x06, 0xE0, 0xB3, 0x6A, 0x21, 0x46, 0x20, 0x39, -0x01, 0x22, 0x8A, 0x40, 0x93, 0x43, 0xB3, 0x62, -0x03, 0x2D, 0x02, 0xD2, 0x69, 0x1C, 0xC9, 0xB2, -0x00, 0xE0, 0x08, 0x21, 0xF2, 0x6A, 0x25, 0x4B, -0x1A, 0x40, 0x01, 0x23, 0xBB, 0x40, 0x1B, 0x05, -0x1B, 0x09, 0x1A, 0x43, 0xF2, 0x62, 0x72, 0x6B, -0x0E, 0x23, 0x9A, 0x43, 0x01, 0x23, 0x83, 0x40, -0x5B, 0x07, 0x1B, 0x0F, 0x1A, 0x43, 0x72, 0x63, -0x40, 0x1C, 0x82, 0x07, 0x92, 0x0F, 0x90, 0x05, -0x13, 0x05, 0x18, 0x43, 0x93, 0x04, 0x18, 0x43, -0x12, 0x04, 0x16, 0x4F, 0x10, 0x43, 0x40, 0x37, -0xFA, 0x6A, 0xFF, 0x23, 0x1B, 0x04, 0x9A, 0x43, -0x10, 0x43, 0xF8, 0x62, 0xF0, 0x6B, 0x09, 0x07, -0xF4, 0x22, 0x09, 0x0E, 0x90, 0x43, 0x09, 0x1D, -0x08, 0x43, 0xF0, 0x63, 0xB0, 0x6B, 0x74, 0x21, -0x88, 0x43, 0x6D, 0x1C, 0x69, 0x07, 0x49, 0x0E, -0x09, 0x1D, 0x08, 0x43, 0xB0, 0x63, 0x01, 0xF0, -0x9F, 0xFB, 0x01, 0xF0, 0x1D, 0xFD, 0x64, 0x1C, -0xE4, 0xB2, 0x30, 0x2C, 0x9A, 0xD3, 0x04, 0x48, -0x60, 0x22, 0x01, 0x68, 0x06, 0x48, 0xFF, 0xF7, -0x73, 0xFA, 0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, -0x4C, 0x01, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, -0x80, 0x02, 0x00, 0x20, 0xFF, 0xFF, 0x00, 0xF0, -0xE4, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x0E, 0x48, -0x00, 0x68, 0x0E, 0x4C, 0x40, 0x05, 0x40, 0x0F, -0x20, 0x70, 0x00, 0x20, 0x01, 0xF0, 0xF2, 0xFE, -0x0B, 0x49, 0x01, 0x20, 0x08, 0x70, 0x08, 0x20, -0x20, 0x70, 0x0A, 0x49, 0x06, 0x20, 0x08, 0x70, -0x01, 0x20, 0x09, 0x49, 0x80, 0x02, 0x08, 0x80, -0x01, 0xF0, 0x50, 0xFE, 0x01, 0xF0, 0xEC, 0xFC, -0x01, 0xF0, 0x82, 0xFB, 0x01, 0x20, 0x10, 0xBD, -0x00, 0x11, 0x00, 0x50, 0x39, 0x01, 0x00, 0x20, -0x3B, 0x01, 0x00, 0x20, 0xCD, 0x08, 0x00, 0x20, -0x46, 0x01, 0x00, 0x20, 0xF0, 0xB5, 0x3C, 0x49, -0x00, 0x20, 0x48, 0x80, 0x3B, 0x49, 0x42, 0x1E, -0xCA, 0x61, 0x12, 0x0C, 0x0A, 0x62, 0x3A, 0x4A, -0x4A, 0x62, 0x8A, 0x62, 0xCA, 0x62, 0x0A, 0x06, -0x13, 0x68, 0x14, 0x13, 0xA3, 0x43, 0x13, 0x60, -0x36, 0x4A, 0x01, 0x27, 0x97, 0x61, 0xD7, 0x61, -0x34, 0x4B, 0x40, 0x3B, 0xD8, 0x63, 0x10, 0x60, -0x90, 0x60, 0x33, 0x4D, 0x2C, 0x7F, 0xED, 0x7E, -0x23, 0x04, 0x2D, 0x02, 0x2B, 0x43, 0x0C, 0x33, -0x4B, 0x60, 0x8B, 0x60, 0x08, 0x61, 0x48, 0x61, -0x1F, 0x21, 0x09, 0x04, 0x11, 0x62, 0x29, 0x49, -0x2C, 0x4B, 0xC0, 0x31, 0x8B, 0x61, 0xCB, 0x61, -0x26, 0x4E, 0x40, 0x3E, 0xF0, 0x60, 0x1B, 0x06, -0x33, 0x61, 0x14, 0x23, 0x8B, 0x62, 0x34, 0x23, -0xCB, 0x63, 0x24, 0x4B, 0x54, 0x25, 0x80, 0x3B, -0x1D, 0x61, 0x74, 0x25, 0x5D, 0x62, 0xD0, 0x25, -0xCD, 0x62, 0xF0, 0x25, 0x1D, 0x60, 0xFF, 0x25, -0x11, 0x35, 0x5D, 0x61, 0xFF, 0x25, 0x31, 0x35, -0x9D, 0x62, 0x08, 0x62, 0x1E, 0x4B, 0x4B, 0x62, -0x1E, 0x49, 0x09, 0x68, 0x50, 0x31, 0xCB, 0x7B, -0x8D, 0x7B, 0x19, 0x02, 0x29, 0x43, 0xC9, 0x1C, -0x89, 0x05, 0x1B, 0x4B, 0x89, 0x0D, 0xC9, 0x18, -0x91, 0x62, 0x1A, 0x49, 0x1A, 0x4B, 0x0A, 0x68, -0x10, 0x49, 0x40, 0x31, 0x9A, 0x42, 0x01, 0xD1, -0x18, 0x4A, 0x00, 0xE0, 0x18, 0x4A, 0x4A, 0x63, -0x0C, 0x3C, 0x05, 0x22, 0xE1, 0xB2, 0x52, 0x02, -0x89, 0x18, 0x0A, 0x4A, 0x80, 0x32, 0x91, 0x60, -0x15, 0x49, 0x14, 0x4A, 0x0A, 0x60, 0x48, 0x60, -0xF1, 0x68, 0x14, 0x48, 0x01, 0x60, 0x31, 0x69, -0x41, 0x60, 0x13, 0x48, 0x07, 0x70, 0x05, 0x20, -0x00, 0x02, 0x30, 0x60, 0xF0, 0xBD, 0x00, 0x00, -0x98, 0x01, 0x00, 0x20, 0x40, 0x10, 0x00, 0x50, -0x55, 0x55, 0x55, 0x55, 0xC0, 0x11, 0x00, 0x50, -0xA8, 0x06, 0x00, 0x20, 0x03, 0x00, 0x03, 0x00, -0x00, 0x3F, 0x3F, 0x3F, 0x80, 0x02, 0x00, 0x20, -0x00, 0x30, 0xBC, 0x00, 0xE4, 0x06, 0x00, 0x20, -0xA1, 0x00, 0x03, 0xF3, 0x00, 0x26, 0x30, 0x00, -0x00, 0x26, 0x30, 0x08, 0x21, 0x20, 0x00, 0x00, -0x00, 0x19, 0x00, 0x50, 0x24, 0x00, 0x00, 0x20, -0x35, 0x00, 0x00, 0x20, 0x08, 0x49, 0x10, 0xB5, -0x5A, 0x20, 0x08, 0x74, 0x00, 0x20, 0x0A, 0x46, -0x48, 0x74, 0x20, 0x3A, 0x14, 0x5C, 0x4B, 0x7C, -0x40, 0x1C, 0x1B, 0x19, 0xC0, 0xB2, 0x4B, 0x74, -0x31, 0x28, 0xF7, 0xD3, 0x10, 0xBD, 0x00, 0x00, -0xF8, 0x04, 0x00, 0x20, 0x70, 0xB5, 0x2F, 0x49, -0x2D, 0x48, 0x08, 0x60, 0x2F, 0x4B, 0x2E, 0x48, -0x58, 0x60, 0x2E, 0x4C, 0x2E, 0x48, 0x80, 0x34, -0xA0, 0x60, 0x2E, 0x48, 0x40, 0x78, 0x41, 0x1E, -0xC9, 0x05, 0xC9, 0x0D, 0x19, 0x61, 0x2C, 0x4A, -0x00, 0x21, 0x11, 0x62, 0x2B, 0x4A, 0x2C, 0x4E, -0x15, 0x68, 0x26, 0x4A, 0x40, 0x32, 0xB5, 0x42, -0x01, 0xD1, 0x2A, 0x4D, 0x00, 0xE0, 0x2A, 0x4D, -0x55, 0x63, 0x05, 0x02, 0x29, 0x48, 0x6D, 0x1C, -0x05, 0x60, 0x41, 0x60, 0x09, 0x25, 0x1F, 0x48, -0x2D, 0x05, 0x40, 0x38, 0x05, 0x63, 0x41, 0x63, -0x81, 0x63, 0xD9, 0x63, 0x11, 0x60, 0xA1, 0x62, -0x23, 0x4B, 0x19, 0x70, 0x04, 0x23, 0x03, 0x60, -0x83, 0x02, 0xC3, 0x60, 0x01, 0x61, 0xC3, 0x68, -0x20, 0x49, 0x0B, 0x60, 0x03, 0x69, 0x4B, 0x60, -0x03, 0x21, 0x1F, 0x4B, 0x09, 0x06, 0x19, 0x60, -0x01, 0x21, 0x49, 0x02, 0x11, 0x62, 0x81, 0x04, -0xD1, 0x62, 0x81, 0x69, 0x03, 0x23, 0x9B, 0x03, -0x19, 0x43, 0x81, 0x61, 0xC1, 0x69, 0x19, 0x4B, -0x19, 0x43, 0xC1, 0x61, 0xD0, 0x21, 0x11, 0x63, -0xC1, 0x6A, 0x10, 0x22, 0x11, 0x43, 0xC1, 0x62, -0x55, 0x20, 0xE0, 0x62, 0x60, 0x21, 0x14, 0x48, -0xFF, 0xF7, 0x4F, 0xF9, 0x12, 0x48, 0x60, 0x21, -0x60, 0x30, 0xFF, 0xF7, 0x4A, 0xF9, 0x70, 0xBD, -0x00, 0x00, 0x04, 0x20, 0x4C, 0x01, 0x00, 0x20, -0x0C, 0x0D, 0xF2, 0x00, 0x40, 0x10, 0x00, 0x50, -0xE6, 0x0A, 0x00, 0x00, 0xC8, 0x06, 0x00, 0x20, -0xC0, 0x11, 0x00, 0x50, 0xE4, 0x06, 0x00, 0x20, -0xA1, 0x00, 0x03, 0xF3, 0x00, 0x36, 0x30, 0x04, -0x00, 0x36, 0x30, 0x0C, 0x00, 0x19, 0x00, 0x50, -0x35, 0x00, 0x00, 0x20, 0x24, 0x00, 0x00, 0x20, -0x00, 0x07, 0x00, 0x50, 0x00, 0xC0, 0x00, 0xC0, -0x00, 0x20, 0x00, 0x50, 0x10, 0xB5, 0x1C, 0x48, -0x64, 0x24, 0x41, 0x79, 0x02, 0x79, 0x09, 0x02, -0x11, 0x43, 0x80, 0x22, 0x91, 0x43, 0x01, 0x71, -0x09, 0x0A, 0x41, 0x71, 0x41, 0x79, 0x02, 0x79, -0x09, 0x02, 0x11, 0x43, 0x2D, 0x22, 0x52, 0x02, -0x11, 0x43, 0x01, 0x71, 0x09, 0x0A, 0x41, 0x71, -0x81, 0x88, 0x12, 0xA0, 0xFF, 0xF7, 0xCE, 0xF9, -0x06, 0xE0, 0x21, 0x46, 0x16, 0xA0, 0xFF, 0xF7, -0xC9, 0xF9, 0x01, 0x20, 0xFF, 0xF7, 0x52, 0xFA, -0x01, 0xF0, 0x97, 0xFD, 0x00, 0x28, 0x04, 0xD1, -0x20, 0x46, 0x64, 0x1E, 0x24, 0xB2, 0x00, 0x28, -0xEF, 0xD1, 0x64, 0x1C, 0x08, 0xD1, 0x01, 0x20, -0xFF, 0xF7, 0x86, 0xF9, 0x10, 0xA0, 0xFF, 0xF7, -0xB5, 0xF9, 0x0A, 0x20, 0xFF, 0xF7, 0x3E, 0xFA, -0x00, 0x20, 0xFF, 0xF7, 0x7D, 0xF9, 0x10, 0xBD, -0x88, 0x02, 0x00, 0x20, 0x46, 0x54, 0x20, 0x55, -0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x53, 0x74, -0x61, 0x74, 0x75, 0x73, 0x20, 0x28, 0x30, 0x78, -0x25, 0x34, 0x78, 0x29, 0x0D, 0x0A, 0x00, 0x00, -0x49, 0x4E, 0x54, 0x20, 0x4C, 0x6F, 0x77, 0x21, -0x21, 0x28, 0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, -0x49, 0x4E, 0x54, 0x20, 0x70, 0x61, 0x64, 0x20, -0x69, 0x73, 0x20, 0x6C, 0x6F, 0x77, 0x20, 0x6C, -0x65, 0x76, 0x65, 0x6C, 0x20, 0x21, 0x21, 0x00, -0x10, 0xB5, 0x16, 0x4C, 0x01, 0x21, 0xE0, 0x89, -0x89, 0x02, 0x88, 0x42, 0x01, 0xD0, 0x03, 0x20, -0x60, 0x71, 0x12, 0x48, 0x30, 0x21, 0x30, 0x30, -0xFF, 0xF7, 0xBB, 0xF8, 0x00, 0x20, 0x60, 0x72, -0xA0, 0x71, 0xE0, 0x62, 0x1E, 0x21, 0x21, 0x72, -0x0D, 0x49, 0x20, 0x71, 0x08, 0x70, 0x0D, 0x49, -0x09, 0x78, 0xA1, 0x81, 0x60, 0x81, 0x60, 0x8A, -0x09, 0x21, 0x09, 0x03, 0x08, 0x43, 0x60, 0x82, -0x03, 0x20, 0x40, 0x02, 0xE0, 0x61, 0x0D, 0x20, -0xC0, 0x01, 0x20, 0x62, 0x07, 0x20, 0x00, 0x02, -0x60, 0x62, 0x0F, 0x20, 0xC0, 0x01, 0xA0, 0x62, -0x10, 0xBD, 0x00, 0x00, 0x38, 0x01, 0x00, 0x20, -0x7E, 0x02, 0x00, 0x20, 0x7F, 0x02, 0x00, 0x20, -0x10, 0x48, 0xC0, 0x6B, 0x10, 0x49, 0x08, 0x60, -0x10, 0x48, 0x03, 0x21, 0x41, 0x71, 0x10, 0x49, -0x41, 0x61, 0x0F, 0x49, 0x60, 0x31, 0x81, 0x61, -0x01, 0x21, 0x01, 0x70, 0x07, 0x22, 0x42, 0x70, -0x0C, 0x4B, 0x05, 0x22, 0x1A, 0x70, 0x0C, 0x4B, -0x1A, 0x70, 0x0C, 0x4B, 0x55, 0x22, 0xDA, 0x70, -0x04, 0x22, 0x02, 0x82, 0x00, 0x22, 0xC2, 0x70, -0x09, 0x22, 0x12, 0x03, 0x42, 0x82, 0x81, 0x70, -0x70, 0x47, 0x00, 0x00, 0x80, 0x09, 0x00, 0x50, -0xE4, 0x06, 0x00, 0x20, 0x38, 0x01, 0x00, 0x20, -0x00, 0x00, 0x04, 0x20, 0x7D, 0x02, 0x00, 0x20, -0xCD, 0x08, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, -0x04, 0x22, 0x0F, 0x49, 0x0C, 0x28, 0x10, 0xD0, -0x8B, 0x05, 0x0D, 0x28, 0x08, 0x6A, 0x10, 0xD0, -0x18, 0x43, 0x08, 0x62, 0x88, 0x6A, 0x10, 0x43, -0x88, 0x62, 0x0A, 0x4A, 0x01, 0x20, 0x10, 0x70, -0xC8, 0x68, 0xC8, 0x60, 0x88, 0x6A, 0x88, 0x62, -0x70, 0x47, 0x08, 0x6A, 0x40, 0x00, 0x40, 0x08, -0x00, 0xE0, 0x18, 0x43, 0x08, 0x62, 0x88, 0x6A, -0x90, 0x43, 0x88, 0x62, 0xF0, 0xE7, 0x00, 0x00, -0x00, 0x06, 0x00, 0x50, 0xD0, 0x00, 0x00, 0x20, -0x00, 0xB5, 0x08, 0x49, 0x0A, 0x28, 0x05, 0xD0, -0x07, 0x48, 0x00, 0x0C, 0x48, 0x63, 0x07, 0x48, -0x08, 0x63, 0x00, 0xBD, 0x06, 0x48, 0x00, 0x68, -0x08, 0x62, 0x0D, 0x20, 0xFF, 0xF7, 0xCC, 0xFF, -0x00, 0xBD, 0x00, 0x00, 0x00, 0x06, 0x00, 0x50, -0xBC, 0x02, 0x00, 0x20, 0xCC, 0x02, 0x00, 0x20, -0xC0, 0x02, 0x00, 0x20, 0x00, 0xB5, 0x1B, 0x48, -0x00, 0x21, 0x0B, 0x00, 0xFF, 0xF7, 0x86, 0xF8, -0x0C, 0x07, 0x0A, 0x0E, 0x11, 0x2A, 0x15, 0x18, -0x1B, 0x1E, 0x21, 0x24, 0x27, 0x2A, 0x16, 0x4A, -0x02, 0x80, 0x22, 0xE0, 0x14, 0x4A, 0x12, 0x1D, -0x42, 0x80, 0x1E, 0xE0, 0x13, 0x4A, 0x82, 0x80, -0x1B, 0xE0, 0x11, 0x4A, 0x10, 0x32, 0xC2, 0x80, -0x17, 0xE0, 0x11, 0x4A, 0x42, 0x81, 0x14, 0xE0, -0x10, 0x4A, 0x82, 0x81, 0x11, 0xE0, 0x10, 0x4A, -0xC2, 0x81, 0x0E, 0xE0, 0x0F, 0x4A, 0x02, 0x82, -0x0B, 0xE0, 0x0F, 0x4A, 0x42, 0x82, 0x08, 0xE0, -0x0E, 0x4A, 0x82, 0x82, 0x05, 0xE0, 0x0E, 0x4A, -0xC2, 0x82, 0x02, 0xE0, 0x0D, 0x4A, 0x4B, 0x00, -0xC2, 0x52, 0x49, 0x1C, 0x89, 0xB2, 0x0C, 0x29, -0xCB, 0xD3, 0x00, 0xBD, 0xCC, 0x02, 0x00, 0x20, -0x90, 0x02, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20, -0x34, 0x01, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20, -0x10, 0x05, 0x00, 0x20, 0xD8, 0x06, 0x00, 0x20, -0xC0, 0x02, 0x00, 0x20, 0xBC, 0x02, 0x00, 0x20, -0x9C, 0x01, 0x00, 0x20, 0xC4, 0x02, 0x00, 0x20, -0x10, 0xB5, 0x27, 0x4A, 0x11, 0x68, 0x26, 0x48, -0x40, 0x30, 0x01, 0x61, 0x51, 0x68, 0x41, 0x61, -0x91, 0x68, 0x81, 0x61, 0xD1, 0x68, 0xC1, 0x61, -0x83, 0x69, 0xF0, 0x21, 0x8B, 0x43, 0x83, 0x61, -0x83, 0x69, 0x0B, 0x43, 0x83, 0x61, 0x1F, 0x49, -0x1F, 0x4B, 0x09, 0x68, 0x99, 0x42, 0x07, 0xD1, -0x43, 0x69, 0x5B, 0x04, 0x04, 0xD5, 0x43, 0x69, -0x01, 0x24, 0x64, 0x03, 0x1B, 0x1B, 0x43, 0x61, -0x17, 0x48, 0x40, 0x38, 0x03, 0x68, 0x01, 0x24, -0x24, 0x06, 0x23, 0x43, 0x03, 0x60, 0x16, 0x48, -0x40, 0x1C, 0x81, 0x42, 0x09, 0xD1, 0x05, 0x20, -0x00, 0x07, 0x41, 0x69, 0x52, 0x68, 0xD2, 0x0A, -0x12, 0x1F, 0x12, 0x07, 0x12, 0x0A, 0x11, 0x43, -0x41, 0x61, 0x30, 0x21, 0x0F, 0x48, 0xFF, 0xF7, -0xB3, 0xF9, 0x0E, 0x48, 0x40, 0x21, 0xC0, 0x30, -0xFF, 0xF7, 0xAE, 0xF9, 0x0B, 0x20, 0xFE, 0xF7, -0xC1, 0xFF, 0x03, 0x20, 0xFE, 0xF7, 0xBE, 0xFF, -0x00, 0x20, 0xFE, 0xF7, 0xBB, 0xFF, 0x05, 0x20, -0xFE, 0xF7, 0xB8, 0xFF, 0x09, 0x20, 0xFE, 0xF7, -0xB5, 0xFF, 0x01, 0x20, 0x10, 0xBD, 0x00, 0x00, -0x40, 0x14, 0x00, 0x50, 0xE4, 0x06, 0x00, 0x20, -0xA1, 0x00, 0x03, 0xF3, 0xDC, 0x34, 0x00, 0x00, -0xFE, 0xB5, 0x20, 0x48, 0x0A, 0x25, 0x45, 0x5F, -0x01, 0x26, 0x29, 0x46, 0x1E, 0xA0, 0xFF, 0xF7, -0x39, 0xF8, 0x23, 0x48, 0x72, 0x04, 0x01, 0x68, -0x22, 0x4F, 0x91, 0x43, 0x00, 0x24, 0x01, 0x60, -0x21, 0x48, 0x00, 0x68, 0x00, 0x19, 0xC0, 0x7E, -0x41, 0x28, 0x18, 0xD0, 0x1F, 0x49, 0x60, 0x00, -0x0A, 0x5E, 0x1F, 0x49, 0x0B, 0x5A, 0xD0, 0x1A, -0x00, 0xB2, 0xA8, 0x42, 0x0B, 0xDA, 0x00, 0x90, -0x21, 0x46, 0x01, 0x95, 0x1B, 0xA0, 0xFF, 0xF7, -0x1D, 0xF8, 0x38, 0x5D, 0x20, 0x21, 0x08, 0x43, -0x38, 0x55, 0x00, 0x26, 0x03, 0xE0, 0x38, 0x5D, -0xDF, 0x21, 0x08, 0x40, 0x38, 0x55, 0x64, 0x1C, -0xE4, 0xB2, 0x30, 0x2C, 0xDC, 0xD3, 0x01, 0x2E, -0x0A, 0xD0, 0x0D, 0x4A, 0x01, 0x21, 0x10, 0x68, -0x49, 0x04, 0x08, 0x43, 0x10, 0x60, 0x1C, 0xA0, -0xFF, 0xF7, 0x04, 0xF8, 0x30, 0x46, 0xFE, 0xBD, -0x1F, 0xA0, 0xF9, 0xE7, 0x84, 0x06, 0x00, 0x20, -0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x43, 0x20, -0x43, 0x68, 0x65, 0x63, 0x6B, 0x20, 0x54, 0x48, -0x44, 0x3D, 0x25, 0x64, 0x20, 0x0D, 0x0A, 0x00, -0x0C, 0x05, 0x00, 0x20, 0xD8, 0x04, 0x00, 0x20, -0x80, 0x02, 0x00, 0x20, 0xAC, 0x03, 0x00, 0x20, -0x4C, 0x07, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, -0x20, 0x43, 0x43, 0x20, 0x4E, 0x47, 0x20, 0x50, -0x69, 0x6E, 0x5B, 0x25, 0x64, 0x5D, 0x2C, 0x20, -0x44, 0x69, 0x66, 0x66, 0x3D, 0x25, 0x64, 0x2D, -0x25, 0x64, 0x3D, 0x25, 0x64, 0x2C, 0x20, 0x4C, -0x6F, 0x77, 0x54, 0x48, 0x44, 0x20, 0x3D, 0x20, -0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x43, -0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x5B, -0x4E, 0x47, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, -0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x43, -0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x5B, -0x50, 0x61, 0x73, 0x73, 0x5D, 0x0D, 0x0A, 0x00, -0xF8, 0xB5, 0x28, 0x48, 0x02, 0x21, 0x0A, 0x22, -0x41, 0x5E, 0x82, 0x5E, 0x03, 0x20, 0x80, 0x03, -0x42, 0x43, 0x10, 0x14, 0x86, 0x46, 0x24, 0x48, -0x01, 0x24, 0x02, 0x68, 0xA2, 0x43, 0x02, 0x60, -0x22, 0x48, 0x00, 0x22, 0x00, 0x68, 0x84, 0x46, -0x60, 0x46, 0x80, 0x18, 0xC0, 0x7E, 0x41, 0x28, -0x23, 0xD0, 0x53, 0x00, 0x1E, 0x4F, 0x1F, 0x4D, -0xF8, 0x5E, 0xEE, 0x5A, 0x00, 0x25, 0x86, 0x1B, -0x36, 0xB2, 0xFE, 0x52, 0x8E, 0x42, 0x00, 0xDA, -0x01, 0x25, 0x00, 0x28, 0x07, 0xD1, 0x1A, 0x48, -0x1A, 0x4E, 0xC0, 0x5A, 0xF3, 0x5A, 0xC0, 0x1A, -0x00, 0xB2, 0x70, 0x45, 0x01, 0xDB, 0x00, 0x2D, -0x06, 0xD0, 0x17, 0x48, 0x01, 0x24, 0x83, 0x5C, -0x23, 0x43, 0x83, 0x54, 0x00, 0x24, 0x04, 0xE0, -0x13, 0x48, 0x83, 0x5C, 0x5B, 0x08, 0x5B, 0x00, -0x83, 0x54, 0x52, 0x1C, 0xD2, 0xB2, 0x30, 0x2A, -0xD2, 0xD3, 0x01, 0x2C, 0x09, 0xD0, 0x0F, 0xA0, -0xFE, 0xF7, 0x70, 0xFF, 0x06, 0x48, 0x01, 0x22, -0x01, 0x68, 0x11, 0x43, 0x01, 0x60, 0x20, 0x46, -0xF8, 0xBD, 0x11, 0xA0, 0xFE, 0xF7, 0x66, 0xFF, -0xF9, 0xE7, 0x00, 0x00, 0x84, 0x06, 0x00, 0x20, -0x0C, 0x05, 0x00, 0x20, 0x80, 0x02, 0x00, 0x20, -0x48, 0x03, 0x00, 0x20, 0x74, 0x04, 0x00, 0x20, -0xAC, 0x03, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, -0xD8, 0x04, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, -0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x54, 0x48, -0x44, 0x3A, 0x25, 0x64, 0x29, 0x20, 0x5B, 0x4E, -0x47, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, -0x4F, 0x70, 0x65, 0x6E, 0x20, 0x54, 0x65, 0x73, -0x74, 0x28, 0x54, 0x48, 0x44, 0x3A, 0x25, 0x64, -0x29, 0x20, 0x5B, 0x50, 0x61, 0x73, 0x73, 0x5D, -0x0D, 0x0A, 0x00, 0x00, 0xF0, 0xB5, 0x17, 0x49, -0x00, 0x20, 0x7D, 0x27, 0x09, 0x68, 0xFF, 0x00, -0x02, 0x46, 0x89, 0x07, 0x24, 0xD5, 0x14, 0x49, -0x14, 0x4D, 0x09, 0x68, 0xCE, 0x26, 0x0B, 0x18, -0xDB, 0x7E, 0x41, 0x2B, 0x0A, 0xD0, 0x12, 0x4B, -0x44, 0x00, 0x1B, 0x5F, 0xBB, 0x42, 0x00, 0xDA, -0x01, 0x22, 0x2B, 0x5C, 0x9C, 0x07, 0x01, 0xD5, -0x33, 0x40, 0x2B, 0x54, 0x40, 0x1C, 0xC0, 0xB2, -0x30, 0x28, 0xEC, 0xD3, 0x00, 0x2A, 0x0B, 0xD0, -0x00, 0x20, 0x0A, 0x18, 0xD2, 0x7E, 0x41, 0x2A, -0x02, 0xD0, 0x2A, 0x5C, 0x32, 0x40, 0x2A, 0x54, -0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, 0xF4, 0xD3, -0x01, 0x20, 0xF0, 0xBD, 0x0C, 0x05, 0x00, 0x20, -0x80, 0x02, 0x00, 0x20, 0xD8, 0x04, 0x00, 0x20, -0xE4, 0x02, 0x00, 0x20, 0x70, 0xB5, 0x20, 0x48, -0x01, 0x25, 0xC4, 0x7D, 0x1F, 0x48, 0x80, 0x88, -0x40, 0x07, 0x01, 0xD4, 0x01, 0x20, 0x70, 0xBD, -0xFF, 0xF7, 0xDC, 0xFB, 0x1C, 0x48, 0xFE, 0xF7, -0x09, 0xFF, 0x00, 0x28, 0x0F, 0xD1, 0x1B, 0xA0, -0xFE, 0xF7, 0xEC, 0xFE, 0x01, 0x20, 0x20, 0x49, -0x00, 0x25, 0x08, 0x70, 0x00, 0x2C, 0x06, 0xD0, -0x64, 0x1E, 0xE4, 0xB2, 0x21, 0x46, 0x1D, 0xA0, -0xFE, 0xF7, 0xE0, 0xFE, 0xE8, 0xE7, 0xFF, 0xF7, -0x6F, 0xF8, 0xFF, 0xF7, 0xE5, 0xFA, 0xFF, 0xF7, -0x23, 0xFF, 0x00, 0x28, 0x07, 0xD1, 0x00, 0x25, -0x00, 0x2C, 0x04, 0xD0, 0x64, 0x1E, 0xE4, 0xB2, -0x21, 0x46, 0x1C, 0xA0, 0x0A, 0xE0, 0xFF, 0xF7, -0x8B, 0xFE, 0x00, 0x28, 0x0A, 0xD1, 0x00, 0x25, -0x00, 0x2C, 0x07, 0xD0, 0x64, 0x1E, 0xE4, 0xB2, -0x21, 0x46, 0x1C, 0xA0, 0xFE, 0xF7, 0xC2, 0xFE, -0x01, 0x25, 0xC9, 0xE7, 0x28, 0x46, 0x70, 0xBD, -0xA8, 0x06, 0x00, 0x20, 0x74, 0x06, 0x00, 0x20, -0xAC, 0x03, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, -0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x61, -0x6C, 0x69, 0x62, 0x72, 0x61, 0x74, 0x69, 0x6F, -0x6E, 0x20, 0x4E, 0x47, 0x21, 0x0D, 0x0A, 0x00, -0xD8, 0x04, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, -0x20, 0x43, 0x61, 0x6C, 0x69, 0x62, 0x72, 0x61, -0x74, 0x69, 0x6F, 0x6E, 0x20, 0x52, 0x65, 0x74, -0x72, 0x79, 0x20, 0x28, 0x25, 0x64, 0x29, 0x0D, -0x0A, 0x00, 0x00, 0x00, 0x4F, 0x70, 0x65, 0x6E, -0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x52, 0x65, -0x74, 0x72, 0x79, 0x20, 0x28, 0x25, 0x64, 0x29, -0x0D, 0x0A, 0x00, 0x00, 0x4F, 0x70, 0x65, 0x6E, -0x20, 0x43, 0x43, 0x20, 0x52, 0x65, 0x74, 0x72, -0x79, 0x20, 0x28, 0x25, 0x64, 0x29, 0x0D, 0x0A, -0x00, 0x00, 0x00, 0x00, 0x10, 0xB5, 0x15, 0x4A, -0x11, 0x78, 0x81, 0x42, 0x25, 0xD0, 0x14, 0x49, -0x0B, 0x68, 0x01, 0x24, 0xA4, 0x02, 0x23, 0x43, -0x0B, 0x60, 0x10, 0x70, 0x05, 0x22, 0x40, 0x24, -0x10, 0x49, 0x12, 0x07, 0x80, 0x23, 0x00, 0x28, -0x09, 0xD0, 0x08, 0x68, 0x40, 0x06, 0x0C, 0xD4, -0x08, 0x68, 0x20, 0x43, 0x08, 0x60, 0xD0, 0x68, -0x98, 0x43, 0xD0, 0x60, 0x05, 0xE0, 0xD0, 0x68, -0x18, 0x43, 0xD0, 0x60, 0x08, 0x68, 0xA0, 0x43, -0x08, 0x60, 0x08, 0x68, 0x18, 0x43, 0x08, 0x60, -0xD0, 0x68, 0x01, 0x21, 0xC9, 0x03, 0x08, 0x43, -0xD0, 0x60, 0x10, 0xBD, 0x04, 0x00, 0x00, 0x20, -0x00, 0x10, 0x00, 0x50, 0x00, 0x11, 0x00, 0x50, -0x06, 0x49, 0x83, 0x20, 0x08, 0x70, 0x06, 0x49, -0x00, 0x20, 0x08, 0x70, 0x05, 0x48, 0x00, 0x68, -0x05, 0x49, 0x40, 0x05, 0x40, 0x0F, 0x08, 0x73, -0x70, 0x47, 0x00, 0x00, 0xE0, 0x08, 0x00, 0x20, -0x34, 0x00, 0x00, 0x20, 0x00, 0x11, 0x00, 0x50, -0x24, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x04, 0x46, -0x05, 0x22, 0x01, 0x20, 0x52, 0x04, 0xCB, 0x07, -0x01, 0xD0, 0x00, 0x20, 0x10, 0xBD, 0x07, 0x4B, -0x9C, 0x61, 0x09, 0x02, 0x49, 0x1C, 0x59, 0x61, -0x02, 0xE0, 0x52, 0x1E, 0x00, 0xD1, 0x00, 0x20, -0x59, 0x69, 0xC9, 0x07, 0xF2, 0xD0, 0x00, 0x2A, -0xF7, 0xD1, 0x10, 0xBD, 0x40, 0x14, 0x00, 0x50, -0xF0, 0xB5, 0x90, 0xB0, 0xAD, 0x49, 0x09, 0x91, -0xAC, 0x49, 0xAD, 0x4A, 0x60, 0x31, 0x07, 0x92, -0x0A, 0x91, 0xAC, 0x49, 0xAC, 0x4A, 0x08, 0x91, -0x12, 0x68, 0x0C, 0x92, 0xAB, 0x4A, 0xAC, 0x4E, -0x12, 0x68, 0x0D, 0x92, 0x00, 0x22, 0x17, 0x46, -0x14, 0x46, 0x00, 0x92, 0x07, 0x9A, 0x06, 0x91, -0xA8, 0x4D, 0x05, 0x92, 0x00, 0x21, 0x8A, 0x00, -0x05, 0x9B, 0x49, 0x1C, 0x9E, 0x50, 0x06, 0x9B, -0x89, 0xB2, 0x9D, 0x50, 0x18, 0x29, 0xF6, 0xD3, -0x01, 0x21, 0x89, 0x02, 0x03, 0x91, 0x01, 0x46, -0x04, 0x90, 0x50, 0x31, 0x50, 0x38, 0x09, 0xB2, -0x00, 0xB2, 0x0E, 0x91, 0x0F, 0x90, 0x00, 0xF0, -0x0B, 0xFD, 0x00, 0xF0, 0xB1, 0xFF, 0x00, 0xF0, -0x69, 0xF9, 0x9B, 0x48, 0x00, 0x78, 0xC0, 0x07, -0x20, 0xD1, 0x00, 0x26, 0x00, 0x21, 0x09, 0x9A, -0x88, 0x00, 0x13, 0x58, 0x0C, 0x9A, 0x49, 0x1C, -0x13, 0x50, 0x0A, 0x9A, 0x89, 0xB2, 0x13, 0x58, -0x0D, 0x9A, 0x18, 0x29, 0x13, 0x50, 0x05, 0x9A, -0x13, 0x58, 0x09, 0x9A, 0x13, 0x50, 0x06, 0x9A, -0x13, 0x58, 0x0A, 0x9A, 0x13, 0x50, 0xEA, 0xD3, -0x00, 0xF0, 0x12, 0xFE, 0x00, 0xF0, 0x90, 0xFF, -0x00, 0xF0, 0x48, 0xF9, 0x8A, 0x48, 0x00, 0x78, -0xC0, 0x07, 0x02, 0xD0, 0x00, 0x20, 0x10, 0xB0, -0xF0, 0xBD, 0x00, 0x25, 0x3C, 0xE0, 0x00, 0x2D, -0x3A, 0xD0, 0x02, 0x91, 0x01, 0x90, 0x05, 0x2E, -0x7A, 0xD9, 0x30, 0x2F, 0x78, 0xD2, 0x08, 0x98, -0x82, 0x4F, 0x0B, 0x90, 0x78, 0x6B, 0x03, 0x21, -0x09, 0x05, 0x88, 0x43, 0x01, 0x21, 0x49, 0x05, -0x40, 0x18, 0x78, 0x63, 0x00, 0xF0, 0xF0, 0xFD, -0x00, 0xF0, 0x6E, 0xFF, 0x00, 0xF0, 0x26, 0xF9, -0x79, 0x6B, 0x03, 0x20, 0x00, 0x05, 0x81, 0x43, -0x01, 0x20, 0x00, 0x05, 0x08, 0x18, 0x78, 0x63, -0x00, 0x27, 0x00, 0x20, 0x84, 0x46, 0x76, 0x48, -0x01, 0x68, 0x60, 0x46, 0x08, 0x18, 0xC0, 0x7E, -0x41, 0x28, 0x1F, 0xD0, 0x60, 0x46, 0x41, 0x00, -0x0B, 0x98, 0x0A, 0x2E, 0x40, 0x5A, 0x23, 0xD2, -0x70, 0x4A, 0x12, 0x68, 0x53, 0x5E, 0x04, 0x9A, -0x93, 0x42, 0x01, 0xDA, 0x03, 0x9A, 0x90, 0x43, -0x03, 0x9A, 0x52, 0x08, 0x10, 0x43, 0x7D, 0xE0, -0x6B, 0x48, 0x00, 0x68, 0x01, 0x90, 0x62, 0x48, -0x00, 0x68, 0x02, 0x90, 0x00, 0x98, 0x30, 0x28, -0x77, 0xD0, 0x07, 0x98, 0x0B, 0x90, 0x00, 0x20, -0x00, 0x90, 0xD6, 0xE7, 0x00, 0x2D, 0x02, 0xD0, -0x7F, 0x1C, 0xBF, 0xB2, 0x88, 0xE0, 0x00, 0x98, -0x40, 0x1C, 0x80, 0xB2, 0x00, 0x90, 0x83, 0xE0, -0x10, 0x2E, 0x13, 0xD2, 0x5D, 0x4A, 0x01, 0x23, -0x12, 0x68, 0x9B, 0x02, 0x54, 0x5E, 0xE2, 0x1D, -0xFF, 0x32, 0xFA, 0x32, 0x9A, 0x42, 0x09, 0xD9, -0x00, 0x2C, 0x00, 0xDA, 0x64, 0x42, 0x22, 0xB2, -0x40, 0x32, 0xD3, 0x17, 0x5B, 0x0E, 0x9A, 0x18, -0xD4, 0x11, 0x00, 0xE0, 0x01, 0x24, 0x0B, 0x2E, -0x24, 0xD9, 0x01, 0x9A, 0x52, 0x5E, 0x96, 0x46, -0x00, 0x2A, 0x0E, 0xD0, 0x4F, 0x4B, 0x1B, 0x68, -0x5B, 0x5E, 0x5A, 0x43, 0x00, 0x2A, 0x19, 0xDA, -0x02, 0x9A, 0x54, 0x5A, 0x22, 0x1A, 0x01, 0x2A, -0x05, 0xD0, 0x52, 0x1C, 0x03, 0xD0, 0x10, 0xE0, -0x61, 0xE0, 0x00, 0x24, 0x0E, 0xE0, 0x72, 0x46, -0x00, 0x2A, 0x00, 0xDA, 0x52, 0x42, 0x00, 0x2B, -0x00, 0xDA, 0x5B, 0x42, 0x9A, 0x42, 0x04, 0xDA, -0x20, 0x46, 0x01, 0x9A, 0x00, 0x24, 0x54, 0x52, -0x00, 0xE0, 0x01, 0x24, 0x3F, 0x4A, 0x0F, 0x9B, -0x12, 0x68, 0x52, 0x5E, 0x9A, 0x42, 0x0F, 0xDA, -0x02, 0x05, 0x06, 0xD5, 0x00, 0x1B, 0x01, 0x22, -0x80, 0xB2, 0xD2, 0x02, 0x90, 0x42, 0x10, 0xD3, -0x16, 0xE0, 0xA0, 0x42, 0x02, 0xDD, 0x00, 0x1B, -0x80, 0xB2, 0x11, 0xE0, 0x00, 0x20, 0x0F, 0xE0, -0x0E, 0x9B, 0x9A, 0x42, 0x12, 0xDD, 0x02, 0x05, -0x05, 0xD5, 0x00, 0x19, 0x80, 0xB2, 0x33, 0x4A, -0x04, 0xE0, 0x10, 0x46, 0x04, 0xE0, 0x00, 0x19, -0x31, 0x4A, 0x80, 0xB2, 0x90, 0x42, 0xF8, 0xD8, -0x00, 0x2C, 0x04, 0xD0, 0x00, 0x2D, 0x0F, 0xD1, -0x0A, 0xE0, 0x20, 0xE0, 0x00, 0x24, 0x00, 0x2D, -0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, 0x07, 0xE0, -0x00, 0x9A, 0x52, 0x1C, 0x92, 0xB2, 0x00, 0x92, -0x07, 0x9A, 0x05, 0x2E, 0x50, 0x52, 0x04, 0xD8, -0x01, 0x22, 0xD2, 0x02, 0x10, 0x43, 0x08, 0x9A, -0x50, 0x52, 0x00, 0x2C, 0x04, 0xD0, 0x1F, 0x48, -0x01, 0x9A, 0x00, 0x68, 0x40, 0x5A, 0x50, 0x52, -0x60, 0x46, 0x40, 0x1C, 0x80, 0xB2, 0x84, 0x46, -0x30, 0x28, 0x00, 0xD2, 0x43, 0xE7, 0x13, 0x48, -0x1C, 0x4A, 0x01, 0x68, 0x10, 0x68, 0x6D, 0x1C, -0xED, 0xB2, 0x02, 0x2D, 0x00, 0xD2, 0x16, 0xE7, -0x03, 0x98, 0x40, 0x08, 0x03, 0x90, 0x00, 0x98, -0x30, 0x28, 0x02, 0xD1, 0x30, 0x2F, 0x00, 0xD1, -0x40, 0x26, 0x76, 0x1C, 0xB6, 0xB2, 0x40, 0x2E, -0x00, 0xD8, 0xE3, 0xE6, 0x0B, 0x49, 0x48, 0x6B, -0x03, 0x22, 0x12, 0x05, 0x10, 0x43, 0x48, 0x63, -0x01, 0x20, 0xFC, 0xE6, 0x00, 0x20, 0x00, 0x50, -0x9C, 0x09, 0x00, 0x20, 0x00, 0x00, 0x01, 0x20, -0x5C, 0x01, 0x00, 0x20, 0x60, 0x01, 0x00, 0x20, -0x00, 0x04, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x0C, -0x34, 0x00, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, -0x80, 0x02, 0x00, 0x20, 0x4C, 0x01, 0x00, 0x20, -0x54, 0x01, 0x00, 0x20, 0xFF, 0x0F, 0x00, 0x00, -0xFF, 0x07, 0x00, 0x00, 0x58, 0x01, 0x00, 0x20, -0x10, 0xB5, 0x0B, 0x49, 0x30, 0x24, 0x00, 0x28, -0x0A, 0x4A, 0x0B, 0x4B, 0x08, 0x68, 0x06, 0xD0, -0x20, 0x43, 0x08, 0x60, 0x09, 0x48, 0x10, 0x60, -0x08, 0x48, 0x60, 0x30, 0x05, 0xE0, 0xA0, 0x43, -0x08, 0x60, 0x07, 0x48, 0x10, 0x60, 0x06, 0x48, -0x60, 0x30, 0x18, 0x60, 0x10, 0xBD, 0x00, 0x00, -0x00, 0x10, 0x00, 0x50, 0x4C, 0x01, 0x00, 0x20, -0x50, 0x01, 0x00, 0x20, 0x00, 0x00, 0x04, 0x20, -0x00, 0x10, 0x04, 0x20, 0xF0, 0xB5, 0x11, 0x48, -0x01, 0x68, 0x11, 0x4D, 0x89, 0x06, 0x30, 0x22, -0x60, 0x35, 0x00, 0x29, 0x0E, 0x4B, 0x0F, 0x4C, -0x0F, 0x4E, 0x10, 0x4F, 0x01, 0x68, 0x0A, 0xDA, -0x91, 0x43, 0x01, 0x60, 0x23, 0x60, 0x0E, 0x48, -0x35, 0x60, 0x07, 0x60, 0x0B, 0x48, 0x0D, 0x49, -0x60, 0x30, 0x08, 0x60, 0xF0, 0xBD, 0x11, 0x43, -0x01, 0x60, 0x08, 0x48, 0x27, 0x60, 0x60, 0x30, -0x30, 0x60, 0x07, 0x48, 0x03, 0x60, 0x07, 0x48, -0x05, 0x60, 0xF0, 0xBD, 0x00, 0x10, 0x00, 0x50, -0x00, 0x00, 0x04, 0x20, 0x44, 0x00, 0x00, 0x20, -0x48, 0x00, 0x00, 0x20, 0x00, 0x10, 0x04, 0x20, -0x4C, 0x01, 0x00, 0x20, 0x50, 0x01, 0x00, 0x20, -0x00, 0xB5, 0x09, 0x48, 0x00, 0x78, 0x00, 0x28, -0x08, 0xD0, 0x01, 0x28, 0x09, 0xD0, 0x02, 0x28, -0x03, 0xD1, 0x06, 0x49, 0x04, 0x20, 0x00, 0xF0, -0x55, 0xF8, 0x00, 0xBD, 0x04, 0x49, 0x01, 0x20, -0xF9, 0xE7, 0x04, 0x49, 0x02, 0x20, 0xF6, 0xE7, -0xCD, 0x08, 0x00, 0x20, 0x6C, 0x08, 0x00, 0x20, -0x1C, 0x02, 0x00, 0x20, 0x0C, 0x08, 0x00, 0x20, -0xF8, 0xB5, 0x07, 0x46, 0x00, 0xF0, 0x4C, 0xFB, -0x1C, 0x4D, 0x60, 0x21, 0x28, 0x46, 0xFE, 0xF7, -0x68, 0xFB, 0x00, 0x24, 0x00, 0xF0, 0xEC, 0xFD, -0xFF, 0xF7, 0xA4, 0xFF, 0xFE, 0xF7, 0xFF, 0xFB, -0x00, 0x2C, 0x0C, 0xD0, 0x16, 0x48, 0x00, 0x22, -0x06, 0x68, 0x50, 0x00, 0x43, 0x19, 0x19, 0x88, -0x30, 0x5A, 0x52, 0x1C, 0x08, 0x18, 0xD2, 0xB2, -0x18, 0x80, 0x30, 0x2A, 0xF5, 0xD3, 0x64, 0x1C, -0xE4, 0xB2, 0x05, 0x2C, 0xE6, 0xD3, 0x00, 0x20, -0x29, 0x46, 0x42, 0x00, 0x52, 0x18, 0x00, 0x23, -0xD3, 0x5E, 0x40, 0x1C, 0x9B, 0x10, 0xC0, 0xB2, -0x13, 0x80, 0x30, 0x28, 0xF5, 0xD3, 0x09, 0x4C, -0x20, 0x78, 0x38, 0x42, 0x07, 0xD1, 0x08, 0x48, -0x60, 0x22, 0x00, 0x68, 0xFE, 0xF7, 0x20, 0xFB, -0x20, 0x78, 0x38, 0x43, 0x20, 0x70, 0x01, 0x20, -0xF8, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, -0x4C, 0x01, 0x00, 0x20, 0x36, 0x00, 0x00, 0x20, -0xCC, 0x00, 0x00, 0x20, 0xF0, 0xB5, 0x22, 0x48, -0x00, 0x22, 0x00, 0x68, 0x14, 0x46, 0x21, 0x4B, -0x98, 0x42, 0x28, 0xD8, 0x20, 0x48, 0x00, 0x78, -0x04, 0x28, 0x27, 0xD1, 0x1F, 0x4B, 0x20, 0x4D, -0x1E, 0x68, 0x00, 0x20, 0x33, 0x18, 0xDB, 0x7E, -0x41, 0x2B, 0x08, 0xD0, 0x42, 0x00, 0x57, 0x19, -0x00, 0x23, 0xFB, 0x5E, 0x8A, 0x5E, 0x9B, 0x1A, -0x00, 0xD5, 0x5B, 0x42, 0x1A, 0xB2, 0x94, 0x42, -0x00, 0xDA, 0x14, 0x46, 0x40, 0x1C, 0xC0, 0xB2, -0x30, 0x28, 0xEB, 0xD3, 0x60, 0x20, 0x80, 0x5D, -0x90, 0x36, 0x40, 0x1C, 0x72, 0x7B, 0x60, 0x43, -0x33, 0x7B, 0x00, 0x11, 0x12, 0x02, 0x00, 0xB2, -0x1A, 0x43, 0x82, 0x42, 0x03, 0xDA, 0x0F, 0x49, -0x00, 0x20, 0x08, 0x70, 0xF0, 0xBD, 0x00, 0x20, -0x42, 0x00, 0x8B, 0x5E, 0x00, 0x26, 0xDC, 0x00, -0xE3, 0x1A, 0x54, 0x19, 0xA6, 0x5F, 0x40, 0x1C, -0x9B, 0x19, 0xDB, 0x10, 0xC0, 0xB2, 0x8B, 0x52, -0x30, 0x28, 0xF1, 0xD3, 0xF0, 0xBD, 0x00, 0x00, -0x64, 0x01, 0x00, 0x20, 0x40, 0x77, 0x1B, 0x00, -0x7E, 0x02, 0x00, 0x20, 0x80, 0x02, 0x00, 0x20, -0x00, 0x00, 0x01, 0x20, 0x36, 0x00, 0x00, 0x20, -0xF8, 0xB5, 0x27, 0x48, 0x27, 0x49, 0x00, 0x78, -0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x08, 0xD0, -0x02, 0x28, 0x20, 0xD0, 0x05, 0x28, 0x36, 0xD1, -0x3F, 0xE0, 0x01, 0x25, 0x2C, 0x46, 0x22, 0x48, -0x02, 0xE0, 0x02, 0x25, 0x21, 0x48, 0x2C, 0x46, -0x08, 0x60, 0x00, 0x22, 0x20, 0x49, 0x28, 0x46, -0xFE, 0xF7, 0x14, 0xFC, 0x03, 0x27, 0x1F, 0x4E, -0x3F, 0x05, 0x00, 0x28, 0x03, 0xD0, 0x1E, 0x48, -0x00, 0x78, 0x00, 0x28, 0x0B, 0xD0, 0x01, 0x22, -0x21, 0x46, 0x28, 0x46, 0x00, 0xF0, 0x3C, 0xF8, -0x00, 0x28, 0x1E, 0xD0, 0x17, 0xE0, 0x04, 0x25, -0x2C, 0x46, 0x18, 0x48, 0xE4, 0xE7, 0x70, 0x6B, -0x38, 0x43, 0x70, 0x63, 0x16, 0x48, 0x00, 0xF0, -0xD1, 0xFA, 0x00, 0x28, 0x0B, 0xD1, 0x20, 0x46, -0xFF, 0xF7, 0x2E, 0xFF, 0x00, 0x28, 0x06, 0xD1, -0x01, 0x22, 0x21, 0x46, 0x28, 0x46, 0x00, 0xF0, -0x23, 0xF8, 0x00, 0x28, 0x04, 0xD0, 0x0F, 0x48, -0x00, 0x78, 0xC0, 0x07, 0x05, 0xD0, 0x00, 0x20, -0xF8, 0xBD, 0x70, 0x6B, 0x38, 0x43, 0x70, 0x63, -0xF9, 0xE7, 0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, -0x7D, 0x02, 0x00, 0x20, 0xCC, 0x00, 0x00, 0x20, -0x1C, 0x02, 0x00, 0x20, 0x0C, 0x08, 0x00, 0x20, -0x00, 0x20, 0x00, 0x50, 0x80, 0x10, 0x00, 0x50, -0x3B, 0x01, 0x00, 0x20, 0x6C, 0x08, 0x00, 0x20, -0x00, 0x00, 0x01, 0x20, 0x34, 0x00, 0x00, 0x20, -0xF7, 0xB5, 0x07, 0x46, 0x14, 0x48, 0x00, 0x25, -0x05, 0x70, 0x78, 0x07, 0x1D, 0xD0, 0x01, 0x21, -0x02, 0x20, 0x00, 0xF0, 0x9F, 0xFB, 0x11, 0x4C, -0x60, 0x6B, 0x03, 0x26, 0x36, 0x05, 0x01, 0x21, -0xB0, 0x43, 0x09, 0x05, 0x40, 0x18, 0x60, 0x63, -0x00, 0x20, 0xFF, 0xF7, 0x01, 0xFD, 0x00, 0x28, -0x0D, 0xD0, 0x01, 0x98, 0xFF, 0xF7, 0xE8, 0xFE, -0x01, 0x22, 0x09, 0x49, 0x38, 0x46, 0xFE, 0xF7, -0xA9, 0xFB, 0x08, 0x48, 0x05, 0x70, 0x08, 0x48, -0x05, 0x70, 0x01, 0x20, 0xFE, 0xBD, 0x60, 0x6B, -0x30, 0x43, 0x60, 0x63, 0x00, 0x20, 0xFE, 0xBD, -0xD3, 0x00, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, -0x00, 0x20, 0x00, 0x50, 0x3B, 0x01, 0x00, 0x20, -0x3C, 0x01, 0x00, 0x20, 0x70, 0xB5, 0x10, 0x4C, -0x60, 0x6B, 0x03, 0x25, 0x2D, 0x05, 0x01, 0x26, -0xA8, 0x43, 0x36, 0x05, 0x80, 0x19, 0x60, 0x63, -0x0C, 0x49, 0x02, 0x20, 0x08, 0x5E, 0xFF, 0xF7, -0xD3, 0xFC, 0x00, 0x28, 0x60, 0x6B, 0x09, 0xD0, -0xA8, 0x43, 0x80, 0x19, 0x60, 0x63, 0x08, 0x49, -0x00, 0x20, 0x08, 0x70, 0x07, 0x49, 0x08, 0x70, -0x01, 0x20, 0x70, 0xBD, 0xA8, 0x43, 0x80, 0x19, -0x60, 0x63, 0x00, 0x20, 0x70, 0xBD, 0x00, 0x00, -0x80, 0x10, 0x00, 0x50, 0x98, 0x01, 0x00, 0x20, -0x3B, 0x01, 0x00, 0x20, 0x3C, 0x01, 0x00, 0x20, -0xF0, 0xB5, 0x01, 0x24, 0xEE, 0x4B, 0xA4, 0x02, -0x23, 0x20, 0x13, 0x21, 0x29, 0x22, 0x1C, 0x60, -0x40, 0x01, 0x89, 0x01, 0x52, 0x01, 0x1B, 0x1D, -0x07, 0xC3, 0xEA, 0x4B, 0x1D, 0x68, 0x2B, 0x46, -0x40, 0x33, 0xAC, 0x46, 0x5D, 0x7C, 0x1E, 0x7C, -0x2D, 0x04, 0x36, 0x02, 0x35, 0x43, 0x0C, 0x35, -0x26, 0x46, 0x35, 0x60, 0x5D, 0x7C, 0x1E, 0x7C, -0x2D, 0x04, 0x36, 0x02, 0x35, 0x43, 0x0C, 0x35, -0x26, 0x46, 0x75, 0x60, 0x01, 0x25, 0xB5, 0x60, -0xF5, 0x60, 0x1D, 0x7D, 0x6D, 0x1E, 0xED, 0x05, -0xED, 0x09, 0x6D, 0x1C, 0x35, 0x61, 0xDC, 0x4D, -0x75, 0x61, 0x03, 0x25, 0xB5, 0x61, 0x00, 0x25, -0xF5, 0x61, 0x03, 0x25, 0x2D, 0x02, 0x35, 0x62, -0x9E, 0x7E, 0x1D, 0x7E, 0x77, 0x00, 0xF6, 0x19, -0xAE, 0x19, 0x36, 0x04, 0x2E, 0x43, 0x25, 0x46, -0xEE, 0x62, 0x9D, 0x7E, 0x1E, 0x7E, 0x6F, 0x00, -0xEF, 0x19, 0xBF, 0x1C, 0xF7, 0x19, 0x7F, 0x05, -0x7F, 0x09, 0x75, 0x19, 0x2F, 0x43, 0x25, 0x46, -0x2F, 0x63, 0x1E, 0x7E, 0x9D, 0x7E, 0x6F, 0x00, -0xEF, 0x19, 0x3F, 0x1D, 0xF7, 0x19, 0x6D, 0x00, -0x7F, 0x05, 0x75, 0x19, 0x7F, 0x09, 0x2F, 0x43, -0x25, 0x46, 0x6F, 0x63, 0x00, 0x25, 0x26, 0x46, -0xB5, 0x63, 0x9E, 0x7E, 0x1D, 0x7E, 0x77, 0x00, -0xF6, 0x19, 0xBC, 0x36, 0xAE, 0x19, 0x36, 0x04, -0xBC, 0x35, 0x2E, 0x43, 0x25, 0x46, 0xEE, 0x63, -0x9D, 0x7E, 0x1E, 0x7E, 0x6F, 0x00, 0xEF, 0x19, -0xBE, 0x37, 0xF7, 0x19, 0x7F, 0x05, 0x75, 0x19, -0x7F, 0x09, 0xBC, 0x35, 0x2F, 0x43, 0x25, 0x46, -0x2F, 0x64, 0x9D, 0x7E, 0x1E, 0x7E, 0x6F, 0x00, -0xEF, 0x19, 0xC0, 0x37, 0xF7, 0x19, 0x6D, 0x00, -0xBC, 0x35, 0x7F, 0x05, 0x75, 0x19, 0x7F, 0x09, -0x2F, 0x43, 0x25, 0x46, 0x6F, 0x64, 0x00, 0x25, -0x26, 0x46, 0xB5, 0x64, 0x75, 0x62, 0xB1, 0x4D, -0xB5, 0x62, 0x65, 0x46, 0x50, 0x35, 0xEE, 0x7B, -0xAF, 0x7B, 0x35, 0x02, 0x3D, 0x43, 0xED, 0x1C, -0xAE, 0x05, 0xAD, 0x4D, 0xB6, 0x0D, 0x75, 0x19, -0x26, 0x46, 0xF5, 0x64, 0xAB, 0x4D, 0x2E, 0x68, -0xAB, 0x4D, 0xAE, 0x42, 0x01, 0xD1, 0xAB, 0x4D, -0x00, 0xE0, 0xAB, 0x4D, 0x25, 0x65, 0x5D, 0x7C, -0xA2, 0x4E, 0x0C, 0x3D, 0xEF, 0xB2, 0x05, 0x25, -0x6D, 0x02, 0x7D, 0x19, 0x65, 0x65, 0x1D, 0x7D, -0x00, 0x27, 0x2D, 0x02, 0x21, 0x35, 0xA5, 0x65, -0x02, 0x25, 0xE5, 0x65, 0x5C, 0x7C, 0x1B, 0x7C, -0x24, 0x04, 0x1B, 0x02, 0x1C, 0x43, 0x0C, 0x34, -0x04, 0x60, 0x34, 0x68, 0x23, 0x46, 0x40, 0x33, -0xA4, 0x46, 0x5C, 0x7C, 0x1D, 0x7C, 0x24, 0x04, -0x2D, 0x02, 0x2C, 0x43, 0x0C, 0x34, 0x44, 0x60, -0x01, 0x24, 0x84, 0x60, 0xC4, 0x60, 0x5C, 0x7D, -0x64, 0x1E, 0xE4, 0x05, 0xE4, 0x09, 0x64, 0x1C, -0x04, 0x61, 0x8F, 0x4C, 0x44, 0x61, 0x03, 0x24, -0xC7, 0x61, 0x84, 0x61, 0x24, 0x02, 0x04, 0x62, -0xDD, 0x7E, 0x1C, 0x7E, 0x6E, 0x00, 0xAD, 0x19, -0x65, 0x19, 0x2D, 0x04, 0x25, 0x43, 0xC5, 0x62, -0xDC, 0x7E, 0x1D, 0x7E, 0x66, 0x00, 0xA6, 0x19, -0xB6, 0x1C, 0xAE, 0x19, 0x76, 0x05, 0x76, 0x09, -0x2C, 0x19, 0x26, 0x43, 0x06, 0x63, 0xDC, 0x7E, -0x1D, 0x7E, 0x66, 0x00, 0xA6, 0x19, 0x36, 0x1D, -0xAE, 0x19, 0x76, 0x05, 0x64, 0x00, 0x76, 0x09, -0x2C, 0x19, 0x26, 0x43, 0x87, 0x63, 0x46, 0x63, -0xDD, 0x7E, 0x1C, 0x7E, 0x6E, 0x00, 0xAD, 0x19, -0xBC, 0x35, 0x65, 0x19, 0x2D, 0x04, 0xBC, 0x34, -0x25, 0x43, 0xC5, 0x63, 0xDC, 0x7E, 0x1D, 0x7E, -0x66, 0x00, 0xA6, 0x19, 0xBE, 0x36, 0xAE, 0x19, -0x76, 0x05, 0x2C, 0x19, 0x76, 0x09, 0xBC, 0x34, -0x26, 0x43, 0x06, 0x64, 0xDC, 0x7E, 0x1D, 0x7E, -0x66, 0x00, 0xA6, 0x19, 0xC0, 0x36, 0xAE, 0x19, -0x64, 0x00, 0x76, 0x05, 0xBC, 0x34, 0x76, 0x09, -0x2C, 0x19, 0x26, 0x43, 0x87, 0x64, 0x47, 0x62, -0x6A, 0x4C, 0x46, 0x64, 0x84, 0x62, 0x64, 0x46, -0x50, 0x34, 0xE5, 0x7B, 0xA6, 0x7B, 0x2C, 0x02, -0x34, 0x43, 0xE4, 0x1C, 0xA5, 0x05, 0x66, 0x4C, -0xAD, 0x0D, 0x2C, 0x19, 0xC4, 0x64, 0x65, 0x4C, -0x25, 0x68, 0x65, 0x4C, 0xA5, 0x42, 0x01, 0xD1, -0x64, 0x4C, 0x00, 0xE0, 0x64, 0x4C, 0x04, 0x65, -0x5C, 0x7C, 0x5C, 0x4E, 0x0C, 0x3C, 0xE5, 0xB2, -0x05, 0x24, 0x64, 0x02, 0x2D, 0x19, 0x45, 0x65, -0x5D, 0x7D, 0x2D, 0x02, 0x21, 0x35, 0x85, 0x65, -0x02, 0x25, 0xC5, 0x65, 0xD8, 0x7C, 0x9B, 0x7C, -0x00, 0x04, 0x1B, 0x02, 0x18, 0x43, 0x0C, 0x30, -0x08, 0x60, 0x30, 0x68, 0x40, 0x30, 0xC3, 0x7C, -0x85, 0x7C, 0x1B, 0x04, 0x2D, 0x02, 0x2B, 0x43, -0x0C, 0x33, 0x4B, 0x60, 0x83, 0x7D, 0x55, 0x4D, -0x5B, 0x1E, 0xDB, 0x05, 0xDB, 0x0D, 0x8B, 0x60, -0x83, 0x7D, 0x5B, 0x08, 0x5B, 0x1E, 0xDB, 0x05, -0xDB, 0x0D, 0xCB, 0x60, 0x0F, 0x61, 0x4D, 0x61, -0xCF, 0x61, 0x01, 0x25, 0x8D, 0x61, 0x2D, 0x02, -0x0D, 0x62, 0x45, 0x7E, 0xCD, 0x62, 0x45, 0x7E, -0x06, 0x7F, 0x3B, 0x46, 0xAD, 0x19, 0x0D, 0x63, -0x06, 0x7F, 0x45, 0x7E, 0x8B, 0x63, 0xCB, 0x63, -0x0B, 0x64, 0x40, 0x4F, 0x4B, 0x64, 0x76, 0x00, -0x8B, 0x64, 0x3F, 0x37, 0xAD, 0x19, 0x8F, 0x62, -0x4D, 0x63, 0x3D, 0x4D, 0x4B, 0x62, 0xED, 0x1C, -0xCD, 0x64, 0x3C, 0x4D, 0x3C, 0x4E, 0x2D, 0x68, -0xB5, 0x42, 0x01, 0xD1, 0x3B, 0x4D, 0x00, 0xE0, -0x3B, 0x4D, 0x0D, 0x65, 0xC5, 0x7C, 0x0C, 0x3D, -0xED, 0xB2, 0x2D, 0x19, 0x4D, 0x65, 0x85, 0x7D, -0x6D, 0x08, 0x2D, 0x02, 0x21, 0x35, 0x8D, 0x65, -0xCB, 0x65, 0xC1, 0x7C, 0x80, 0x7C, 0x09, 0x04, -0x00, 0x02, 0x01, 0x43, 0x0C, 0x31, 0x2B, 0x48, -0x11, 0x60, 0x00, 0x68, 0x40, 0x30, 0xC1, 0x7C, -0x85, 0x7C, 0x09, 0x04, 0x2D, 0x02, 0x29, 0x43, -0x0C, 0x31, 0x51, 0x60, 0xC1, 0x7D, 0x49, 0x1E, -0xC9, 0x05, 0xC9, 0x0D, 0x91, 0x60, 0xC1, 0x7D, -0x13, 0x61, 0x49, 0x08, 0x49, 0x1E, 0xC9, 0x05, -0xC9, 0x0D, 0xD1, 0x60, 0x27, 0x49, 0x51, 0x61, -0x01, 0x21, 0xD3, 0x61, 0x91, 0x61, 0x09, 0x02, -0x11, 0x62, 0x41, 0x7E, 0xD1, 0x62, 0x41, 0x7E, -0x45, 0x7F, 0x49, 0x19, 0x11, 0x63, 0x45, 0x7F, -0x41, 0x7E, 0x6D, 0x00, 0x49, 0x19, 0x93, 0x63, -0x51, 0x63, 0x41, 0x7E, 0xBC, 0x31, 0xD1, 0x63, -0x41, 0x7E, 0x45, 0x7F, 0x49, 0x19, 0xBC, 0x31, -0x11, 0x64, 0x45, 0x7F, 0x41, 0x7E, 0x6D, 0x00, -0xBC, 0x35, 0x49, 0x19, 0x93, 0x64, 0x51, 0x64, -0x01, 0x21, 0x89, 0x04, 0x97, 0x62, 0x51, 0x62, -0x0F, 0x49, 0xC9, 0x1C, 0xD1, 0x64, 0x0F, 0x49, -0x09, 0x68, 0xB1, 0x42, 0x01, 0xD1, 0x0F, 0x49, -0x00, 0xE0, 0x0F, 0x49, 0x11, 0x65, 0xC1, 0x7C, -0x0C, 0x39, 0xC9, 0xB2, 0x09, 0x19, 0x51, 0x65, -0xC0, 0x7D, 0xD3, 0x65, 0x40, 0x08, 0x00, 0x02, -0x21, 0x30, 0x90, 0x65, 0xF0, 0xBD, 0x00, 0x00, -0x2C, 0x06, 0x00, 0x20, 0x80, 0x02, 0x00, 0x20, -0x03, 0x00, 0x03, 0x00, 0x00, 0x3F, 0x3F, 0x3F, -0x00, 0x30, 0xBC, 0x00, 0xE4, 0x06, 0x00, 0x20, -0xA1, 0x00, 0x03, 0xF3, 0x00, 0x26, 0x31, 0x00, -0x00, 0x26, 0x31, 0x08, 0x01, 0x00, 0x01, 0x00, -0x00, 0xB5, 0x00, 0xF0, 0xA5, 0xFA, 0x00, 0xF0, -0x3B, 0xF9, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, -0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x00, 0x20, -0xFF, 0xF7, 0x32, 0xFC, 0x09, 0x48, 0x01, 0x78, -0x09, 0x48, 0x00, 0x29, 0x04, 0xD0, 0x01, 0x68, -0xFF, 0x22, 0x01, 0x32, 0x11, 0x43, 0x01, 0x60, -0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x91, 0x43, -0x01, 0x60, 0x04, 0x49, 0x02, 0x20, 0x08, 0x72, -0x00, 0xBD, 0x00, 0x00, 0x35, 0x00, 0x00, 0x20, -0x00, 0x10, 0x00, 0x50, 0x24, 0x00, 0x00, 0x20, -0x10, 0xB5, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, -0x04, 0x24, 0x21, 0x43, 0x41, 0x60, 0x0B, 0x48, -0x09, 0x49, 0x41, 0x60, 0x0A, 0x49, 0x81, 0x60, -0xFF, 0xF7, 0xD6, 0xFD, 0x05, 0x20, 0x00, 0x07, -0x81, 0x69, 0x21, 0x43, 0x81, 0x61, 0x80, 0x21, -0x06, 0x48, 0xFD, 0xF7, 0xE2, 0xFF, 0x80, 0x21, -0x05, 0x48, 0xFD, 0xF7, 0xDE, 0xFF, 0x10, 0xBD, -0x1F, 0x1F, 0x5F, 0x1F, 0x00, 0x10, 0x00, 0x50, -0x1F, 0x1F, 0x1F, 0x1F, 0x1C, 0x0A, 0x00, 0x20, -0x9C, 0x01, 0x00, 0x20, 0x10, 0xB5, 0x0F, 0x49, -0x0A, 0x78, 0x0F, 0x49, 0x00, 0x2A, 0x09, 0x78, -0x04, 0xD0, 0x01, 0x2A, 0x07, 0xD0, 0x02, 0x2A, -0x12, 0xD1, 0x09, 0xE0, 0xC9, 0x07, 0x0F, 0xD0, -0x60, 0x22, 0x0A, 0x49, 0x08, 0xE0, 0x89, 0x07, -0x0A, 0xD5, 0x60, 0x22, 0x08, 0x49, 0x03, 0xE0, -0x49, 0x07, 0x05, 0xD5, 0x60, 0x22, 0x07, 0x49, -0xFD, 0xF7, 0x9E, 0xFF, 0x01, 0x20, 0x10, 0xBD, -0x00, 0x20, 0x10, 0xBD, 0x7D, 0x02, 0x00, 0x20, -0x36, 0x00, 0x00, 0x20, 0x1C, 0x02, 0x00, 0x20, -0x0C, 0x08, 0x00, 0x20, 0x6C, 0x08, 0x00, 0x20, -0x10, 0xB5, 0x13, 0x49, 0x13, 0x4B, 0x09, 0x68, -0x13, 0x4A, 0x40, 0x31, 0x00, 0x28, 0x0F, 0xD0, -0x01, 0x28, 0x12, 0xD0, 0x02, 0x28, 0x15, 0xD0, -0x03, 0x28, 0x08, 0xD1, 0x48, 0x7E, 0x84, 0x1E, -0x1C, 0x80, 0x49, 0x7F, 0x4B, 0x00, 0xC9, 0x18, -0x89, 0x1C, 0x40, 0x18, 0x10, 0x80, 0x10, 0xBD, -0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, 0x89, 0x7E, -0xF4, 0xE7, 0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, -0xC9, 0x7E, 0xEF, 0xE7, 0x48, 0x7E, 0x84, 0x1E, -0x1C, 0x80, 0x09, 0x7F, 0xEA, 0xE7, 0x00, 0x00, -0x80, 0x02, 0x00, 0x20, 0x3A, 0x00, 0x00, 0x20, -0x3C, 0x00, 0x00, 0x20, 0x10, 0xB5, 0x1C, 0x4A, -0x00, 0x21, 0x11, 0x60, 0x51, 0x60, 0x1B, 0x4A, -0x01, 0x21, 0x11, 0x70, 0x1A, 0x49, 0x1B, 0x4A, -0x09, 0x78, 0x00, 0x29, 0x06, 0xD0, 0x3C, 0x24, -0x0C, 0x23, 0x01, 0x29, 0x0A, 0xD0, 0x02, 0x29, -0x20, 0xD1, 0x11, 0xE0, 0x78, 0x21, 0x00, 0x28, -0x11, 0x70, 0x01, 0xD0, 0x00, 0x20, 0x08, 0xE0, -0x01, 0x20, 0x06, 0xE0, 0x00, 0x28, 0x02, 0xD0, -0x14, 0x70, 0x02, 0x20, 0x01, 0xE0, 0x13, 0x70, -0x03, 0x20, 0x00, 0xF0, 0x83, 0xF9, 0x0D, 0xE0, -0x00, 0x28, 0x02, 0xD0, 0x14, 0x70, 0x02, 0x20, -0x01, 0xE0, 0x13, 0x70, 0x03, 0x20, 0x00, 0xF0, -0x79, 0xF9, 0x09, 0x48, 0x81, 0x6A, 0x09, 0x4A, -0x11, 0x40, 0x81, 0x62, 0x05, 0x20, 0x08, 0x49, -0x00, 0x02, 0x08, 0x60, 0x10, 0xBD, 0x00, 0x00, -0x24, 0x00, 0x00, 0x20, 0x35, 0x00, 0x00, 0x20, -0x7D, 0x02, 0x00, 0x20, 0x7F, 0x02, 0x00, 0x20, -0xC0, 0x11, 0x00, 0x50, 0xFF, 0xFF, 0x00, 0xF8, -0x00, 0x10, 0x00, 0x50, 0x10, 0xB5, 0x17, 0x4C, -0x17, 0x49, 0x20, 0x70, 0x01, 0x20, 0x08, 0x70, -0x00, 0xF0, 0xB2, 0xF9, 0x00, 0xF0, 0x48, 0xF8, -0x01, 0x20, 0xFD, 0xF7, 0xB5, 0xFF, 0xFF, 0xF7, -0x97, 0xFB, 0x01, 0x20, 0xFF, 0xF7, 0xA6, 0xFF, -0x00, 0xF0, 0xBC, 0xFB, 0xFE, 0xF7, 0x54, 0xFE, -0x20, 0x78, 0x0E, 0x49, 0x05, 0x28, 0x08, 0x70, -0x0F, 0xD0, 0xFF, 0xF7, 0x3D, 0xFC, 0x00, 0x28, -0x0C, 0xD0, 0x0B, 0x48, 0x00, 0x7A, 0x00, 0x28, -0x03, 0xD1, 0x00, 0x21, 0x02, 0x20, 0x00, 0xF0, -0x41, 0xF8, 0x08, 0x48, 0x00, 0x78, 0xC0, 0x07, -0x01, 0xD0, 0x00, 0x20, 0x10, 0xBD, 0x01, 0x20, -0x10, 0xBD, 0x00, 0x00, 0x7D, 0x02, 0x00, 0x20, -0x7C, 0x02, 0x00, 0x20, 0xCD, 0x08, 0x00, 0x20, -0x24, 0x00, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20, -0x08, 0x49, 0x02, 0x20, 0x08, 0x72, 0x08, 0x48, -0x01, 0x78, 0x08, 0x48, 0x00, 0x29, 0x01, 0x68, -0x04, 0xD0, 0x01, 0x22, 0x92, 0x02, 0x91, 0x43, -0x01, 0x60, 0x70, 0x47, 0x01, 0x22, 0x11, 0x43, -0xFA, 0xE7, 0x00, 0x00, 0x24, 0x00, 0x00, 0x20, -0x35, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, -0x10, 0xB5, 0x08, 0x48, 0x01, 0x68, 0x01, 0x22, -0x92, 0x02, 0x11, 0x43, 0x01, 0x60, 0x00, 0x68, -0x05, 0x4C, 0xC0, 0x07, 0x03, 0xD0, 0x02, 0x20, -0x20, 0x72, 0x00, 0xF0, 0x59, 0xF9, 0x00, 0x20, -0x20, 0x72, 0x10, 0xBD, 0x00, 0x10, 0x00, 0x50, -0x24, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x00, -0x0E, 0x46, 0x16, 0xD0, 0xFF, 0xF7, 0xA4, 0xFE, -0x00, 0x24, 0x6D, 0x1E, 0x07, 0xE0, 0x00, 0xF0, -0x47, 0xF9, 0xFF, 0xF7, 0xFF, 0xFA, 0xFF, 0xF7, -0xC3, 0xFF, 0x64, 0x1C, 0xE4, 0xB2, 0xAC, 0x42, -0xF5, 0xDB, 0x00, 0xF0, 0x3D, 0xF9, 0xFF, 0xF7, -0xF5, 0xFA, 0x00, 0x2E, 0x02, 0xD0, 0xFF, 0xF7, -0xCF, 0xFF, 0x70, 0xBD, 0xFF, 0xF7, 0xB4, 0xFF, -0x70, 0xBD, 0x00, 0x00, 0xF0, 0xB5, 0x01, 0x21, -0x31, 0x4A, 0x89, 0x07, 0x05, 0x27, 0x0C, 0x13, -0x04, 0x25, 0x3F, 0x07, 0x12, 0x68, 0x2F, 0x4B, -0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x04, 0xD0, -0x02, 0x28, 0x1A, 0xD0, 0x03, 0x28, 0x40, 0xD1, -0x17, 0xE0, 0x08, 0x68, 0xA0, 0x43, 0x08, 0x60, -0x01, 0x20, 0x98, 0x61, 0xD8, 0x61, 0x27, 0x49, -0x00, 0x20, 0x40, 0x39, 0xC8, 0x63, 0x18, 0x60, -0x98, 0x60, 0x25, 0x48, 0x82, 0x42, 0x30, 0xD1, -0x88, 0x6B, 0xA8, 0x43, 0x88, 0x63, 0x78, 0x69, -0x01, 0x21, 0x09, 0x04, 0x88, 0x43, 0x78, 0x61, -0x27, 0xE0, 0x0E, 0x68, 0x26, 0x43, 0x0E, 0x60, -0x62, 0x21, 0x99, 0x61, 0xD9, 0x61, 0x1B, 0x4C, -0x1C, 0x49, 0x40, 0x3C, 0xE1, 0x63, 0x1C, 0x49, -0x09, 0x68, 0x40, 0x31, 0x02, 0x28, 0x48, 0x7E, -0x23, 0xD0, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D, -0x18, 0x60, 0x4E, 0x7E, 0x48, 0x7F, 0x41, 0x00, -0x40, 0x18, 0x80, 0x1C, 0x30, 0x18, 0x40, 0x05, -0x40, 0x0D, 0x98, 0x60, 0x10, 0x48, 0x82, 0x42, -0x07, 0xD1, 0xA0, 0x6B, 0x28, 0x43, 0xA0, 0x63, -0x79, 0x69, 0x01, 0x20, 0x00, 0x04, 0x01, 0x43, -0x79, 0x61, 0x19, 0x68, 0x0D, 0x48, 0x41, 0x63, -0x19, 0x68, 0x81, 0x63, 0x99, 0x68, 0x0B, 0x48, -0x40, 0x30, 0x41, 0x60, 0x99, 0x68, 0x81, 0x60, -0xF0, 0xBD, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D, -0x18, 0x60, 0x4E, 0x7E, 0x08, 0x7F, 0xDA, 0xE7, -0xE4, 0x06, 0x00, 0x20, 0xC0, 0x11, 0x00, 0x50, -0xA2, 0x00, 0x03, 0xF3, 0x01, 0x00, 0x01, 0x00, -0x80, 0x02, 0x00, 0x20, 0x80, 0x13, 0x00, 0x50, -0x2F, 0x4A, 0x90, 0x6A, 0x2F, 0x49, 0x08, 0x60, -0xD0, 0x6B, 0x48, 0x60, 0x2C, 0x48, 0x40, 0x30, -0x03, 0x69, 0x8B, 0x60, 0x43, 0x6A, 0xCB, 0x60, -0xD3, 0x6A, 0x0B, 0x61, 0x03, 0x68, 0x4B, 0x61, -0x43, 0x69, 0x8B, 0x61, 0x83, 0x6A, 0xCB, 0x61, -0x93, 0x6A, 0x0B, 0x62, 0xD3, 0x6B, 0x4B, 0x62, -0x03, 0x69, 0x8B, 0x62, 0x43, 0x6A, 0xCB, 0x62, -0xD3, 0x6A, 0x0B, 0x63, 0x03, 0x68, 0x4B, 0x63, -0x43, 0x69, 0x8B, 0x63, 0x83, 0x6A, 0xCB, 0x63, -0x93, 0x6A, 0x1E, 0x49, 0x40, 0x31, 0x0B, 0x60, -0xD3, 0x6B, 0x4B, 0x60, 0x03, 0x69, 0x8B, 0x60, -0x43, 0x6A, 0xCB, 0x60, 0xD3, 0x6A, 0x0B, 0x61, -0x03, 0x68, 0x4B, 0x61, 0x43, 0x69, 0x8B, 0x61, -0x83, 0x6A, 0xCB, 0x61, 0x93, 0x6A, 0x0B, 0x62, -0xD3, 0x6B, 0x4B, 0x62, 0x03, 0x69, 0x8B, 0x62, -0x43, 0x6A, 0xCB, 0x62, 0xD2, 0x6A, 0x0A, 0x63, -0x02, 0x68, 0x4A, 0x63, 0x42, 0x69, 0x8A, 0x63, -0x80, 0x6A, 0xC8, 0x63, 0x0C, 0x49, 0xC0, 0x31, -0x88, 0x6A, 0x80, 0x05, 0x82, 0x09, 0x88, 0x6A, -0x80, 0x05, 0x80, 0x0D, 0x02, 0x43, 0x09, 0x48, -0x80, 0x30, 0x02, 0x60, 0x8A, 0x6A, 0x89, 0x6A, -0x92, 0x05, 0x92, 0x09, 0x89, 0x05, 0x89, 0x0D, -0x0A, 0x43, 0x42, 0x60, 0x04, 0x49, 0xC1, 0x60, -0x01, 0x61, 0xFF, 0x21, 0x81, 0x60, 0x70, 0x47, -0x00, 0x11, 0x00, 0x50, 0x00, 0x13, 0x00, 0x50, -0xBC, 0x00, 0xBC, 0x00, 0x70, 0xB5, 0x04, 0x46, -0x81, 0x00, 0x26, 0x48, 0x41, 0x58, 0x26, 0x48, -0x0A, 0x68, 0x42, 0x60, 0x4A, 0x68, 0x82, 0x60, -0x8A, 0x68, 0x02, 0x61, 0xCA, 0x68, 0x42, 0x61, -0x22, 0x4D, 0x08, 0x69, 0x28, 0x62, 0x20, 0x4A, -0x48, 0x69, 0xC0, 0x32, 0x90, 0x61, 0x88, 0x69, -0xD0, 0x61, 0x48, 0x6A, 0x10, 0x62, 0x88, 0x6A, -0x50, 0x62, 0x1B, 0x48, 0xCB, 0x69, 0x40, 0x38, -0xC3, 0x60, 0x0B, 0x6A, 0x03, 0x61, 0xCB, 0x6A, -0x93, 0x62, 0x0B, 0x6B, 0xD3, 0x63, 0x17, 0x4B, -0x4E, 0x6B, 0x80, 0x3B, 0x1E, 0x61, 0x8E, 0x6B, -0x5E, 0x62, 0xCE, 0x6B, 0xD6, 0x62, 0x0A, 0x6C, -0x1A, 0x60, 0x4A, 0x6C, 0x5A, 0x61, 0x8A, 0x6C, -0x9A, 0x62, 0xCA, 0x6C, 0xAA, 0x62, 0x0E, 0x4B, -0x0A, 0x6D, 0x40, 0x33, 0x5A, 0x63, 0x0C, 0x4B, -0x4A, 0x6D, 0x80, 0x33, 0x9A, 0x60, 0x0C, 0x4A, -0x8B, 0x6D, 0x13, 0x60, 0xC9, 0x6D, 0x51, 0x60, -0xC2, 0x68, 0x0A, 0x49, 0x0A, 0x60, 0x00, 0x69, -0x48, 0x60, 0xFF, 0xF7, 0x51, 0xFF, 0x20, 0x46, -0xFF, 0xF7, 0xDE, 0xFD, 0x20, 0x46, 0xFF, 0xF7, -0xD9, 0xFE, 0x70, 0xBD, 0x2C, 0x06, 0x00, 0x20, -0x40, 0x10, 0x00, 0x50, 0xC0, 0x11, 0x00, 0x50, -0x00, 0x19, 0x00, 0x50, 0x24, 0x00, 0x00, 0x20, -0x30, 0xB5, 0x05, 0x20, 0x40, 0x04, 0x00, 0x23, -0x0B, 0x4A, 0x0C, 0x49, 0x0C, 0x4C, 0x05, 0xE0, -0x0D, 0x78, 0xED, 0x07, 0x01, 0xD0, 0x13, 0x72, -0x09, 0xE0, 0x40, 0x1E, 0x15, 0x7A, 0x02, 0x2D, -0x02, 0xD0, 0x25, 0x68, 0xED, 0x07, 0x02, 0xD0, -0x00, 0x28, 0xF1, 0xD1, 0x01, 0xE0, 0x00, 0x28, -0x00, 0xD1, 0x13, 0x72, 0x30, 0xBD, 0x00, 0x00, -0x24, 0x00, 0x00, 0x20, 0x34, 0x00, 0x00, 0x20, -0x00, 0x10, 0x00, 0x50, 0xF8, 0xB5, 0x1D, 0x48, -0x01, 0x25, 0x01, 0x68, 0x00, 0x24, 0x02, 0x22, -0x91, 0x43, 0x1B, 0x4F, 0x1B, 0x4E, 0x01, 0x60, -0x1B, 0x48, 0x00, 0x68, 0x00, 0x19, 0xC0, 0x7E, -0x41, 0x28, 0x14, 0xD0, 0x19, 0x48, 0x61, 0x00, -0x42, 0x5E, 0x04, 0x20, 0x38, 0x5E, 0x82, 0x42, -0x09, 0xDA, 0x21, 0x46, 0x16, 0xA0, 0xFD, 0xF7, -0xFD, 0xFD, 0x30, 0x5D, 0x02, 0x21, 0x08, 0x43, -0x30, 0x55, 0x00, 0x25, 0x03, 0xE0, 0x30, 0x5D, -0xFD, 0x21, 0x08, 0x40, 0x30, 0x55, 0x64, 0x1C, -0xE4, 0xB2, 0x30, 0x2C, 0xE0, 0xD3, 0x15, 0x49, -0x01, 0x2D, 0x79, 0x5E, 0x09, 0xD0, 0x14, 0xA0, -0xFD, 0xF7, 0xE8, 0xFD, 0x05, 0x49, 0x02, 0x22, -0x08, 0x68, 0x10, 0x43, 0x08, 0x60, 0x28, 0x46, -0xF8, 0xBD, 0x16, 0xA0, 0xFD, 0xF7, 0xDE, 0xFD, -0xF9, 0xE7, 0x00, 0x00, 0x0C, 0x05, 0x00, 0x20, -0x84, 0x06, 0x00, 0x20, 0xD8, 0x04, 0x00, 0x20, -0x80, 0x02, 0x00, 0x20, 0xE4, 0x02, 0x00, 0x20, -0x53, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x4E, 0x47, -0x20, 0x44, 0x61, 0x74, 0x61, 0x5B, 0x25, 0x64, -0x5D, 0x20, 0x3D, 0x20, 0x25, 0x64, 0x0D, 0x0A, -0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, -0x53, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x54, 0x65, -0x73, 0x74, 0x28, 0x54, 0x48, 0x44, 0x3A, 0x25, -0x64, 0x29, 0x20, 0x5B, 0x4E, 0x47, 0x5D, 0x0D, -0x0A, 0x00, 0x00, 0x00, 0x53, 0x68, 0x6F, 0x72, -0x74, 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x54, -0x48, 0x44, 0x3A, 0x25, 0x64, 0x29, 0x20, 0x5B, -0x50, 0x61, 0x73, 0x73, 0x5D, 0x0D, 0x0A, 0x00, -0x70, 0xB5, 0x0F, 0x48, 0x01, 0x25, 0xC4, 0x7F, -0x0E, 0x48, 0x80, 0x88, 0x00, 0x07, 0x01, 0xD4, -0x01, 0x20, 0x70, 0xBD, 0xFE, 0xF7, 0x32, 0xFB, -0xFE, 0xF7, 0xD8, 0xF9, 0xFF, 0xF7, 0x7E, 0xFF, -0x00, 0x28, 0x0A, 0xD1, 0x00, 0x25, 0x00, 0x2C, -0x07, 0xD0, 0x64, 0x1E, 0xE4, 0xB2, 0x21, 0x46, -0x05, 0xA0, 0xFD, 0xF7, 0x8B, 0xFD, 0x01, 0x25, -0xEE, 0xE7, 0x28, 0x46, 0x70, 0xBD, 0x00, 0x00, -0xA8, 0x06, 0x00, 0x20, 0x74, 0x06, 0x00, 0x20, -0x53, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x52, 0x65, -0x74, 0x72, 0x79, 0x20, 0x28, 0x25, 0x64, 0x29, -0x0D, 0x0A, 0x00, 0x00, 0xFE, 0xB5, 0x24, 0x49, -0x10, 0x26, 0x12, 0x27, 0x8E, 0x5F, 0xCF, 0x5F, -0x22, 0x49, 0x01, 0x91, 0x22, 0x49, 0x01, 0x20, -0x0A, 0x68, 0x03, 0x04, 0x9A, 0x43, 0x21, 0x4D, -0x00, 0x24, 0x0A, 0x60, 0x20, 0x49, 0x09, 0x68, -0x09, 0x19, 0xC9, 0x7E, 0x41, 0x29, 0x25, 0xD0, -0x01, 0x9A, 0x61, 0x00, 0x52, 0x5A, 0x52, 0x05, -0x52, 0x0D, 0x6A, 0x52, 0x53, 0x05, 0x03, 0xD5, -0x89, 0x23, 0x9B, 0x00, 0xD2, 0x1A, 0x6A, 0x52, -0x18, 0x4A, 0x6B, 0x5A, 0x52, 0x5A, 0xD1, 0x1A, -0x09, 0xB2, 0xB1, 0x42, 0x01, 0xDC, 0xB9, 0x42, -0x0B, 0xDA, 0x00, 0x91, 0x21, 0x46, 0x14, 0xA0, -0xFD, 0xF7, 0x48, 0xFD, 0x18, 0x48, 0x10, 0x22, -0x01, 0x5D, 0x11, 0x43, 0x01, 0x55, 0x00, 0x20, -0x04, 0xE0, 0x15, 0x49, 0xEF, 0x23, 0x0A, 0x5D, -0x1A, 0x40, 0x0A, 0x55, 0x64, 0x1C, 0xE4, 0xB2, -0x30, 0x2C, 0xCF, 0xD3, 0x01, 0x28, 0x05, 0xD0, -0x05, 0x4B, 0x01, 0x22, 0x19, 0x68, 0x12, 0x04, -0x11, 0x43, 0x19, 0x60, 0xFE, 0xBD, 0x00, 0x00, -0x84, 0x06, 0x00, 0x20, 0x00, 0x20, 0x00, 0x50, -0x0C, 0x05, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20, -0x80, 0x02, 0x00, 0x20, 0xEC, 0x06, 0x00, 0x20, -0x55, 0x43, 0x5B, 0x25, 0x64, 0x5D, 0x20, 0x4E, -0x47, 0x21, 0x20, 0x25, 0x64, 0x2D, 0x25, 0x64, -0x3D, 0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, 0x00, -0xD8, 0x04, 0x00, 0x20, 0x70, 0xB5, 0x0E, 0x48, -0x01, 0x25, 0x44, 0x79, 0x0D, 0x48, 0x80, 0x88, -0xC0, 0x06, 0x01, 0xD4, 0x01, 0x20, 0x70, 0xBD, -0xFF, 0xF7, 0x90, 0xFF, 0x00, 0x28, 0x0C, 0xD1, -0x00, 0x25, 0x00, 0x2C, 0x09, 0xD0, 0x64, 0x1E, -0xE4, 0xB2, 0xFE, 0xF7, 0xBF, 0xF9, 0x21, 0x46, -0x05, 0xA0, 0xFD, 0xF7, 0xFB, 0xFC, 0x01, 0x25, -0xEE, 0xE7, 0x28, 0x46, 0x70, 0xBD, 0x00, 0x00, -0xC8, 0x06, 0x00, 0x20, 0x74, 0x06, 0x00, 0x20, -0x55, 0x43, 0x20, 0x4E, 0x47, 0x20, 0x52, 0x65, -0x74, 0x72, 0x79, 0x20, 0x28, 0x25, 0x64, 0x29, -0x0D, 0x0A, 0x00, 0x00, 0x01, 0x21, 0x89, 0x07, -0x00, 0xB5, 0x8A, 0x14, 0x00, 0x28, 0x08, 0x68, -0x04, 0xD0, 0x10, 0x43, 0x08, 0x60, 0xFD, 0xF7, -0x07, 0xFD, 0x00, 0xBD, 0x90, 0x43, 0x08, 0x60, -0x00, 0xBD, 0x00, 0x00, 0xF8, 0xB5, 0x42, 0x4F, -0x01, 0x24, 0x38, 0x7B, 0x41, 0x49, 0x0A, 0x78, -0x41, 0x4B, 0x42, 0x4D, 0x42, 0x4E, 0x90, 0x42, -0x05, 0xD1, 0x28, 0x78, 0x81, 0x28, 0x02, 0xD0, -0x30, 0x78, 0xC0, 0x07, 0x54, 0xD0, 0x00, 0x20, -0x30, 0x70, 0x3A, 0x48, 0x00, 0x78, 0x07, 0x28, -0x0B, 0xD3, 0x18, 0x68, 0x40, 0x05, 0x40, 0x0F, -0x38, 0x73, 0x01, 0x20, 0xFD, 0xF7, 0x02, 0xFF, -0x00, 0x20, 0x30, 0x70, 0x38, 0x7B, 0x02, 0x28, -0x14, 0xD0, 0x38, 0x7B, 0x31, 0x4A, 0x10, 0x70, -0x81, 0x20, 0x28, 0x70, 0x33, 0x48, 0x01, 0x23, -0x01, 0x88, 0x1B, 0x03, 0x19, 0x43, 0x01, 0x80, -0x10, 0x78, 0x02, 0x27, 0x03, 0x00, 0xFD, 0xF7, -0x49, 0xFC, 0x07, 0x23, 0x23, 0x2C, 0x4A, 0x0B, -0x17, 0x0B, 0x4A, 0x00, 0x83, 0x20, 0x28, 0x70, -0x05, 0x20, 0xFF, 0xF7, 0xA7, 0xFC, 0xF8, 0xBD, -0x01, 0x20, 0xFE, 0xF7, 0x17, 0xFE, 0x00, 0x20, -0xFF, 0xF7, 0xA0, 0xFC, 0x00, 0x28, 0x01, 0xD0, -0x01, 0x20, 0x33, 0xE0, 0x00, 0x24, 0x32, 0xE0, -0x01, 0x20, 0xFE, 0xF7, 0x0B, 0xFE, 0x01, 0x20, -0xFF, 0xF7, 0x94, 0xFC, 0x00, 0x28, 0xF5, 0xD0, -0x04, 0x20, 0x27, 0xE0, 0x2F, 0x70, 0x26, 0xE0, -0x00, 0x20, 0xFE, 0xF7, 0xFF, 0xFD, 0x02, 0x20, -0xFF, 0xF7, 0x88, 0xFC, 0x00, 0x28, 0xF5, 0xD1, -0xE8, 0xE7, 0x83, 0x20, 0x28, 0x70, 0x05, 0x20, -0xFF, 0xF7, 0x80, 0xFC, 0x04, 0x46, 0x16, 0xE0, -0x15, 0x4A, 0x10, 0x78, 0x00, 0x28, 0x12, 0xD0, -0x40, 0x1E, 0x00, 0x06, 0x00, 0x0E, 0x10, 0x70, -0x0D, 0xD1, 0x18, 0x68, 0x40, 0x05, 0x40, 0x0F, -0x38, 0x73, 0x38, 0x7B, 0x09, 0x78, 0x88, 0x42, -0x05, 0xD0, 0x30, 0x78, 0x01, 0x21, 0x08, 0x43, -0x30, 0x70, 0x81, 0x20, 0x28, 0x70, 0x30, 0x78, -0xC0, 0x07, 0x02, 0xD0, 0x81, 0x20, 0x28, 0x70, -0x00, 0x24, 0x20, 0x46, 0xF8, 0xBD, 0x00, 0x00, -0x24, 0x00, 0x00, 0x20, 0x39, 0x01, 0x00, 0x20, -0x00, 0x11, 0x00, 0x50, 0xE0, 0x08, 0x00, 0x20, -0x34, 0x00, 0x00, 0x20, 0x4A, 0x01, 0x00, 0x20, -0x38, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x46, -0x01, 0x24, 0xFE, 0xF7, 0xE9, 0xFA, 0x00, 0x2D, -0x09, 0xD0, 0xF0, 0x20, 0xFD, 0xF7, 0xAC, 0xFB, -0xFE, 0xF7, 0x9E, 0xFB, 0xFE, 0xF7, 0x4E, 0xFB, -0x00, 0x20, 0xFE, 0xF7, 0xB3, 0xFD, 0xFE, 0xF7, -0xE3, 0xFD, 0xFF, 0xF7, 0x75, 0xFB, 0x20, 0x46, -0x70, 0xBD, 0x01, 0x20, 0x80, 0x07, 0x40, 0x69, -0x40, 0x05, 0x01, 0xD5, 0x01, 0x20, 0x70, 0x47, -0x00, 0x20, 0x70, 0x47, 0x70, 0xB5, 0x18, 0x4D, -0x1E, 0x20, 0x28, 0x70, 0x68, 0x78, 0x29, 0x46, -0x00, 0x24, 0x14, 0x31, 0x02, 0x28, 0x0C, 0xD8, -0x00, 0x28, 0x05, 0xD1, 0xA8, 0x78, 0x00, 0x28, -0x02, 0xD0, 0x08, 0x46, 0x1C, 0x30, 0x04, 0x70, -0x48, 0x88, 0x00, 0x28, 0x01, 0xD1, 0x03, 0x20, -0x68, 0x71, 0xAC, 0x70, 0x2C, 0x81, 0xEC, 0x80, -0xAC, 0x81, 0x6C, 0x81, 0x4C, 0x80, 0x8C, 0x80, -0x0C, 0x72, 0x0A, 0x48, 0x44, 0x70, 0x84, 0x70, -0x04, 0x70, 0x07, 0x48, 0xA0, 0x21, 0x30, 0x30, -0xFD, 0xF7, 0x3B, 0xFB, 0xEC, 0x70, 0x78, 0x20, -0xE8, 0x81, 0x01, 0x20, 0x28, 0x71, 0x2C, 0x61, -0x00, 0xF0, 0xC2, 0xF8, 0x70, 0xBD, 0x00, 0x00, -0xCC, 0x08, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, -0x06, 0x4A, 0x07, 0x4B, 0x00, 0x21, 0x00, 0x20, -0x40, 0x1C, 0x90, 0x42, 0xFC, 0xDB, 0x49, 0x1C, -0x99, 0x42, 0xF8, 0xDB, 0x03, 0x48, 0x01, 0x21, -0x01, 0x60, 0x70, 0x47, 0x10, 0x27, 0x00, 0x00, -0xB8, 0x0B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, -0x70, 0xB5, 0x7B, 0x24, 0x21, 0x48, 0x24, 0x02, -0x04, 0x60, 0xF7, 0x20, 0xC0, 0x01, 0x20, 0x4A, -0xC1, 0x7C, 0x11, 0x70, 0x1F, 0x4A, 0x01, 0x7D, -0x11, 0x70, 0x1F, 0x4A, 0x41, 0x7D, 0x11, 0x70, -0x1E, 0x49, 0x80, 0x7D, 0x08, 0x70, 0x60, 0x7C, -0x21, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, -0x1B, 0x49, 0x05, 0x46, 0x08, 0x80, 0xE0, 0x7C, -0xA1, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, -0x18, 0x49, 0x08, 0x80, 0x20, 0x7E, 0x61, 0x7E, -0x48, 0x43, 0x17, 0x49, 0x08, 0x80, 0x17, 0x48, -0xFD, 0xF7, 0xFB, 0xFA, 0x10, 0x21, 0xC8, 0x41, -0x15, 0x49, 0x08, 0x60, 0x04, 0x22, 0x21, 0x1D, -0x14, 0x48, 0xFD, 0xF7, 0xCD, 0xFA, 0x11, 0x49, -0xFF, 0x22, 0x1D, 0x32, 0x89, 0x1F, 0x12, 0x48, -0xFD, 0xF7, 0xC6, 0xFA, 0x60, 0x7D, 0x22, 0x7D, -0x01, 0x02, 0x11, 0x43, 0x28, 0x46, 0x0A, 0x22, -0x50, 0x43, 0xFD, 0xF7, 0xA9, 0xFA, 0x0D, 0x49, -0x08, 0x80, 0x70, 0xBD, 0x80, 0x02, 0x00, 0x20, -0x18, 0x00, 0x00, 0x20, 0x19, 0x00, 0x00, 0x20, -0x1A, 0x00, 0x00, 0x20, 0x1B, 0x00, 0x00, 0x20, -0x20, 0x00, 0x00, 0x20, 0x22, 0x00, 0x00, 0x20, -0x1C, 0x00, 0x00, 0x20, 0x10, 0x7B, 0x00, 0x00, -0xD8, 0x06, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20, -0x10, 0x05, 0x00, 0x20, 0x1E, 0x00, 0x00, 0x20, -0x00, 0x20, 0x0E, 0x49, 0x08, 0x70, 0x17, 0xE0, -0x0C, 0x48, 0x00, 0x78, 0x00, 0x28, 0x02, 0xD0, -0x07, 0x28, 0x0F, 0xD1, 0x0B, 0xE0, 0x00, 0xF0, -0x13, 0xF8, 0x01, 0x28, 0x03, 0xD1, 0x07, 0x20, -0x06, 0x49, 0x08, 0x70, 0x02, 0xE0, 0x03, 0x20, -0x04, 0x49, 0x08, 0x70, 0x03, 0xE0, 0xFD, 0xF7, -0x4F, 0xFB, 0x00, 0xE0, 0x00, 0xBF, 0x00, 0xBF, -0xE6, 0xE7, 0x00, 0x00, 0xD1, 0x00, 0x00, 0x20, -0x10, 0xB5, 0x00, 0x24, 0xFD, 0xF7, 0x12, 0xFD, -0xFD, 0xF7, 0xAE, 0xFD, 0x01, 0x20, 0xFF, 0xF7, -0x0D, 0xFF, 0x04, 0x46, 0x00, 0x2C, 0x00, 0xD1, -0x10, 0xBD, 0x03, 0x49, 0x03, 0x48, 0x81, 0x70, -0x09, 0x0A, 0xC1, 0x70, 0x01, 0x20, 0xF7, 0xE7, -0xAA, 0x55, 0x00, 0x00, 0x88, 0x02, 0x00, 0x20, -0x05, 0x48, 0x01, 0x78, 0x82, 0x29, 0x05, 0xD1, -0xC0, 0x79, 0x01, 0x28, 0x02, 0xD1, 0x03, 0x49, -0xFF, 0x20, 0x08, 0x70, 0x70, 0x47, 0x00, 0x00, -0xE0, 0x08, 0x00, 0x20, 0x40, 0x01, 0x00, 0x20, -0x06, 0x48, 0x00, 0x21, 0x81, 0x80, 0xC1, 0x80, -0x41, 0x70, 0x81, 0x70, 0x01, 0x70, 0x04, 0x49, -0x01, 0x81, 0x41, 0x81, 0x03, 0x49, 0x03, 0x20, -0x08, 0x70, 0x70, 0x47, 0x0C, 0x00, 0x00, 0x20, -0xFF, 0x7F, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x20, -0x08, 0x49, 0x78, 0x20, 0x08, 0x70, 0x08, 0x49, -0x00, 0x20, 0x08, 0x70, 0x08, 0x48, 0x07, 0x49, -0x01, 0x81, 0x41, 0x81, 0x07, 0x49, 0x03, 0x20, -0x08, 0x70, 0x07, 0x49, 0x01, 0x20, 0x08, 0x70, -0x70, 0x47, 0x00, 0x00, 0x7F, 0x02, 0x00, 0x20, -0xCF, 0x08, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, -0x0C, 0x00, 0x00, 0x20, 0xD3, 0x00, 0x00, 0x20, -0x7C, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, -0x10, 0x03, 0x42, 0x88, 0x0C, 0x00, 0x00, 0x40, -0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x50, -0x1F, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x50, -0x64, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x50, -0x77, 0x00, 0x01, 0x48, 0x44, 0x09, 0x00, 0x50, -0x39, 0x5A, 0x5B, 0x00, 0x10, 0x06, 0x00, 0x50, -0x00, 0x00, 0x06, 0x07, 0x00, 0x06, 0x00, 0x50, -0x00, 0x00, 0x00, 0x78, 0x08, 0x06, 0x00, 0x50, -0x0C, 0x30, 0x00, 0x00, 0x28, 0x06, 0x00, 0x50, -0x06, 0x00, 0x00, 0x00, 0x2C, 0x06, 0x00, 0x50, -0x0A, 0x66, 0x00, 0x00, 0x30, 0x06, 0x00, 0x50, -0xCC, 0x02, 0x00, 0x20, 0x34, 0x06, 0x00, 0x50, -0x00, 0x20, 0x00, 0x00, 0x44, 0x00, 0x00, 0x50, -0x01, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x50, -0x50, 0x71, 0x00, 0x00, 0x20, 0x00, 0x00, 0x50, -0x24, 0x29, 0x00, 0x00, 0x14, 0x00, 0x00, 0x40, -0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, -0x14, 0x33, 0x43, 0xC8, 0x0C, 0x00, 0x00, 0x40, -0x29, 0x0A, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, -0x10, 0x32, 0x00, 0x00, 0x1C, 0x0E, 0x00, 0x50, -0x03, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x50, -0x14, 0x07, 0x00, 0x00, 0x28, 0x00, 0x00, 0x40, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x50, -0x00, 0x00, 0x00, 0x00, 0x58, 0x10, 0x00, 0x50, -0x01, 0x00, 0x00, 0x00, 0x04, 0x11, 0x00, 0x50, -0x78, 0x11, 0x00, 0x00, 0x0C, 0x11, 0x00, 0x50, -0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x00, 0x50, -0x78, 0x01, 0x00, 0x00, 0x14, 0x11, 0x00, 0x50, -0xC8, 0x03, 0x60, 0x00, 0x18, 0x10, 0x00, 0x50, -0x00, 0x00, 0x00, 0x00, 0x1C, 0x10, 0x00, 0x50, -0x00, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x50, -0x31, 0x00, 0x00, 0x00, 0x3C, 0x10, 0x00, 0x50, -0x00, 0x00, 0x10, 0x00, 0xB4, 0x10, 0x00, 0x50, -0x00, 0x26, 0x31, 0x08, 0xC0, 0x10, 0x00, 0x50, -0x33, 0x03, 0x33, 0x03, 0xC4, 0x10, 0x00, 0x50, -0x33, 0x03, 0x33, 0x03, 0xC8, 0x10, 0x00, 0x50, -0x0C, 0x0A, 0x00, 0x00, 0xCC, 0x10, 0x00, 0x50, -0x1A, 0x00, 0x00, 0x00, 0xD0, 0x10, 0x00, 0x50, -0x03, 0x19, 0x19, 0x00, 0xF0, 0x11, 0x00, 0x50, -0x12, 0x00, 0x00, 0x00, 0xEC, 0x11, 0x00, 0x50, -0x5C, 0x00, 0x00, 0x00, 0xF4, 0x11, 0x00, 0x50, -0x01, 0x00, 0x01, 0x00, 0x2C, 0x10, 0x00, 0x50, -0x10, 0x00, 0x90, 0x00, 0x30, 0x10, 0x00, 0x50, -0x20, 0x0C, 0x90, 0x00, 0x34, 0x10, 0x00, 0x50, -0x30, 0x0C, 0x30, 0x0C, 0x38, 0x10, 0x00, 0x50, -0xFF, 0x0F, 0x00, 0x00, 0x7C, 0x10, 0x00, 0x50, -0x88, 0x88, 0xFE, 0x88, 0x80, 0x10, 0x00, 0x50, -0x88, 0xFF, 0x00, 0x00, 0x84, 0x10, 0x00, 0x50, -0x55, 0x55, 0x55, 0x55, 0x88, 0x10, 0x00, 0x50, -0x55, 0x55, 0x55, 0x55, 0x8C, 0x10, 0x00, 0x50, -0x55, 0x55, 0x55, 0x55, 0xE8, 0x10, 0x00, 0x50, -0x3F, 0x16, 0x3F, 0x15, 0x04, 0x00, 0x00, 0x40, -0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0x00, 0x00, 0x00, 0x00, 0x60, 0x38, 0x00, 0x00, -0x04, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, -0x5C, 0x0A, 0x00, 0x00, 0x68, 0x38, 0x00, 0x00, -0x0C, 0x00, 0x00, 0x20, 0x94, 0x0F, 0x00, 0x00, -0x6A, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x36, 0x03, 0x35, 0x03, 0x2C, 0x03, 0x9C, 0x03, -0xE9, 0x02, 0x28, 0x03, 0x29, 0x03, 0x1C, 0x03, -0x9D, 0x03, 0x2F, 0x03, 0x4A, 0x03, 0x4D, 0x03, -0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, -0x3D, 0x03, 0x3C, 0x03, 0x2C, 0x03, 0x9C, 0x03, -0xD3, 0x02, 0x28, 0x03, 0x29, 0x03, 0xD6, 0x02, -0x9E, 0x03, 0x30, 0x03, 0x37, 0x03, 0x3A, 0x03, -0xFF, 0x03, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0x00, 0x00, 0x00, 0x78, 0x7F, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0x42, 0x53, 0x71, 0x97, + 0x60, 0x0E, 0x00, 0x20, 0xB1, 0x08, 0x00, 0x00, + 0xB9, 0x08, 0x00, 0x00, 0x95, 0x08, 0x00, 0x00, + 0xBD, 0x08, 0x00, 0x00, 0xBF, 0x08, 0x00, 0x00, + 0xC1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC3, 0x08, 0x00, 0x00, + 0xC5, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC7, 0x08, 0x00, 0x00, 0x81, 0x0F, 0x00, 0x00, + 0xE1, 0x0C, 0x00, 0x00, 0x01, 0x0A, 0x00, 0x00, + 0xCB, 0x08, 0x00, 0x00, 0x61, 0x0E, 0x00, 0x00, + 0xA1, 0x0D, 0x00, 0x00, 0x41, 0x0A, 0x00, 0x00, + 0xCB, 0x08, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00, + 0xCB, 0x08, 0x00, 0x00, 0x41, 0x0B, 0x00, 0x00, + 0xE1, 0x0A, 0x00, 0x00, 0xA1, 0x0A, 0x00, 0x00, + 0xC1, 0x0A, 0x00, 0x00, 0x81, 0x0A, 0x00, 0x00, + 0xCB, 0x08, 0x00, 0x00, 0xCB, 0x08, 0x00, 0x00, + 0x03, 0x48, 0x85, 0x46, 0x00, 0xF0, 0x80, 0xF8, + 0x00, 0x48, 0x00, 0x47, 0xC1, 0x35, 0x00, 0x00, + 0x60, 0x0E, 0x00, 0x20, 0x04, 0x20, 0x71, 0x46, + 0x08, 0x42, 0x02, 0xD0, 0xEF, 0xF3, 0x09, 0x80, + 0x01, 0xE0, 0xEF, 0xF3, 0x08, 0x80, 0x71, 0x46, + 0x00, 0x4A, 0x10, 0x47, 0xD9, 0x34, 0x00, 0x00, + 0x06, 0x48, 0x80, 0x47, 0x06, 0x48, 0x00, 0x47, + 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, + 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, 0xFE, 0xE7, + 0xFE, 0xE7, 0xFE, 0xE7, 0x15, 0x0A, 0x00, 0x00, + 0x81, 0x08, 0x00, 0x00, 0x30, 0xB5, 0x0B, 0x46, + 0x01, 0x46, 0x00, 0x20, 0x20, 0x22, 0x01, 0x24, + 0x09, 0xE0, 0x0D, 0x46, 0xD5, 0x40, 0x9D, 0x42, + 0x05, 0xD3, 0x1D, 0x46, 0x95, 0x40, 0x49, 0x1B, + 0x25, 0x46, 0x95, 0x40, 0x40, 0x19, 0x15, 0x46, + 0x52, 0x1E, 0x00, 0x2D, 0xF1, 0xDC, 0x30, 0xBD, + 0x70, 0xB5, 0x00, 0x24, 0x25, 0x46, 0x00, 0x28, + 0x01, 0xDA, 0x01, 0x24, 0x40, 0x42, 0x00, 0x29, + 0x01, 0xDA, 0x01, 0x25, 0x49, 0x42, 0xFF, 0xF7, + 0xDD, 0xFF, 0xAC, 0x42, 0x00, 0xD0, 0x40, 0x42, + 0x00, 0x2C, 0x00, 0xD0, 0x49, 0x42, 0x70, 0xBD, + 0x03, 0x46, 0x0B, 0x43, 0x9B, 0x07, 0x03, 0xD0, + 0x09, 0xE0, 0x08, 0xC9, 0x12, 0x1F, 0x08, 0xC0, + 0x04, 0x2A, 0xFA, 0xD2, 0x03, 0xE0, 0x0B, 0x78, + 0x49, 0x1C, 0x03, 0x70, 0x40, 0x1C, 0x52, 0x1E, + 0xF9, 0xD2, 0x70, 0x47, 0xD2, 0xB2, 0x01, 0xE0, + 0x02, 0x70, 0x40, 0x1C, 0x49, 0x1E, 0xFB, 0xD2, + 0x70, 0x47, 0x00, 0x22, 0xF6, 0xE7, 0x10, 0xB5, + 0x04, 0x46, 0x08, 0x46, 0x11, 0x46, 0x02, 0x46, + 0x20, 0x46, 0xFF, 0xF7, 0xEF, 0xFF, 0x20, 0x46, + 0x10, 0xBD, 0x00, 0x1D, 0x03, 0x21, 0x40, 0x1E, + 0x03, 0x78, 0x12, 0x02, 0x1A, 0x43, 0x49, 0x1E, + 0xF9, 0xD5, 0x10, 0x46, 0x70, 0x47, 0x00, 0x00, + 0x06, 0x4C, 0x01, 0x25, 0x06, 0x4E, 0x05, 0xE0, + 0x20, 0x46, 0xE3, 0x68, 0x07, 0xC8, 0x2B, 0x43, + 0x98, 0x47, 0x10, 0x34, 0xB4, 0x42, 0xF7, 0xD3, + 0xFF, 0xF7, 0x72, 0xFF, 0x10, 0x3B, 0x00, 0x00, + 0x30, 0x3B, 0x00, 0x00, 0xC1, 0x06, 0xC9, 0x0E, + 0x01, 0x20, 0x88, 0x40, 0x01, 0x49, 0x08, 0x60, + 0x70, 0x47, 0x00, 0x00, 0x00, 0xE1, 0x00, 0xE0, + 0x0B, 0x49, 0x10, 0xB5, 0x88, 0x42, 0x01, 0xD9, + 0x01, 0x20, 0x10, 0xBD, 0x01, 0x02, 0x09, 0x0A, + 0x08, 0x48, 0x49, 0x1E, 0x41, 0x61, 0x08, 0x49, + 0x07, 0x23, 0xCA, 0x69, 0x12, 0x02, 0x12, 0x0A, + 0x04, 0x04, 0x22, 0x43, 0xCA, 0x61, 0x00, 0x21, + 0x81, 0x61, 0x03, 0x61, 0x08, 0x46, 0x10, 0xBD, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xE0, 0x00, 0xE0, + 0x04, 0xED, 0x00, 0xE0, 0x70, 0x47, 0x00, 0x00, + 0x03, 0x49, 0x02, 0x20, 0x08, 0x60, 0x02, 0x49, + 0x80, 0x39, 0x08, 0x60, 0x70, 0x47, 0x00, 0x00, + 0x80, 0xE1, 0x00, 0xE0, 0x62, 0xB6, 0x02, 0x48, + 0x00, 0x21, 0x01, 0x60, 0x70, 0x47, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x40, 0x30, 0xB4, 0x74, 0x46, + 0x64, 0x1E, 0x25, 0x78, 0x64, 0x1C, 0xAB, 0x42, + 0x00, 0xD2, 0x1D, 0x46, 0x63, 0x5D, 0x5B, 0x00, + 0xE3, 0x18, 0x30, 0xBC, 0x18, 0x47, 0x00, 0x00, + 0x05, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, + 0x01, 0x20, 0x80, 0x07, 0x01, 0x6A, 0x03, 0x22, + 0xD2, 0x03, 0x11, 0x43, 0x01, 0x62, 0x70, 0x47, + 0x34, 0x00, 0x00, 0x20, 0x02, 0xE0, 0x08, 0xC8, + 0x12, 0x1F, 0x08, 0xC1, 0x00, 0x2A, 0xFA, 0xD1, + 0x70, 0x47, 0x00, 0x20, 0x01, 0xE0, 0x01, 0xC1, + 0x12, 0x1F, 0x00, 0x2A, 0xFB, 0xD1, 0x70, 0x47, + 0x10, 0xB5, 0x00, 0xF0, 0x55, 0xF8, 0x10, 0xBD, + 0x01, 0x20, 0x80, 0x07, 0x41, 0x6A, 0x82, 0x13, + 0x11, 0x43, 0x41, 0x62, 0x41, 0x6A, 0xC2, 0x13, + 0x11, 0x43, 0x41, 0x62, 0x70, 0x47, 0x10, 0xB5, + 0x02, 0xF0, 0xD4, 0xF8, 0x10, 0xBD, 0x00, 0x00, + 0x03, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, + 0x02, 0x49, 0x01, 0x20, 0xC8, 0x60, 0x70, 0x47, + 0x5C, 0x01, 0x00, 0x20, 0x00, 0x03, 0x00, 0x50, + 0x10, 0xB5, 0x00, 0xF0, 0xE7, 0xFA, 0x10, 0xBD, + 0x70, 0x47, 0x00, 0x00, 0x04, 0x49, 0x06, 0x22, + 0x00, 0x28, 0x08, 0x68, 0x01, 0xD0, 0x10, 0x43, + 0x00, 0xE0, 0x90, 0x43, 0x08, 0x60, 0x70, 0x47, + 0x00, 0x09, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x47, 0x00, 0x00, 0x03, 0x48, 0x02, 0x49, + 0x41, 0x60, 0x03, 0x49, 0x81, 0x60, 0x70, 0x47, + 0x1F, 0x1F, 0x5F, 0x1F, 0x00, 0x10, 0x00, 0x50, + 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xB5, 0x07, 0x48, 0x01, 0x69, 0x02, 0x29, + 0x06, 0xD1, 0x01, 0x61, 0x05, 0x49, 0x8A, 0x78, + 0x00, 0x2A, 0x02, 0xD0, 0x02, 0xF0, 0x92, 0xFA, + 0x00, 0xBD, 0x01, 0x20, 0xC8, 0x70, 0x00, 0xBD, + 0x00, 0x02, 0x00, 0x50, 0xE0, 0x05, 0x00, 0x20, + 0x01, 0x21, 0x89, 0x07, 0x0A, 0x15, 0x00, 0x28, + 0x48, 0x69, 0x02, 0xD0, 0x10, 0x43, 0x48, 0x61, + 0x70, 0x47, 0x90, 0x43, 0xFB, 0xE7, 0x00, 0x00, + 0x30, 0xB5, 0x1E, 0x4B, 0x58, 0x68, 0x99, 0x68, + 0x00, 0x28, 0x01, 0xDA, 0xDA, 0x04, 0x5A, 0x60, + 0x1B, 0x4A, 0x15, 0x68, 0x01, 0x24, 0x05, 0x40, + 0x00, 0x2D, 0x02, 0xD1, 0x50, 0x68, 0x08, 0x42, + 0x0F, 0xD0, 0x18, 0x48, 0x04, 0x70, 0xFF, 0xF7, + 0xBD, 0xFF, 0x14, 0x72, 0x16, 0x48, 0x00, 0x78, + 0x00, 0x28, 0x06, 0xD0, 0x18, 0x68, 0x01, 0x21, + 0x00, 0x09, 0x00, 0x01, 0x89, 0x02, 0x08, 0x43, + 0x18, 0x60, 0x58, 0x68, 0x80, 0x02, 0x02, 0xD5, + 0x01, 0x20, 0x40, 0x05, 0x58, 0x60, 0x58, 0x68, + 0x00, 0x04, 0x03, 0xD5, 0x01, 0x20, 0xC0, 0x03, + 0x58, 0x60, 0x94, 0x72, 0x58, 0x68, 0x80, 0x00, + 0x03, 0xD5, 0x01, 0x20, 0x40, 0x07, 0x58, 0x60, + 0xD4, 0x72, 0x58, 0x68, 0x40, 0x00, 0x03, 0xD5, + 0x01, 0x20, 0x80, 0x07, 0x58, 0x60, 0x94, 0x72, + 0x30, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, + 0x20, 0x00, 0x00, 0x20, 0x39, 0x00, 0x00, 0x20, + 0x31, 0x00, 0x00, 0x20, 0x01, 0x28, 0x05, 0xD0, + 0x02, 0x28, 0x05, 0xD0, 0x04, 0x28, 0x06, 0xD0, + 0x00, 0x20, 0x70, 0x47, 0x03, 0x48, 0x70, 0x47, + 0x02, 0x48, 0xC0, 0x30, 0x70, 0x47, 0x02, 0x48, + 0x70, 0x47, 0x00, 0x00, 0x78, 0x7C, 0x00, 0x00, + 0xF8, 0x7D, 0x00, 0x00, 0x10, 0xB5, 0x00, 0xF0, + 0x67, 0xF8, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x47, 0x00, 0x00, 0xF8, 0xB5, 0x06, 0x46, + 0x0D, 0x46, 0x30, 0x23, 0xFF, 0xF7, 0xDE, 0xFF, + 0x04, 0x00, 0x28, 0xD0, 0x19, 0x4F, 0x00, 0x2A, + 0x21, 0xD0, 0x00, 0x20, 0xFF, 0xF7, 0x52, 0xFF, + 0x02, 0xE0, 0x98, 0x00, 0x29, 0x58, 0x21, 0x50, + 0x18, 0x46, 0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, + 0xF7, 0xD1, 0x38, 0x68, 0x01, 0x88, 0x31, 0x43, + 0x01, 0x80, 0x11, 0x49, 0x09, 0x68, 0xCA, 0x79, + 0x8B, 0x79, 0x11, 0x02, 0x19, 0x43, 0x41, 0x80, + 0xC1, 0x21, 0x89, 0x00, 0x0D, 0x48, 0x00, 0xF0, + 0x1D, 0xF8, 0x0D, 0x49, 0xC8, 0x63, 0x01, 0x20, + 0xFF, 0xF7, 0x34, 0xFF, 0x0C, 0xE0, 0x38, 0x68, + 0x00, 0x88, 0x30, 0x40, 0x03, 0xD1, 0xF8, 0xBD, + 0x98, 0x00, 0x21, 0x58, 0x29, 0x50, 0x18, 0x46, + 0x5B, 0x1E, 0xDB, 0xB2, 0x00, 0x28, 0xF7, 0xD1, + 0x01, 0x20, 0xF8, 0xBD, 0x08, 0x00, 0x00, 0x20, + 0x98, 0x01, 0x00, 0x20, 0x78, 0x7C, 0x00, 0x00, + 0x40, 0x7F, 0x00, 0x00, 0x70, 0xB5, 0x41, 0x18, + 0x49, 0x1E, 0x64, 0x24, 0x09, 0x04, 0x0B, 0x4D, + 0x01, 0x43, 0x69, 0x63, 0xE8, 0x68, 0x81, 0x21, + 0x09, 0x06, 0x08, 0x43, 0xE8, 0x60, 0x02, 0xE0, + 0x01, 0x20, 0xFF, 0xF7, 0xA3, 0xFF, 0xE8, 0x68, + 0xC0, 0x01, 0x04, 0xD5, 0x20, 0x46, 0x64, 0x1E, + 0xA4, 0xB2, 0x00, 0x28, 0xF4, 0xD1, 0xA8, 0x6B, + 0x70, 0xBD, 0x00, 0x00, 0x40, 0x09, 0x00, 0x50, + 0x03, 0x49, 0x0A, 0x68, 0x10, 0x18, 0x0A, 0x68, + 0x90, 0x42, 0xFC, 0xD1, 0x70, 0x47, 0x00, 0x00, + 0x5C, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x70, 0xB5, 0x01, 0x25, 0xAD, 0x07, 0xE8, 0x68, + 0x40, 0x08, 0x40, 0x00, 0xE8, 0x60, 0x19, 0x48, + 0x41, 0x68, 0x01, 0x26, 0x49, 0x07, 0x00, 0x29, + 0x25, 0xDA, 0x42, 0x68, 0x04, 0x21, 0x0A, 0x43, + 0x42, 0x60, 0x15, 0x48, 0x00, 0x68, 0x15, 0x4C, + 0x40, 0x05, 0x40, 0x0F, 0x20, 0x73, 0x20, 0x7B, + 0x06, 0x28, 0x00, 0xD1, 0x21, 0x73, 0x20, 0x7B, + 0x02, 0x28, 0x18, 0xD0, 0x20, 0x7B, 0x06, 0x28, + 0x05, 0xD8, 0x20, 0x7B, 0x04, 0x28, 0x02, 0xD3, + 0x01, 0x20, 0x01, 0xF0, 0x31, 0xF9, 0x20, 0x7B, + 0x0B, 0x49, 0x09, 0x78, 0x88, 0x42, 0x06, 0xD0, + 0x0A, 0x48, 0x01, 0x78, 0x31, 0x43, 0x01, 0x70, + 0x09, 0x49, 0x81, 0x20, 0x08, 0x70, 0xE8, 0x68, + 0x30, 0x43, 0xE8, 0x60, 0x70, 0xBD, 0x00, 0x20, + 0xEB, 0xE7, 0x00, 0x00, 0x40, 0x00, 0x00, 0x50, + 0x00, 0x11, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20, + 0x39, 0x01, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20, + 0x20, 0x08, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x46, + 0x06, 0x4C, 0x01, 0xF0, 0x7B, 0xFC, 0x00, 0x21, + 0x8A, 0x00, 0x49, 0x1C, 0xA0, 0x58, 0xC9, 0xB2, + 0xA8, 0x50, 0x18, 0x29, 0xF8, 0xD3, 0x01, 0x20, + 0x70, 0xBD, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, + 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, 0x82, 0x14, + 0x11, 0x43, 0x41, 0x60, 0x70, 0x47, 0x00, 0x00, + 0x10, 0xB5, 0x14, 0x48, 0x80, 0x69, 0x40, 0x04, + 0x23, 0xD5, 0x13, 0x4C, 0xE0, 0x68, 0xA2, 0x68, + 0x02, 0x40, 0xE0, 0x68, 0xA1, 0x68, 0x88, 0x43, + 0xE0, 0x60, 0x10, 0x07, 0x03, 0xD5, 0x08, 0x20, + 0xE0, 0x60, 0x02, 0xF0, 0x85, 0xFD, 0x50, 0x07, + 0x0E, 0xD5, 0x04, 0x20, 0xE0, 0x60, 0x02, 0xF0, + 0x7F, 0xFD, 0x00, 0xF0, 0xCB, 0xF8, 0x00, 0x28, + 0x06, 0xD1, 0x08, 0x48, 0x00, 0x78, 0x00, 0x28, + 0x02, 0xD0, 0x01, 0x20, 0xFF, 0xF7, 0x48, 0xFE, + 0xFF, 0x20, 0xF3, 0x30, 0xE0, 0x60, 0x01, 0x20, + 0xE0, 0x60, 0x10, 0xBD, 0x00, 0x09, 0x00, 0x50, + 0x00, 0x05, 0x00, 0x50, 0x90, 0x02, 0x00, 0x20, + 0x70, 0xB5, 0x0C, 0x49, 0x00, 0x20, 0x0C, 0x4B, + 0x89, 0x25, 0x0C, 0x68, 0xAD, 0x00, 0x21, 0x18, + 0xC9, 0x7E, 0x41, 0x29, 0x08, 0xD0, 0x42, 0x00, + 0x99, 0x5A, 0x49, 0x05, 0x49, 0x0D, 0x99, 0x52, + 0x4E, 0x05, 0x01, 0xD5, 0x49, 0x1B, 0x99, 0x52, + 0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, 0xEE, 0xD3, + 0x01, 0x20, 0x70, 0xBD, 0x98, 0x01, 0x00, 0x20, + 0xAC, 0x03, 0x00, 0x20, 0x01, 0x21, 0x89, 0x07, + 0x00, 0xB5, 0x8A, 0x14, 0x00, 0x28, 0x08, 0x68, + 0x04, 0xD0, 0x10, 0x43, 0x08, 0x60, 0xFF, 0xF7, + 0x9F, 0xFF, 0x00, 0xBD, 0x90, 0x43, 0x08, 0x60, + 0x00, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF8, 0xB5, 0x33, 0x48, 0x80, 0x69, 0x40, 0x04, + 0x61, 0xD5, 0x32, 0x4D, 0xA8, 0x6A, 0xE9, 0x68, + 0xAC, 0x68, 0x0C, 0x40, 0xE9, 0x68, 0xAA, 0x68, + 0x91, 0x43, 0xE9, 0x60, 0x2E, 0x4E, 0xC0, 0x07, + 0x15, 0xD0, 0x68, 0x69, 0x89, 0x27, 0xC0, 0xB2, + 0xEF, 0x60, 0xAA, 0x6A, 0x01, 0x21, 0x0A, 0x43, + 0xAA, 0x62, 0x0B, 0x28, 0x03, 0xD2, 0x31, 0x70, + 0x00, 0xF0, 0x92, 0xFD, 0x03, 0xE0, 0x00, 0x21, + 0x31, 0x70, 0x00, 0xF0, 0x69, 0xFD, 0xBC, 0x43, + 0x01, 0x20, 0xFF, 0xF7, 0xE5, 0xFD, 0x23, 0x48, + 0xA1, 0x04, 0x04, 0xD5, 0x29, 0x6A, 0x81, 0x43, + 0x29, 0x62, 0x41, 0x14, 0xE9, 0x60, 0xE1, 0x04, + 0x05, 0xD5, 0x29, 0x6A, 0x81, 0x43, 0x29, 0x62, + 0x01, 0x20, 0x00, 0x03, 0xE8, 0x60, 0xA0, 0x05, + 0x02, 0xD5, 0x01, 0x20, 0x40, 0x02, 0xE8, 0x60, + 0x20, 0x07, 0x09, 0xD5, 0x30, 0x78, 0x01, 0x28, + 0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, 0x48, 0xFD, + 0x64, 0x08, 0x64, 0x00, 0x09, 0x20, 0xE8, 0x60, + 0x60, 0x07, 0x17, 0xD5, 0x30, 0x78, 0x01, 0x28, + 0x02, 0xD1, 0x0A, 0x20, 0x00, 0xF0, 0x3C, 0xFD, + 0x05, 0x20, 0xE8, 0x60, 0xA8, 0x69, 0x0E, 0x49, + 0x88, 0x42, 0x08, 0xD0, 0xA9, 0x69, 0x0C, 0x48, + 0x40, 0x1C, 0x81, 0x42, 0x03, 0xD0, 0xA9, 0x69, + 0xC0, 0x1C, 0x81, 0x42, 0x02, 0xD1, 0x01, 0x20, + 0xFF, 0xF7, 0xAA, 0xFD, 0xFF, 0x20, 0xF3, 0x30, + 0xE8, 0x60, 0x01, 0x20, 0xE8, 0x60, 0xF8, 0xBD, + 0x00, 0x09, 0x00, 0x50, 0x00, 0x06, 0x00, 0x50, + 0xCC, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x40, + 0x88, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x02, 0x46, + 0x07, 0x49, 0x60, 0x32, 0x11, 0x80, 0x00, 0x21, + 0x51, 0x80, 0x4C, 0x00, 0x53, 0x88, 0x04, 0x5B, + 0x49, 0x1C, 0x1B, 0x19, 0xC9, 0xB2, 0x53, 0x80, + 0x31, 0x29, 0xF6, 0xD3, 0x10, 0xBD, 0x00, 0x00, + 0xAA, 0x55, 0x00, 0x00, 0x01, 0x20, 0x80, 0x07, + 0x40, 0x69, 0x40, 0x05, 0x01, 0xD5, 0x01, 0x20, + 0x70, 0x47, 0x00, 0x20, 0x70, 0x47, 0x00, 0x00, + 0x02, 0x48, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, + 0x70, 0x47, 0x00, 0x00, 0x1C, 0x08, 0x00, 0x20, + 0x01, 0x20, 0x01, 0x49, 0x08, 0x70, 0x70, 0x47, + 0xD3, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x20, 0x00, 0x07, 0x82, 0x69, 0x0C, 0x49, + 0x00, 0x2A, 0x07, 0xDA, 0x82, 0x69, 0xC3, 0x00, + 0x92, 0x00, 0x92, 0x08, 0xD2, 0x18, 0x82, 0x61, + 0x01, 0x22, 0x4A, 0x72, 0x82, 0x69, 0x52, 0x00, + 0x08, 0xD5, 0x82, 0x69, 0x01, 0x23, 0x92, 0x00, + 0x92, 0x08, 0x9B, 0x07, 0xD2, 0x18, 0x82, 0x61, + 0x00, 0x20, 0x48, 0x72, 0x70, 0x47, 0x00, 0x00, + 0x20, 0x08, 0x00, 0x20, 0x70, 0xB5, 0x1D, 0x4D, + 0x68, 0x78, 0x00, 0x06, 0x2A, 0xD5, 0x05, 0x20, + 0xFF, 0xF7, 0x04, 0xFE, 0x01, 0x20, 0xFF, 0xF7, + 0x43, 0xFD, 0x68, 0x78, 0x40, 0x06, 0x40, 0x0E, + 0x68, 0x70, 0x68, 0x78, 0x16, 0x4C, 0x10, 0x38, + 0x03, 0x00, 0xFF, 0xF7, 0x0F, 0xFD, 0x05, 0x04, + 0x06, 0x0C, 0x1C, 0x1E, 0x0E, 0x00, 0x06, 0x20, + 0x06, 0xE0, 0x01, 0x20, 0x20, 0x70, 0x11, 0xA0, + 0x02, 0xF0, 0xDF, 0xFB, 0x01, 0xE0, 0x02, 0x20, + 0x20, 0x70, 0x68, 0x78, 0x80, 0x21, 0x08, 0x43, + 0x28, 0x71, 0x00, 0x0A, 0x68, 0x71, 0x00, 0x20, + 0xFF, 0xF7, 0x22, 0xFD, 0xA9, 0x88, 0x0C, 0xA0, + 0x02, 0xF0, 0xCF, 0xFB, 0x70, 0xBD, 0x03, 0x20, + 0xEE, 0xE7, 0x0D, 0x48, 0x69, 0x78, 0x82, 0x88, + 0x0C, 0xA0, 0x02, 0xF0, 0xC6, 0xFB, 0x05, 0x20, + 0xE6, 0xE7, 0x00, 0x00, 0x88, 0x02, 0x00, 0x20, + 0xCE, 0x00, 0x00, 0x20, 0x43, 0x4D, 0x44, 0x5F, + 0x49, 0x4E, 0x49, 0x54, 0x0D, 0x0A, 0x00, 0x00, + 0x43, 0x4D, 0x44, 0x20, 0x45, 0x78, 0x69, 0x74, + 0x5B, 0x25, 0x78, 0x5D, 0x3D, 0x0D, 0x0A, 0x00, + 0x74, 0x06, 0x00, 0x20, 0x43, 0x4D, 0x44, 0x3D, + 0x30, 0x78, 0x25, 0x78, 0x3A, 0x25, 0x78, 0x0D, + 0x0A, 0x00, 0x00, 0x00, 0xF8, 0xB5, 0x1A, 0x4C, + 0x01, 0x20, 0x20, 0x70, 0x19, 0x4E, 0x00, 0x20, + 0x30, 0x71, 0x70, 0x71, 0x18, 0x48, 0xB0, 0x70, + 0x00, 0x0A, 0xF0, 0x70, 0x00, 0x20, 0xFF, 0xF7, + 0xC9, 0xFE, 0x16, 0x4F, 0x04, 0x25, 0x20, 0x78, + 0x03, 0x00, 0xFF, 0xF7, 0xB7, 0xFC, 0x07, 0x14, + 0x05, 0x12, 0x12, 0x16, 0x19, 0x1F, 0x14, 0x00, + 0x37, 0x71, 0x38, 0x0A, 0x70, 0x71, 0x25, 0x70, + 0x70, 0x20, 0x30, 0x70, 0x0E, 0x48, 0x30, 0x71, + 0x00, 0x0A, 0x70, 0x71, 0x00, 0xF0, 0x92, 0xFB, + 0xE9, 0xE7, 0x00, 0xF0, 0x8F, 0xFB, 0x25, 0x70, + 0xE5, 0xE7, 0xFF, 0xF7, 0x7B, 0xFF, 0xE2, 0xE7, + 0x08, 0xA0, 0x02, 0xF0, 0x76, 0xFB, 0x00, 0xF0, + 0x11, 0xF8, 0xF2, 0xE7, 0xF8, 0xBD, 0x00, 0x00, + 0xCE, 0x00, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20, + 0xCC, 0x55, 0x00, 0x00, 0x55, 0xAA, 0x00, 0x00, + 0x11, 0x5A, 0x00, 0x00, 0x5B, 0x53, 0x54, 0x5D, + 0x0D, 0x0A, 0x00, 0x00, 0x70, 0xB5, 0x1F, 0x48, + 0x00, 0x25, 0x84, 0x88, 0x01, 0x21, 0x00, 0x2C, + 0x1A, 0xD0, 0x0B, 0x00, 0xFF, 0xF7, 0x7E, 0xFC, + 0x07, 0x16, 0x05, 0x13, 0x1A, 0x16, 0x0F, 0x1F, + 0x16, 0x00, 0x00, 0xF0, 0x55, 0xF8, 0x00, 0xF0, + 0xC1, 0xF9, 0x01, 0x28, 0x01, 0xD1, 0x05, 0x21, + 0x08, 0xE0, 0x06, 0x21, 0x06, 0xE0, 0x02, 0xF0, + 0x99, 0xF8, 0x02, 0x21, 0x02, 0xE0, 0x00, 0xF0, + 0x9F, 0xFE, 0x03, 0x21, 0x00, 0x2C, 0xE4, 0xD1, + 0x28, 0x46, 0x70, 0xBD, 0x01, 0xF0, 0xF6, 0xFE, + 0x00, 0xF0, 0x5E, 0xFE, 0xED, 0xE7, 0x0C, 0xA0, + 0x02, 0xF0, 0x37, 0xFB, 0x0E, 0x48, 0xFF, 0xF7, + 0xE9, 0xFE, 0x0E, 0x48, 0xFF, 0xF7, 0xE6, 0xFE, + 0x0D, 0x48, 0xFF, 0xF7, 0xE3, 0xFE, 0x0D, 0x48, + 0xFF, 0xF7, 0xE0, 0xFE, 0x00, 0xF0, 0x8A, 0xFA, + 0x0B, 0x48, 0x01, 0x68, 0x0B, 0xA0, 0x02, 0xF0, + 0x24, 0xFB, 0xE1, 0xE7, 0x74, 0x06, 0x00, 0x20, + 0x54, 0x45, 0x53, 0x54, 0x5F, 0x43, 0x4D, 0x44, + 0x5F, 0x45, 0x58, 0x49, 0x54, 0x0D, 0x0A, 0x00, + 0xE4, 0x02, 0x00, 0x20, 0x48, 0x03, 0x00, 0x20, + 0xAC, 0x03, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20, + 0x0C, 0x05, 0x00, 0x20, 0x67, 0x5F, 0x75, 0x33, + 0x32, 0x5F, 0x77, 0x65, 0x61, 0x72, 0x61, 0x62, + 0x6C, 0x65, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x5F, + 0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x3A, 0x20, + 0x30, 0x78, 0x25, 0x78, 0x0D, 0x0A, 0x00, 0x00, + 0x10, 0xB5, 0xC8, 0x21, 0x0E, 0x48, 0xFF, 0xF7, + 0xB8, 0xFB, 0xC8, 0x21, 0x0D, 0x48, 0xFF, 0xF7, + 0xB4, 0xFB, 0xC8, 0x21, 0x0C, 0x48, 0xFF, 0xF7, + 0xB0, 0xFB, 0xC8, 0x21, 0x0B, 0x48, 0xFF, 0xF7, + 0xAC, 0xFB, 0x64, 0x21, 0x0A, 0x48, 0xFF, 0xF7, + 0xA8, 0xFB, 0x32, 0x21, 0x09, 0x48, 0xFF, 0xF7, + 0xA4, 0xFB, 0x09, 0x49, 0x00, 0x20, 0x08, 0x60, + 0x08, 0xA0, 0x02, 0xF0, 0xDE, 0xFA, 0x10, 0xBD, + 0xE4, 0x02, 0x00, 0x20, 0x48, 0x03, 0x00, 0x20, + 0xAC, 0x03, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20, + 0x74, 0x04, 0x00, 0x20, 0xD8, 0x04, 0x00, 0x20, + 0x0C, 0x05, 0x00, 0x20, 0x46, 0x54, 0x20, 0x52, + 0x65, 0x73, 0x75, 0x6C, 0x74, 0x20, 0x49, 0x6E, + 0x69, 0x74, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0xF3, 0xB5, 0xC1, 0xB0, 0x42, 0x98, 0x02, 0x28, + 0x01, 0xD2, 0x02, 0x20, 0x42, 0x90, 0xC0, 0x21, + 0x68, 0x46, 0xFF, 0xF7, 0x7A, 0xFB, 0x01, 0xF0, + 0xA3, 0xFB, 0x01, 0xF0, 0xD3, 0xFD, 0x01, 0xF0, + 0x7B, 0xF8, 0x00, 0x24, 0x6E, 0x46, 0x1A, 0xE0, + 0x01, 0xF0, 0xE4, 0xFC, 0x01, 0xF0, 0xCA, 0xFD, + 0x01, 0xF0, 0x72, 0xF8, 0x18, 0x49, 0x00, 0x20, + 0x0A, 0x68, 0x18, 0x49, 0x0F, 0x68, 0x39, 0x18, + 0xC9, 0x7E, 0x41, 0x29, 0x05, 0xD0, 0x81, 0x00, + 0x45, 0x00, 0x73, 0x58, 0x55, 0x5F, 0x5B, 0x19, + 0x73, 0x50, 0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, + 0xF1, 0xD3, 0x64, 0x1C, 0xE4, 0xB2, 0x42, 0x98, + 0x84, 0x42, 0xE1, 0xD3, 0x01, 0xF0, 0xAE, 0xFD, + 0x01, 0xF0, 0xDC, 0xFC, 0x0B, 0x4D, 0x00, 0x24, + 0x28, 0x68, 0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, + 0x07, 0xD0, 0xA0, 0x00, 0x30, 0x58, 0x42, 0x99, + 0xFF, 0xF7, 0x16, 0xFB, 0x41, 0x99, 0x62, 0x00, + 0x88, 0x52, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C, + 0xEE, 0xD3, 0x01, 0x20, 0x43, 0xB0, 0xF0, 0xBD, + 0x4C, 0x01, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, + 0x70, 0xB5, 0x24, 0x48, 0xFF, 0x24, 0x41, 0x6B, + 0x2D, 0x34, 0x03, 0x22, 0x12, 0x05, 0x91, 0x43, + 0x01, 0x22, 0x12, 0x05, 0x89, 0x18, 0x41, 0x63, + 0x1F, 0x4D, 0x20, 0x48, 0xA9, 0x7E, 0xFF, 0xF7, + 0x9F, 0xFF, 0xE8, 0x7E, 0x29, 0x7E, 0x41, 0x43, + 0x89, 0x04, 0x28, 0x7F, 0x09, 0x0E, 0x02, 0x04, + 0x09, 0x02, 0x0A, 0x43, 0x17, 0x49, 0x0C, 0x32, + 0x40, 0x39, 0x4A, 0x60, 0x0C, 0x38, 0xC0, 0xB2, + 0xC9, 0x14, 0x40, 0x18, 0x13, 0x49, 0x40, 0x31, + 0x88, 0x60, 0xA9, 0x7E, 0x14, 0x48, 0xFF, 0xF7, + 0x87, 0xFF, 0x14, 0x49, 0x12, 0x4B, 0x0A, 0x68, + 0x00, 0x20, 0x11, 0x18, 0xC9, 0x7E, 0x41, 0x29, + 0x03, 0xD0, 0x41, 0x00, 0x5D, 0x5A, 0x2D, 0x1B, + 0x5D, 0x52, 0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, + 0xF3, 0xD3, 0x0D, 0xA0, 0x02, 0xF0, 0x39, 0xFA, + 0x01, 0x21, 0x08, 0x48, 0x01, 0xF0, 0x1C, 0xFE, + 0x0C, 0xA0, 0x02, 0xF0, 0x32, 0xFA, 0x01, 0x21, + 0x05, 0x48, 0x01, 0xF0, 0x15, 0xFE, 0x01, 0x20, + 0x70, 0xBD, 0x00, 0x00, 0x80, 0x10, 0x00, 0x50, + 0xA8, 0x06, 0x00, 0x20, 0x74, 0x04, 0x00, 0x20, + 0x48, 0x03, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, + 0x42, 0x61, 0x73, 0x65, 0x6C, 0x69, 0x6E, 0x65, + 0x0D, 0x0A, 0x00, 0x00, 0x51, 0x75, 0x69, 0x63, + 0x6B, 0x28, 0x30, 0x78, 0x31, 0x32, 0x43, 0x29, + 0x0D, 0x0A, 0x00, 0x00, 0xF8, 0xB5, 0x3C, 0x48, + 0x00, 0x24, 0x60, 0x21, 0x00, 0x68, 0xFF, 0xF7, + 0xCC, 0xFA, 0x3A, 0x4E, 0x3A, 0x48, 0x00, 0x68, + 0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x5A, 0xD0, + 0xA5, 0x07, 0xAD, 0x0F, 0xA7, 0x08, 0x0C, 0x21, + 0x20, 0x46, 0xFF, 0xF7, 0x7B, 0xFA, 0x88, 0x08, + 0x00, 0x21, 0xC9, 0x43, 0x71, 0x62, 0x09, 0x0C, + 0xB1, 0x62, 0x20, 0x2C, 0x05, 0xD2, 0x72, 0x6A, + 0x01, 0x21, 0xA1, 0x40, 0x8A, 0x43, 0x72, 0x62, + 0x06, 0xE0, 0xB3, 0x6A, 0x21, 0x46, 0x20, 0x39, + 0x01, 0x22, 0x8A, 0x40, 0x93, 0x43, 0xB3, 0x62, + 0x03, 0x2D, 0x02, 0xD2, 0x69, 0x1C, 0xC9, 0xB2, + 0x00, 0xE0, 0x08, 0x21, 0xF2, 0x6A, 0x27, 0x4B, + 0x1A, 0x40, 0x01, 0x23, 0xBB, 0x40, 0x1B, 0x05, + 0x1B, 0x09, 0x1A, 0x43, 0xF2, 0x62, 0x72, 0x6B, + 0x0E, 0x23, 0x9A, 0x43, 0x01, 0x23, 0x83, 0x40, + 0x5B, 0x07, 0x1B, 0x0F, 0x1A, 0x43, 0x72, 0x63, + 0x40, 0x1C, 0x82, 0x07, 0x92, 0x0F, 0x90, 0x05, + 0x13, 0x05, 0x18, 0x43, 0x93, 0x04, 0x18, 0x43, + 0x12, 0x04, 0x18, 0x4F, 0x10, 0x43, 0x40, 0x37, + 0xFA, 0x6A, 0xFF, 0x23, 0x1B, 0x04, 0x9A, 0x43, + 0x10, 0x43, 0xF8, 0x62, 0xF0, 0x6B, 0x09, 0x07, + 0xF4, 0x22, 0x09, 0x0E, 0x90, 0x43, 0x09, 0x1D, + 0x08, 0x43, 0xF0, 0x63, 0xB0, 0x6B, 0x74, 0x21, + 0x88, 0x43, 0x6D, 0x1C, 0x69, 0x07, 0x49, 0x0E, + 0x09, 0x1D, 0x08, 0x43, 0xB0, 0x63, 0x01, 0xF0, + 0xE1, 0xFB, 0x01, 0xF0, 0xC7, 0xFC, 0x64, 0x1C, + 0xE4, 0xB2, 0x30, 0x2C, 0x9A, 0xD3, 0x06, 0x48, + 0x60, 0x22, 0x01, 0x68, 0x08, 0x48, 0xFF, 0xF7, + 0x47, 0xFA, 0x01, 0x21, 0x06, 0x48, 0x01, 0xF0, + 0x83, 0xFD, 0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, + 0x4C, 0x01, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, + 0x98, 0x01, 0x00, 0x20, 0xFF, 0xFF, 0x00, 0xF0, + 0xE4, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x00, 0xF0, + 0xC1, 0xF8, 0x0E, 0x48, 0x00, 0x68, 0x0E, 0x4C, + 0x40, 0x05, 0x40, 0x0F, 0x20, 0x70, 0x00, 0x20, + 0x01, 0xF0, 0xA2, 0xFF, 0x0B, 0x49, 0x01, 0x20, + 0x08, 0x70, 0x08, 0x20, 0x20, 0x70, 0x0A, 0x49, + 0x06, 0x20, 0x08, 0x70, 0x01, 0x20, 0x09, 0x49, + 0x80, 0x02, 0x08, 0x80, 0x01, 0xF0, 0xF2, 0xFE, + 0x01, 0xF0, 0x90, 0xFC, 0x01, 0xF0, 0xBE, 0xFB, + 0x01, 0x20, 0x10, 0xBD, 0x00, 0x11, 0x00, 0x50, + 0x39, 0x01, 0x00, 0x20, 0x3B, 0x01, 0x00, 0x20, + 0x0D, 0x08, 0x00, 0x20, 0x46, 0x01, 0x00, 0x20, + 0xF8, 0xB5, 0x36, 0x48, 0x00, 0x24, 0x36, 0x4D, + 0x44, 0x80, 0x2C, 0x60, 0x6C, 0x60, 0x35, 0xA0, + 0x02, 0xF0, 0x5B, 0xF9, 0x32, 0x48, 0x61, 0x1E, + 0x80, 0x38, 0xC1, 0x61, 0x09, 0x0C, 0x01, 0x62, + 0x35, 0x49, 0x41, 0x62, 0x81, 0x62, 0xC1, 0x62, + 0x01, 0x06, 0x0A, 0x68, 0x0B, 0x13, 0x9A, 0x43, + 0x0A, 0x60, 0x32, 0x49, 0x01, 0x26, 0x8E, 0x61, + 0xCE, 0x61, 0x30, 0x4A, 0x40, 0x3A, 0xD4, 0x63, + 0x0C, 0x60, 0x8C, 0x60, 0x2E, 0x4B, 0x1F, 0x7F, + 0xDB, 0x7E, 0x3A, 0x04, 0x1B, 0x02, 0x1A, 0x43, + 0x0C, 0x32, 0xBC, 0x46, 0x42, 0x60, 0x82, 0x60, + 0x1F, 0x22, 0x02, 0x61, 0x44, 0x61, 0x0C, 0x62, + 0x1F, 0x48, 0x28, 0x4A, 0x40, 0x30, 0x82, 0x61, + 0x03, 0x22, 0xC2, 0x61, 0x1C, 0x4A, 0xC0, 0x3A, + 0xD4, 0x60, 0x03, 0x23, 0x1B, 0x02, 0x13, 0x61, + 0x14, 0x23, 0x83, 0x62, 0x34, 0x23, 0xC3, 0x63, + 0x17, 0x4B, 0x54, 0x27, 0x80, 0x33, 0x1F, 0x61, + 0x5C, 0x62, 0xD0, 0x27, 0xC7, 0x62, 0xF0, 0x27, + 0x1F, 0x60, 0xFF, 0x27, 0x11, 0x37, 0x5F, 0x61, + 0x9C, 0x62, 0x04, 0x62, 0x1A, 0x4B, 0x43, 0x62, + 0x1A, 0x48, 0x88, 0x62, 0x0E, 0x49, 0x1A, 0x48, + 0x40, 0x39, 0x48, 0x63, 0x67, 0x46, 0x0C, 0x3F, + 0xF8, 0xB2, 0xC9, 0x14, 0x40, 0x18, 0xA8, 0x60, + 0x17, 0x48, 0x16, 0x49, 0x01, 0x60, 0x44, 0x60, + 0xD1, 0x68, 0x16, 0x48, 0x01, 0x60, 0x11, 0x69, + 0x41, 0x60, 0x15, 0x48, 0x06, 0x70, 0x10, 0x15, + 0x10, 0x60, 0x14, 0xA0, 0x02, 0xF0, 0xF9, 0xF8, + 0xF8, 0xBD, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x20, + 0xC0, 0x10, 0x00, 0x50, 0x4F, 0x70, 0x65, 0x6E, + 0x20, 0x49, 0x6E, 0x69, 0x74, 0x20, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x0D, 0x0A, 0x00, 0x00, 0x00, + 0x55, 0x55, 0x55, 0x55, 0xC0, 0x11, 0x00, 0x50, + 0xA8, 0x06, 0x00, 0x20, 0x03, 0x00, 0x03, 0x00, + 0x00, 0x3F, 0x3F, 0x3F, 0x03, 0x30, 0xBC, 0x00, + 0x00, 0x26, 0x30, 0x00, 0x21, 0x20, 0x00, 0x00, + 0x00, 0x19, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20, + 0x31, 0x00, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, + 0x20, 0x49, 0x6E, 0x69, 0x74, 0x20, 0x45, 0x6E, + 0x64, 0x0D, 0x0A, 0x00, 0x10, 0xB5, 0x90, 0xB0, + 0x40, 0x21, 0x68, 0x46, 0xFF, 0xF7, 0x85, 0xF9, + 0x41, 0x22, 0x40, 0x21, 0x68, 0x46, 0xFF, 0xF7, + 0x79, 0xF9, 0x0E, 0x49, 0x00, 0x20, 0x0B, 0x68, + 0x6A, 0x46, 0x19, 0x18, 0xC9, 0x7E, 0x41, 0x29, + 0x00, 0xD0, 0x50, 0x54, 0x40, 0x1C, 0xC0, 0xB2, + 0x30, 0x28, 0xF6, 0xD3, 0x08, 0x49, 0x00, 0x20, + 0x13, 0x5C, 0x44, 0x00, 0x40, 0x1C, 0xC0, 0xB2, + 0x0B, 0x53, 0x40, 0x28, 0xF8, 0xD3, 0x00, 0x21, + 0x03, 0x48, 0x01, 0xF0, 0x8D, 0xFC, 0x01, 0x20, + 0x10, 0xB0, 0x10, 0xBD, 0x98, 0x01, 0x00, 0x20, + 0x9C, 0x01, 0x00, 0x20, 0x0E, 0x49, 0x10, 0xB5, + 0x5A, 0x20, 0x08, 0x74, 0x00, 0x20, 0x0A, 0x46, + 0x48, 0x74, 0x20, 0x3A, 0x14, 0x5C, 0x4B, 0x7C, + 0x40, 0x1C, 0x1B, 0x19, 0xC0, 0xB2, 0x4B, 0x74, + 0x31, 0x28, 0xF7, 0xD3, 0x07, 0x49, 0x00, 0x20, + 0x13, 0x5C, 0x44, 0x00, 0x40, 0x1C, 0xC0, 0xB2, + 0x0B, 0x53, 0x40, 0x28, 0xF8, 0xD3, 0x01, 0x21, + 0x02, 0x48, 0x01, 0xF0, 0x69, 0xFC, 0x10, 0xBD, + 0xF8, 0x04, 0x00, 0x20, 0x9C, 0x01, 0x00, 0x20, + 0x70, 0xB5, 0x2E, 0xA0, 0x02, 0xF0, 0x79, 0xF8, + 0x32, 0x49, 0x31, 0x48, 0x08, 0x60, 0x33, 0x4B, + 0x31, 0x48, 0x58, 0x60, 0x31, 0x4C, 0x32, 0x48, + 0x80, 0x34, 0xA0, 0x60, 0x31, 0x48, 0x40, 0x78, + 0x41, 0x1E, 0xC9, 0x05, 0xC9, 0x0D, 0x19, 0x61, + 0x2F, 0x4A, 0x00, 0x21, 0x11, 0x62, 0x2B, 0x4A, + 0x2E, 0x4D, 0x40, 0x32, 0x55, 0x63, 0x05, 0x02, + 0x2D, 0x48, 0x6D, 0x1C, 0x05, 0x60, 0x41, 0x60, + 0x09, 0x25, 0x26, 0x48, 0x2D, 0x05, 0x40, 0x38, + 0x05, 0x63, 0x41, 0x63, 0x81, 0x63, 0xD9, 0x63, + 0x11, 0x60, 0xA1, 0x62, 0x27, 0x4B, 0x19, 0x70, + 0x04, 0x23, 0x03, 0x60, 0x83, 0x02, 0xC3, 0x60, + 0x01, 0x61, 0xC3, 0x68, 0x24, 0x49, 0x0B, 0x60, + 0x03, 0x69, 0x4B, 0x60, 0x03, 0x21, 0x23, 0x4B, + 0x09, 0x06, 0x19, 0x60, 0x01, 0x21, 0x49, 0x02, + 0x11, 0x62, 0x81, 0x04, 0xD1, 0x62, 0x81, 0x69, + 0x03, 0x23, 0x9B, 0x03, 0x19, 0x43, 0x81, 0x61, + 0xC1, 0x69, 0x1D, 0x4B, 0x19, 0x43, 0xC1, 0x61, + 0xD0, 0x21, 0x11, 0x63, 0xC1, 0x6A, 0x10, 0x22, + 0x11, 0x43, 0xC1, 0x62, 0x55, 0x20, 0xE0, 0x62, + 0x60, 0x21, 0x18, 0x48, 0xFF, 0xF7, 0xE9, 0xF8, + 0x16, 0x48, 0x60, 0x21, 0x60, 0x30, 0xFF, 0xF7, + 0xE4, 0xF8, 0x15, 0xA0, 0x02, 0xF0, 0x21, 0xF8, + 0x70, 0xBD, 0x00, 0x00, 0x53, 0x68, 0x6F, 0x72, + 0x74, 0x20, 0x49, 0x6E, 0x69, 0x74, 0x20, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x0D, 0x0A, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x20, 0x4C, 0x01, 0x00, 0x20, + 0x0C, 0x0D, 0xF2, 0x00, 0x40, 0x10, 0x00, 0x50, + 0xE6, 0x0A, 0x00, 0x00, 0xC8, 0x06, 0x00, 0x20, + 0xC0, 0x11, 0x00, 0x50, 0x00, 0x36, 0x30, 0x04, + 0x00, 0x19, 0x00, 0x50, 0x31, 0x00, 0x00, 0x20, + 0x20, 0x00, 0x00, 0x20, 0x00, 0x07, 0x00, 0x50, + 0x00, 0xC0, 0x00, 0xC0, 0x00, 0x20, 0x00, 0x50, + 0x53, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x49, 0x6E, + 0x69, 0x74, 0x20, 0x45, 0x6E, 0x64, 0x0D, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x10, 0xB5, 0x1C, 0x48, + 0x64, 0x24, 0x41, 0x79, 0x02, 0x79, 0x09, 0x02, + 0x11, 0x43, 0x80, 0x22, 0x91, 0x43, 0x01, 0x71, + 0x09, 0x0A, 0x41, 0x71, 0x41, 0x79, 0x02, 0x79, + 0x09, 0x02, 0x11, 0x43, 0x2D, 0x22, 0x52, 0x02, + 0x11, 0x43, 0x01, 0x71, 0x09, 0x0A, 0x41, 0x71, + 0x81, 0x88, 0x12, 0xA0, 0x01, 0xF0, 0xD5, 0xFF, + 0x06, 0xE0, 0x21, 0x46, 0x16, 0xA0, 0x01, 0xF0, + 0xD0, 0xFF, 0x01, 0x20, 0xFF, 0xF7, 0xDA, 0xF9, + 0xFF, 0xF7, 0x94, 0xFB, 0x00, 0x28, 0x04, 0xD1, + 0x20, 0x46, 0x64, 0x1E, 0x24, 0xB2, 0x00, 0x28, + 0xEF, 0xD1, 0x64, 0x1C, 0x08, 0xD1, 0x01, 0x20, + 0xFF, 0xF7, 0x0E, 0xF9, 0x10, 0xA0, 0x01, 0xF0, + 0xBC, 0xFF, 0x0A, 0x20, 0xFF, 0xF7, 0xC6, 0xF9, + 0x00, 0x20, 0xFF, 0xF7, 0x05, 0xF9, 0x10, 0xBD, + 0x88, 0x02, 0x00, 0x20, 0x46, 0x54, 0x20, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x20, 0x28, 0x30, 0x78, + 0x25, 0x34, 0x78, 0x29, 0x0D, 0x0A, 0x00, 0x00, + 0x49, 0x4E, 0x54, 0x20, 0x4C, 0x6F, 0x77, 0x21, + 0x21, 0x28, 0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, + 0x49, 0x4E, 0x54, 0x20, 0x70, 0x61, 0x64, 0x20, + 0x69, 0x73, 0x20, 0x6C, 0x6F, 0x77, 0x20, 0x6C, + 0x65, 0x76, 0x65, 0x6C, 0x20, 0x21, 0x21, 0x00, + 0x70, 0xB5, 0x17, 0x48, 0x5A, 0x25, 0x01, 0x78, + 0x16, 0x48, 0x00, 0x29, 0x25, 0xD0, 0x09, 0x21, + 0x01, 0x70, 0x15, 0x4C, 0x01, 0x21, 0xE0, 0x89, + 0x89, 0x02, 0x88, 0x42, 0x01, 0xD0, 0x03, 0x20, + 0x60, 0x71, 0x11, 0x48, 0x30, 0x21, 0x28, 0x30, + 0xFF, 0xF7, 0x3B, 0xF8, 0x00, 0x20, 0x60, 0x72, + 0xA0, 0x71, 0x60, 0x62, 0x16, 0x21, 0x21, 0x72, + 0x0C, 0x49, 0x20, 0x71, 0x08, 0x70, 0xA5, 0x81, + 0x60, 0x81, 0x60, 0x8A, 0x09, 0x21, 0x09, 0x03, + 0x08, 0x43, 0x60, 0x82, 0x03, 0x20, 0x40, 0x02, + 0xE0, 0x61, 0x0D, 0x20, 0xC0, 0x01, 0x20, 0x62, + 0x70, 0xBD, 0x05, 0x70, 0xD9, 0xE7, 0x00, 0x00, + 0x92, 0x01, 0x00, 0x20, 0x94, 0x01, 0x00, 0x20, + 0x38, 0x01, 0x00, 0x20, 0x93, 0x01, 0x00, 0x20, + 0x0E, 0x48, 0x03, 0x21, 0x41, 0x71, 0x0E, 0x49, + 0x41, 0x61, 0x0D, 0x49, 0x60, 0x31, 0x81, 0x61, + 0x01, 0x21, 0x01, 0x70, 0x07, 0x22, 0x42, 0x70, + 0x0A, 0x4B, 0x05, 0x22, 0x1A, 0x70, 0x0A, 0x4B, + 0x1A, 0x70, 0x0A, 0x4B, 0x55, 0x22, 0xDA, 0x70, + 0x04, 0x22, 0x02, 0x82, 0x00, 0x22, 0xC2, 0x70, + 0x09, 0x22, 0x12, 0x03, 0x42, 0x82, 0x81, 0x70, + 0x70, 0x47, 0x00, 0x00, 0x38, 0x01, 0x00, 0x20, + 0x00, 0x00, 0x04, 0x20, 0x92, 0x01, 0x00, 0x20, + 0x0D, 0x08, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, + 0x04, 0x22, 0x0F, 0x49, 0x0C, 0x28, 0x10, 0xD0, + 0x8B, 0x05, 0x0D, 0x28, 0x08, 0x6A, 0x10, 0xD0, + 0x18, 0x43, 0x08, 0x62, 0x88, 0x6A, 0x10, 0x43, + 0x88, 0x62, 0x0A, 0x4A, 0x01, 0x20, 0x10, 0x70, + 0xC8, 0x68, 0xC8, 0x60, 0x88, 0x6A, 0x88, 0x62, + 0x70, 0x47, 0x08, 0x6A, 0x40, 0x00, 0x40, 0x08, + 0x00, 0xE0, 0x18, 0x43, 0x08, 0x62, 0x88, 0x6A, + 0x90, 0x43, 0x88, 0x62, 0xF0, 0xE7, 0x00, 0x00, + 0x00, 0x06, 0x00, 0x50, 0xCC, 0x00, 0x00, 0x20, + 0x00, 0xB5, 0x08, 0x49, 0x0A, 0x28, 0x05, 0xD0, + 0x07, 0x48, 0x00, 0x0C, 0x48, 0x63, 0x07, 0x48, + 0x08, 0x63, 0x00, 0xBD, 0x06, 0x48, 0x00, 0x68, + 0x08, 0x62, 0x0D, 0x20, 0xFF, 0xF7, 0xCC, 0xFF, + 0x00, 0xBD, 0x00, 0x00, 0x00, 0x06, 0x00, 0x50, + 0xBC, 0x02, 0x00, 0x20, 0xCC, 0x02, 0x00, 0x20, + 0xC0, 0x02, 0x00, 0x20, 0x10, 0xB5, 0x1B, 0x49, + 0x00, 0x20, 0x03, 0x00, 0xFF, 0xF7, 0x12, 0xF8, + 0x0C, 0x07, 0x0A, 0x0E, 0x26, 0x26, 0x11, 0x14, + 0x17, 0x1A, 0x1D, 0x20, 0x23, 0x26, 0x16, 0x4A, + 0x0A, 0x80, 0x1E, 0xE0, 0x14, 0x4A, 0x12, 0x1D, + 0x4A, 0x80, 0x1A, 0xE0, 0x13, 0x4A, 0x8A, 0x80, + 0x17, 0xE0, 0x13, 0x4A, 0x4A, 0x81, 0x14, 0xE0, + 0x12, 0x4A, 0x8A, 0x81, 0x11, 0xE0, 0x12, 0x4A, + 0xCA, 0x81, 0x0E, 0xE0, 0x11, 0x4A, 0x0A, 0x82, + 0x0B, 0xE0, 0x11, 0x4A, 0x4A, 0x82, 0x08, 0xE0, + 0x10, 0x4A, 0x8A, 0x82, 0x05, 0xE0, 0x10, 0x4A, + 0xCA, 0x82, 0x02, 0xE0, 0x0F, 0x4A, 0x43, 0x00, + 0xCA, 0x52, 0x40, 0x1C, 0x80, 0xB2, 0x0C, 0x28, + 0xCF, 0xD3, 0x0D, 0xA0, 0x01, 0xF0, 0xBD, 0xFE, + 0x10, 0xBD, 0x00, 0x00, 0xCC, 0x02, 0x00, 0x20, + 0x90, 0x02, 0x00, 0x20, 0x88, 0x02, 0x00, 0x20, + 0x34, 0x01, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20, + 0xE4, 0x06, 0x00, 0x20, 0xD8, 0x06, 0x00, 0x20, + 0xC0, 0x02, 0x00, 0x20, 0xBC, 0x02, 0x00, 0x20, + 0x9C, 0x01, 0x00, 0x20, 0xC4, 0x02, 0x00, 0x20, + 0x49, 0x32, 0x43, 0x20, 0x4F, 0x4B, 0x0D, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x70, 0xB5, 0x01, 0x25, + 0xCA, 0x07, 0x0A, 0xD0, 0x00, 0x20, 0x70, 0xBD, + 0x93, 0x00, 0xC4, 0x58, 0x66, 0x1C, 0x02, 0xD0, + 0x1B, 0x18, 0x5B, 0x68, 0x23, 0x60, 0x92, 0x1C, + 0x92, 0xB2, 0x8A, 0x42, 0xF4, 0xD3, 0x28, 0x46, + 0x70, 0xBD, 0x00, 0x00, 0x10, 0xB5, 0x1D, 0x49, + 0x0A, 0x68, 0x1C, 0x48, 0x40, 0x30, 0x02, 0x61, + 0x4A, 0x68, 0x42, 0x61, 0x8A, 0x68, 0x82, 0x61, + 0xC9, 0x68, 0xC1, 0x61, 0x82, 0x69, 0xF0, 0x21, + 0x8A, 0x43, 0x82, 0x61, 0x82, 0x69, 0x0A, 0x43, + 0x82, 0x61, 0x41, 0x69, 0x49, 0x04, 0x04, 0xD5, + 0x41, 0x69, 0x01, 0x22, 0x52, 0x03, 0x89, 0x1A, + 0x41, 0x61, 0x10, 0x48, 0x40, 0x38, 0x01, 0x68, + 0x01, 0x22, 0x12, 0x06, 0x11, 0x43, 0x01, 0x60, + 0x30, 0x21, 0x0D, 0x48, 0xFF, 0xF7, 0xC6, 0xFF, + 0x0B, 0x48, 0x40, 0x21, 0xC0, 0x30, 0xFF, 0xF7, + 0xC1, 0xFF, 0x0B, 0x20, 0xFE, 0xF7, 0x46, 0xFF, + 0x03, 0x20, 0xFE, 0xF7, 0x43, 0xFF, 0x00, 0x20, + 0xFE, 0xF7, 0x40, 0xFF, 0x05, 0x20, 0xFE, 0xF7, + 0x3D, 0xFF, 0x09, 0x20, 0xFE, 0xF7, 0x3A, 0xFF, + 0x01, 0x20, 0x10, 0xBD, 0x40, 0x14, 0x00, 0x50, + 0x50, 0x39, 0x00, 0x00, 0xFE, 0xB5, 0x27, 0x48, + 0x0A, 0x25, 0x45, 0x5F, 0x01, 0x26, 0x29, 0x46, + 0x25, 0xA0, 0x01, 0xF0, 0x42, 0xFE, 0x2A, 0x48, + 0x72, 0x04, 0x01, 0x68, 0x29, 0x4F, 0x91, 0x43, + 0x00, 0x24, 0x01, 0x60, 0x28, 0x48, 0x00, 0x68, + 0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x18, 0xD0, + 0x26, 0x49, 0x60, 0x00, 0x0A, 0x5E, 0x26, 0x49, + 0x0B, 0x5A, 0xD0, 0x1A, 0x00, 0xB2, 0xA8, 0x42, + 0x0B, 0xDA, 0x00, 0x90, 0x21, 0x46, 0x01, 0x95, + 0x22, 0xA0, 0x01, 0xF0, 0x26, 0xFE, 0x38, 0x5D, + 0x20, 0x21, 0x08, 0x43, 0x38, 0x55, 0x00, 0x26, + 0x03, 0xE0, 0x38, 0x5D, 0xDF, 0x21, 0x08, 0x40, + 0x38, 0x55, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C, + 0xDC, 0xD3, 0x01, 0x2E, 0x18, 0xD0, 0x14, 0x4A, + 0x01, 0x21, 0x10, 0x68, 0x49, 0x04, 0x08, 0x43, + 0x10, 0x60, 0x23, 0xA0, 0x01, 0xF0, 0x0D, 0xFE, + 0x27, 0xA0, 0x01, 0xF0, 0x0A, 0xFE, 0x01, 0x21, + 0x10, 0x48, 0x01, 0xF0, 0xED, 0xF9, 0x27, 0xA0, + 0x01, 0xF0, 0x03, 0xFE, 0x01, 0x21, 0x0E, 0x48, + 0x01, 0xF0, 0xE6, 0xF9, 0x30, 0x46, 0xFE, 0xBD, + 0x27, 0xA0, 0xEB, 0xE7, 0x84, 0x06, 0x00, 0x20, + 0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x43, 0x20, + 0x43, 0x68, 0x65, 0x63, 0x6B, 0x20, 0x54, 0x48, + 0x44, 0x3D, 0x25, 0x64, 0x20, 0x0D, 0x0A, 0x00, + 0x0C, 0x05, 0x00, 0x20, 0xD8, 0x04, 0x00, 0x20, + 0x98, 0x01, 0x00, 0x20, 0xAC, 0x03, 0x00, 0x20, + 0x4C, 0x07, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, + 0x20, 0x43, 0x43, 0x20, 0x4E, 0x47, 0x20, 0x50, + 0x69, 0x6E, 0x5B, 0x25, 0x64, 0x5D, 0x2C, 0x20, + 0x44, 0x69, 0x66, 0x66, 0x3D, 0x25, 0x64, 0x2D, + 0x25, 0x64, 0x3D, 0x25, 0x64, 0x2C, 0x20, 0x4C, + 0x6F, 0x77, 0x54, 0x48, 0x44, 0x20, 0x3D, 0x20, + 0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x43, + 0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x5B, + 0x4E, 0x47, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, + 0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x43, 0x0D, + 0x0A, 0x00, 0x00, 0x00, 0x47, 0x6F, 0x6C, 0x64, + 0x65, 0x6E, 0x20, 0x4F, 0x70, 0x65, 0x6E, 0x20, + 0x43, 0x43, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0x53, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x43, + 0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x5B, + 0x50, 0x61, 0x73, 0x73, 0x5D, 0x0D, 0x0A, 0x00, + 0xFE, 0xB5, 0x32, 0x49, 0x02, 0x20, 0x08, 0x5E, + 0x00, 0x90, 0x0A, 0x20, 0x08, 0x5E, 0x03, 0x21, + 0x89, 0x03, 0x48, 0x43, 0x00, 0x14, 0x01, 0x90, + 0x2D, 0x48, 0x01, 0x27, 0x01, 0x68, 0xB9, 0x43, + 0x00, 0x24, 0x01, 0x60, 0x2B, 0x48, 0x00, 0x68, + 0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x30, 0xD0, + 0x65, 0x00, 0x29, 0x4A, 0x29, 0x48, 0x56, 0x5F, + 0x41, 0x5B, 0x00, 0x20, 0x71, 0x1A, 0x0B, 0xB2, + 0x53, 0x53, 0x00, 0x99, 0x8B, 0x42, 0x05, 0xDA, + 0x32, 0x46, 0x21, 0x46, 0x24, 0xA0, 0x01, 0xF0, + 0x78, 0xFD, 0x01, 0x20, 0x00, 0x2E, 0x0E, 0xD1, + 0x2B, 0x49, 0x2C, 0x4A, 0x49, 0x5B, 0x52, 0x5B, + 0x89, 0x1A, 0x0A, 0xB2, 0x01, 0x99, 0x8A, 0x42, + 0x05, 0xDA, 0x0B, 0x46, 0x21, 0x46, 0x28, 0xA0, + 0x01, 0xF0, 0x67, 0xFD, 0x01, 0xE0, 0x00, 0x28, + 0x06, 0xD0, 0x2F, 0x48, 0x01, 0x22, 0x01, 0x5D, + 0x11, 0x43, 0x01, 0x55, 0x00, 0x27, 0x04, 0xE0, + 0x2B, 0x48, 0x01, 0x5D, 0x49, 0x08, 0x49, 0x00, + 0x01, 0x55, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C, + 0xC4, 0xD3, 0x00, 0x99, 0x01, 0x2F, 0x10, 0xD0, + 0x26, 0xA0, 0x01, 0xF0, 0x4E, 0xFD, 0x0A, 0x49, + 0x01, 0x22, 0x08, 0x68, 0x10, 0x43, 0x08, 0x60, + 0x29, 0xA0, 0x01, 0xF0, 0x46, 0xFD, 0x01, 0x21, + 0x07, 0x48, 0x01, 0xF0, 0x29, 0xF9, 0x38, 0x46, + 0xFE, 0xBD, 0x2A, 0xA0, 0x01, 0xF0, 0x3D, 0xFD, + 0xF2, 0xE7, 0x00, 0x00, 0x84, 0x06, 0x00, 0x20, + 0x0C, 0x05, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, + 0x48, 0x03, 0x00, 0x20, 0x74, 0x04, 0x00, 0x20, + 0x47, 0x6C, 0x6F, 0x62, 0x61, 0x6C, 0x20, 0x54, + 0x48, 0x44, 0x20, 0x4F, 0x70, 0x65, 0x6E, 0x20, + 0x4E, 0x47, 0x20, 0x44, 0x61, 0x74, 0x61, 0x5B, + 0x25, 0x64, 0x5D, 0x5B, 0x25, 0x64, 0x3A, 0x25, + 0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0xAC, 0x03, 0x00, 0x20, 0x4C, 0x07, 0x00, 0x20, + 0x4F, 0x70, 0x65, 0x6E, 0x20, 0x4E, 0x47, 0x20, + 0x50, 0x69, 0x6E, 0x5B, 0x25, 0x64, 0x5D, 0x2C, + 0x20, 0x43, 0x43, 0x5F, 0x44, 0x69, 0x66, 0x66, + 0x3D, 0x25, 0x64, 0x2C, 0x20, 0x54, 0x48, 0x44, + 0x3D, 0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, 0x00, + 0xD8, 0x04, 0x00, 0x20, 0x4F, 0x70, 0x65, 0x6E, + 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x54, 0x48, + 0x44, 0x3A, 0x25, 0x64, 0x29, 0x20, 0x5B, 0x4E, + 0x47, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0x51, 0x75, 0x69, 0x63, 0x6B, 0x20, 0x2D, 0x20, + 0x42, 0x61, 0x73, 0x65, 0x6C, 0x69, 0x6E, 0x65, + 0x0D, 0x0A, 0x00, 0x00, 0x4F, 0x70, 0x65, 0x6E, + 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x54, 0x48, + 0x44, 0x3A, 0x25, 0x64, 0x29, 0x20, 0x5B, 0x50, + 0x61, 0x73, 0x73, 0x5D, 0x0D, 0x0A, 0x00, 0x00, + 0xF0, 0xB5, 0x17, 0x49, 0x00, 0x20, 0x7D, 0x27, + 0x09, 0x68, 0xFF, 0x00, 0x02, 0x46, 0x89, 0x07, + 0x24, 0xD5, 0x14, 0x49, 0x14, 0x4D, 0x09, 0x68, + 0xCE, 0x26, 0x0B, 0x18, 0xDB, 0x7E, 0x41, 0x2B, + 0x0A, 0xD0, 0x12, 0x4B, 0x44, 0x00, 0x1B, 0x5F, + 0xBB, 0x42, 0x00, 0xDA, 0x01, 0x22, 0x2B, 0x5C, + 0x9C, 0x07, 0x01, 0xD5, 0x33, 0x40, 0x2B, 0x54, + 0x40, 0x1C, 0xC0, 0xB2, 0x30, 0x28, 0xEC, 0xD3, + 0x00, 0x2A, 0x0B, 0xD0, 0x00, 0x20, 0x0A, 0x18, + 0xD2, 0x7E, 0x41, 0x2A, 0x02, 0xD0, 0x2A, 0x5C, + 0x32, 0x40, 0x2A, 0x54, 0x40, 0x1C, 0xC0, 0xB2, + 0x30, 0x28, 0xF4, 0xD3, 0x01, 0x20, 0xF0, 0xBD, + 0x0C, 0x05, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, + 0xD8, 0x04, 0x00, 0x20, 0xE4, 0x02, 0x00, 0x20, + 0x70, 0xB5, 0x20, 0x48, 0x01, 0x25, 0xC4, 0x7D, + 0x1F, 0x48, 0x80, 0x88, 0x40, 0x07, 0x01, 0xD4, + 0x01, 0x20, 0x70, 0xBD, 0xFF, 0xF7, 0x34, 0xFB, + 0x1C, 0x48, 0xFE, 0xF7, 0x5F, 0xFF, 0x00, 0x28, + 0x0F, 0xD1, 0x1B, 0xA0, 0x01, 0xF0, 0x91, 0xFC, + 0x01, 0x20, 0x20, 0x49, 0x00, 0x25, 0x08, 0x70, + 0x00, 0x2C, 0x06, 0xD0, 0x64, 0x1E, 0xE4, 0xB2, + 0x21, 0x46, 0x1D, 0xA0, 0x01, 0xF0, 0x85, 0xFC, + 0xE8, 0xE7, 0xFE, 0xF7, 0x95, 0xFF, 0xFF, 0xF7, + 0x0B, 0xFA, 0xFF, 0xF7, 0xDD, 0xFE, 0x00, 0x28, + 0x07, 0xD1, 0x00, 0x25, 0x00, 0x2C, 0x04, 0xD0, + 0x64, 0x1E, 0xE4, 0xB2, 0x21, 0x46, 0x1C, 0xA0, + 0x0A, 0xE0, 0xFF, 0xF7, 0x27, 0xFE, 0x00, 0x28, + 0x0A, 0xD1, 0x00, 0x25, 0x00, 0x2C, 0x07, 0xD0, + 0x64, 0x1E, 0xE4, 0xB2, 0x21, 0x46, 0x1C, 0xA0, + 0x01, 0xF0, 0x67, 0xFC, 0x01, 0x25, 0xC9, 0xE7, + 0x28, 0x46, 0x70, 0xBD, 0xA8, 0x06, 0x00, 0x20, + 0x74, 0x06, 0x00, 0x20, 0xAC, 0x03, 0x00, 0x20, + 0x4F, 0x70, 0x65, 0x6E, 0x20, 0x54, 0x65, 0x73, + 0x74, 0x20, 0x43, 0x61, 0x6C, 0x69, 0x62, 0x72, + 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x4E, 0x47, + 0x21, 0x0D, 0x0A, 0x00, 0xD8, 0x04, 0x00, 0x20, + 0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x61, 0x6C, + 0x69, 0x62, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, + 0x20, 0x52, 0x65, 0x74, 0x72, 0x79, 0x20, 0x28, + 0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, 0x00, + 0x4F, 0x70, 0x65, 0x6E, 0x20, 0x54, 0x65, 0x73, + 0x74, 0x20, 0x52, 0x65, 0x74, 0x72, 0x79, 0x20, + 0x28, 0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, + 0x4F, 0x70, 0x65, 0x6E, 0x20, 0x43, 0x43, 0x20, + 0x52, 0x65, 0x74, 0x72, 0x79, 0x20, 0x28, 0x25, + 0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0x10, 0xB5, 0x17, 0x4A, 0x11, 0x78, 0x81, 0x42, + 0x29, 0xD0, 0x16, 0x49, 0x0B, 0x68, 0x01, 0x24, + 0xA4, 0x02, 0x23, 0x43, 0x0B, 0x60, 0x10, 0x70, + 0x05, 0x22, 0x40, 0x24, 0x12, 0x49, 0x12, 0x07, + 0x80, 0x23, 0x00, 0x28, 0x09, 0xD0, 0x08, 0x68, + 0x40, 0x06, 0x0C, 0xD4, 0x08, 0x68, 0x20, 0x43, + 0x08, 0x60, 0xD0, 0x68, 0x98, 0x43, 0xD0, 0x60, + 0x05, 0xE0, 0xD0, 0x68, 0x18, 0x43, 0xD0, 0x60, + 0x08, 0x68, 0xA0, 0x43, 0x08, 0x60, 0x08, 0x68, + 0x18, 0x43, 0x08, 0x60, 0xD0, 0x68, 0x01, 0x21, + 0xC9, 0x03, 0x08, 0x43, 0xD0, 0x60, 0xE1, 0x20, + 0x00, 0x02, 0x01, 0xF0, 0x3B, 0xF8, 0x10, 0xBD, + 0x04, 0x00, 0x00, 0x20, 0x00, 0x10, 0x00, 0x50, + 0x00, 0x11, 0x00, 0x50, 0x06, 0x49, 0x83, 0x20, + 0x08, 0x70, 0x06, 0x49, 0x00, 0x20, 0x08, 0x70, + 0x05, 0x48, 0x00, 0x68, 0x05, 0x49, 0x40, 0x05, + 0x40, 0x0F, 0x08, 0x73, 0x70, 0x47, 0x00, 0x00, + 0x20, 0x08, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20, + 0x00, 0x11, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20, + 0xF0, 0xB5, 0x04, 0x46, 0x8F, 0xB0, 0xA6, 0x48, + 0x0A, 0x90, 0xA5, 0x48, 0xA6, 0x4D, 0x60, 0x30, + 0x0B, 0x90, 0xA4, 0x48, 0x03, 0x90, 0xA5, 0x48, + 0x05, 0x95, 0x01, 0x68, 0x22, 0x46, 0xA4, 0xA0, + 0x01, 0xF0, 0xC7, 0xFB, 0x03, 0x98, 0x08, 0x90, + 0xA5, 0x48, 0xA6, 0x4A, 0x00, 0x21, 0x09, 0x95, + 0x8B, 0x00, 0x08, 0x9D, 0x49, 0x1C, 0xE8, 0x50, + 0x09, 0x9D, 0x89, 0xB2, 0xEA, 0x50, 0x18, 0x29, + 0xF6, 0xD3, 0x01, 0x20, 0x80, 0x02, 0x06, 0x90, + 0x20, 0x46, 0x07, 0x94, 0x50, 0x30, 0x00, 0xB2, + 0x50, 0x3C, 0x0C, 0x90, 0x20, 0xB2, 0x0D, 0x90, + 0x00, 0xF0, 0x96, 0xFC, 0x00, 0xF0, 0xC6, 0xFE, + 0x00, 0xF0, 0x6E, 0xF9, 0x98, 0x48, 0x00, 0x78, + 0xC0, 0x07, 0x18, 0xD1, 0x01, 0x90, 0x00, 0x20, + 0x08, 0x9A, 0x81, 0x00, 0x53, 0x58, 0x0A, 0x9A, + 0x40, 0x1C, 0x53, 0x50, 0x09, 0x9A, 0x80, 0xB2, + 0x53, 0x58, 0x0B, 0x9A, 0x18, 0x28, 0x53, 0x50, + 0xF2, 0xD3, 0x00, 0xF0, 0xC7, 0xFD, 0x00, 0xF0, + 0xAD, 0xFE, 0x00, 0xF0, 0x55, 0xF9, 0x8C, 0x48, + 0x00, 0x78, 0xC0, 0x07, 0x02, 0xD0, 0x00, 0x20, + 0x0F, 0xB0, 0xF0, 0xBD, 0x3B, 0x46, 0x89, 0xA0, + 0x00, 0x9A, 0x01, 0x99, 0x01, 0xF0, 0x81, 0xFB, + 0x00, 0x25, 0x2D, 0xE0, 0x00, 0x2D, 0x2B, 0xD0, + 0x01, 0x98, 0x05, 0x28, 0x73, 0xD9, 0x30, 0x2F, + 0x71, 0xD2, 0x7A, 0x4C, 0x05, 0x98, 0x40, 0x3C, + 0x04, 0x90, 0x60, 0x6B, 0x03, 0x26, 0x36, 0x05, + 0x01, 0x21, 0xB0, 0x43, 0x49, 0x05, 0x40, 0x18, + 0x60, 0x63, 0x00, 0xF0, 0x9F, 0xFD, 0x00, 0xF0, + 0x85, 0xFE, 0x00, 0xF0, 0x2D, 0xF9, 0x60, 0x6B, + 0x01, 0x21, 0xB0, 0x43, 0x09, 0x05, 0x40, 0x18, + 0x60, 0x63, 0x00, 0x27, 0x00, 0x26, 0x79, 0x48, + 0x00, 0x68, 0x80, 0x19, 0xC0, 0x7E, 0x41, 0x28, + 0x0E, 0xD0, 0x70, 0x00, 0x04, 0x99, 0x02, 0x90, + 0x0C, 0x5A, 0x16, 0x2E, 0x12, 0xD0, 0x1A, 0xE0, + 0x00, 0x98, 0x30, 0x28, 0x77, 0xD0, 0x03, 0x98, + 0x04, 0x90, 0x00, 0x20, 0x00, 0x90, 0xE9, 0xE7, + 0x00, 0x2D, 0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, + 0x68, 0xE0, 0x00, 0x98, 0x40, 0x1C, 0x80, 0xB2, + 0x00, 0x90, 0x63, 0xE0, 0x6A, 0x48, 0x2C, 0x23, + 0x00, 0x68, 0x22, 0x46, 0xC3, 0x5E, 0x31, 0x46, + 0x68, 0xA0, 0x01, 0xF0, 0x36, 0xFB, 0x01, 0x98, + 0x0A, 0x28, 0x0C, 0xD2, 0x64, 0x49, 0x02, 0x98, + 0x09, 0x68, 0x09, 0x5E, 0x07, 0x98, 0x81, 0x42, + 0x01, 0xDA, 0x06, 0x98, 0x84, 0x43, 0x06, 0x98, + 0x40, 0x08, 0x04, 0x43, 0x32, 0xE0, 0x5E, 0x48, + 0x02, 0x99, 0x00, 0x68, 0x41, 0x5E, 0x00, 0x29, + 0x01, 0xDB, 0x08, 0x46, 0x00, 0xE0, 0x48, 0x42, + 0x00, 0xB2, 0x40, 0x30, 0xC2, 0x17, 0x52, 0x0E, + 0x10, 0x18, 0x0D, 0x9A, 0xC0, 0x11, 0x91, 0x42, + 0x10, 0xDA, 0x21, 0x05, 0x06, 0xD5, 0x20, 0x1A, + 0x84, 0xB2, 0x01, 0x20, 0xC0, 0x02, 0x84, 0x42, + 0x11, 0xD3, 0x17, 0xE0, 0x84, 0x42, 0x03, 0xDD, + 0x20, 0x1A, 0x84, 0xB2, 0x12, 0xE0, 0x2E, 0xE0, + 0x00, 0x24, 0x0F, 0xE0, 0x0C, 0x9A, 0x91, 0x42, + 0x15, 0xDD, 0x21, 0x05, 0x05, 0xD5, 0x20, 0x18, + 0x84, 0xB2, 0x4D, 0x48, 0x04, 0xE0, 0x04, 0x46, + 0x04, 0xE0, 0x20, 0x18, 0x84, 0xB2, 0x4B, 0x48, + 0x84, 0x42, 0xF8, 0xD8, 0x00, 0x2D, 0x0F, 0xD0, + 0x01, 0x20, 0xC0, 0x02, 0x04, 0x43, 0x05, 0x99, + 0x02, 0x98, 0x0C, 0x52, 0x0E, 0xE0, 0x00, 0x2D, + 0x02, 0xD0, 0x7F, 0x1C, 0xBF, 0xB2, 0xF3, 0xE7, + 0x00, 0x98, 0x40, 0x1C, 0x80, 0xB2, 0x00, 0x90, + 0x03, 0x99, 0x02, 0x98, 0x0C, 0x52, 0x01, 0x98, + 0x05, 0x28, 0xE9, 0xD9, 0x76, 0x1C, 0xB6, 0xB2, + 0x30, 0x2E, 0x00, 0xD2, 0x77, 0xE7, 0x6D, 0x1C, + 0xED, 0xB2, 0x02, 0x2D, 0x00, 0xD2, 0x51, 0xE7, + 0x06, 0x98, 0x40, 0x08, 0x06, 0x90, 0x38, 0xA0, + 0x01, 0xF0, 0xCB, 0xFA, 0x00, 0x98, 0x30, 0x28, + 0x03, 0xD1, 0x30, 0x2F, 0x01, 0xD1, 0x40, 0x20, + 0x01, 0x90, 0x01, 0x98, 0x40, 0x1C, 0x80, 0xB2, + 0x01, 0x90, 0x40, 0x28, 0x00, 0xD8, 0x1A, 0xE7, + 0x1C, 0x4C, 0x40, 0x3C, 0x60, 0x6B, 0x03, 0x25, + 0x2D, 0x05, 0x01, 0x21, 0xA8, 0x43, 0x09, 0x05, + 0x40, 0x18, 0x60, 0x63, 0x00, 0xF0, 0xE6, 0xFC, + 0x00, 0xF0, 0xCC, 0xFD, 0x00, 0xF0, 0x74, 0xF8, + 0x21, 0x4E, 0x01, 0x21, 0x30, 0x68, 0x00, 0xF0, + 0x8F, 0xFE, 0x60, 0x6B, 0x01, 0x21, 0xA8, 0x43, + 0x49, 0x05, 0x40, 0x18, 0x60, 0x63, 0x00, 0xF0, + 0xD5, 0xFC, 0x00, 0xF0, 0xBB, 0xFD, 0x00, 0xF0, + 0x63, 0xF8, 0x01, 0x21, 0x30, 0x68, 0x00, 0xF0, + 0x7F, 0xFE, 0x60, 0x6B, 0x28, 0x43, 0x60, 0x63, + 0x00, 0xF0, 0xC8, 0xFC, 0x00, 0xF0, 0xAE, 0xFD, + 0x00, 0xF0, 0x56, 0xF8, 0x01, 0x21, 0x30, 0x68, + 0x00, 0xF0, 0x72, 0xFE, 0x01, 0x20, 0xFF, 0xE6, + 0x00, 0x20, 0x00, 0x50, 0xF4, 0x05, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x20, 0xC0, 0x10, 0x00, 0x50, + 0x5B, 0x43, 0x46, 0x42, 0x3A, 0x25, 0x78, 0x3A, + 0x25, 0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x04, 0x00, 0x0C, 0x00, 0x0C, + 0x30, 0x00, 0x00, 0x20, 0x5B, 0x25, 0x64, 0x3A, + 0x25, 0x64, 0x3A, 0x25, 0x64, 0x5D, 0x20, 0x0D, + 0x0A, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20, + 0x4C, 0x01, 0x00, 0x20, 0x25, 0x64, 0x2C, 0x30, + 0x78, 0x25, 0x78, 0x2C, 0x25, 0x64, 0x2C, 0x00, + 0xFF, 0x0F, 0x00, 0x00, 0xFF, 0x07, 0x00, 0x00, + 0x0D, 0x0A, 0x00, 0x00, 0x10, 0xB5, 0x0B, 0x49, + 0x30, 0x24, 0x00, 0x28, 0x0A, 0x4A, 0x0B, 0x4B, + 0x08, 0x68, 0x06, 0xD0, 0x20, 0x43, 0x08, 0x60, + 0x09, 0x48, 0x10, 0x60, 0x08, 0x48, 0x60, 0x30, + 0x05, 0xE0, 0xA0, 0x43, 0x08, 0x60, 0x07, 0x48, + 0x10, 0x60, 0x06, 0x48, 0x60, 0x30, 0x18, 0x60, + 0x10, 0xBD, 0x00, 0x00, 0x00, 0x10, 0x00, 0x50, + 0x4C, 0x01, 0x00, 0x20, 0x50, 0x01, 0x00, 0x20, + 0x00, 0x00, 0x04, 0x20, 0x00, 0x10, 0x04, 0x20, + 0xF0, 0xB5, 0x11, 0x48, 0x01, 0x68, 0x11, 0x4D, + 0x89, 0x06, 0x30, 0x22, 0x60, 0x35, 0x00, 0x29, + 0x0E, 0x4B, 0x0F, 0x4C, 0x0F, 0x4E, 0x10, 0x4F, + 0x01, 0x68, 0x0A, 0xDA, 0x91, 0x43, 0x01, 0x60, + 0x23, 0x60, 0x0E, 0x48, 0x35, 0x60, 0x07, 0x60, + 0x0B, 0x48, 0x0D, 0x49, 0x60, 0x30, 0x08, 0x60, + 0xF0, 0xBD, 0x11, 0x43, 0x01, 0x60, 0x08, 0x48, + 0x27, 0x60, 0x60, 0x30, 0x30, 0x60, 0x07, 0x48, + 0x03, 0x60, 0x07, 0x48, 0x05, 0x60, 0xF0, 0xBD, + 0x00, 0x10, 0x00, 0x50, 0x00, 0x00, 0x04, 0x20, + 0x40, 0x00, 0x00, 0x20, 0x44, 0x00, 0x00, 0x20, + 0x00, 0x10, 0x04, 0x20, 0x4C, 0x01, 0x00, 0x20, + 0x50, 0x01, 0x00, 0x20, 0x00, 0xB5, 0x09, 0x48, + 0x00, 0x78, 0x00, 0x28, 0x08, 0xD0, 0x01, 0x28, + 0x09, 0xD0, 0x02, 0x28, 0x03, 0xD1, 0x06, 0x49, + 0x04, 0x20, 0x00, 0xF0, 0x65, 0xF8, 0x00, 0xBD, + 0x04, 0x49, 0x01, 0x20, 0xF9, 0xE7, 0x04, 0x49, + 0x02, 0x20, 0xF6, 0xE7, 0x0D, 0x08, 0x00, 0x20, + 0x70, 0x05, 0x00, 0x20, 0x1C, 0x02, 0x00, 0x20, + 0x10, 0x05, 0x00, 0x20, 0xF8, 0xB5, 0x07, 0x46, + 0x00, 0xF0, 0xD2, 0xFA, 0x21, 0x4D, 0x60, 0x21, + 0x28, 0x46, 0xFE, 0xF7, 0xA2, 0xFA, 0x00, 0x24, + 0x00, 0xF0, 0xFC, 0xFC, 0xFF, 0xF7, 0xA4, 0xFF, + 0xFE, 0xF7, 0x39, 0xFB, 0x00, 0x2C, 0x0C, 0xD0, + 0x1B, 0x49, 0x00, 0x20, 0x0E, 0x68, 0x42, 0x00, + 0x51, 0x19, 0x0B, 0x88, 0xB2, 0x5A, 0x40, 0x1C, + 0x9A, 0x18, 0xC0, 0xB2, 0x0A, 0x80, 0x30, 0x28, + 0xF5, 0xD3, 0x64, 0x1C, 0xE4, 0xB2, 0x05, 0x2C, + 0xE6, 0xD3, 0x00, 0x20, 0x41, 0x00, 0x49, 0x19, + 0x00, 0x22, 0x8A, 0x5E, 0x40, 0x1C, 0x92, 0x10, + 0xC0, 0xB2, 0x0A, 0x80, 0x30, 0x28, 0xF5, 0xD3, + 0x39, 0x46, 0x0E, 0xA0, 0x01, 0xF0, 0xB9, 0xF9, + 0x01, 0x21, 0x28, 0x46, 0x00, 0xF0, 0x9C, 0xFD, + 0x01, 0x20, 0xFE, 0xF7, 0xBF, 0xFB, 0x0C, 0x4C, + 0x20, 0x78, 0x38, 0x42, 0x08, 0xD1, 0x0B, 0x48, + 0x60, 0x22, 0x29, 0x46, 0x00, 0x68, 0xFE, 0xF7, + 0x4F, 0xFA, 0x20, 0x78, 0x38, 0x43, 0x20, 0x70, + 0x01, 0x20, 0xF8, 0xBD, 0x00, 0x00, 0x01, 0x20, + 0x4C, 0x01, 0x00, 0x20, 0x42, 0x43, 0x5F, 0x4F, + 0x4B, 0x5F, 0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, + 0xF0, 0xB5, 0x21, 0x48, 0x00, 0x22, 0x00, 0x68, + 0x20, 0x4C, 0xA0, 0x42, 0x28, 0xD8, 0x20, 0x48, + 0x00, 0x78, 0x04, 0x28, 0x27, 0xD1, 0x1F, 0x4D, + 0x1F, 0x4C, 0x2D, 0x68, 0x00, 0x20, 0x2E, 0x18, + 0xF6, 0x7E, 0x41, 0x2E, 0x08, 0xD0, 0x43, 0x00, + 0x1F, 0x19, 0x00, 0x26, 0xBE, 0x5F, 0xCB, 0x5E, + 0xF3, 0x1A, 0x00, 0xD5, 0x5B, 0x42, 0x1B, 0xB2, + 0x9A, 0x42, 0x00, 0xDA, 0x1A, 0x46, 0x40, 0x1C, + 0xC0, 0xB2, 0x30, 0x28, 0xEB, 0xD3, 0x60, 0x20, + 0x40, 0x5D, 0x90, 0x35, 0x40, 0x1C, 0x50, 0x43, + 0x6A, 0x7B, 0x2B, 0x7B, 0x00, 0x11, 0x12, 0x02, + 0x00, 0xB2, 0x1A, 0x43, 0x82, 0x42, 0x03, 0xDA, + 0x0E, 0x49, 0x00, 0x20, 0x08, 0x70, 0xF0, 0xBD, + 0x00, 0x20, 0x42, 0x00, 0x8B, 0x5E, 0x00, 0x26, + 0xDD, 0x00, 0xEB, 0x1A, 0x15, 0x19, 0xAE, 0x5F, + 0x40, 0x1C, 0x9B, 0x19, 0xDB, 0x10, 0xC0, 0xB2, + 0x8B, 0x52, 0x30, 0x28, 0xF1, 0xD3, 0xF0, 0xBD, + 0x5C, 0x01, 0x00, 0x20, 0x40, 0x77, 0x1B, 0x00, + 0x93, 0x01, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x20, 0x38, 0x00, 0x00, 0x20, + 0x70, 0xB5, 0x21, 0x48, 0x21, 0x49, 0x00, 0x78, + 0x00, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x08, 0xD0, + 0x02, 0x28, 0x16, 0xD0, 0x05, 0x28, 0x2F, 0xD1, + 0x34, 0xE0, 0x01, 0x25, 0x2C, 0x46, 0x1C, 0x48, + 0x02, 0xE0, 0x02, 0x25, 0x1B, 0x48, 0x2C, 0x46, + 0x08, 0x60, 0x00, 0x22, 0x1A, 0x49, 0x28, 0x46, + 0xFE, 0xF7, 0x40, 0xFB, 0x00, 0x28, 0x18, 0xD0, + 0x18, 0x48, 0x00, 0x78, 0x00, 0x28, 0x04, 0xD0, + 0x13, 0xE0, 0x04, 0x25, 0x2C, 0x46, 0x16, 0x48, + 0xEE, 0xE7, 0x16, 0x48, 0x41, 0x6B, 0x03, 0x22, + 0x12, 0x05, 0x11, 0x43, 0x41, 0x63, 0x14, 0x48, + 0x00, 0xF0, 0x4C, 0xFA, 0x00, 0x28, 0x0B, 0xD1, + 0x20, 0x46, 0xFF, 0xF7, 0x27, 0xFF, 0x00, 0x28, + 0x06, 0xD1, 0x01, 0x22, 0x21, 0x46, 0x28, 0x46, + 0x00, 0xF0, 0x1E, 0xF8, 0x00, 0x28, 0x04, 0xD0, + 0x0C, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, + 0x00, 0x20, 0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD, + 0x92, 0x01, 0x00, 0x20, 0x48, 0x00, 0x00, 0x20, + 0x1C, 0x02, 0x00, 0x20, 0x10, 0x05, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x50, 0x3B, 0x01, 0x00, 0x20, + 0x70, 0x05, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, + 0x00, 0x00, 0x01, 0x20, 0x30, 0x00, 0x00, 0x20, + 0xF7, 0xB5, 0x07, 0x46, 0x14, 0x48, 0x00, 0x25, + 0x05, 0x70, 0x78, 0x07, 0x1D, 0xD0, 0x01, 0x21, + 0x02, 0x20, 0x00, 0xF0, 0x45, 0xFB, 0x11, 0x4C, + 0x60, 0x6B, 0x03, 0x26, 0x36, 0x05, 0x01, 0x21, + 0xB0, 0x43, 0x09, 0x05, 0x40, 0x18, 0x60, 0x63, + 0x00, 0x20, 0xFF, 0xF7, 0xFD, 0xFC, 0x00, 0x28, + 0x0D, 0xD0, 0x01, 0x98, 0xFF, 0xF7, 0xE6, 0xFE, + 0x01, 0x22, 0x09, 0x49, 0x38, 0x46, 0xFE, 0xF7, + 0xE1, 0xFA, 0x08, 0x48, 0x05, 0x70, 0x08, 0x48, + 0x05, 0x70, 0x01, 0x20, 0xFE, 0xBD, 0x60, 0x6B, + 0x30, 0x43, 0x60, 0x63, 0x00, 0x20, 0xFE, 0xBD, + 0x90, 0x01, 0x00, 0x20, 0x80, 0x10, 0x00, 0x50, + 0x00, 0x20, 0x00, 0x50, 0x3B, 0x01, 0x00, 0x20, + 0x3C, 0x01, 0x00, 0x20, 0x70, 0xB5, 0x10, 0x4C, + 0x60, 0x6B, 0x03, 0x25, 0x2D, 0x05, 0x01, 0x26, + 0xA8, 0x43, 0x36, 0x05, 0x80, 0x19, 0x60, 0x63, + 0x0C, 0x49, 0x02, 0x20, 0x08, 0x5E, 0xFF, 0xF7, + 0xCF, 0xFC, 0x00, 0x28, 0x60, 0x6B, 0x09, 0xD0, + 0xA8, 0x43, 0x80, 0x19, 0x60, 0x63, 0x08, 0x49, + 0x00, 0x20, 0x08, 0x70, 0x07, 0x49, 0x08, 0x70, + 0x01, 0x20, 0x70, 0xBD, 0xA8, 0x43, 0x80, 0x19, + 0x60, 0x63, 0x00, 0x20, 0x70, 0xBD, 0x00, 0x00, + 0x80, 0x10, 0x00, 0x50, 0xCE, 0x00, 0x00, 0x20, + 0x3B, 0x01, 0x00, 0x20, 0x3C, 0x01, 0x00, 0x20, + 0xF0, 0xB5, 0xB4, 0x48, 0x01, 0x22, 0x23, 0x23, + 0x13, 0x24, 0x92, 0x02, 0x5B, 0x01, 0xA4, 0x01, + 0x05, 0x46, 0x1C, 0xC5, 0x29, 0x21, 0x49, 0x01, + 0xC1, 0x60, 0xAF, 0x48, 0x07, 0x68, 0x38, 0x46, + 0x40, 0x30, 0x45, 0x7C, 0x06, 0x7C, 0x2D, 0x04, + 0x36, 0x02, 0x35, 0x43, 0x0C, 0x35, 0x15, 0x60, + 0x45, 0x7C, 0x06, 0x7C, 0x2D, 0x04, 0x36, 0x02, + 0x35, 0x43, 0x0C, 0x35, 0x55, 0x60, 0x01, 0x25, + 0x95, 0x60, 0xD5, 0x60, 0x05, 0x7D, 0xBC, 0x46, + 0x6E, 0x1E, 0x6D, 0x08, 0x6D, 0x1E, 0xF6, 0x05, + 0xED, 0x05, 0xF6, 0x09, 0xED, 0x0D, 0x2E, 0x43, + 0x16, 0x61, 0xA0, 0x4D, 0x55, 0x61, 0x03, 0x25, + 0x95, 0x61, 0x03, 0x26, 0x36, 0x02, 0x00, 0x25, + 0x16, 0x62, 0xD5, 0x61, 0x06, 0x7E, 0xD6, 0x62, + 0x06, 0x7E, 0x87, 0x7E, 0xF6, 0x19, 0x16, 0x63, + 0x87, 0x7E, 0x06, 0x7E, 0x7F, 0x00, 0xF6, 0x19, + 0x56, 0x63, 0x95, 0x63, 0x06, 0x7E, 0xBC, 0x36, + 0xD6, 0x63, 0x06, 0x7E, 0x87, 0x7E, 0xF6, 0x19, + 0xBC, 0x36, 0x16, 0x64, 0x07, 0x7E, 0x86, 0x7E, + 0x95, 0x64, 0x76, 0x00, 0xBC, 0x36, 0xBE, 0x19, + 0x56, 0x64, 0x67, 0x46, 0x55, 0x62, 0x8E, 0x4E, + 0x50, 0x37, 0x96, 0x62, 0xBC, 0x46, 0xFE, 0x7B, + 0xBF, 0x7B, 0x36, 0x02, 0x3E, 0x43, 0xF6, 0x1C, + 0xB7, 0x05, 0x8A, 0x4E, 0xBF, 0x0D, 0xBE, 0x19, + 0xD6, 0x64, 0x89, 0x4E, 0x16, 0x65, 0x46, 0x7C, + 0x0C, 0x3E, 0xF7, 0xB2, 0x05, 0x26, 0x76, 0x02, + 0xBE, 0x19, 0x56, 0x65, 0x06, 0x7D, 0x36, 0x02, + 0x21, 0x36, 0x96, 0x65, 0xD5, 0x65, 0x42, 0x7C, + 0x06, 0x7C, 0x12, 0x04, 0x36, 0x02, 0x32, 0x43, + 0x0C, 0x32, 0x1A, 0x60, 0x42, 0x7C, 0x06, 0x7C, + 0x12, 0x04, 0x36, 0x02, 0x32, 0x43, 0x0C, 0x32, + 0x5A, 0x60, 0x01, 0x22, 0x9A, 0x60, 0xDA, 0x60, + 0x42, 0x7D, 0x56, 0x1E, 0x52, 0x08, 0x52, 0x1E, + 0xF6, 0x05, 0xD2, 0x05, 0xF7, 0x09, 0xD2, 0x0D, + 0x17, 0x43, 0x72, 0x4A, 0x1F, 0x61, 0x5A, 0x61, + 0x03, 0x22, 0xDD, 0x61, 0x9A, 0x61, 0x12, 0x02, + 0x1A, 0x62, 0x02, 0x7E, 0xDA, 0x62, 0x02, 0x7E, + 0xC6, 0x7E, 0x92, 0x19, 0x1A, 0x63, 0xC6, 0x7E, + 0x02, 0x7E, 0x76, 0x00, 0x92, 0x19, 0x9D, 0x63, + 0x5A, 0x63, 0x02, 0x7E, 0x67, 0x46, 0xBC, 0x32, + 0xDA, 0x63, 0x02, 0x7E, 0xC6, 0x7E, 0x92, 0x19, + 0xBC, 0x32, 0x1A, 0x64, 0xC6, 0x7E, 0x02, 0x7E, + 0x76, 0x00, 0xBC, 0x36, 0x9D, 0x64, 0x92, 0x19, + 0x5D, 0x62, 0x5A, 0x64, 0x60, 0x4A, 0x9A, 0x62, + 0xFA, 0x7B, 0xBE, 0x7B, 0x12, 0x02, 0x32, 0x43, + 0xD2, 0x1C, 0x96, 0x05, 0x5D, 0x4A, 0xB6, 0x0D, + 0xB2, 0x18, 0xDA, 0x64, 0x5C, 0x4A, 0x1A, 0x65, + 0x42, 0x7C, 0x0C, 0x3A, 0xD6, 0xB2, 0x05, 0x22, + 0x52, 0x02, 0xB2, 0x18, 0x5A, 0x65, 0x42, 0x7D, + 0xDD, 0x65, 0x12, 0x02, 0x21, 0x32, 0x9A, 0x65, + 0xC2, 0x7C, 0x83, 0x7C, 0x12, 0x04, 0x1B, 0x02, + 0x1A, 0x43, 0x0C, 0x32, 0x23, 0x46, 0x1A, 0x60, + 0xC2, 0x7C, 0x83, 0x7C, 0x12, 0x04, 0x1B, 0x02, + 0x1A, 0x43, 0x0C, 0x32, 0x23, 0x46, 0x5A, 0x60, + 0x82, 0x7D, 0x52, 0x1E, 0xD3, 0x05, 0xDB, 0x0D, + 0x22, 0x46, 0x93, 0x60, 0x82, 0x7D, 0x52, 0x08, + 0x52, 0x1E, 0xD2, 0x05, 0x23, 0x46, 0xD2, 0x0D, + 0x1D, 0x61, 0xDA, 0x60, 0x47, 0x4A, 0x5A, 0x61, + 0x26, 0x46, 0x01, 0x23, 0xF5, 0x61, 0xB3, 0x61, + 0x13, 0x12, 0x33, 0x62, 0x43, 0x7E, 0xF3, 0x62, + 0x43, 0x7E, 0x06, 0x7F, 0x9B, 0x19, 0x26, 0x46, + 0x33, 0x63, 0x06, 0x7F, 0x43, 0x7E, 0x76, 0x00, + 0x9B, 0x19, 0x26, 0x46, 0xB5, 0x63, 0xF5, 0x63, + 0x35, 0x64, 0x73, 0x63, 0x65, 0x64, 0x38, 0x4B, + 0xA5, 0x64, 0x3F, 0x33, 0x65, 0x62, 0xA3, 0x62, + 0x36, 0x4B, 0xDB, 0x1C, 0xE3, 0x64, 0x36, 0x4B, + 0x23, 0x65, 0xC6, 0x7C, 0x0C, 0x3E, 0xF7, 0xB2, + 0x05, 0x26, 0x76, 0x02, 0xBF, 0x19, 0x67, 0x65, + 0x87, 0x7D, 0x7F, 0x08, 0x3F, 0x02, 0x21, 0x37, + 0xA7, 0x65, 0xE5, 0x65, 0xC4, 0x7C, 0x80, 0x7C, + 0x24, 0x04, 0x00, 0x02, 0x04, 0x43, 0x0C, 0x34, + 0x27, 0x48, 0x0C, 0x60, 0x00, 0x68, 0x40, 0x30, + 0xC4, 0x7C, 0x87, 0x7C, 0x24, 0x04, 0x3F, 0x02, + 0x3C, 0x43, 0x0C, 0x34, 0x4C, 0x60, 0xC4, 0x7D, + 0x64, 0x1E, 0xE4, 0x05, 0xE4, 0x0D, 0x8C, 0x60, + 0xC4, 0x7D, 0x0D, 0x61, 0x64, 0x08, 0x64, 0x1E, + 0xE4, 0x05, 0xE4, 0x0D, 0xCC, 0x60, 0x4A, 0x61, + 0x01, 0x22, 0xCD, 0x61, 0x8A, 0x61, 0x12, 0x02, + 0x0A, 0x62, 0x42, 0x7E, 0xCA, 0x62, 0x42, 0x7E, + 0x44, 0x7F, 0x12, 0x19, 0x0A, 0x63, 0x44, 0x7F, + 0x42, 0x7E, 0x64, 0x00, 0x12, 0x19, 0x8D, 0x63, + 0x4A, 0x63, 0x42, 0x7E, 0xBC, 0x32, 0xCA, 0x63, + 0x42, 0x7E, 0x44, 0x7F, 0x12, 0x19, 0xBC, 0x32, + 0x0A, 0x64, 0x42, 0x7F, 0x44, 0x7E, 0x52, 0x00, + 0xBC, 0x32, 0xA2, 0x18, 0x8D, 0x64, 0x4A, 0x64, + 0x01, 0x22, 0x92, 0x04, 0x4A, 0x62, 0x0C, 0x4A, + 0x3F, 0x32, 0x8A, 0x62, 0x0B, 0x4A, 0x0B, 0x65, + 0xD2, 0x1C, 0xCA, 0x64, 0xC2, 0x7C, 0x0C, 0x3A, + 0xD2, 0xB2, 0x92, 0x19, 0x4A, 0x65, 0xC0, 0x7D, + 0xCD, 0x65, 0x40, 0x08, 0x00, 0x02, 0x21, 0x30, + 0x88, 0x65, 0xF0, 0xBD, 0xD0, 0x05, 0x00, 0x20, + 0x98, 0x01, 0x00, 0x20, 0x03, 0x00, 0x03, 0x00, + 0x00, 0x3F, 0x3F, 0x3F, 0x00, 0x30, 0xBC, 0x00, + 0x00, 0x26, 0x31, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x10, 0xB5, 0x00, 0xF0, 0x2F, 0xFA, 0x00, 0xF0, + 0x5D, 0xF9, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, + 0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x00, 0x20, + 0xFF, 0xF7, 0xAC, 0xFC, 0x09, 0x48, 0x01, 0x78, + 0x09, 0x48, 0x00, 0x29, 0x04, 0xD0, 0x01, 0x68, + 0xFF, 0x22, 0x01, 0x32, 0x11, 0x43, 0x01, 0x60, + 0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x91, 0x43, + 0x01, 0x60, 0x04, 0x49, 0x02, 0x20, 0x08, 0x72, + 0x10, 0xBD, 0x00, 0x00, 0x31, 0x00, 0x00, 0x20, + 0x00, 0x10, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20, + 0x10, 0xB5, 0x01, 0x20, 0x80, 0x07, 0x41, 0x68, + 0x04, 0x22, 0x11, 0x43, 0x41, 0x60, 0x09, 0x48, + 0x07, 0x49, 0x41, 0x60, 0x08, 0x49, 0x81, 0x60, + 0xFF, 0xF7, 0x52, 0xFE, 0x80, 0x21, 0x07, 0x48, + 0xFD, 0xF7, 0x9B, 0xFF, 0x80, 0x21, 0x06, 0x48, + 0xFD, 0xF7, 0x97, 0xFF, 0x10, 0xBD, 0x00, 0x00, + 0x1F, 0x1F, 0x5F, 0x1F, 0x00, 0x10, 0x00, 0x50, + 0x1F, 0x1F, 0x1F, 0x1F, 0xDC, 0x08, 0x00, 0x20, + 0x9C, 0x01, 0x00, 0x20, 0x10, 0xB5, 0x0F, 0x49, + 0x0A, 0x78, 0x0F, 0x49, 0x00, 0x2A, 0x09, 0x78, + 0x04, 0xD0, 0x01, 0x2A, 0x07, 0xD0, 0x02, 0x2A, + 0x12, 0xD1, 0x09, 0xE0, 0xC9, 0x07, 0x0F, 0xD0, + 0x60, 0x22, 0x0A, 0x49, 0x08, 0xE0, 0x89, 0x07, + 0x0A, 0xD5, 0x60, 0x22, 0x08, 0x49, 0x03, 0xE0, + 0x49, 0x07, 0x05, 0xD5, 0x60, 0x22, 0x07, 0x49, + 0xFD, 0xF7, 0x56, 0xFF, 0x01, 0x20, 0x10, 0xBD, + 0x00, 0x20, 0x10, 0xBD, 0x92, 0x01, 0x00, 0x20, + 0x38, 0x00, 0x00, 0x20, 0x1C, 0x02, 0x00, 0x20, + 0x10, 0x05, 0x00, 0x20, 0x70, 0x05, 0x00, 0x20, + 0x10, 0xB5, 0x13, 0x49, 0x13, 0x4B, 0x09, 0x68, + 0x13, 0x4A, 0x40, 0x31, 0x00, 0x28, 0x0F, 0xD0, + 0x01, 0x28, 0x12, 0xD0, 0x02, 0x28, 0x15, 0xD0, + 0x03, 0x28, 0x08, 0xD1, 0x48, 0x7E, 0x84, 0x1E, + 0x1C, 0x80, 0x49, 0x7F, 0x4B, 0x00, 0xC9, 0x18, + 0x89, 0x1C, 0x40, 0x18, 0x10, 0x80, 0x10, 0xBD, + 0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, 0x89, 0x7E, + 0xF4, 0xE7, 0x08, 0x7E, 0x84, 0x1E, 0x1C, 0x80, + 0xC9, 0x7E, 0xEF, 0xE7, 0x48, 0x7E, 0x84, 0x1E, + 0x1C, 0x80, 0x09, 0x7F, 0xEA, 0xE7, 0x00, 0x00, + 0x98, 0x01, 0x00, 0x20, 0x3A, 0x00, 0x00, 0x20, + 0x3C, 0x00, 0x00, 0x20, 0xF8, 0xB5, 0x05, 0x46, + 0x20, 0x48, 0x00, 0x21, 0x01, 0x60, 0x41, 0x60, + 0x1F, 0x49, 0x01, 0x20, 0x08, 0x70, 0x1F, 0x48, + 0x1F, 0x4C, 0x00, 0x78, 0x00, 0x28, 0x06, 0xD0, + 0x2D, 0x26, 0x09, 0x27, 0x01, 0x28, 0x0D, 0xD0, + 0x02, 0x28, 0x29, 0xD1, 0x17, 0xE0, 0x1B, 0xA0, + 0x00, 0xF0, 0x5B, 0xFE, 0x5A, 0x20, 0x00, 0x2D, + 0x20, 0x70, 0x01, 0xD0, 0x00, 0x20, 0x0B, 0xE0, + 0x01, 0x20, 0x09, 0xE0, 0x18, 0xA0, 0x00, 0xF0, + 0x50, 0xFE, 0x00, 0x2D, 0x02, 0xD0, 0x26, 0x70, + 0x02, 0x20, 0x01, 0xE0, 0x27, 0x70, 0x03, 0x20, + 0x00, 0xF0, 0x0C, 0xF9, 0x10, 0xE0, 0x14, 0xA0, + 0x00, 0xF0, 0x43, 0xFE, 0x00, 0x2D, 0x02, 0xD0, + 0x26, 0x70, 0x02, 0x20, 0x01, 0xE0, 0x27, 0x70, + 0x03, 0x20, 0x00, 0xF0, 0xFF, 0xF8, 0x11, 0x48, + 0x81, 0x6A, 0x11, 0x4A, 0x11, 0x40, 0x81, 0x62, + 0x05, 0x20, 0x10, 0x49, 0x00, 0x02, 0x08, 0x60, + 0xF8, 0xBD, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, + 0x31, 0x00, 0x00, 0x20, 0x92, 0x01, 0x00, 0x20, + 0x94, 0x01, 0x00, 0x20, 0x41, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x64, 0x6C, 0x65, 0x0D, 0x0A, 0x00, 0x00, + 0x47, 0x65, 0x73, 0x74, 0x75, 0x72, 0x65, 0x0D, + 0x0A, 0x00, 0x00, 0x00, 0xC0, 0x11, 0x00, 0x50, + 0xFF, 0xFF, 0x00, 0xF8, 0x00, 0x10, 0x00, 0x50, + 0x70, 0xB5, 0x19, 0x4D, 0x19, 0x4C, 0x28, 0x70, + 0x02, 0x46, 0x21, 0x78, 0x18, 0xA0, 0x00, 0xF0, + 0x08, 0xFE, 0x1B, 0x49, 0x01, 0x20, 0x08, 0x70, + 0x00, 0xF0, 0x20, 0xF9, 0x00, 0xF0, 0x4E, 0xF8, + 0x01, 0x20, 0xFD, 0xF7, 0x4D, 0xFF, 0xFF, 0xF7, + 0xF5, 0xFB, 0x01, 0x20, 0xFF, 0xF7, 0x86, 0xFF, + 0x00, 0xF0, 0x42, 0xFC, 0xFE, 0xF7, 0x64, 0xFE, + 0x28, 0x78, 0x20, 0x70, 0x05, 0x28, 0x0F, 0xD0, + 0xFF, 0xF7, 0xAA, 0xFC, 0x00, 0x28, 0x0C, 0xD0, + 0x0E, 0x48, 0x00, 0x7A, 0x00, 0x28, 0x03, 0xD1, + 0x00, 0x21, 0x02, 0x20, 0x00, 0xF0, 0x48, 0xF8, + 0x0B, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x01, 0xD0, + 0x00, 0x20, 0x70, 0xBD, 0x01, 0x20, 0x70, 0xBD, + 0x92, 0x01, 0x00, 0x20, 0x0D, 0x08, 0x00, 0x20, + 0x53, 0x4D, 0x3D, 0x5B, 0x25, 0x64, 0x3A, 0x25, + 0x64, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0x91, 0x01, 0x00, 0x20, 0x20, 0x00, 0x00, 0x20, + 0x30, 0x00, 0x00, 0x20, 0x08, 0x49, 0x02, 0x20, + 0x08, 0x72, 0x08, 0x48, 0x01, 0x78, 0x08, 0x48, + 0x00, 0x29, 0x01, 0x68, 0x04, 0xD0, 0x01, 0x22, + 0x92, 0x02, 0x91, 0x43, 0x01, 0x60, 0x70, 0x47, + 0x01, 0x22, 0x11, 0x43, 0xFA, 0xE7, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x20, 0x31, 0x00, 0x00, 0x20, + 0x00, 0x10, 0x00, 0x50, 0x10, 0xB5, 0x08, 0x48, + 0x01, 0x68, 0x01, 0x22, 0x92, 0x02, 0x11, 0x43, + 0x01, 0x60, 0x00, 0x68, 0x05, 0x4C, 0xC0, 0x07, + 0x03, 0xD0, 0x02, 0x20, 0x20, 0x72, 0x00, 0xF0, + 0xC1, 0xF8, 0x00, 0x20, 0x20, 0x72, 0x10, 0xBD, + 0x00, 0x10, 0x00, 0x50, 0x20, 0x00, 0x00, 0x20, + 0x70, 0xB5, 0x05, 0x00, 0x0E, 0x46, 0x16, 0xD0, + 0xFF, 0xF7, 0x82, 0xFE, 0x00, 0x24, 0x6D, 0x1E, + 0x07, 0xE0, 0x00, 0xF0, 0xAF, 0xF8, 0xFF, 0xF7, + 0x57, 0xFB, 0xFF, 0xF7, 0xC3, 0xFF, 0x64, 0x1C, + 0xE4, 0xB2, 0xAC, 0x42, 0xF5, 0xDB, 0x00, 0xF0, + 0xA5, 0xF8, 0xFF, 0xF7, 0x4D, 0xFB, 0x00, 0x2E, + 0x02, 0xD0, 0xFF, 0xF7, 0xCF, 0xFF, 0x70, 0xBD, + 0xFF, 0xF7, 0xB4, 0xFF, 0x70, 0xBD, 0x00, 0x00, + 0x30, 0xB5, 0x01, 0x24, 0x1C, 0x4A, 0xA4, 0x07, + 0x23, 0x13, 0x1B, 0x49, 0x40, 0x32, 0x00, 0x28, + 0x21, 0xD0, 0x01, 0x28, 0x1F, 0xD0, 0x02, 0x28, + 0x01, 0xD0, 0x03, 0x28, 0x1A, 0xD1, 0x25, 0x68, + 0x1D, 0x43, 0x25, 0x60, 0x62, 0x23, 0x93, 0x61, + 0xD3, 0x61, 0x14, 0x4B, 0xCB, 0x63, 0x14, 0x49, + 0x09, 0x68, 0x40, 0x31, 0x02, 0x28, 0x48, 0x7E, + 0x17, 0xD0, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D, + 0x10, 0x60, 0x4B, 0x7E, 0x48, 0x7F, 0x41, 0x00, + 0x40, 0x18, 0x80, 0x1C, 0x18, 0x18, 0x40, 0x05, + 0x40, 0x0D, 0x90, 0x60, 0x30, 0xBD, 0x20, 0x68, + 0x98, 0x43, 0x20, 0x60, 0x01, 0x20, 0x90, 0x61, + 0xD0, 0x61, 0x00, 0x20, 0xC8, 0x63, 0x10, 0x60, + 0xF3, 0xE7, 0x40, 0x1E, 0x40, 0x05, 0x40, 0x0D, + 0x10, 0x60, 0x4B, 0x7E, 0x08, 0x7F, 0xE6, 0xE7, + 0x80, 0x11, 0x00, 0x50, 0x01, 0x00, 0x01, 0x00, + 0x98, 0x01, 0x00, 0x20, 0x70, 0xB5, 0x04, 0x46, + 0x81, 0x00, 0x25, 0x48, 0x41, 0x58, 0x25, 0x48, + 0x0A, 0x68, 0x42, 0x60, 0x4A, 0x68, 0x82, 0x60, + 0x8A, 0x68, 0x02, 0x61, 0xCA, 0x68, 0x42, 0x61, + 0x21, 0x4D, 0x08, 0x69, 0x28, 0x62, 0x1F, 0x4A, + 0x48, 0x69, 0xC0, 0x32, 0x90, 0x61, 0x88, 0x69, + 0xD0, 0x61, 0x48, 0x6A, 0x10, 0x62, 0x88, 0x6A, + 0x50, 0x62, 0x1A, 0x48, 0xCB, 0x69, 0x40, 0x38, + 0xC3, 0x60, 0x0B, 0x6A, 0x03, 0x61, 0xCB, 0x6A, + 0x93, 0x62, 0x0B, 0x6B, 0xD3, 0x63, 0x16, 0x4B, + 0x4E, 0x6B, 0x80, 0x3B, 0x1E, 0x61, 0x8E, 0x6B, + 0x5E, 0x62, 0xCE, 0x6B, 0xD6, 0x62, 0x0A, 0x6C, + 0x1A, 0x60, 0x4A, 0x6C, 0x5A, 0x61, 0x8A, 0x6C, + 0x9A, 0x62, 0xCA, 0x6C, 0xAA, 0x62, 0x0D, 0x4B, + 0x0A, 0x6D, 0x40, 0x33, 0x5A, 0x63, 0x0B, 0x4B, + 0x4A, 0x6D, 0x80, 0x33, 0x9A, 0x60, 0x0B, 0x4A, + 0x8B, 0x6D, 0x13, 0x60, 0xC9, 0x6D, 0x51, 0x60, + 0xC2, 0x68, 0x09, 0x49, 0x0A, 0x60, 0x00, 0x69, + 0x48, 0x60, 0x20, 0x46, 0xFF, 0xF7, 0x50, 0xFE, + 0x20, 0x46, 0xFF, 0xF7, 0x71, 0xFF, 0x70, 0xBD, + 0xD0, 0x05, 0x00, 0x20, 0x40, 0x10, 0x00, 0x50, + 0xC0, 0x11, 0x00, 0x50, 0x00, 0x19, 0x00, 0x50, + 0x20, 0x00, 0x00, 0x20, 0x70, 0xB5, 0x05, 0x20, + 0x40, 0x04, 0x00, 0x23, 0x0C, 0x4A, 0x0D, 0x49, + 0x0D, 0x4C, 0x05, 0xE0, 0x0D, 0x78, 0xED, 0x07, + 0x01, 0xD0, 0x13, 0x72, 0x09, 0xE0, 0x40, 0x1E, + 0x15, 0x7A, 0x02, 0x2D, 0x02, 0xD0, 0x25, 0x68, + 0xED, 0x07, 0x02, 0xD0, 0x00, 0x28, 0xF1, 0xD1, + 0x01, 0xE0, 0x00, 0x28, 0x03, 0xD1, 0x13, 0x72, + 0x04, 0xA0, 0x00, 0xF0, 0xC6, 0xFC, 0x70, 0xBD, + 0x20, 0x00, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20, + 0x00, 0x10, 0x00, 0x50, 0x57, 0x53, 0x46, 0x20, + 0x54, 0x4F, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0xF8, 0xB5, 0x1D, 0x48, 0x01, 0x25, 0x01, 0x68, + 0x00, 0x24, 0x02, 0x22, 0x91, 0x43, 0x1B, 0x4F, + 0x1B, 0x4E, 0x01, 0x60, 0x1B, 0x48, 0x00, 0x68, + 0x00, 0x19, 0xC0, 0x7E, 0x41, 0x28, 0x14, 0xD0, + 0x19, 0x48, 0x61, 0x00, 0x42, 0x5E, 0x04, 0x20, + 0x38, 0x5E, 0x82, 0x42, 0x09, 0xDA, 0x21, 0x46, + 0x16, 0xA0, 0x00, 0xF0, 0x9E, 0xFC, 0x30, 0x5D, + 0x02, 0x21, 0x08, 0x43, 0x30, 0x55, 0x00, 0x25, + 0x03, 0xE0, 0x30, 0x5D, 0xFD, 0x21, 0x08, 0x40, + 0x30, 0x55, 0x64, 0x1C, 0xE4, 0xB2, 0x30, 0x2C, + 0xE0, 0xD3, 0x15, 0x49, 0x01, 0x2D, 0x79, 0x5E, + 0x09, 0xD0, 0x14, 0xA0, 0x00, 0xF0, 0x89, 0xFC, + 0x05, 0x49, 0x02, 0x22, 0x08, 0x68, 0x10, 0x43, + 0x08, 0x60, 0x28, 0x46, 0xF8, 0xBD, 0x16, 0xA0, + 0x00, 0xF0, 0x7F, 0xFC, 0xF9, 0xE7, 0x00, 0x00, + 0x0C, 0x05, 0x00, 0x20, 0x84, 0x06, 0x00, 0x20, + 0xD8, 0x04, 0x00, 0x20, 0x98, 0x01, 0x00, 0x20, + 0xE4, 0x02, 0x00, 0x20, 0x53, 0x68, 0x6F, 0x72, + 0x74, 0x20, 0x4E, 0x47, 0x20, 0x44, 0x61, 0x74, + 0x61, 0x5B, 0x25, 0x64, 0x5D, 0x20, 0x3D, 0x20, + 0x25, 0x64, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x53, 0x68, 0x6F, 0x72, + 0x74, 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x54, + 0x48, 0x44, 0x3A, 0x25, 0x64, 0x29, 0x20, 0x5B, + 0x4E, 0x47, 0x5D, 0x0D, 0x0A, 0x00, 0x00, 0x00, + 0x53, 0x68, 0x6F, 0x72, 0x74, 0x20, 0x54, 0x65, + 0x73, 0x74, 0x28, 0x54, 0x48, 0x44, 0x3A, 0x25, + 0x64, 0x29, 0x20, 0x5B, 0x50, 0x61, 0x73, 0x73, + 0x5D, 0x0D, 0x0A, 0x00, 0x70, 0xB5, 0x0F, 0x48, + 0x01, 0x25, 0xC4, 0x7F, 0x0E, 0x48, 0x80, 0x88, + 0x00, 0x07, 0x01, 0xD4, 0x01, 0x20, 0x70, 0xBD, + 0xFE, 0xF7, 0xBE, 0xFB, 0xFE, 0xF7, 0x26, 0xFA, + 0xFF, 0xF7, 0x7E, 0xFF, 0x00, 0x28, 0x0A, 0xD1, + 0x00, 0x25, 0x00, 0x2C, 0x07, 0xD0, 0x64, 0x1E, + 0xE4, 0xB2, 0x21, 0x46, 0x05, 0xA0, 0x00, 0xF0, + 0x2C, 0xFC, 0x01, 0x25, 0xEE, 0xE7, 0x28, 0x46, + 0x70, 0xBD, 0x00, 0x00, 0xA8, 0x06, 0x00, 0x20, + 0x74, 0x06, 0x00, 0x20, 0x53, 0x68, 0x6F, 0x72, + 0x74, 0x20, 0x52, 0x65, 0x74, 0x72, 0x79, 0x20, + 0x28, 0x25, 0x64, 0x29, 0x0D, 0x0A, 0x00, 0x00, + 0xF8, 0xB5, 0x1B, 0x4E, 0x05, 0x46, 0x0C, 0x46, + 0x80, 0x21, 0x30, 0x46, 0xFD, 0xF7, 0xD1, 0xFC, + 0x00, 0x2C, 0x04, 0xD0, 0x31, 0x46, 0x28, 0x46, + 0x00, 0xF0, 0x42, 0xFA, 0x04, 0xE0, 0x80, 0x22, + 0x29, 0x46, 0x30, 0x46, 0xFD, 0xF7, 0xAC, 0xFC, + 0x29, 0x46, 0x12, 0xA0, 0x00, 0xF0, 0x01, 0xFC, + 0x00, 0x25, 0x14, 0x4F, 0x13, 0xE0, 0x00, 0x24, + 0x08, 0xE0, 0x68, 0x43, 0x00, 0x19, 0x40, 0x00, + 0x31, 0x5E, 0x11, 0xA0, 0x00, 0xF0, 0xF5, 0xFB, + 0x64, 0x1C, 0xE4, 0xB2, 0x38, 0x68, 0x00, 0x7E, + 0xA0, 0x42, 0xF2, 0xD8, 0x0E, 0xA0, 0x00, 0xF0, + 0xEC, 0xFB, 0x6D, 0x1C, 0xED, 0xB2, 0x38, 0x68, + 0x40, 0x7E, 0xA8, 0x42, 0xE7, 0xD8, 0x0A, 0xA0, + 0x00, 0xF0, 0xE3, 0xFB, 0xF8, 0xBD, 0x00, 0x00, + 0x4C, 0x00, 0x00, 0x20, 0x49, 0x6D, 0x61, 0x67, + 0x65, 0x3A, 0x30, 0x78, 0x25, 0x78, 0x0D, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20, + 0x25, 0x36, 0x64, 0x2C, 0x00, 0x00, 0x00, 0x00, + 0x0D, 0x0A, 0x00, 0x00, 0x08, 0x49, 0x8A, 0x78, + 0x52, 0x1E, 0x8A, 0x70, 0x4B, 0x78, 0x0A, 0x1D, + 0xD2, 0x5C, 0x02, 0x70, 0x48, 0x78, 0x40, 0x1C, + 0x48, 0x70, 0x48, 0x78, 0x10, 0x28, 0x01, 0xD1, + 0x00, 0x20, 0x48, 0x70, 0x70, 0x47, 0x00, 0x00, + 0xE0, 0x05, 0x00, 0x20, 0xF8, 0xB5, 0x01, 0x27, + 0x05, 0x46, 0xBF, 0x07, 0x38, 0x68, 0x08, 0x21, + 0x08, 0x43, 0x38, 0x60, 0x01, 0x23, 0x15, 0x48, + 0x80, 0x22, 0x02, 0x60, 0x14, 0x48, 0x00, 0x21, + 0x81, 0x70, 0x01, 0x70, 0x41, 0x70, 0xC3, 0x70, + 0x12, 0x4B, 0x98, 0x68, 0x02, 0x21, 0x88, 0x43, + 0x98, 0x60, 0xF8, 0x68, 0x10, 0x43, 0xF8, 0x60, + 0x0F, 0x4C, 0x61, 0x61, 0x0F, 0x48, 0x00, 0x68, + 0xE9, 0x00, 0x46, 0x06, 0x0E, 0x48, 0xFD, 0xF7, + 0x15, 0xFC, 0xE0, 0x60, 0x30, 0x20, 0xA0, 0x60, + 0x06, 0x49, 0x80, 0x20, 0x80, 0x39, 0x08, 0x60, + 0x08, 0x20, 0x78, 0x60, 0xE0, 0x68, 0x68, 0x43, + 0xC1, 0x00, 0x08, 0xA0, 0x00, 0xF0, 0x89, 0xFB, + 0xF8, 0xBD, 0x00, 0x00, 0x80, 0xE1, 0x00, 0xE0, + 0xE0, 0x05, 0x00, 0x20, 0x40, 0x09, 0x00, 0x50, + 0x00, 0x02, 0x00, 0x50, 0x00, 0x11, 0x00, 0x50, + 0x00, 0x36, 0x6E, 0x01, 0x55, 0x41, 0x52, 0x54, + 0x28, 0x25, 0x64, 0x29, 0x21, 0x0D, 0x0A, 0x00, + 0x70, 0xB5, 0x14, 0x4A, 0x91, 0x78, 0x14, 0x4C, + 0x0E, 0x29, 0x02, 0xD3, 0x61, 0x68, 0x49, 0x07, + 0xFC, 0xD5, 0x61, 0x69, 0x02, 0x25, 0xA9, 0x43, + 0x61, 0x61, 0x91, 0x78, 0x00, 0x26, 0x10, 0x29, + 0x0D, 0xD2, 0x0C, 0x49, 0x13, 0x78, 0x09, 0x1D, + 0xC8, 0x54, 0x10, 0x78, 0x40, 0x1C, 0x10, 0x70, + 0x10, 0x78, 0x10, 0x28, 0x00, 0xD1, 0x16, 0x70, + 0x90, 0x78, 0x40, 0x1C, 0x90, 0x70, 0xD0, 0x78, + 0x00, 0x28, 0x03, 0xD0, 0xD6, 0x70, 0x20, 0x46, + 0xFF, 0xF7, 0x80, 0xFF, 0x60, 0x69, 0x28, 0x43, + 0x60, 0x61, 0x70, 0xBD, 0xE0, 0x05, 0x00, 0x20, + 0x00, 0x02, 0x00, 0x50, 0xFE, 0xB5, 0x2F, 0x48, + 0x10, 0x26, 0x12, 0x27, 0x86, 0x5F, 0xC7, 0x5F, + 0x2D, 0x48, 0x01, 0x90, 0x2D, 0x48, 0x01, 0x25, + 0x01, 0x68, 0x2A, 0x04, 0x91, 0x43, 0x00, 0x24, + 0x01, 0x60, 0x2B, 0x48, 0x00, 0x68, 0x00, 0x19, + 0xC0, 0x7E, 0x41, 0x28, 0x26, 0xD0, 0x01, 0x99, + 0x60, 0x00, 0x09, 0x5A, 0x4A, 0x05, 0x52, 0x0D, + 0x26, 0x49, 0x53, 0x05, 0x0A, 0x52, 0x03, 0xD5, + 0x89, 0x23, 0x9B, 0x00, 0xD2, 0x1A, 0x0A, 0x52, + 0x23, 0x4A, 0x0B, 0x5A, 0x12, 0x5A, 0xD0, 0x1A, + 0x00, 0xB2, 0xB0, 0x42, 0x01, 0xDC, 0xB8, 0x42, + 0x0B, 0xDA, 0x00, 0x90, 0x21, 0x46, 0x1F, 0xA0, + 0x00, 0xF0, 0x17, 0xFB, 0x23, 0x48, 0x10, 0x22, + 0x01, 0x5D, 0x11, 0x43, 0x01, 0x55, 0x00, 0x25, + 0x04, 0xE0, 0x20, 0x48, 0xEF, 0x22, 0x01, 0x5D, + 0x11, 0x40, 0x01, 0x55, 0x64, 0x1C, 0xE4, 0xB2, + 0x30, 0x2C, 0xCE, 0xD3, 0x01, 0x2D, 0x18, 0xD0, + 0x10, 0x4A, 0x01, 0x20, 0x11, 0x68, 0x00, 0x04, + 0x01, 0x43, 0x19, 0xA0, 0x11, 0x60, 0x00, 0xF0, + 0xFC, 0xFA, 0x1C, 0xA0, 0x00, 0xF0, 0xF9, 0xFA, + 0x01, 0x21, 0x0C, 0x48, 0xFF, 0xF7, 0xDC, 0xFE, + 0x1A, 0xA0, 0x00, 0xF0, 0xF2, 0xFA, 0x01, 0x21, + 0x09, 0x48, 0xFF, 0xF7, 0xD5, 0xFE, 0x28, 0x46, + 0xFE, 0xBD, 0x19, 0xA0, 0x00, 0xF0, 0xE9, 0xFA, + 0xEB, 0xE7, 0x00, 0x00, 0x84, 0x06, 0x00, 0x20, + 0x00, 0x20, 0x00, 0x50, 0x0C, 0x05, 0x00, 0x20, + 0x98, 0x01, 0x00, 0x20, 0x10, 0x04, 0x00, 0x20, + 0xEC, 0x06, 0x00, 0x20, 0x55, 0x43, 0x5B, 0x25, + 0x64, 0x5D, 0x20, 0x4E, 0x47, 0x21, 0x20, 0x25, + 0x64, 0x2D, 0x25, 0x64, 0x3D, 0x25, 0x64, 0x0D, + 0x0A, 0x00, 0x00, 0x00, 0xD8, 0x04, 0x00, 0x20, + 0x55, 0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, + 0x5B, 0x4E, 0x47, 0x5D, 0x3A, 0x30, 0x78, 0x25, + 0x78, 0x0D, 0x0A, 0x00, 0x55, 0x43, 0x0D, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x6F, 0x6C, 0x64, + 0x65, 0x6E, 0x20, 0x55, 0x43, 0x0D, 0x0A, 0x00, + 0x55, 0x43, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, + 0x5B, 0x50, 0x61, 0x73, 0x73, 0x5D, 0x0D, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x70, 0xB5, 0x0E, 0x48, + 0x01, 0x25, 0x44, 0x79, 0x0D, 0x48, 0x80, 0x88, + 0xC0, 0x06, 0x01, 0xD4, 0x01, 0x20, 0x70, 0xBD, + 0xFF, 0xF7, 0x5C, 0xFF, 0x00, 0x28, 0x0C, 0xD1, + 0x00, 0x25, 0x00, 0x2C, 0x09, 0xD0, 0x64, 0x1E, + 0xE4, 0xB2, 0xFE, 0xF7, 0x0B, 0xF9, 0x21, 0x46, + 0x05, 0xA0, 0x00, 0xF0, 0x96, 0xFA, 0x01, 0x25, + 0xEE, 0xE7, 0x28, 0x46, 0x70, 0xBD, 0x00, 0x00, + 0xC8, 0x06, 0x00, 0x20, 0x74, 0x06, 0x00, 0x20, + 0x55, 0x43, 0x20, 0x4E, 0x47, 0x20, 0x52, 0x65, + 0x74, 0x72, 0x79, 0x20, 0x28, 0x25, 0x64, 0x29, + 0x0D, 0x0A, 0x00, 0x00, 0xF8, 0xB5, 0x3D, 0x4F, + 0x01, 0x24, 0x38, 0x7B, 0x3C, 0x49, 0x0A, 0x78, + 0x3C, 0x4E, 0x3D, 0x4D, 0x90, 0x42, 0x05, 0xD1, + 0x28, 0x78, 0x81, 0x28, 0x02, 0xD0, 0x30, 0x78, + 0xC0, 0x07, 0x63, 0xD0, 0x00, 0x20, 0x30, 0x70, + 0x35, 0x48, 0x00, 0x78, 0x07, 0x28, 0x0C, 0xD3, + 0x36, 0x48, 0x00, 0x68, 0x40, 0x05, 0x40, 0x0F, + 0x38, 0x73, 0x01, 0x20, 0xFD, 0xF7, 0xDC, 0xFC, + 0x00, 0x20, 0x30, 0x70, 0x38, 0x7B, 0x02, 0x28, + 0x13, 0xD0, 0x38, 0x7B, 0x2C, 0x4F, 0x38, 0x70, + 0x81, 0x20, 0x28, 0x70, 0x2E, 0x4A, 0x01, 0x21, + 0x10, 0x88, 0x09, 0x03, 0x08, 0x43, 0x10, 0x80, + 0x38, 0x78, 0x03, 0x00, 0xFD, 0xF7, 0x76, 0xFB, + 0x07, 0x25, 0x27, 0x37, 0x3D, 0x0B, 0x18, 0x0B, + 0x3D, 0x00, 0x83, 0x20, 0x28, 0x70, 0x05, 0x20, + 0xFF, 0xF7, 0x36, 0xFC, 0xF8, 0xBD, 0x25, 0xA0, + 0x00, 0xF0, 0x43, 0xFA, 0x01, 0x20, 0xFE, 0xF7, + 0x1B, 0xFE, 0x00, 0x20, 0xFF, 0xF7, 0x2C, 0xFC, + 0x00, 0x28, 0x1E, 0xD0, 0x01, 0x20, 0x1A, 0xE0, + 0x20, 0xA0, 0x00, 0xF0, 0x36, 0xFA, 0x01, 0x20, + 0xFE, 0xF7, 0x0E, 0xFE, 0x01, 0x20, 0xFF, 0xF7, + 0x1F, 0xFC, 0x00, 0x28, 0x11, 0xD0, 0x04, 0x20, + 0x0D, 0xE0, 0x1C, 0xA0, 0x00, 0xE0, 0x1D, 0xA0, + 0x00, 0xF0, 0x27, 0xFA, 0x00, 0x20, 0xFE, 0xF7, + 0xFF, 0xFD, 0x02, 0x20, 0xFF, 0xF7, 0x10, 0xFC, + 0x00, 0x28, 0x02, 0xD0, 0x02, 0x20, 0x28, 0x70, + 0x07, 0xE0, 0x00, 0x24, 0x05, 0xE0, 0x83, 0x20, + 0x28, 0x70, 0x05, 0x20, 0xFF, 0xF7, 0x04, 0xFC, + 0x04, 0x46, 0x2A, 0x78, 0x39, 0x78, 0x13, 0xA0, + 0x00, 0xF0, 0x0F, 0xFA, 0x30, 0x78, 0xC0, 0x07, + 0x02, 0xD0, 0x81, 0x20, 0x28, 0x70, 0x00, 0x24, + 0x20, 0x46, 0xF8, 0xBD, 0x20, 0x00, 0x00, 0x20, + 0x39, 0x01, 0x00, 0x20, 0x30, 0x00, 0x00, 0x20, + 0x20, 0x08, 0x00, 0x20, 0x00, 0x11, 0x00, 0x50, + 0x4A, 0x01, 0x00, 0x20, 0x44, 0x41, 0x0D, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x44, 0x49, 0x0D, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x44, 0x53, 0x54, 0x42, + 0x0D, 0x0A, 0x00, 0x00, 0x44, 0x47, 0x0D, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x44, 0x53, 0x50, 0x3D, + 0x25, 0x64, 0x2C, 0x50, 0x57, 0x52, 0x3D, 0x25, + 0x64, 0x20, 0x0D, 0x0A, 0x00, 0x00, 0x00, 0x00, + 0x70, 0xB5, 0x05, 0x46, 0x01, 0x24, 0xFE, 0xF7, + 0x83, 0xFA, 0x00, 0x2D, 0x09, 0xD0, 0xF0, 0x20, + 0xFD, 0xF7, 0xCA, 0xFA, 0xFE, 0xF7, 0x4A, 0xFB, + 0xFE, 0xF7, 0xE0, 0xFA, 0x00, 0x20, 0xFE, 0xF7, + 0xAB, 0xFD, 0xFE, 0xF7, 0xDF, 0xFD, 0xFF, 0xF7, + 0xDF, 0xFA, 0x20, 0x46, 0x70, 0xBD, 0x00, 0x00, + 0x30, 0xB5, 0x08, 0x4A, 0x14, 0x68, 0x1B, 0x34, + 0x00, 0x22, 0xA3, 0x5C, 0x41, 0x2B, 0x03, 0xD0, + 0x55, 0x00, 0x45, 0x5B, 0x5B, 0x00, 0xCD, 0x52, + 0x52, 0x1C, 0xD2, 0xB2, 0x30, 0x2A, 0xF4, 0xD3, + 0x30, 0xBD, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20, + 0x70, 0xB5, 0x17, 0x4D, 0x16, 0x20, 0x28, 0x70, + 0x68, 0x78, 0x29, 0x46, 0x00, 0x24, 0x14, 0x31, + 0x02, 0x28, 0x0C, 0xD8, 0x00, 0x28, 0x05, 0xD1, + 0xA8, 0x78, 0x00, 0x28, 0x02, 0xD0, 0x08, 0x46, + 0x1C, 0x30, 0x04, 0x70, 0x48, 0x88, 0x00, 0x28, + 0x01, 0xD1, 0x03, 0x20, 0x68, 0x71, 0xAC, 0x70, + 0x2C, 0x81, 0xEC, 0x80, 0xAC, 0x81, 0x6C, 0x81, + 0x4C, 0x80, 0x8C, 0x80, 0x0C, 0x72, 0x09, 0x48, + 0x44, 0x70, 0x84, 0x70, 0x04, 0x70, 0x06, 0x48, + 0xA0, 0x21, 0x30, 0x30, 0xFD, 0xF7, 0x4D, 0xFA, + 0xEC, 0x70, 0x5A, 0x20, 0xE8, 0x81, 0x2C, 0x71, + 0x2C, 0x61, 0x00, 0xF0, 0x11, 0xFA, 0x70, 0xBD, + 0x0C, 0x08, 0x00, 0x20, 0x90, 0x02, 0x00, 0x20, + 0x06, 0x4A, 0x07, 0x4B, 0x00, 0x21, 0x00, 0x20, + 0x40, 0x1C, 0x90, 0x42, 0xFC, 0xDB, 0x49, 0x1C, + 0x99, 0x42, 0xF8, 0xDB, 0x03, 0x48, 0x01, 0x21, + 0x01, 0x60, 0x70, 0x47, 0x10, 0x27, 0x00, 0x00, + 0xB8, 0x0B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, + 0x70, 0xB5, 0x7B, 0x24, 0x21, 0x48, 0x24, 0x02, + 0x04, 0x60, 0xF7, 0x20, 0xC0, 0x01, 0x20, 0x4A, + 0xC1, 0x7C, 0x11, 0x70, 0x1F, 0x4A, 0x01, 0x7D, + 0x11, 0x70, 0x1F, 0x4A, 0x41, 0x7D, 0x11, 0x70, + 0x1E, 0x49, 0x80, 0x7D, 0x08, 0x70, 0x60, 0x7C, + 0x21, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, + 0x1B, 0x49, 0x05, 0x46, 0x08, 0x80, 0xE0, 0x7C, + 0xA1, 0x7C, 0x00, 0x06, 0x00, 0x14, 0x08, 0x43, + 0x18, 0x49, 0x08, 0x80, 0x20, 0x7E, 0x61, 0x7E, + 0x48, 0x43, 0x17, 0x49, 0x08, 0x80, 0x17, 0x48, + 0xFD, 0xF7, 0x0F, 0xFA, 0x10, 0x21, 0xC8, 0x41, + 0x15, 0x49, 0x08, 0x60, 0x04, 0x22, 0x21, 0x1D, + 0x14, 0x48, 0xFD, 0xF7, 0xE1, 0xF9, 0x11, 0x49, + 0x06, 0x22, 0x89, 0x1F, 0x12, 0x48, 0xFD, 0xF7, + 0xDB, 0xF9, 0x60, 0x7D, 0x22, 0x7D, 0x01, 0x02, + 0x11, 0x43, 0x28, 0x46, 0x0A, 0x22, 0x50, 0x43, + 0xFD, 0xF7, 0xBE, 0xF9, 0x0D, 0x49, 0x08, 0x80, + 0x70, 0xBD, 0x00, 0x00, 0x98, 0x01, 0x00, 0x20, + 0x0C, 0x00, 0x00, 0x20, 0x0D, 0x00, 0x00, 0x20, + 0x0E, 0x00, 0x00, 0x20, 0x0F, 0x00, 0x00, 0x20, + 0x1C, 0x00, 0x00, 0x20, 0x1E, 0x00, 0x00, 0x20, + 0x18, 0x00, 0x00, 0x20, 0x10, 0x7B, 0x00, 0x00, + 0xD8, 0x06, 0x00, 0x20, 0xE0, 0x06, 0x00, 0x20, + 0xE4, 0x06, 0x00, 0x20, 0x1A, 0x00, 0x00, 0x20, + 0x00, 0x20, 0x0E, 0x49, 0x08, 0x70, 0x17, 0xE0, + 0x0C, 0x48, 0x00, 0x78, 0x00, 0x28, 0x02, 0xD0, + 0x07, 0x28, 0x0F, 0xD1, 0x0B, 0xE0, 0x00, 0xF0, + 0x13, 0xF8, 0x01, 0x28, 0x03, 0xD1, 0x07, 0x20, + 0x06, 0x49, 0x08, 0x70, 0x02, 0xE0, 0x03, 0x20, + 0x04, 0x49, 0x08, 0x70, 0x03, 0xE0, 0xFD, 0xF7, + 0x63, 0xFA, 0x00, 0xE0, 0x00, 0xBF, 0x00, 0xBF, + 0xE6, 0xE7, 0x00, 0x00, 0xD2, 0x00, 0x00, 0x20, + 0x10, 0xB5, 0x00, 0x24, 0xFD, 0xF7, 0xC4, 0xFC, + 0x00, 0xF0, 0xF2, 0xF8, 0x01, 0x20, 0xFF, 0xF7, + 0x03, 0xFF, 0x04, 0x46, 0x00, 0x2C, 0x04, 0xD1, + 0x05, 0xA0, 0x00, 0xF0, 0xDE, 0xF8, 0x00, 0x20, + 0x10, 0xBD, 0x06, 0x49, 0x06, 0x48, 0x81, 0x70, + 0x09, 0x0A, 0xC1, 0x70, 0x01, 0x20, 0xF7, 0xE7, + 0x49, 0x4E, 0x49, 0x54, 0x20, 0x4E, 0x47, 0x0D, + 0x0A, 0x00, 0x00, 0x00, 0xAA, 0x55, 0x00, 0x00, + 0x88, 0x02, 0x00, 0x20, 0xF7, 0xB5, 0x00, 0x25, + 0x84, 0xB0, 0x0C, 0x46, 0x16, 0x46, 0x61, 0x27, + 0x5C, 0xE0, 0x25, 0x28, 0x54, 0xD1, 0x64, 0x1C, + 0x00, 0x22, 0x20, 0x78, 0x13, 0x46, 0x00, 0x28, + 0x57, 0xD0, 0x25, 0x28, 0x4C, 0xD0, 0x2D, 0x28, + 0x01, 0xD1, 0x64, 0x1C, 0x01, 0x23, 0x02, 0x20, + 0x21, 0x78, 0x30, 0x29, 0x07, 0xD1, 0x64, 0x1C, + 0x03, 0x43, 0xF9, 0xE7, 0x0A, 0x21, 0x4A, 0x43, + 0x30, 0x3A, 0x82, 0x18, 0x64, 0x1C, 0x20, 0x78, + 0x01, 0x46, 0x30, 0x39, 0x09, 0x29, 0xF5, 0xD9, + 0xC1, 0xB2, 0x73, 0x29, 0x0A, 0xD0, 0x64, 0x28, + 0x10, 0xD0, 0x78, 0x28, 0x13, 0xD0, 0x58, 0x28, + 0x19, 0xD0, 0x75, 0x28, 0x1F, 0xD0, 0x63, 0x28, + 0x23, 0xD0, 0x2E, 0xE0, 0x02, 0xCE, 0x00, 0x29, + 0x00, 0xD1, 0x1D, 0xA1, 0x04, 0x98, 0x00, 0xF0, + 0xD3, 0xF8, 0x0A, 0xE0, 0x68, 0x46, 0x8C, 0xC0, + 0x02, 0xCE, 0x01, 0x23, 0x13, 0xE0, 0x68, 0x46, + 0x8C, 0xC0, 0x08, 0xE0, 0x04, 0x98, 0x00, 0xF0, + 0x3C, 0xF8, 0x45, 0x19, 0x19, 0xE0, 0x41, 0x20, + 0x01, 0x93, 0x00, 0x92, 0x02, 0x90, 0x02, 0xCE, + 0x00, 0x23, 0x10, 0x22, 0xF2, 0xE7, 0x68, 0x46, + 0x8C, 0xC0, 0x02, 0xCE, 0x00, 0x23, 0x0A, 0x22, + 0xEC, 0xE7, 0x02, 0xCE, 0x68, 0x46, 0x01, 0x73, + 0x00, 0x21, 0x41, 0x73, 0x03, 0xA9, 0xD9, 0xE7, + 0xC1, 0xB2, 0x04, 0x98, 0x00, 0xF0, 0x14, 0xF8, + 0x6D, 0x1C, 0x64, 0x1C, 0x20, 0x78, 0x00, 0x28, + 0x9F, 0xD1, 0x04, 0x98, 0x00, 0x28, 0x03, 0xD0, + 0x04, 0x99, 0x00, 0x20, 0x09, 0x68, 0x08, 0x70, + 0x28, 0x46, 0x07, 0xB0, 0xF0, 0xBD, 0x00, 0x00, + 0x28, 0x6E, 0x75, 0x6C, 0x6C, 0x29, 0x00, 0x00, + 0x10, 0xB5, 0x00, 0x28, 0x05, 0xD0, 0x02, 0x68, + 0x11, 0x70, 0x01, 0x68, 0x49, 0x1C, 0x01, 0x60, + 0x10, 0xBD, 0xC8, 0xB2, 0xFF, 0xF7, 0xD4, 0xFC, + 0x10, 0xBD, 0xFF, 0xB5, 0x00, 0x27, 0x83, 0xB0, + 0x0C, 0x9D, 0x3E, 0x46, 0x08, 0x00, 0x3A, 0x46, + 0x05, 0xD0, 0x00, 0x2B, 0x12, 0xD0, 0x05, 0x9B, + 0x0A, 0x2B, 0x0B, 0xD0, 0x0E, 0xE0, 0x30, 0x20, + 0x69, 0x46, 0x08, 0x70, 0x4A, 0x70, 0x2A, 0x46, + 0x0D, 0x9B, 0x03, 0x98, 0x00, 0xF0, 0x74, 0xF8, + 0x07, 0xB0, 0xF0, 0xBD, 0x00, 0x29, 0x01, 0xDA, + 0x01, 0x27, 0x40, 0x42, 0x02, 0xAC, 0x69, 0x46, + 0x03, 0x34, 0xCA, 0x72, 0x0A, 0xE0, 0x05, 0x99, + 0xFD, 0xF7, 0xA0, 0xF8, 0x0A, 0x29, 0x02, 0xDB, + 0x0E, 0x9A, 0x89, 0x18, 0x3A, 0x39, 0x30, 0x31, + 0x64, 0x1E, 0x21, 0x70, 0x00, 0x28, 0xF2, 0xD1, + 0x00, 0x2F, 0x0E, 0xD0, 0x00, 0x2D, 0x09, 0xD0, + 0x0D, 0x98, 0x80, 0x07, 0x06, 0xD5, 0x2D, 0x21, + 0x03, 0x98, 0xFF, 0xF7, 0xB9, 0xFF, 0x76, 0x1C, + 0x6D, 0x1E, 0x02, 0xE0, 0x2D, 0x20, 0x64, 0x1E, + 0x20, 0x70, 0x2A, 0x46, 0x21, 0x46, 0x0D, 0x9B, + 0x03, 0x98, 0x00, 0xF0, 0x45, 0xF8, 0x80, 0x19, + 0xCE, 0xE7, 0x0F, 0xB4, 0x10, 0xB5, 0x03, 0xAA, + 0x00, 0x20, 0x02, 0x99, 0xFF, 0xF7, 0x2E, 0xFF, + 0x10, 0xBC, 0x08, 0xBC, 0x04, 0xB0, 0x18, 0x47, + 0x10, 0xB5, 0xFF, 0xF7, 0x85, 0xFE, 0x00, 0xF0, + 0x8D, 0xF8, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x03, 0x35, 0x03, 0x2C, 0x03, 0x9C, 0x03, + 0xE9, 0x02, 0x28, 0x03, 0x29, 0x03, 0x1C, 0x03, + 0x9D, 0x03, 0x2F, 0x03, 0x4A, 0x03, 0x4D, 0x03, + 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0x03, + 0x3D, 0x03, 0x3C, 0x03, 0x2C, 0x03, 0x9C, 0x03, + 0xD3, 0x02, 0x28, 0x03, 0x29, 0x03, 0xD6, 0x02, + 0x9E, 0x03, 0x30, 0x03, 0x37, 0x03, 0x3A, 0x03, + 0xFF, 0x03, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xB5, 0x00, 0x25, 0x20, 0x27, 0x81, 0xB0, + 0x0E, 0x46, 0x14, 0x46, 0x00, 0x2A, 0x0E, 0xDD, + 0x00, 0x20, 0x01, 0xE0, 0x40, 0x1C, 0x49, 0x1C, + 0x0A, 0x78, 0x00, 0x2A, 0xFA, 0xD1, 0xA0, 0x42, + 0x01, 0xDB, 0x00, 0x24, 0x00, 0xE0, 0x24, 0x1A, + 0x98, 0x07, 0x00, 0xD5, 0x30, 0x27, 0xD8, 0x07, + 0x06, 0xD0, 0x0D, 0xE0, 0x39, 0x46, 0x01, 0x98, + 0xFF, 0xF7, 0x4A, 0xFF, 0x6D, 0x1C, 0x64, 0x1E, + 0x00, 0x2C, 0xF7, 0xDC, 0x04, 0xE0, 0x01, 0x98, + 0xFF, 0xF7, 0x42, 0xFF, 0x6D, 0x1C, 0x76, 0x1C, + 0x31, 0x78, 0x00, 0x29, 0xF7, 0xD1, 0x05, 0xE0, + 0x39, 0x46, 0x01, 0x98, 0xFF, 0xF7, 0x38, 0xFF, + 0x6D, 0x1C, 0x64, 0x1E, 0x00, 0x2C, 0xF7, 0xDC, + 0x28, 0x46, 0x05, 0xB0, 0xF0, 0xBD, 0x00, 0x00, + 0x05, 0x48, 0x01, 0x78, 0x82, 0x29, 0x05, 0xD1, + 0xC0, 0x79, 0x01, 0x28, 0x02, 0xD1, 0x03, 0x49, + 0xFF, 0x20, 0x08, 0x70, 0x70, 0x47, 0x00, 0x00, + 0x20, 0x08, 0x00, 0x20, 0x40, 0x01, 0x00, 0x20, + 0x05, 0x48, 0x00, 0x21, 0x01, 0x80, 0x41, 0x80, + 0x04, 0x49, 0x81, 0x80, 0xC1, 0x80, 0x04, 0x49, + 0x03, 0x20, 0x08, 0x70, 0x70, 0x47, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x20, 0xFF, 0x7F, 0x00, 0x00, + 0x90, 0x01, 0x00, 0x20, 0x08, 0x49, 0x5A, 0x20, + 0x08, 0x70, 0x08, 0x49, 0x00, 0x20, 0x08, 0x70, + 0x08, 0x48, 0x07, 0x49, 0x81, 0x80, 0xC1, 0x80, + 0x07, 0x49, 0x03, 0x20, 0x08, 0x70, 0x07, 0x49, + 0x01, 0x20, 0x08, 0x70, 0x70, 0x47, 0x00, 0x00, + 0x94, 0x01, 0x00, 0x20, 0x0F, 0x08, 0x00, 0x20, + 0xFF, 0x7F, 0x00, 0x00, 0x10, 0x00, 0x00, 0x20, + 0x90, 0x01, 0x00, 0x20, 0x91, 0x01, 0x00, 0x20, + 0x00, 0x00, 0x00, 0x40, 0x10, 0x03, 0x42, 0x88, + 0x0C, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x09, 0x00, 0x50, 0x1F, 0x00, 0x00, 0x00, + 0x04, 0x03, 0x00, 0x50, 0x64, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x50, 0x77, 0x00, 0x01, 0x48, + 0x44, 0x09, 0x00, 0x50, 0x39, 0x5A, 0x5B, 0x00, + 0x10, 0x06, 0x00, 0x50, 0x00, 0x00, 0x06, 0x07, + 0x00, 0x06, 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, + 0x08, 0x06, 0x00, 0x50, 0x0C, 0x30, 0x00, 0x00, + 0x28, 0x06, 0x00, 0x50, 0x06, 0x00, 0x00, 0x00, + 0x2C, 0x06, 0x00, 0x50, 0x0A, 0x66, 0x00, 0x00, + 0x30, 0x06, 0x00, 0x50, 0xCC, 0x02, 0x00, 0x20, + 0x34, 0x06, 0x00, 0x50, 0x00, 0x20, 0x00, 0x00, + 0x44, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00, + 0x1C, 0x00, 0x00, 0x50, 0x50, 0x71, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x50, 0x24, 0x29, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x40, 0x04, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x14, 0x33, 0x43, 0xC8, + 0x0C, 0x00, 0x00, 0x40, 0x29, 0x0A, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x40, 0x10, 0x32, 0x00, 0x00, + 0x1C, 0x0E, 0x00, 0x50, 0x03, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x50, 0x14, 0x07, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x2C, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x10, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00, + 0x04, 0x11, 0x00, 0x50, 0x78, 0x11, 0x00, 0x00, + 0x0C, 0x11, 0x00, 0x50, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x11, 0x00, 0x50, 0x78, 0x01, 0x00, 0x00, + 0x14, 0x11, 0x00, 0x50, 0xC8, 0x03, 0x60, 0x00, + 0x4C, 0x00, 0x00, 0x50, 0x31, 0x00, 0x00, 0x00, + 0x3C, 0x10, 0x00, 0x50, 0x00, 0x00, 0x10, 0x00, + 0xB4, 0x10, 0x00, 0x50, 0x00, 0x26, 0x31, 0x00, + 0xC0, 0x10, 0x00, 0x50, 0x33, 0x03, 0x33, 0x03, + 0xC4, 0x10, 0x00, 0x50, 0x33, 0x03, 0x33, 0x03, + 0xC8, 0x10, 0x00, 0x50, 0x0C, 0x0A, 0x00, 0x00, + 0xCC, 0x10, 0x00, 0x50, 0x1A, 0x00, 0x00, 0x00, + 0xD0, 0x10, 0x00, 0x50, 0x03, 0x19, 0x19, 0x00, + 0xF0, 0x11, 0x00, 0x50, 0x12, 0x00, 0x00, 0x00, + 0xEC, 0x11, 0x00, 0x50, 0x5C, 0x00, 0x00, 0x00, + 0xF4, 0x11, 0x00, 0x50, 0x01, 0x00, 0x01, 0x00, + 0x2C, 0x10, 0x00, 0x50, 0x10, 0x00, 0x90, 0x00, + 0x30, 0x10, 0x00, 0x50, 0x20, 0x0C, 0x90, 0x00, + 0x34, 0x10, 0x00, 0x50, 0x30, 0x0C, 0x30, 0x0C, + 0x38, 0x10, 0x00, 0x50, 0xFF, 0x0F, 0x00, 0x00, + 0x7C, 0x10, 0x00, 0x50, 0x88, 0x88, 0xFE, 0x88, + 0x80, 0x10, 0x00, 0x50, 0x88, 0xFF, 0x00, 0x00, + 0x84, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55, + 0x88, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55, + 0x8C, 0x10, 0x00, 0x50, 0x55, 0x55, 0x55, 0x55, + 0xE8, 0x10, 0x00, 0x50, 0x3F, 0x16, 0x3F, 0x15, + 0x04, 0x00, 0x00, 0x40, 0x04, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x3B, 0x00, 0x00, 0x04, 0x00, 0x00, 0x20, + 0x08, 0x00, 0x00, 0x00, 0x5C, 0x0A, 0x00, 0x00, + 0x38, 0x3B, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x20, + 0x54, 0x0E, 0x00, 0x00, 0x6A, 0x0A, 0x00, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x78, 0x7F, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x52, 0x57, 0x41, 0x66, }; const unsigned char u8_rad_testpara_30[] = { -0xA2, 0x00, 0x03, 0xF3, 0x02, 0x02, 0x40, 0x04, -0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, -0x40, 0x01, 0x68, 0x01, 0xFA, 0x00, 0x18, 0x01, -0x05, 0x05, 0x00, 0x00, 0x01, 0x05, 0x41, 0x06, -0x0A, 0x0B, 0x41, 0x0F, 0x10, 0x14, 0x41, 0x15, -0x02, 0x07, 0x41, 0x16, 0x41, 0x41, 0x41, 0x41, -0x41, 0x41, 0x41, 0x04, 0x03, 0x09, 0x41, 0x08, -0x0E, 0x0D, 0x41, 0x13, 0x12, 0x18, 0x41, 0x17, -0x0C, 0x11, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, -0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, -0x0C, 0x3E, 0x0C, 0x3E, 0x06, 0x06, 0x02, 0x02, -0x14, 0x03, 0x06, 0x06, 0x01, 0x01, 0xE6, 0x00, -0x1F, 0x01, 0x08, 0x14, 0x82, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, 0x01, -0xB2, 0x00, 0x73, 0x00, 0x3C, 0x00, 0x09, 0x78, -0x0F, 0x08, 0x1D, 0x40, 0x48, 0x40, 0x48, 0x00, -0x00, 0x00, 0x88, 0x00, 0x62, 0x00, 0x24, 0x00, -0x06, 0x32, 0x6C, 0x41, 0x0F, 0x00, 0x00, 0x45, -0x4C, 0x00, 0x00, 0x06, 0x00, 0xA0, 0x00, 0x78, -0x00, 0x64, 0xCE, 0x15, 0xF4, 0x0D, 0x7D, 0x03, -0xDF, 0x00, 0x16, 0x0B, 0xD8, 0x01, 0x7C, 0x06, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x55, 0xAA, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0x01, 0xA8, 0x56, 0x2E, 0xE0, 0x85, 0x46, 0x49, + 0xA1, 0x00, 0x03, 0xF3, 0x02, 0x02, 0x40, 0x01, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x01, 0x68, 0x01, 0x44, 0x01, 0x44, 0x01, + 0x05, 0x05, 0x00, 0x00, 0x01, 0x05, 0x41, 0x06, + 0x0A, 0x0B, 0x41, 0x0F, 0x10, 0x14, 0x41, 0x15, + 0x02, 0x07, 0x41, 0x16, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x04, 0x03, 0x09, 0x41, 0x08, + 0x0E, 0x0D, 0x41, 0x13, 0x12, 0x18, 0x41, 0x17, + 0x0C, 0x11, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x3E, 0x0C, 0x3E, 0x08, 0x08, 0x02, 0x02, + 0x14, 0x03, 0x08, 0x08, 0x01, 0x01, 0xC8, 0x00, + 0x1A, 0x01, 0x08, 0x14, 0x82, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, 0x01, + 0xA0, 0x00, 0x64, 0x00, 0x4A, 0x00, 0x0A, 0x78, + 0x0F, 0x08, 0x1A, 0x4A, 0x4A, 0x3C, 0x3C, 0x00, + 0x00, 0x00, 0x6A, 0x00, 0x38, 0x00, 0x1C, 0x00, + 0x06, 0x32, 0x69, 0xE3, 0x0D, 0x00, 0x00, 0x72, + 0x45, 0x00, 0x00, 0x06, 0x00, 0xA0, 0x00, 0x78, + 0x00, 0x64, 0xDB, 0x13, 0xB5, 0x0C, 0x2D, 0x03, + 0xCB, 0x00, 0x14, 0x0A, 0xB1, 0x01, 0xF1, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x55, 0xAA, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x09, 0x5D, 0xA5, 0xB2, 0x5E, 0xA0, 0x80, 0x7C, }; diff --git a/raydium/raydium_driver.h b/raydium/raydium_driver.h index 9a9e98a3fa..5a9d138f5a 100644 --- a/raydium/raydium_driver.h +++ b/raydium/raydium_driver.h @@ -245,7 +245,7 @@ #define PARA_FW_VERSION_OFFSET 4 #define ENABLE_FW_LOADER 1 -#define FW_NAME "RM6D030.bin" +#define FW_NAME "RM6D030_v0.1.bin" #define PINCTRL_STATE_ACTIVE "pmx_ts_active" #define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" From 5dbb06d45e4211fc357175d5d887a570a8dd1d62 Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Wed, 10 May 2023 05:54:42 +0530 Subject: [PATCH 151/170] touch: raydium: Touch_offload using debugfs Enablement of Debugfs manual touch_offload for raydium touch sensor. Change-Id: I1e3297f5958d7e2264fc3a3687feeef45620585f Signed-off-by: Srikanth Katteboina --- Android.bp | 7 + Android.mk | 11 ++ Kbuild | 9 ++ config/gki_monacotouch.conf | 1 + config/gki_monacotouchconf.h | 1 + glink_interface_ts/glink_interface.c | 121 ++++++++++++++++++ glink_interface_ts/glink_interface.h | 73 +++++++++++ raydium/raydium_driver.c | 34 ++++- raydium/raydium_sysfs.c | 185 +++++++++++++++++++++++++++ touch_driver_board.mk | 1 + touch_driver_product.mk | 1 + 11 files changed, 442 insertions(+), 2 deletions(-) create mode 100644 Android.bp create mode 100644 glink_interface_ts/glink_interface.c create mode 100644 glink_interface_ts/glink_interface.h diff --git a/Android.bp b/Android.bp new file mode 100644 index 0000000000..75d9b80723 --- /dev/null +++ b/Android.bp @@ -0,0 +1,7 @@ +cc_library_headers { + name: "qti_glink_touch_kernel_headers", + export_include_dirs: [ + "glink_interface_ts", + ], + vendor_available: true, +} diff --git a/Android.mk b/Android.mk index bd41ca7273..0a8b956d13 100644 --- a/Android.mk +++ b/Android.mk @@ -92,6 +92,17 @@ ifeq ($(TARGET_BOARD_PLATFORM), monaco) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := glink_comm.ko + LOCAL_MODULE_KBUILD_NAME := glink_comm.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + ########################################################### include $(CLEAR_VARS) LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) diff --git a/Kbuild b/Kbuild index 392d00f3df..298f4bf844 100644 --- a/Kbuild +++ b/Kbuild @@ -156,6 +156,15 @@ ifeq ($(CONFIG_TOUCHSCREEN_DUMMY), y) obj-$(CONFIG_MSM_TOUCH) += dummy_ts.o endif +ifeq ($(CONFIG_TOUCHSCREEN_MSM_GLINK), y) + + LINUXINCLUDE += -I$(TOUCH_ROOT)/glink_interface_ts + + glink_comm-y := ./glink_interface_ts/glink_interface.o + + obj-$(CONFIG_MSM_TOUCH) += glink_comm.o +endif + ifeq ($(CONFIG_TOUCHSCREEN_SYNAPTICS_TCM), y) synaptics_tcm_ts-y := \ ./synaptics_tcm/synaptics_tcm_core.o \ diff --git a/config/gki_monacotouch.conf b/config/gki_monacotouch.conf index 810b5c9c49..cfa8246980 100644 --- a/config/gki_monacotouch.conf +++ b/config/gki_monacotouch.conf @@ -4,3 +4,4 @@ export CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT=y export CONFIG_TOUCHSCREEN_PARADE_I2C=y export CONFIG_TOUCHSCREEN_PARADE_DEVICE_ACCESS=y export CONFIG_TOUCHSCREEN_RM_TS=y +export CONFIG_TOUCHSCREEN_MSM_GLINK=y diff --git a/config/gki_monacotouchconf.h b/config/gki_monacotouchconf.h index 34f79b80d4..e940a0f454 100644 --- a/config/gki_monacotouchconf.h +++ b/config/gki_monacotouchconf.h @@ -5,3 +5,4 @@ #define CONFIG_TOUCHSCREEN_PARADE_BUTTON 1 #define CONFIG_TOUCHSCREEN_PARADE_PROXIMITY 1 #define CONFIG_TOUCHSCREEN_RM_TS 1 +#define CONFIG_TOUCHSCREEN_MSM_GLINK 1 diff --git a/glink_interface_ts/glink_interface.c b/glink_interface_ts/glink_interface.c new file mode 100644 index 0000000000..9281d17639 --- /dev/null +++ b/glink_interface_ts/glink_interface.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include + +static struct glink_touch_dev *touch_pdev = NULL; +struct touch_channel_ops touch_ops; + + +void glink_touch_channel_init(void (*fn1)(bool), void (*fn2)(void *data, int len)) +{ + touch_ops.glink_channel_state = fn1; + touch_ops.rx_msg = fn2; +} +EXPORT_SYMBOL(glink_touch_channel_init); + +int glink_touch_tx_msg(void *msg, size_t len) +{ + int ret = 0; + + if (touch_pdev == NULL || !touch_pdev->chnl_state) { + pr_err("pmsg_device is null, channel is closed\n"); + return -ENETRESET; + } + + touch_pdev->message = msg; + touch_pdev->message_length = len; + if (touch_pdev->message) { + ret = rpmsg_send(touch_pdev->channel, + touch_pdev->message, touch_pdev->message_length); + if (ret) + pr_err("rpmsg_send failed: %d\n", ret); + + } + + return ret; +} +EXPORT_SYMBOL(glink_touch_tx_msg); + +static int glink_touch_probe(struct rpmsg_device *touch_rpdev) +{ + int ret = 0; + void *msg = NULL; + + pr_info("%s Start of glink_touch_probe\n", __func__); + touch_pdev = devm_kzalloc(&touch_rpdev->dev, sizeof(*touch_pdev), GFP_KERNEL); + if (!touch_pdev) + return -ENOMEM; + + touch_pdev->channel = touch_rpdev->ept; + touch_pdev->dev = &touch_rpdev->dev; + if (touch_pdev->channel == NULL) + return -ENOMEM; + + touch_pdev->chnl_state = true; + dev_set_drvdata(&touch_rpdev->dev, touch_pdev); + + /* send a callback to slate-MSM touch driver*/ + touch_ops.glink_channel_state(true); + if (touch_pdev->message == NULL) + ret = glink_touch_tx_msg(msg, 0); + + pr_info("%s End of glink_touch_probe\n", __func__); + return 0; +} + +static void glink_touch_remove(struct rpmsg_device *touch_rpdev) +{ + touch_pdev->chnl_state = false; + touch_pdev->message = NULL; + dev_dbg(&touch_rpdev->dev, "rpmsg client driver is removed\n"); + touch_ops.glink_channel_state(false); + dev_set_drvdata(&touch_rpdev->dev, NULL); + +} + +static int glink_touch_cb(struct rpmsg_device *touch_rpdev, + void *data, int len, void *priv, u32 src) +{ + struct glink_touch_dev *touch_dev = + dev_get_drvdata(&touch_rpdev->dev); + + if (!touch_dev) + return -ENODEV; + touch_ops.rx_msg(data, len); + + return 0; +} + +static const struct rpmsg_device_id glink_touch_driver_id_table[] = { + { "touch-ctrl" }, + {}, +}; +MODULE_DEVICE_TABLE(rpmsg, glink_touch_driver_id_table); + +static const struct of_device_id glink_touch_driver_of_match[] = { + { .compatible = "qcom,slatetouch-rpmsg" }, + {}, +}; + +static struct rpmsg_driver glink_touch_client = { + .id_table = glink_touch_driver_id_table, + .probe = glink_touch_probe, + .callback = glink_touch_cb, + .remove = glink_touch_remove, + .drv = { + .name = "glink_interface", + .of_match_table = glink_touch_driver_of_match, + }, +}; +module_rpmsg_driver(glink_touch_client); + +MODULE_DESCRIPTION("Interface Driver for MSM TOUCH and RPMSG"); +MODULE_LICENSE("GPL v2"); diff --git a/glink_interface_ts/glink_interface.h b/glink_interface_ts/glink_interface.h new file mode 100644 index 0000000000..381b1dccd2 --- /dev/null +++ b/glink_interface_ts/glink_interface.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef GLINKINTERFACE_H +#define GLINKINTERFACE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define TOUCH_GLINK_INTENT_SIZE 0x04 +#define TOUCH_MSG_SIZE 0x08 +#define TIMEOUT_MS 2000 + +struct glink_touch_priv { + void *handle; + struct mutex glink_mutex; + struct mutex touch_state_mutex; + void *lhndl; + char rx_buf[TOUCH_GLINK_INTENT_SIZE]; + bool glink_touch_cmplt; + wait_queue_head_t link_state_wait; + bool msm_touch_rpmsg; +}; + +static void *glink_touch_drv; + +enum touch_slate_cmds { + TOUCH_ENTER_PREPARE = 0x1100, + TOUCH_ENTER, + TOUCH_EXIT_PREPARE, + TOUCH_EXIT +}; + +struct glink_touch_dev { + struct rpmsg_endpoint *channel; + struct device *dev; + bool chnl_state; + void *message; + size_t message_length; +}; + +struct touch_channel_ops { + void (*glink_channel_state)(bool state); + void (*rx_msg)(void *data, int len); +}; + +void glink_touch_channel_init(void (*fn1)(bool), void (*fn2)(void *, int)); + + +#if IS_ENABLED(CONFIG_MSM_SLATERSB_RPMSG) +int glink_touch_tx_msg(void *msg, size_t len); +#else +static inline int glink_touch_tx_msg(void *msg, size_t len) +{ + return -EIO; +} +#endif + +#endif \ No newline at end of file diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 6d8ef874d5..a216fa6ece 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -39,6 +39,8 @@ #include #include #include "raydium_driver.h" +#include +#include #if defined(CONFIG_FB) #include #include @@ -60,7 +62,6 @@ struct raydium_slot_status gst_slot_init = {0xFF, 0, 0}; static int raydium_enable_regulator(struct raydium_ts_data *cd, bool en); - #if (defined(CONFIG_RM_SYSFS_DEBUG)) const struct attribute_group raydium_attr_group; #endif /*end of CONFIG_RM_SYSFS_DEBUG*/ @@ -89,6 +90,7 @@ unsigned char g_u8_checkflag; #endif unsigned char g_u8_log_level; struct raydium_ts_data *g_raydium_ts; + /******************************************************************************* * Name: raydium_variable_init * Brief: @@ -1076,15 +1078,17 @@ static int raydium_touch_report(unsigned char *p_u8_buf, return 0; } + int raydium_read_touchdata(unsigned char *p_u8_tp_status, unsigned char *p_u8_buf) { + int i32_ret = 0; unsigned char u8_points_amount; static unsigned char u8_seq_no; unsigned char u8_retry; unsigned char u8_read_size; unsigned char u8_read_buf[MAX_REPORT_PACKET_SIZE]; - u8_retry = 3; + u8_retry = 100; mutex_lock(&g_raydium_ts->lock); while (u8_retry != 0) { @@ -2100,6 +2104,29 @@ static void raydium_input_set(struct input_dev *input_dev) gst_slot[i] = gst_slot_init; } + +void touch_notify_glink_channel_state(bool state) +{ + LOGD(LOG_INFO, "%s:[touch] channel state: %d\n", __func__, state); +} + +void glink_touch_rx_msg(void *data, int len) +{ + struct glink_touch_priv *dev = + container_of(glink_touch_drv, struct glink_touch_priv, lhndl); + LOGD(LOG_INFO, "%s:[touch] TOUCH_RX_MSG Start:\n", __func__); + + if (len > TOUCH_GLINK_INTENT_SIZE) { + LOGD(LOG_ERR, "Invalid TOUCH glink intent size\n"); + return; + } + dev->glink_touch_cmplt = true; + wake_up(&dev->link_state_wait); + memcpy(dev->rx_buf, data, len); + LOGD(LOG_INFO, "%s: TOUCH_RX_MSG End:\n", __func__); +} + + static int raydium_set_resolution(void) { unsigned char u8_buf[4]; @@ -2370,6 +2397,9 @@ static int raydium_ts_probe(struct i2c_client *client, ret = -EPROBE_DEFER; goto exit_check_i2c; } + + glink_touch_channel_init(&touch_notify_glink_channel_state, &glink_touch_rx_msg); + #if defined(CONFIG_DRM) || defined(CONFIG_PANEL_NOTIFIER) /* Setup active dsi panel */ active_panel = pdata->active_panel; diff --git a/raydium/raydium_sysfs.c b/raydium/raydium_sysfs.c index e61675457a..ac69d4976a 100644 --- a/raydium/raydium_sysfs.c +++ b/raydium/raydium_sysfs.c @@ -25,8 +25,16 @@ #include #include #include +#include +#include #include #include "raydium_driver.h" +#include + + +static void raydium_ts_touch_entry(void); +static void raydium_ts_touch_exit(void); +static int raydium_ts_gpio_config(bool on); static ssize_t raydium_touch_calibration_show(struct device *dev, struct device_attribute *attr, @@ -221,6 +229,174 @@ static ssize_t raydium_palm_status_show(struct device *dev, u16_len = strlen(p_i8_buf); return u16_len + 1; } + + +static int raydium_ts_gpio_config(bool on) +{ + int i32_err = 0; + + if (on) { + if (gpio_is_valid(g_raydium_ts->irq_gpio)) { + i32_err = gpio_request(g_raydium_ts->irq_gpio, + "raydium_irq_gpio"); + if (i32_err) { + LOGD(LOG_ERR, "[touch]irq gpio request failed"); + goto err_irq_gpio_req; + } + + i32_err = gpio_direction_input(g_raydium_ts->irq_gpio); + if (i32_err) { + LOGD(LOG_ERR, "[touch]set_direction for irq gpio failed\n"); + goto err_irq_gpio_dir; + } + } + if (gpio_is_valid(g_raydium_ts->rst_gpio)) { + i32_err = gpio_request(g_raydium_ts->rst_gpio, + "raydium_rst_gpio"); + if (i32_err) { + LOGD(LOG_ERR, "[touch]rst gpio request failed"); + goto err_irq_gpio_req; + } + + i32_err = gpio_direction_output(g_raydium_ts->rst_gpio, 0); + msleep(RAYDIUM_RESET_INTERVAL_10MSEC); + if (i32_err) { + LOGD(LOG_ERR, + "[touch]set_direction for rst gpio failed\n"); + goto err_rst_gpio_dir; + } + + i32_err = gpio_direction_output(g_raydium_ts->rst_gpio, 1); + if (i32_err) { + LOGD(LOG_ERR, + "[touch]set_direction for irq gpio failed\n"); + goto err_rst_gpio_dir; + } + } + } else { + if (gpio_is_valid(g_raydium_ts->irq_gpio)) + gpio_free(g_raydium_ts->irq_gpio); + } + return 0; +err_rst_gpio_dir: + if (gpio_is_valid(g_raydium_ts->rst_gpio)) + gpio_free(g_raydium_ts->rst_gpio); + return i32_err; +err_irq_gpio_dir: + if (gpio_is_valid(g_raydium_ts->irq_gpio)) + gpio_free(g_raydium_ts->irq_gpio); +err_irq_gpio_req: + return i32_err; +} + +static void raydium_ts_touch_entry(void) +{ + void *glink_send_msg; + unsigned char u8_i = 0; + + int glink_touch_enter_prep = TOUCH_ENTER_PREPARE; + int glink_touch_enter = TOUCH_ENTER; + + /*glink touch enter prepare cmd */ + glink_send_msg = &glink_touch_enter_prep; + LOGD(LOG_INFO, "[touch] glink_send_msg = %d\n", glink_send_msg); + glink_touch_tx_msg(glink_send_msg, TOUCH_MSG_SIZE); + + /*glink touch enter cmd */ + glink_send_msg = &glink_touch_enter; + LOGD(LOG_INFO, "[touch]glink_send_msg = %d\n", glink_send_msg); + glink_touch_tx_msg(glink_send_msg, TOUCH_MSG_SIZE); + + //Release the gpio's + if (gpio_is_valid(g_raydium_ts->rst_gpio)) + gpio_free(g_raydium_ts->rst_gpio); + + if (gpio_is_valid(g_raydium_ts->irq_gpio)) + gpio_free(g_raydium_ts->irq_gpio); + + raydium_irq_control(DISABLE); + + if (!cancel_work_sync(&g_raydium_ts->work)) + LOGD(LOG_DEBUG, "[touch]workqueue is empty!\n"); + + /* release all touches */ + for (u8_i = 0; u8_i < g_raydium_ts->u8_max_touchs; u8_i++) { + pr_err("[touch]%s 1111\n", __func__); + input_mt_slot(g_raydium_ts->input_dev, u8_i); + input_mt_report_slot_state(g_raydium_ts->input_dev, + MT_TOOL_FINGER, + false); + } + + input_mt_report_pointer_emulation(g_raydium_ts->input_dev, false); + input_sync(g_raydium_ts->input_dev); + +} + + +static void raydium_ts_touch_exit(void) +{ + + int ret = 0; + void *glink_send_msg; + int glink_touch_exit_prep = TOUCH_EXIT_PREPARE; + int glink_touch_exit = TOUCH_EXIT; + + /*glink touch exit prepare cmd */ + glink_send_msg = &glink_touch_exit_prep; + LOGD(LOG_INFO, "[touch]glink_send_msg = %d\n", glink_send_msg); + glink_touch_tx_msg(glink_send_msg, TOUCH_MSG_SIZE); + + //Configure the gpio's + ret = raydium_ts_gpio_config(true); + if (ret < 0) { + LOGD(LOG_ERR, "[touch]failed to configure the gpios\n"); + goto err_gpio_req; + } + + /*glink touch exit cmd */ + glink_send_msg = &glink_touch_exit; + LOGD(LOG_INFO, "[touch]glink_send_msg = %d\n", glink_send_msg); + glink_touch_tx_msg(glink_send_msg, TOUCH_MSG_SIZE); + + +err_gpio_req: + return; + +} + +static ssize_t raydium_touch_offload_store(struct device *dev, + struct device_attribute *attr, + const char *p_i8_buf, size_t count) +{ + int i32_ret = 0; + unsigned char u8_mode; + + /* receive command line arguments string */ + if (count > 2) + return -EINVAL; + + i32_ret = kstrtou8(p_i8_buf, 16, &u8_mode); + if (i32_ret < 0) + return i32_ret; + + switch (u8_mode) { + case 0: /* Disable Touch offload */ + + LOGD(LOG_INFO, "[touch]RAD %s disable touch offload!!\n", __func__); + raydium_ts_touch_entry(); + break; + + case 1: /* Enable Touch offload */ + + LOGD(LOG_INFO, "[touch]RAD %s enable touch offload!!\n", __func__); + raydium_ts_touch_exit(); + break; + } + + return count; +} + static ssize_t raydium_touch_lock_store(struct device *dev, struct device_attribute *attr, const char *p_i8_buf, size_t count) @@ -1363,6 +1539,14 @@ static DEVICE_ATTR(raydium_i2c_touch_lock, 0644, NULL, raydium_touch_lock_store); +/* Touch Offload (W) + * example: echo 1 > raydium_touch_offload ==> enable touch offload + * echo 0 > raydium_touch_offload ==> disable touch offload + */ +static DEVICE_ATTR(raydium_touch_offload, 0644, + NULL, + raydium_touch_offload_store); + /* Log level (W) * example: echo 1 > raydium_log_level ==> modify log level */ @@ -1436,6 +1620,7 @@ struct attribute *raydium_attributes[] = { &dev_attr_raydium_i2c_pda2_page.attr, &dev_attr_raydium_i2c_raw_data.attr, &dev_attr_raydium_i2c_touch_lock.attr, + &dev_attr_raydium_touch_offload.attr, &dev_attr_raydium_fw_upgrade.attr, &dev_attr_raydium_check_fw_version.attr, &dev_attr_raydium_check_panel_version.attr, diff --git a/touch_driver_board.mk b/touch_driver_board.mk index 6e11fe5a85..71df480a7a 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -12,6 +12,7 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/pt_ts.ko \ $(KERNEL_MODULES_OUT)/pt_i2c.ko \ $(KERNEL_MODULES_OUT)/pt_device_access.ko \ + $(KERNEL_MODULES_OUT)/glink_comm.ko \ $(KERNEL_MODULES_OUT)/raydium_ts.ko else ifeq ($(TARGET_BOARD_PLATFORM), kona) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko diff --git a/touch_driver_product.mk b/touch_driver_product.mk index 4d0876df58..0de0c14e2c 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -10,6 +10,7 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/pt_ts.ko \ $(KERNEL_MODULES_OUT)/pt_i2c.ko \ $(KERNEL_MODULES_OUT)/pt_device_access.ko \ + $(KERNEL_MODULES_OUT)/glink_comm.ko \ $(KERNEL_MODULES_OUT)/raydium_ts.ko else ifeq ($(TARGET_BOARD_PLATFORM), kona) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko From 7c25ef2f97868dd708afb9b5d2ff89836eb15c34 Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Fri, 29 Sep 2023 15:29:15 +0530 Subject: [PATCH 152/170] touch: raydium: glink slate acknowledgement Implemented Glink Slate acknowledgement response for raydium Touch driver. Signed-off-by: Srikanth Katteboina Change-Id: Ieba8293d678a0422e6f2caf968312d0c9d96b972 --- glink_interface_ts/glink_interface.h | 5 +- raydium/raydium_driver.c | 23 +++++--- raydium/raydium_driver.h | 1 + raydium/raydium_sysfs.c | 78 ++++++++++++++++++---------- 4 files changed, 70 insertions(+), 37 deletions(-) diff --git a/glink_interface_ts/glink_interface.h b/glink_interface_ts/glink_interface.h index 381b1dccd2..89031fae82 100644 --- a/glink_interface_ts/glink_interface.h +++ b/glink_interface_ts/glink_interface.h @@ -21,7 +21,7 @@ #include -#define TOUCH_GLINK_INTENT_SIZE 0x04 +#define TOUCH_GLINK_INTENT_SIZE 0x0c #define TOUCH_MSG_SIZE 0x08 #define TIMEOUT_MS 2000 @@ -61,6 +61,7 @@ struct touch_channel_ops { void glink_touch_channel_init(void (*fn1)(bool), void (*fn2)(void *, int)); + #if IS_ENABLED(CONFIG_MSM_SLATERSB_RPMSG) int glink_touch_tx_msg(void *msg, size_t len); #else @@ -70,4 +71,4 @@ static inline int glink_touch_tx_msg(void *msg, size_t len) } #endif -#endif \ No newline at end of file +#endif diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index a216fa6ece..5e2f4158aa 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -2112,20 +2112,27 @@ void touch_notify_glink_channel_state(bool state) void glink_touch_rx_msg(void *data, int len) { - struct glink_touch_priv *dev = - container_of(glink_touch_drv, struct glink_touch_priv, lhndl); - LOGD(LOG_INFO, "%s:[touch] TOUCH_RX_MSG Start:\n", __func__); + int rc = 0; + + LOGD(LOG_INFO, "%s:[touch]TOUCH_RX_MSG Start:\n", __func__); if (len > TOUCH_GLINK_INTENT_SIZE) { LOGD(LOG_ERR, "Invalid TOUCH glink intent size\n"); return; } - dev->glink_touch_cmplt = true; - wake_up(&dev->link_state_wait); - memcpy(dev->rx_buf, data, len); - LOGD(LOG_INFO, "%s: TOUCH_RX_MSG End:\n", __func__); -} + /* check SLATE response */ + slate_ack_resp = *(uint32_t *)&data[8]; + LOGD(LOG_INFO, "[touch]slate_ack_resp :%0x\n", slate_ack_resp); + if (slate_ack_resp == 0x01) { + LOGD(LOG_INFO,"Bad SLATE response\n"); + rc = -EINVAL; + goto err_ret; + } + LOGD(LOG_INFO, "%s:[touch]TOUCH_RX_MSG End:\n", __func__); +err_ret: +return; +} static int raydium_set_resolution(void) { diff --git a/raydium/raydium_driver.h b/raydium/raydium_driver.h index 5a9d138f5a..574967d574 100644 --- a/raydium/raydium_driver.h +++ b/raydium/raydium_driver.h @@ -275,6 +275,7 @@ #include #endif +extern uint32_t slate_ack_resp; enum raydium_fb_state { FB_ON, diff --git a/raydium/raydium_sysfs.c b/raydium/raydium_sysfs.c index ac69d4976a..39df8b6a7f 100644 --- a/raydium/raydium_sysfs.c +++ b/raydium/raydium_sysfs.c @@ -36,6 +36,8 @@ static void raydium_ts_touch_entry(void); static void raydium_ts_touch_exit(void); static int raydium_ts_gpio_config(bool on); +uint32_t slate_ack_resp; + static ssize_t raydium_touch_calibration_show(struct device *dev, struct device_attribute *attr, char *p_i8_buf) @@ -296,62 +298,84 @@ static void raydium_ts_touch_entry(void) int glink_touch_enter_prep = TOUCH_ENTER_PREPARE; int glink_touch_enter = TOUCH_ENTER; + int rc = 0; + + LOGD(LOG_INFO, "%s[touch] raydium_ts_touch_entry Start\n", __func__); /*glink touch enter prepare cmd */ glink_send_msg = &glink_touch_enter_prep; - LOGD(LOG_INFO, "[touch] glink_send_msg = %d\n", glink_send_msg); + LOGD(LOG_INFO, "[touch] glink_send_msg = %0x\n", glink_send_msg); glink_touch_tx_msg(glink_send_msg, TOUCH_MSG_SIZE); + if (slate_ack_resp != 0) { + rc = -EINVAL; + goto err_ret; + } /*glink touch enter cmd */ glink_send_msg = &glink_touch_enter; - LOGD(LOG_INFO, "[touch]glink_send_msg = %d\n", glink_send_msg); + LOGD(LOG_INFO, "[touch]glink_send_msg = %0x\n", glink_send_msg); glink_touch_tx_msg(glink_send_msg, TOUCH_MSG_SIZE); - //Release the gpio's - if (gpio_is_valid(g_raydium_ts->rst_gpio)) - gpio_free(g_raydium_ts->rst_gpio); + if(slate_ack_resp == 0) { + //Release the gpio's + if (gpio_is_valid(g_raydium_ts->rst_gpio)) + gpio_free(g_raydium_ts->rst_gpio); - if (gpio_is_valid(g_raydium_ts->irq_gpio)) - gpio_free(g_raydium_ts->irq_gpio); + if (gpio_is_valid(g_raydium_ts->irq_gpio)) + gpio_free(g_raydium_ts->irq_gpio); - raydium_irq_control(DISABLE); + raydium_irq_control(DISABLE); - if (!cancel_work_sync(&g_raydium_ts->work)) - LOGD(LOG_DEBUG, "[touch]workqueue is empty!\n"); + if (!cancel_work_sync(&g_raydium_ts->work)) + LOGD(LOG_DEBUG, "[touch]workqueue is empty!\n"); - /* release all touches */ - for (u8_i = 0; u8_i < g_raydium_ts->u8_max_touchs; u8_i++) { - pr_err("[touch]%s 1111\n", __func__); - input_mt_slot(g_raydium_ts->input_dev, u8_i); - input_mt_report_slot_state(g_raydium_ts->input_dev, + /* release all touches */ + for (u8_i = 0; u8_i < g_raydium_ts->u8_max_touchs; u8_i++) { + pr_err("[touch]%s 1111\n", __func__); + input_mt_slot(g_raydium_ts->input_dev, u8_i); + input_mt_report_slot_state(g_raydium_ts->input_dev, MT_TOOL_FINGER, false); + } + + input_mt_report_pointer_emulation(g_raydium_ts->input_dev, false); + input_sync(g_raydium_ts->input_dev); } - input_mt_report_pointer_emulation(g_raydium_ts->input_dev, false); - input_sync(g_raydium_ts->input_dev); - + LOGD(LOG_INFO, "%s[touch] raydium_ts_touch_entry Start End\n", __func__); +err_ret: + return; } static void raydium_ts_touch_exit(void) { - int ret = 0; + int ret = 0, rc = 0; void *glink_send_msg; int glink_touch_exit_prep = TOUCH_EXIT_PREPARE; int glink_touch_exit = TOUCH_EXIT; + + LOGD(LOG_INFO, "%s[touch]raydium_ts_touch_exit Start\n", __func__); + /*glink touch exit prepare cmd */ glink_send_msg = &glink_touch_exit_prep; - LOGD(LOG_INFO, "[touch]glink_send_msg = %d\n", glink_send_msg); + LOGD(LOG_INFO, "[touch]glink_send_msg = %0x\n", glink_send_msg); glink_touch_tx_msg(glink_send_msg, TOUCH_MSG_SIZE); - //Configure the gpio's - ret = raydium_ts_gpio_config(true); - if (ret < 0) { - LOGD(LOG_ERR, "[touch]failed to configure the gpios\n"); - goto err_gpio_req; + if (slate_ack_resp != 0) { + rc = -EINVAL; + goto err_ret; + } + + else if(slate_ack_resp == 0) { + //Configure the gpio's + ret = raydium_ts_gpio_config(true); + if (ret < 0) { + LOGD(LOG_ERR, "[touch]failed to configure the gpios\n"); + goto err_ret; + } } /*glink touch exit cmd */ @@ -359,8 +383,8 @@ static void raydium_ts_touch_exit(void) LOGD(LOG_INFO, "[touch]glink_send_msg = %d\n", glink_send_msg); glink_touch_tx_msg(glink_send_msg, TOUCH_MSG_SIZE); - -err_gpio_req: + LOGD(LOG_INFO, "%s[touch] raydium_ts_touch_exit End\n", __func__); +err_ret: return; } From df8c1c7d1f76a7b5301f39e91a3c94e4a13b0e0a Mon Sep 17 00:00:00 2001 From: Jyothi bommidi Date: Tue, 10 Oct 2023 16:13:08 +0530 Subject: [PATCH 153/170] touch: focaltech: add support for ft8726 boot up This change adds support for ft8726 touch IC boot up for panels having firmware already flashed and updates power on sequence to perform delayed probe operations as part of first resume. Change-Id: Ib9f5d1b961a80c3e884ef0cb22577bf295c1d4cb Signed-off-by: Ritesh Kumar Signed-off-by: Jyothi bommidi --- focaltech_touch/focaltech_config.h | 7 ++++--- focaltech_touch/focaltech_core.c | 31 +++++++++++++++++++++++++++--- focaltech_touch/focaltech_core.h | 2 +- focaltech_touch/focaltech_flash.c | 18 +++++++++++++++-- 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/focaltech_touch/focaltech_config.h b/focaltech_touch/focaltech_config.h index c2b68bcbab..a089e1607b 100644 --- a/focaltech_touch/focaltech_config.h +++ b/focaltech_touch/focaltech_config.h @@ -57,6 +57,7 @@ #define _FT7250 0x7250081A #define _FT7120 0x7120081B #define _FT8720 0x8720081C +#define _FT8726 0x8726081C #define _FT8016 0x8016081D @@ -228,7 +229,7 @@ /* * Numbers of modules support */ -#define FTS_GET_MODULE_NUM 0 +#define FTS_GET_MODULE_NUM 2 /* * module_id: mean vendor_id generally, also maybe gpio or lcm_id... @@ -238,7 +239,7 @@ * FTS_GET_MODULE_NUM >= 3, compatible with FTS_MODULE3_ID */ #define FTS_MODULE_ID 0x0000 -#define FTS_MODULE2_ID 0x0000 +#define FTS_MODULE2_ID 0xd566 #define FTS_MODULE3_ID 0x0000 /* @@ -250,7 +251,7 @@ * etc/firmware or by customers */ #define FTS_MODULE_NAME "gvo" -#define FTS_MODULE2_NAME "" +#define FTS_MODULE2_NAME "jdi" #define FTS_MODULE3_NAME "" /* diff --git a/focaltech_touch/focaltech_core.c b/focaltech_touch/focaltech_core.c index 6d3d6a827b..f82645d48c 100644 --- a/focaltech_touch/focaltech_core.c +++ b/focaltech_touch/focaltech_core.c @@ -97,6 +97,7 @@ static void fts_ts_panel_notifier_callback(enum panel_event_notifier_tag tag, static struct ft_chip_t ctype[] = { {0x88, 0x56, 0x52, 0x00, 0x00, 0x00, 0x00, 0x56, 0xB2}, {0x81, 0x54, 0x52, 0x54, 0x52, 0x00, 0x00, 0x54, 0x5C}, + {0x1C, 0x87, 0x26, 0x87, 0x20, 0x87, 0xA0, 0x00, 0x00}, }; /***************************************************************************** @@ -2716,9 +2717,7 @@ static int fts_ts_probe_delayed(struct fts_ts_data *fts_data) goto err_power_init; } #endif - - if (!FTS_CHIP_IDC(fts_data->pdata->type)) - fts_reset_proc(200); + fts_reset_proc(200); ret = fts_get_ic_information(fts_data); if (ret) { @@ -2855,11 +2854,25 @@ static int fts_ts_probe_entry(struct fts_ts_data *ts_data) fts_ts_trusted_touch_init(ts_data); mutex_init(&(ts_data->fts_clk_io_ctrl_mutex)); #endif + +#ifndef CONFIG_ARCH_QTI_VM + if (ts_data->pdata->type == _FT8726) { + atomic_set(&ts_data->delayed_vm_probe_pending, 1); + ts_data->suspended = true; + } else { + ret = fts_ts_probe_delayed(ts_data); + if (ret) { + FTS_ERROR("Failed to enable resources\n"); + goto err_probe_delayed; + } + } +#else ret = fts_ts_probe_delayed(ts_data); if (ret) { FTS_ERROR("Failed to enable resources\n"); goto err_probe_delayed; } +#endif #if defined(CONFIG_DRM) if (ts_data->ts_workqueue) @@ -3026,6 +3039,7 @@ static int fts_ts_suspend(struct device *dev) static int fts_ts_resume(struct device *dev) { struct fts_ts_data *ts_data = fts_data; + int ret = 0; FTS_FUNC_ENTER(); if (!ts_data->suspended) { @@ -3039,6 +3053,17 @@ static int fts_ts_resume(struct device *dev) wait_for_completion_interruptible( &ts_data->trusted_touch_powerdown); #endif + if (ts_data->pdata->type == _FT8726 && + atomic_read(&ts_data->delayed_vm_probe_pending)) { + ret = fts_ts_probe_delayed(ts_data); + if (ret) { + FTS_ERROR("Failed to enable resources\n"); + return ret; + } + ts_data->suspended = false; + atomic_set(&ts_data->delayed_vm_probe_pending, 0); + return ret; + } mutex_lock(&ts_data->transition_lock); diff --git a/focaltech_touch/focaltech_core.h b/focaltech_touch/focaltech_core.h index c63d4cff9b..9cb797d581 100644 --- a/focaltech_touch/focaltech_core.h +++ b/focaltech_touch/focaltech_core.h @@ -299,9 +299,9 @@ struct fts_ts_data { atomic_t trusted_touch_transition; atomic_t trusted_touch_event; atomic_t trusted_touch_abort_status; - atomic_t delayed_vm_probe_pending; atomic_t trusted_touch_mode; #endif + atomic_t delayed_vm_probe_pending; }; enum _FTS_BUS_TYPE { diff --git a/focaltech_touch/focaltech_flash.c b/focaltech_touch/focaltech_flash.c index c1e54b10ff..c3319d8636 100644 --- a/focaltech_touch/focaltech_flash.c +++ b/focaltech_touch/focaltech_flash.c @@ -51,11 +51,25 @@ u8 fw_file[1] = { struct upgrade_module module_list[] = { {FTS_MODULE_ID, FTS_MODULE_NAME, fw_file, sizeof(fw_file)}, + {FTS_MODULE2_ID, FTS_MODULE2_NAME, fw_file, sizeof(fw_file)}, +}; + +struct upgrade_func upgrade_func_ft8720 = { + .ctype = {0x1C}, + .fwveroff = 0x210E, + .fwcfgoff = 0x1000, + .appoff = 0x2000, + .licoff = 0x0000, + .appoff_handle_in_ic = true, + .pramboot_supported = false, + .new_return_value_from_ic = true, + .hid_supported = false, }; struct upgrade_func *upgrade_func_list[] = { &upgrade_func_ft5452, &upgrade_func_ft5652, + &upgrade_func_ft8720, }; struct fts_upgrade *fwupgrade; @@ -1825,8 +1839,8 @@ static int fts_fwupg_get_module_info(struct fts_upgrade *upg) } } if (i >= FTS_GET_MODULE_NUM) { - FTS_ERROR("no module id match, don't get file"); - return -ENODATA; + info = &module_list[0]; + FTS_ERROR("no module id match, default to use first module"); } } From 0fd74014a70bdb83736bb4db0664d1a3a9eb0254 Mon Sep 17 00:00:00 2001 From: Jyothi bommidi Date: Tue, 10 Oct 2023 16:30:39 +0530 Subject: [PATCH 154/170] touch: focaltech: fix pinctrl state in suspend resume In FT8726 touch controller, separate regulators are not needed. But regulator voting and pinctrl suspend / normal calls are done together. This leads to wrong pinctrl state during suspend leading to leakage. Change-Id: Ia9fe89db1c0c87fd02ee4a7cf6f5a485cb7e23d4 Signed-off-by: Ritesh Kumar Signed-off-by: Jyothi bommidi --- focaltech_touch/focaltech_core.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/focaltech_touch/focaltech_core.c b/focaltech_touch/focaltech_core.c index f82645d48c..8d836c5cea 100644 --- a/focaltech_touch/focaltech_core.c +++ b/focaltech_touch/focaltech_core.c @@ -3026,6 +3026,11 @@ static int fts_ts_suspend(struct device *dev) FTS_ERROR("power enter suspend fail"); } #endif + } else { +#if FTS_PINCTRL_EN + fts_pinctrl_select_suspend(ts_data); +#endif + gpio_direction_output(ts_data->pdata->reset_gpio, 0); } } @@ -3073,9 +3078,14 @@ static int fts_ts_resume(struct device *dev) #if FTS_POWER_SOURCE_CUST_EN fts_power_source_resume(ts_data); #endif - fts_reset_proc(200); + } else { +#if FTS_PINCTRL_EN + fts_pinctrl_select_normal(ts_data); +#endif } + fts_reset_proc(200); + fts_wait_tp_to_valid(); fts_ex_mode_recovery(ts_data); From d230941afa3830082fe2747eeb899ca55013ec14 Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Wed, 27 Dec 2023 16:20:00 +0530 Subject: [PATCH 155/170] touch: Add support for Bazel to build touch modules Add support for glink_interface_ts modules to be built with Bazel. Signed-off-by: Srikanth Katteboina Change-Id: Ib35b25a49e2e70b6f2765c9a67a1f7e0f3939ba9 --- BUILD.bazel | 10 +++++++++- target.bzl | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/BUILD.bazel b/BUILD.bazel index add00fdf4f..9bc056eeac 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -47,6 +47,14 @@ ddk_headers( includes = ["config"] ) +ddk_headers( + name = "glink_interface_ts_headers", + hdrs = glob([ + "glink_interface_ts/*.h" + ] + ) +) + ddk_headers( name = "pt_headers", hdrs = glob([ @@ -66,7 +74,7 @@ ddk_headers( ddk_headers( name = "touch_drivers_headers", - hdrs = [":goodix_ts_headers", ":nt36xxx_headers", ":focaltech_headers", ":synaptics_tcm_headers", ":pt_headers", ":raydium_headers", ":config_headers"] + hdrs = [":goodix_ts_headers", ":nt36xxx_headers", ":focaltech_headers", ":synaptics_tcm_headers", ":glink_interface_ts_headers", ":pt_headers", ":raydium_headers", ":config_headers"] ) load(":target.bzl", "define_touch_target") diff --git a/target.bzl b/target.bzl index 4e3db6a6c5..02ced76e6f 100644 --- a/target.bzl +++ b/target.bzl @@ -70,6 +70,7 @@ def define_monaco(t,v): variant = v, registry = touch_driver_modules, modules = [ + "glink_interface_ts", "pt_ts", "pt_i2c", "pt_device_access", @@ -80,6 +81,7 @@ def define_monaco(t,v): "TOUCH_DLKM_ENABLE", "CONFIG_ARCH_MONACO", "CONFIG_MSM_TOUCH", + "CONFIG_TOUCHSCREEN_MSM_GLINK", "CONFIG_TOUCHSCREEN_PARADE", "CONFIG_TOUCHSCREEN_PARADE_DEVICETREE_SUPPORT", "CONFIG_TOUCHSCREEN_PARADE_I2C", From 20cccf755a60082a271b63c6598b686ae4f74709 Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Sat, 20 Jan 2024 08:11:52 +0530 Subject: [PATCH 156/170] touch: LW: Add support for Bazel to build touch modules Add support for touch modules to be built with Bazel for DDK. Signed-off-by: Srikanth Katteboina Change-Id: I774b1668a5959c0d58c6582a1b01692d4e09f856 --- BUILD.bazel | 3 ++- touch_modules.bzl | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/BUILD.bazel b/BUILD.bazel index 9bc056eeac..304267ae29 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -52,7 +52,8 @@ ddk_headers( hdrs = glob([ "glink_interface_ts/*.h" ] - ) + ), + includes = ["glink_interface_ts"] ) ddk_headers( diff --git a/touch_modules.bzl b/touch_modules.bzl index e250a41681..bf4dc064b7 100644 --- a/touch_modules.bzl +++ b/touch_modules.bzl @@ -86,6 +86,15 @@ module_entry( ] ) +#define ddk_module() for glink_interface_ts +module_entry( + name = "glink_interface_ts", + config_option = "CONFIG_TOUCHSCREEN_MSM_GLINK", + srcs = [ + "glink_interface_ts/glink_interface.c" + ] +) + #define ddk_module() for pt_ts module_entry( name = "pt_ts", From 1d39d6b82c220d83d0cc16c06b0f9817fddb1795 Mon Sep 17 00:00:00 2001 From: Srikanth Katteboina Date: Tue, 30 Jan 2024 20:17:44 +0530 Subject: [PATCH 157/170] touch: LW: Add support for Bazel to build touch modules Changed module name to resolve bazel compilation. Signed-off-by: Srikanth Katteboina Change-Id: I70720e9c4093eb6b74fbd413f53cdbf1e4d04480 --- target.bzl | 2 +- touch_modules.bzl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target.bzl b/target.bzl index 02ced76e6f..86142b5942 100644 --- a/target.bzl +++ b/target.bzl @@ -70,7 +70,7 @@ def define_monaco(t,v): variant = v, registry = touch_driver_modules, modules = [ - "glink_interface_ts", + "glink_comm", "pt_ts", "pt_i2c", "pt_device_access", diff --git a/touch_modules.bzl b/touch_modules.bzl index bf4dc064b7..d0dcabbad5 100644 --- a/touch_modules.bzl +++ b/touch_modules.bzl @@ -88,7 +88,7 @@ module_entry( #define ddk_module() for glink_interface_ts module_entry( - name = "glink_interface_ts", + name = "glink_comm", config_option = "CONFIG_TOUCHSCREEN_MSM_GLINK", srcs = [ "glink_interface_ts/glink_interface.c" From 1d69ded5ed592c1ed381c32f1ed66ec15d13c609 Mon Sep 17 00:00:00 2001 From: Syed Ahmed Date: Tue, 13 Feb 2024 11:23:07 +0530 Subject: [PATCH 158/170] touch: bengal: enable touch driver Enable synaptics_tcm touch driver for bengal target. Change-Id: I7c6233b4f345c52a01ebebb92da2cb7d546ef000 Signed-off-by: Syed Ahmed --- Kbuild | 5 +++++ config/gki_bengaltouch.conf | 5 +++++ config/gki_bengaltouchconf.h | 9 +++++++++ 3 files changed, 19 insertions(+) create mode 100644 config/gki_bengaltouch.conf create mode 100644 config/gki_bengaltouchconf.h diff --git a/Kbuild b/Kbuild index 298f4bf844..79fde92fc5 100644 --- a/Kbuild +++ b/Kbuild @@ -46,6 +46,11 @@ ifeq ($(CONFIG_ARCH_TRINKET), y) LINUX_INC += -include $(TOUCH_ROOT)/config/gki_trinkettouchconf.h endif +ifeq ($(CONFIG_ARCH_BENGAL), y) + include $(TOUCH_ROOT)/config/gki_bengaltouch.conf + LINUX_INC += -include $(TOUCH_ROOT)/config/gki_bengaltouchconf.h +endif + LINUX_INC += -Iinclude/linux \ -Iinclude/linux/drm \ -Iinclude/linux/gunyah \ diff --git a/config/gki_bengaltouch.conf b/config/gki_bengaltouch.conf new file mode 100644 index 0000000000..9b5b5f85c6 --- /dev/null +++ b/config/gki_bengaltouch.conf @@ -0,0 +1,5 @@ +export CONFIG_MSM_TOUCH=m +export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM=y +export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C=y +export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE=y +export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH=y diff --git a/config/gki_bengaltouchconf.h b/config/gki_bengaltouchconf.h new file mode 100644 index 0000000000..a750fc1ad0 --- /dev/null +++ b/config/gki_bengaltouchconf.h @@ -0,0 +1,9 @@ +/* +* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. +* SPDX-License-Identifier: GPL-2.0-only +*/ + +#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM 1 +#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C 1 +#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE 1 +#define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH 1 From 4f825c54419e63ed937e701215b68844a66e1c18 Mon Sep 17 00:00:00 2001 From: Akshay Gola Date: Thu, 22 Feb 2024 13:57:00 +0530 Subject: [PATCH 159/170] input: touchscreen: raydium: Remove IRQF_NO_SUSPEND flag Enablement of touch interrupt during device suspend(s2-idle). Change-Id: I29c3f1e61e789f37dba94cf9ed58e42915e02d6a Signed-off-by: Akshay Gola --- raydium/raydium_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 5e2f4158aa..99310bfeca 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -2468,7 +2468,7 @@ static int raydium_ts_probe(struct i2c_client *client, g_raydium_ts->irq = gpio_to_irq(pdata->irq_gpio); ret = request_threaded_irq(g_raydium_ts->irq, NULL, raydium_ts_interrupt, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_NO_SUSPEND, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->dev.driver->name, g_raydium_ts); if (ret < 0) { LOGD(LOG_ERR, "[touch]raydium_probe: request irq failed\n"); From ed3f33c6e5cfd2bbb5feadf006fcdc2f0a1321a2 Mon Sep 17 00:00:00 2001 From: Akshay Gola Date: Thu, 15 Feb 2024 16:48:02 +0530 Subject: [PATCH 160/170] input: touchscreen: pt: Remove IRQF_NO_SUSPEND flag Enablement of touch interrupt during device suspend(s2-idle). Change-Id: Ie2dd0ad0e71c88cace13eb772906d13878d5abc4 Signed-off-by: Akshay Gola --- pt/pt_platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pt/pt_platform.c b/pt/pt_platform.c index b91fc3f22f..4508774f55 100644 --- a/pt/pt_platform.c +++ b/pt/pt_platform.c @@ -1039,7 +1039,7 @@ int pt_setup_irq(struct pt_core_platform_data *pdata, int on, /* use edge triggered interrupts */ irq_flags = IRQF_TRIGGER_FALLING; rc = request_threaded_irq(cd->irq, NULL, pt_irq, - irq_flags | IRQF_ONESHOT | IRQF_NO_SUSPEND, dev_name(dev), cd); + irq_flags | IRQF_ONESHOT, dev_name(dev), cd); if (rc < 0) pt_debug(dev, DL_ERROR, "%s: Error, could not request irq\n", __func__); From 456950d74e205aae2c65b902bf57acbe47dcb647 Mon Sep 17 00:00:00 2001 From: Ritesh Kumar Date: Tue, 5 Mar 2024 16:42:32 +0530 Subject: [PATCH 161/170] touch: enable focaltech touch compilation for volcano target Enable focaltech touch compilation for volcano target. Change-Id: Ic73857254e5503ff65023d2facb40143874bbc65 Signed-off-by: Jyothi bommidi Signed-off-by: Ritesh Kumar --- Android.mk | 11 +++++++++++ target.bzl | 6 ++++-- touch_driver_board.mk | 3 ++- touch_driver_product.mk | 3 ++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Android.mk b/Android.mk index 0a8b956d13..ee5a3250a9 100644 --- a/Android.mk +++ b/Android.mk @@ -330,6 +330,17 @@ else ifeq ($(TARGET_BOARD_PLATFORM), volcano) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := focaltech_fts.ko + LOCAL_MODULE_KBUILD_NAME := focaltech_fts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + else ########################################################### diff --git a/target.bzl b/target.bzl index 86142b5942..a987074b3b 100644 --- a/target.bzl +++ b/target.bzl @@ -11,7 +11,8 @@ def define_pineapple(t,v): "nt36xxx-i2c", "atmel_mxt_ts", "dummy_ts", - "goodix_ts" + "goodix_ts", + "focaltech_fts" ], config_options = [ "TOUCH_DLKM_ENABLE", @@ -20,7 +21,8 @@ def define_pineapple(t,v): "CONFIG_TOUCHSCREEN_GOODIX_BRL", "CONFIG_TOUCHSCREEN_NT36XXX_I2C", "CONFIG_TOUCHSCREEN_ATMEL_MXT", - "CONFIG_TOUCHSCREEN_DUMMY" + "CONFIG_TOUCHSCREEN_DUMMY", + "CONFIG_TOUCH_FOCALTECH" ], ) diff --git a/touch_driver_board.mk b/touch_driver_board.mk index 71df480a7a..3d6464ba52 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -40,7 +40,8 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko else ifeq ($(TARGET_BOARD_PLATFORM), volcano) - BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/goodix_ts.ko + BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/focaltech_fts.ko else BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ diff --git a/touch_driver_product.mk b/touch_driver_product.mk index 0de0c14e2c..e3ded60680 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -38,7 +38,8 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/focaltech_fts.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko else ifeq ($(TARGET_BOARD_PLATFORM), volcano) - PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/goodix_ts.ko + PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/goodix_ts.ko \ + $(KERNEL_MODULES_OUT)/focaltech_fts.ko else PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ $(KERNEL_MODULES_OUT)/goodix_ts.ko \ From baa8973d8f018e98f860d329ed0bbb15ea386292 Mon Sep 17 00:00:00 2001 From: lingwenk Date: Tue, 12 Mar 2024 19:12:14 +0800 Subject: [PATCH 162/170] touch: st: update driver version update driver code version for kernel-5.15 Change-Id:Ia9aa2bdc4fd13832171d19d04db7e913d991508f Signed-off-by: lingwenk --- st/fts.c | 150 +++++++++++++++++++++++-------------------- st/fts.h | 2 + st/fts_lib/ftsTime.c | 4 +- st/fts_lib/ftsTime.h | 2 +- st/fts_lib/ftsTool.c | 4 -- 5 files changed, 86 insertions(+), 76 deletions(-) diff --git a/st/fts.c b/st/fts.c index f5dc14c681..f6d8323345 100644 --- a/st/fts.c +++ b/st/fts.c @@ -38,6 +38,7 @@ #include #else #include +#include #endif #ifdef KERNEL_ABOVE_2_6_38 @@ -1035,6 +1036,76 @@ static int fts_chip_initialization(struct fts_ts_info *info); static int fts_enable_reg(struct fts_ts_info *info, bool enable); static struct drm_panel *active_panel; +#if defined(CONFIG_DRM) +static void st_ts_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data) +{ + struct fts_ts_info *info = client_data; + + if (!notification) { + pr_err("Invalid notification\n"); + return; + } + + logError(0, "%s %s Notification type:%d, early_trigger:%d, sensor_sleep:%d", tag, __func__, + notification->notif_type, + notification->notif_data.early_trigger, + info->sensor_sleep); + + switch (notification->notif_type) { + case DRM_PANEL_EVENT_UNBLANK: + if (!notification->notif_data.early_trigger) { + logError(0, "%s %s: DRM_PANEL_EVENT_UNBLANK\n", tag, __func__); + queue_work(info->event_wq, &info->resume_work); + } + break; + + case DRM_PANEL_EVENT_BLANK: + if (!notification->notif_data.early_trigger) { + logError(0, "%s %s: DRM_PANEL_EVENT_BLANK\n", tag, __func__); + queue_work(info->event_wq, &info->suspend_work); + } + break; + + case DRM_PANEL_EVENT_BLANK_LP: + logError(0, "%s %s:received lp event\n", tag, __func__); + break; + + case DRM_PANEL_EVENT_FPS_CHANGE: + logError(0, "%s %s: Received fps change old fps:%d new fps:%d\n", + tag, __func__, + notification->notif_data.old_fps, + notification->notif_data.new_fps); + break; + + default: + logError(0, "%s %s:notification serviced :%d\n", + tag, __func__, notification->notif_type); + break; + } +} + +static int st_register_for_panel_events(struct device_node *dp, + struct fts_ts_info *info) +{ + void *cookie; + + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, active_panel, + &st_ts_panel_notifier_callback, info); + if (!cookie) { + pr_err("Failed to register for panel events\n"); + return -1; + } + + logError(0, "%s %s registered for panel notifications panel: 0x%x\n", + tag, __func__, active_panel); + + info->notifier_cookie = cookie; + + return 0; +} +#endif void touch_callback(unsigned int status) { @@ -3141,9 +3212,8 @@ static ssize_t fts_stm_cmd_show(struct device *dev, #if defined(CONFIG_FB_MSM) res = fb_unregister_client(&info->notifier); #else - if (active_panel) - res = drm_panel_notifier_unregister(active_panel, - &info->notifier); + if (active_panel && info->notifier_cookie) + panel_event_notifier_unregister(info->notifier_cookie); #endif if (res < 0) { logError(1, "%s ERROR: unregister notifier failed!\n", @@ -3346,9 +3416,8 @@ static ssize_t fts_stm_cmd_show(struct device *dev, if (fb_register_client(&info->notifier) < 0) logError(1, "%s ERROR: register notifier failed!\n", tag); #else - if (active_panel && - drm_panel_notifier_register(active_panel, &info->notifier) < 0) - logError(1, "%s ERROR: register notifier failed!\n", tag); + if (active_panel) + st_register_for_panel_events(info->dev->of_node, info); #endif END: @@ -4617,8 +4686,7 @@ static int fts_init_afterProbe(struct fts_ts_info *info) error |= fb_register_client(&info->notifier); #else if (active_panel) - error |= drm_panel_notifier_register(active_panel, - &info->notifier); + st_register_for_panel_events(info->dev->of_node, info); #endif if (error < OK) @@ -5091,68 +5159,10 @@ static int fts_fb_state_chg_callback(struct notifier_block *nb, return NOTIFY_OK; } -#else -static int fts_fb_state_chg_callback(struct notifier_block *nb, - unsigned long val, void *data) -{ - struct fts_ts_info *info = container_of(nb, struct fts_ts_info, - notifier); - struct drm_panel_notifier *evdata = data; - unsigned int blank; - - if (!evdata) - return 0; - - if (val != DRM_PANEL_EVENT_BLANK) - return 0; - - logError(0, "%s %s: fts notifier begin!\n", tag, __func__); - if (evdata->data && val == DRM_PANEL_EVENT_BLANK && info) { - blank = *(int *) (evdata->data); - - switch (blank) { - case DRM_PANEL_BLANK_POWERDOWN: - if (info->sensor_sleep && info->aoi_notify_enabled) - break; - - if (info->aoi_notify_enabled) - info->aoi_wake_on_suspend = true; - else - info->aoi_wake_on_suspend = false; - - if (info->aoi_wake_on_suspend) { - info->sensor_sleep = true; - __pm_stay_awake(info->wakeup_source); - } else { - queue_work(info->event_wq, &info->suspend_work); - } - break; - - case DRM_PANEL_BLANK_UNBLANK: - if (info->aoi_wake_on_suspend) - __pm_relax(info->wakeup_source); - - if (!info->sensor_sleep) - break; - - if (!info->resume_bit) - queue_work(info->event_wq, &info->resume_work); - - if (info->aoi_wake_on_suspend) - info->sensor_sleep = false; - - break; - default: - break; - } - } - return NOTIFY_OK; -} -#endif - static struct notifier_block fts_noti_block = { .notifier_call = fts_fb_state_chg_callback, }; +#endif static int fts_pinctrl_init(struct fts_ts_info *info) { @@ -5777,7 +5787,9 @@ static int fts_probe_internal(struct i2c_client *client, info->edge_palm_rej_enabled = 0; info->resume_bit = 1; +#if defined(CONFIG_FB_MSM) info->notifier = fts_noti_block; +#endif #ifdef CONFIG_ST_TRUSTED_TOUCH fts_trusted_touch_init(info); @@ -5986,8 +5998,8 @@ static int fts_remove(struct i2c_client *client) #if defined(CONFIG_FB_MSM) fb_unregister_client(&info->notifier); #else - if (active_panel) - drm_panel_notifier_register(active_panel, &info->notifier); + if (active_panel && info->notifier_cookie) + panel_event_notifier_unregister(info->notifier_cookie); #endif /* unregister the device */ diff --git a/st/fts.h b/st/fts.h index 817096209a..ca1888ac3b 100644 --- a/st/fts.h +++ b/st/fts.h @@ -260,6 +260,7 @@ struct trusted_touch_vm_info { * @resume_bit Indicate if screen off/on * @fwupdate_stat Store the result of a fw update triggered by the host * @notifier Used for be notified from a suspend/resume event + * @notifier_cookie saved cookie during panel event notification * @sensor_sleep true susped was called, false resume was called * @wakelock Wake Lock struct * @input_report_mutex mutex for handling the pressure of keys @@ -322,6 +323,7 @@ struct fts_ts_info { int fwupdate_stat; struct notifier_block notifier; + void *notifier_cookie; bool sensor_sleep; struct wakeup_source *wakeup_source; diff --git a/st/fts_lib/ftsTime.c b/st/fts_lib/ftsTime.c index 07d1bf5dba..88d03dc27a 100644 --- a/st/fts_lib/ftsTime.c +++ b/st/fts_lib/ftsTime.c @@ -66,12 +66,12 @@ void startStopWatch(struct StopWatch *w) { - ktime_get_ts(&w->start); + w->start = ktime_to_timespec64(ktime_get()); } void stopStopWatch(struct StopWatch *w) { - ktime_get_ts(&w->end); + w->end = ktime_to_timespec64(ktime_get()); } int elapsedMillisecond(struct StopWatch *w) diff --git a/st/fts_lib/ftsTime.h b/st/fts_lib/ftsTime.h index d98dd2a9fc..64968e77af 100644 --- a/st/fts_lib/ftsTime.h +++ b/st/fts_lib/ftsTime.h @@ -42,7 +42,7 @@ #include "ftsCrossCompile.h" struct StopWatch { - struct timespec start, end; + struct timespec64 start, end; }; void startStopWatch(struct StopWatch *w); diff --git a/st/fts_lib/ftsTool.c b/st/fts_lib/ftsTool.c index 543682d1e3..6dac5b1ddf 100644 --- a/st/fts_lib/ftsTool.c +++ b/st/fts_lib/ftsTool.c @@ -84,10 +84,6 @@ int readB2(u16 address, u8 *outBuf, int len) u8 readEvent[FIFO_EVENT_SIZE] = {0}; u8 cmd[4] = { FTS_CMD_REQU_FW_CONF, 0x00, 0x00, (u8)len }; - if (readEvent == NULL) { - logError(1, "%s %s:ERROR %02X\n", tag, __func__, ERROR_ALLOC); - return ERROR_ALLOC; - } u16ToU8_be(address, &cmd[1]); temp = printHex("Command B2 = ", cmd, 4); if (temp != NULL) From c886491e50486b09a9244beef831fbc76e9aa3e8 Mon Sep 17 00:00:00 2001 From: Saikiran MUppidi Date: Thu, 14 Mar 2024 12:10:54 +0530 Subject: [PATCH 163/170] touch: Touch_offload using debugfs Added show support for touch offload sysfs attribue Change-Id: I5a6a91cfd9df9af35ebd09055204f2d7714cc5dd Signed-off-by: Saikiran Muppidi --- pt/pt_core.c | 25 ++++++++++++++++++++++++- raydium/raydium_driver.c | 1 + raydium/raydium_driver.h | 1 + raydium/raydium_sysfs.c | 13 ++++++++++++- 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/pt/pt_core.c b/pt/pt_core.c index 33fa6c6dea..3a4e914627 100644 --- a/pt/pt_core.c +++ b/pt/pt_core.c @@ -15180,6 +15180,29 @@ exit: return size; } +/******************************************************************************* + * FUNCTION: pt_touch_offload_show + * + * SUMMARY: The show method for the touch_offload sysfs node that allows the TTDL + * to verify touch offload enable or disabled. + * + * RETURN: size of data written to sysfs node + * + * PARAMETERS: + * *dev - pointer to device structure + * *attr - pointer to device attributes + * *buf - pointer to print output buffer + ******************************************************************************/ +static ssize_t pt_touch_offload_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pt_core_data *cd = dev_get_drvdata(dev); + + return scnprintf(buf, PT_MAX_PRBUF_SIZE, + "Touch offload : %s\n", + (cd->touch_offload)? "Enabled" : "Disabled"); +} + /******************************************************************************* * FUNCTION: pt_pip2_version_show * @@ -17573,7 +17596,7 @@ static struct device_attribute attributes[] = { __ATTR(get_param, 0644, pt_get_param_show, pt_get_param_store), __ATTR(pt_touch_offload, 0644, - NULL, pt_touch_offload_store), + pt_touch_offload_show, pt_touch_offload_store), #ifdef EASYWAKE_TSG6 __ATTR(easy_wakeup_gesture, 0644, pt_easy_wakeup_gesture_show, pt_easy_wakeup_gesture_store), diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 5e2f4158aa..f5bf0b828b 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -2477,6 +2477,7 @@ static int raydium_ts_probe(struct i2c_client *client, g_raydium_ts->irq_desc = irq_to_desc(g_raydium_ts->irq); g_raydium_ts->irq_enabled = true; + g_raydium_ts->touch_offload = false; /*disable_irq then enable_irq for avoid Unbalanced enable for IRQ */ diff --git a/raydium/raydium_driver.h b/raydium/raydium_driver.h index 574967d574..78a7052e47 100644 --- a/raydium/raydium_driver.h +++ b/raydium/raydium_driver.h @@ -333,6 +333,7 @@ struct raydium_ts_data { struct pinctrl_state *pinctrl_state_suspend; struct pinctrl_state *pinctrl_state_release; #endif /*end of MSM_NEW_VER*/ + bool touch_offload; }; diff --git a/raydium/raydium_sysfs.c b/raydium/raydium_sysfs.c index 39df8b6a7f..73f8dda024 100644 --- a/raydium/raydium_sysfs.c +++ b/raydium/raydium_sysfs.c @@ -409,18 +409,29 @@ static ssize_t raydium_touch_offload_store(struct device *dev, LOGD(LOG_INFO, "[touch]RAD %s disable touch offload!!\n", __func__); raydium_ts_touch_entry(); + g_raydium_ts->touch_offload = true; break; case 1: /* Enable Touch offload */ LOGD(LOG_INFO, "[touch]RAD %s enable touch offload!!\n", __func__); raydium_ts_touch_exit(); + g_raydium_ts->touch_offload = false; break; } return count; } +static ssize_t raydium_touch_offload_show(struct device *dev, + struct device_attribute *attr, + char *p_i8_buf) +{ + return snprintf(p_i8_buf, PAGE_SIZE, + "Touch offload : %s\n", + (g_raydium_ts->touch_offload)? "Enabled" : "Disabled"); + +} static ssize_t raydium_touch_lock_store(struct device *dev, struct device_attribute *attr, const char *p_i8_buf, size_t count) @@ -1568,7 +1579,7 @@ static DEVICE_ATTR(raydium_i2c_touch_lock, 0644, * echo 0 > raydium_touch_offload ==> disable touch offload */ static DEVICE_ATTR(raydium_touch_offload, 0644, - NULL, + raydium_touch_offload_show, raydium_touch_offload_store); /* Log level (W) From f963a865d9fe4fba8e6d24ec046cf9d31e2a6138 Mon Sep 17 00:00:00 2001 From: Ritesh Kumar Date: Wed, 3 Apr 2024 14:17:51 +0530 Subject: [PATCH 164/170] touch: qts: free the memory of the acl and sgl descriptor Allocated memory for acl and sgl descriptors is not being freed. This is causing memory leak over multiple cycles of TUI tests. To avoid this, free The allocated memory of descriptors. Change-Id: I7d8f0fa53d9060f62aa9a3cb5ba5e5ce11e49d94 Signed-off-by: Ritesh Kumar --- qts/qts_core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qts/qts_core.c b/qts/qts_core.c index 89ecc84be3..5f3df38d2b 100644 --- a/qts/qts_core.c +++ b/qts/qts_core.c @@ -423,7 +423,7 @@ static void qts_trusted_touch_tvm_vm_mode_enable(struct qts_data *qts_data) if (rc < 0) { pr_err("failed to get sync rc:%d\n", rc); - goto acl_fail; + goto sgl_fail; } qts_trusted_touch_set_vm_state(qts_data, TVM_I2C_SESSION_ACQUIRED); @@ -435,6 +435,7 @@ static void qts_trusted_touch_tvm_vm_mode_enable(struct qts_data *qts_data) kfree(expected_sgl_desc); kfree(acl_desc); + kfree(sgl_desc); irq = gh_irq_accept(qts_data->vm_info->irq_label, -1, IRQ_TYPE_EDGE_RISING); qts_trusted_touch_intr_gpio_toggle(qts_data, false); @@ -478,6 +479,8 @@ static void qts_trusted_touch_tvm_vm_mode_enable(struct qts_data *qts_data) return; sgl_cmp_fail: kfree(expected_sgl_desc); +sgl_fail: + kfree(sgl_desc); acl_fail: kfree(acl_desc); accept_fail: From 72063c31fb72e4bb9a29c66b6413c5a9f1cc0ae7 Mon Sep 17 00:00:00 2001 From: Syed Ahmed Date: Fri, 5 Apr 2024 17:06:09 +0530 Subject: [PATCH 165/170] touch: bengal: enable focaltech touch driver Enable focaltech touch driver for bengal target. Change-Id: I5bf0cb67050663a3efbf1885ac4d36cfffc391d3 Signed-off-by: Syed Ahmed --- Android.mk | 11 +++++++++++ config/gki_bengaltouch.conf | 2 ++ config/gki_bengaltouchconf.h | 2 ++ touch_driver_board.mk | 3 ++- touch_driver_product.mk | 3 ++- 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Android.mk b/Android.mk index ee5a3250a9..8fd19235ec 100644 --- a/Android.mk +++ b/Android.mk @@ -280,6 +280,17 @@ else ifeq ($(TARGET_BOARD_PLATFORM), bengal) include $(DLKM_DIR)/Build_external_kernelmodule.mk ########################################################### + ########################################################### + include $(CLEAR_VARS) + LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/**/*) $(wildcard $(LOCAL_PATH)/*) + LOCAL_MODULE := focaltech_fts.ko + LOCAL_MODULE_KBUILD_NAME := focaltech_fts.ko + LOCAL_MODULE_TAGS := optional + #LOCAL_MODULE_DEBUG_ENABLE := true + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/Build_external_kernelmodule.mk + ########################################################### + else ifeq ($(TARGET_BOARD_PLATFORM), trinket) ########################################################### diff --git a/config/gki_bengaltouch.conf b/config/gki_bengaltouch.conf index 9b5b5f85c6..0bdf70a279 100644 --- a/config/gki_bengaltouch.conf +++ b/config/gki_bengaltouch.conf @@ -3,3 +3,5 @@ export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM=y export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C=y export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE=y export CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH=y +export CONFIG_TOUCH_FOCALTECH=y +export CONFIG_TOUCHSCREEN_FTS_DIRECTORY="focaltech_touch" diff --git a/config/gki_bengaltouchconf.h b/config/gki_bengaltouchconf.h index a750fc1ad0..e0d703d824 100644 --- a/config/gki_bengaltouchconf.h +++ b/config/gki_bengaltouchconf.h @@ -7,3 +7,5 @@ #define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_I2C 1 #define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_CORE 1 #define CONFIG_TOUCHSCREEN_SYNAPTICS_TCM_TOUCH 1 +#define CONFIG_TOUCH_FOCALTECH 1 +#define CONFIG_TOUCHSCREEN_FTS_DIRECTORY "focaltech_touch" diff --git a/touch_driver_board.mk b/touch_driver_board.mk index 3d6464ba52..abbee8e91b 100644 --- a/touch_driver_board.mk +++ b/touch_driver_board.mk @@ -33,7 +33,8 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/goodix_ts.ko else ifeq ($(TARGET_BOARD_PLATFORM), bengal) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ - $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko + $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/focaltech_fts.ko else ifeq ($(TARGET_BOARD_PLATFORM), trinket) BOARD_VENDOR_KERNEL_MODULES += $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko else ifeq ($(TARGET_BOARD_PLATFORM), pitti) diff --git a/touch_driver_product.mk b/touch_driver_product.mk index e3ded60680..54f47cae76 100644 --- a/touch_driver_product.mk +++ b/touch_driver_product.mk @@ -31,7 +31,8 @@ ifeq ($(TOUCH_DLKM_ENABLE), true) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/goodix_ts.ko else ifeq ($(TARGET_BOARD_PLATFORM), bengal) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko \ - $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko + $(KERNEL_MODULES_OUT)/nt36xxx-i2c.ko \ + $(KERNEL_MODULES_OUT)/focaltech_fts.ko else ifeq ($(TARGET_BOARD_PLATFORM), trinket) PRODUCT_PACKAGES += $(KERNEL_MODULES_OUT)/synaptics_tcm_ts.ko else ifeq ($(TARGET_BOARD_PLATFORM), pitti) From 8e83dc263907747a275a3531dda5233ef184732f Mon Sep 17 00:00:00 2001 From: Akshay Gola Date: Fri, 17 May 2024 11:20:58 +0530 Subject: [PATCH 166/170] input: touchscreen: raydium: Disable IRQ on Deep sleep entry Signed-off-by: Akshay Gola Change-Id: I4fa5624e393186b61d7ba6a8ed65895c5b2b70f1 --- raydium/raydium_driver.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 4d4efb514b..3e54ad432b 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -1403,15 +1403,18 @@ static void raydium_ts_do_suspend(void) input_sync(g_raydium_ts->input_dev); #ifdef GESTURE_EN - if (device_may_wakeup(&g_raydium_ts->client->dev)) { - LOGD(LOG_INFO, "[touch]Device may wakeup\n"); - if (!enable_irq_wake(g_raydium_ts->irq)) - g_raydium_ts->irq_wake = true; + if (pm_suspend_via_firmware() == false) + { + if (device_may_wakeup(&g_raydium_ts->client->dev)) { + LOGD(LOG_INFO, "[touch]Device may wakeup\n"); + if (!enable_irq_wake(g_raydium_ts->irq)) + g_raydium_ts->irq_wake = true; - } else { - LOGD(LOG_INFO, "[touch]Device not wakeup\n"); + } else { + LOGD(LOG_INFO, "[touch]Device not wakeup\n"); + } + raydium_irq_control(ENABLE); } - raydium_irq_control(ENABLE); #endif g_raydium_ts->is_suspend = 1; From bc89ced93c8e40d4d1093124c82c0cf8b57609ed Mon Sep 17 00:00:00 2001 From: Akshay Gola Date: Tue, 21 May 2024 15:24:15 +0530 Subject: [PATCH 167/170] input: touchscreen: Handle null device pointer to avoid failure due to kernel panic Change-Id: Iee9b5875c67869724fbd3fdd11f4fa5b9c3ac1f5 Signed-off-by: Akshay Gola --- glink_interface_ts/glink_interface.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/glink_interface_ts/glink_interface.c b/glink_interface_ts/glink_interface.c index 9281d17639..400aa327e6 100644 --- a/glink_interface_ts/glink_interface.c +++ b/glink_interface_ts/glink_interface.c @@ -49,6 +49,11 @@ static int glink_touch_probe(struct rpmsg_device *touch_rpdev) int ret = 0; void *msg = NULL; + if (!touch_rpdev) { + pr_err("rpmsg_device is null\n"); + return -ENODEV; + } + pr_info("%s Start of glink_touch_probe\n", __func__); touch_pdev = devm_kzalloc(&touch_rpdev->dev, sizeof(*touch_pdev), GFP_KERNEL); if (!touch_pdev) From f857470e3d86d021d7c8a4ae5db8e70815e2f80e Mon Sep 17 00:00:00 2001 From: Zhenbin Tan Date: Thu, 30 May 2024 16:31:15 +0800 Subject: [PATCH 168/170] touch: raydium: Shaking DUT exit traker mode issue Enter display setting, set AON off, TTB on, TTW on or off, back to watch face, wait for auto enter traker mode, shaking DUT wakeup event will cause exit tracker mode. Change-Id: I976d16da298c016a429975f13eeb4ca307b67330 Signed-off-by: Zhenbin Tan --- raydium/raydium_driver.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 4d4efb514b..ff0421e711 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -1219,9 +1219,9 @@ static void raydium_work_handler(struct work_struct *work) #endif LOGD(LOG_DEBUG, "[touch] elseif u8_tp_status:%x\n", u8_tp_status[POS_GES_STATUS]); /*need check small area*/ - /*if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP */ - /*&& g_u8_wakeup_flag == false) { */ - if (u8_tp_status[POS_GES_STATUS] == 0) { + if (u8_tp_status[POS_GES_STATUS] == RAD_WAKE_UP + && g_u8_wakeup_flag == false) { + /*if (u8_tp_status[POS_GES_STATUS] == 0) {*/ input_report_key(g_raydium_ts->input_dev, KEY_WAKEUP, true); usleep_range(9500, 10500); input_sync(g_raydium_ts->input_dev); From d0a0b026b954cebfbe91e4392c68a94333524531 Mon Sep 17 00:00:00 2001 From: Akshay Gola Date: Thu, 6 Jun 2024 15:16:35 +0530 Subject: [PATCH 169/170] input: touchscreen: raydium: Disable IRQ at deep sleep entry to avoid deep sleep abort Add check to differentiate between normal and deep sleep suspend call. Change-Id: I88e23d101197ef9f3f926fb26523f9d651ee11f9 Signed-off-by: Akshay Gola --- raydium/raydium_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/raydium/raydium_driver.c b/raydium/raydium_driver.c index 3e54ad432b..b2618b4dfc 100644 --- a/raydium/raydium_driver.c +++ b/raydium/raydium_driver.c @@ -1380,7 +1380,7 @@ static void raydium_ts_do_suspend(void) if (g_u8_raw_data_type == 0) g_u8_resetflag = false; - if (g_raydium_ts->is_suspend == 1) { + if (g_raydium_ts->is_suspend == 1 && (pm_suspend_via_firmware() == false)) { LOGD(LOG_WARNING, "[touch]Already in suspend state\n"); return; } From dd441e0e97f2a526152a5561cc9b229afcf94287 Mon Sep 17 00:00:00 2001 From: Zhenbin Tan Date: Tue, 30 Jul 2024 12:16:04 +0800 Subject: [PATCH 170/170] touch: raydium: Disable IRQ wake on offload entry Disable IRQ wake, it should not wakeup system during touch-offload. Change-Id: I0d493faec4569a8a006f7ca375bf460bf207b389 Signed-off-by: Zhenbin Tan --- raydium/raydium_sysfs.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/raydium/raydium_sysfs.c b/raydium/raydium_sysfs.c index 73f8dda024..557f9161e5 100644 --- a/raydium/raydium_sysfs.c +++ b/raydium/raydium_sysfs.c @@ -324,6 +324,17 @@ static void raydium_ts_touch_entry(void) if (gpio_is_valid(g_raydium_ts->irq_gpio)) gpio_free(g_raydium_ts->irq_gpio); +#ifdef GESTURE_EN + if (device_may_wakeup(&g_raydium_ts->client->dev)) { + LOGD(LOG_INFO, "[touch]%s Device may wakeup\n", __func__); + if (g_raydium_ts->irq_wake) { + disable_irq_wake(g_raydium_ts->irq); + g_raydium_ts->irq_wake = false; + } + } else + LOGD(LOG_INFO, "[touch]%s Device not wakeup\n", __func__); +#endif + raydium_irq_control(DISABLE); if (!cancel_work_sync(&g_raydium_ts->work))